From 7f09c432bed80cecfba634933ddc06735e64da00 Mon Sep 17 00:00:00 2001 From: Stelian Pop Date: Sat, 13 Jan 2007 23:04:31 +0100 Subject: sony_acpi: SNC device support for Sony Vaios From: Bjorn Helgaas Even though the devices claimed by sony_acpi.c can not be hot-plugged, the driver registration infrastructure allows the .add() and .remove() methods to be called at any time while the driver is registered. So remove __init and __exit from them. From: Matthew Garrett [UBUNTU:acpi/sony] Add FN hotkey support Source URL of Patch: http://www.kernel.org/git/?p=linux/kernel/git/bcollins/ubuntu-dapper.git;a=commitdiff;h=7a9b49cba4919e8506604629db03add8e0b85767 Signed-off-by: Ben Collins Signed-off-by: Andrew Morton Signed-off-by: Mattia Dongili Signed-off-by: Len Brown --- Documentation/acpi/sony_acpi.txt | 87 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 Documentation/acpi/sony_acpi.txt (limited to 'Documentation') diff --git a/Documentation/acpi/sony_acpi.txt b/Documentation/acpi/sony_acpi.txt new file mode 100644 index 000000000000..35a04bea38d7 --- /dev/null +++ b/Documentation/acpi/sony_acpi.txt @@ -0,0 +1,87 @@ +ACPI Sony Notebook Control Driver (SNC) Readme +---------------------------------------------- + Copyright (C) 2004- 2005 Stelian Pop + +This mini-driver drives the ACPI SNC device present in the +ACPI BIOS of the Sony Vaio laptops. + +It gives access to some extra laptop functionalities. In +its current form, this driver is mainly useful for controlling the +screen brightness, but it may do more in the future. + +You should probably start by trying the sonypi driver, and try +sony_acpi only if sonypi doesn't work for you. + +Usage: +------ + +Loading the sony_acpi module will create a /proc/acpi/sony/ +directory populated with a couple of files. + +You then read/write integer values from/to those files by using +standard UNIX tools. + +The files are: + brightness current screen brightness + brightness_default screen brightness which will be set + when the laptop will be rebooted + cdpower power on/off the internal CD drive + +Note that some files may be missing if they are not supported +by your particular laptop model. + +Example usage: + # echo "1" > /proc/acpi/sony/brightness +sets the lowest screen brightness, + # echo "8" > /proc/acpi/sony/brightness +sets the highest screen brightness, + # cat /proc/acpi/sony/brightness +retrieves the current screen brightness. + +Development: +------------ + +If you want to help with the development of this driver (and +you are not afraid of any side effects doing strange things with +your ACPI BIOS could have on your laptop), load the driver and +pass the option 'debug=1'. + +REPEAT: DON'T DO THIS IF YOU DON'T LIKE RISKY BUSINESS. + +In your kernel logs you will find the list of all ACPI methods +the SNC device has on your laptop. You can see the GBRT/SBRT methods +used to get/set the brightness, but there are others. + +I HAVE NO IDEA WHAT THOSE METHODS DO. + +The sony_acpi driver creates, for some of those methods (the most +current ones found on several Vaio models), an entry under +/proc/acpi/sony/, just like the 'brightness' one. You can create +other entries corresponding to your own laptop methods by further +editing the source (see the 'sony_acpi_values' table, and add a new +structure to this table with your get/set method names). + +Your mission, should you accept it, is to try finding out what +those entries are for, by reading/writing random values from/to those +files and find out what is the impact on your laptop. + +Should you find anything interesting, please report it back to me, +I will not disavow all knowledge of your actions :) + +Bugs/Limitations: +----------------- + +* This driver is not based on official documentation from Sony + (because there is none), so there is no guarantee this driver + will work at all, or do the right thing. Although this hasn't + happened to me, this driver could do very bad things to your + laptop, including permanent damage. + +* The sony_acpi and sonypi drivers do not interact at all. In the + future, sonypi could use sony_acpi to do (part of) its business. + +* spicctrl, which is the userspace tool used to communicate with the + sonypi driver (through /dev/sonypi) does not try to use the + sony_acpi driver. In the future, spicctrl could try sonypi first, + and if it isn't present, try sony_acpi instead. + -- cgit v1.2.3 From ab5bd20696485a3f8c2f27058ace1cc1d6b580b3 Mon Sep 17 00:00:00 2001 From: Mattia Dongili Date: Thu, 8 Feb 2007 20:16:41 +0100 Subject: sony-laptop: Update docs Update documentation to be consistent with current implementation (backlight subsys and platform_device). Signed-off-by: Mattia Dongili Signed-off-by: Len Brown --- Documentation/acpi/sony_acpi.txt | 87 -------------------------------- Documentation/sony-laptop.txt | 106 +++++++++++++++++++++++++++++++++++++++ drivers/misc/Kconfig | 14 +++--- 3 files changed, 113 insertions(+), 94 deletions(-) delete mode 100644 Documentation/acpi/sony_acpi.txt create mode 100644 Documentation/sony-laptop.txt (limited to 'Documentation') diff --git a/Documentation/acpi/sony_acpi.txt b/Documentation/acpi/sony_acpi.txt deleted file mode 100644 index 35a04bea38d7..000000000000 --- a/Documentation/acpi/sony_acpi.txt +++ /dev/null @@ -1,87 +0,0 @@ -ACPI Sony Notebook Control Driver (SNC) Readme ----------------------------------------------- - Copyright (C) 2004- 2005 Stelian Pop - -This mini-driver drives the ACPI SNC device present in the -ACPI BIOS of the Sony Vaio laptops. - -It gives access to some extra laptop functionalities. In -its current form, this driver is mainly useful for controlling the -screen brightness, but it may do more in the future. - -You should probably start by trying the sonypi driver, and try -sony_acpi only if sonypi doesn't work for you. - -Usage: ------- - -Loading the sony_acpi module will create a /proc/acpi/sony/ -directory populated with a couple of files. - -You then read/write integer values from/to those files by using -standard UNIX tools. - -The files are: - brightness current screen brightness - brightness_default screen brightness which will be set - when the laptop will be rebooted - cdpower power on/off the internal CD drive - -Note that some files may be missing if they are not supported -by your particular laptop model. - -Example usage: - # echo "1" > /proc/acpi/sony/brightness -sets the lowest screen brightness, - # echo "8" > /proc/acpi/sony/brightness -sets the highest screen brightness, - # cat /proc/acpi/sony/brightness -retrieves the current screen brightness. - -Development: ------------- - -If you want to help with the development of this driver (and -you are not afraid of any side effects doing strange things with -your ACPI BIOS could have on your laptop), load the driver and -pass the option 'debug=1'. - -REPEAT: DON'T DO THIS IF YOU DON'T LIKE RISKY BUSINESS. - -In your kernel logs you will find the list of all ACPI methods -the SNC device has on your laptop. You can see the GBRT/SBRT methods -used to get/set the brightness, but there are others. - -I HAVE NO IDEA WHAT THOSE METHODS DO. - -The sony_acpi driver creates, for some of those methods (the most -current ones found on several Vaio models), an entry under -/proc/acpi/sony/, just like the 'brightness' one. You can create -other entries corresponding to your own laptop methods by further -editing the source (see the 'sony_acpi_values' table, and add a new -structure to this table with your get/set method names). - -Your mission, should you accept it, is to try finding out what -those entries are for, by reading/writing random values from/to those -files and find out what is the impact on your laptop. - -Should you find anything interesting, please report it back to me, -I will not disavow all knowledge of your actions :) - -Bugs/Limitations: ------------------ - -* This driver is not based on official documentation from Sony - (because there is none), so there is no guarantee this driver - will work at all, or do the right thing. Although this hasn't - happened to me, this driver could do very bad things to your - laptop, including permanent damage. - -* The sony_acpi and sonypi drivers do not interact at all. In the - future, sonypi could use sony_acpi to do (part of) its business. - -* spicctrl, which is the userspace tool used to communicate with the - sonypi driver (through /dev/sonypi) does not try to use the - sony_acpi driver. In the future, spicctrl could try sonypi first, - and if it isn't present, try sony_acpi instead. - diff --git a/Documentation/sony-laptop.txt b/Documentation/sony-laptop.txt new file mode 100644 index 000000000000..dfd26df056f4 --- /dev/null +++ b/Documentation/sony-laptop.txt @@ -0,0 +1,106 @@ +Sony Notebook Control Driver (SNC) Readme +----------------------------------------- + Copyright (C) 2004- 2005 Stelian Pop + Copyright (C) 2007 Mattia Dongili + +This mini-driver drives the SNC device present in the ACPI BIOS of +the Sony Vaio laptops. + +It gives access to some extra laptop functionalities. In its current +form, this driver let the user set or query the screen brightness +through the backlight subsystem and remove/apply power to some devices. + +Backlight control: +------------------ +If your laptop model supports it, you will find sysfs files in the +/sys/class/backlight/sony/ +directory. You will be able to query and set the current screen +brightness: + brightness get/set screen brightness (an iteger + between 0 and 7) + actual_brightness reading from this file will query the HW + to get real brightness value + max_brightness the maximum brightness value + + +Platform specific: +------------------ +Loading the sony-laptop module will create a +/sys/devices/platform/sony-laptop/ +directory populated with some files. + +You then read/write integer values from/to those files by using +standard UNIX tools. + +The files are: + brightness_default screen brightness which will be set + when the laptop will be rebooted + cdpower power on/off the internal CD drive + audiopower power on/off the internal sound card + lanpower power on/off the internal ethernet card + (only in debug mode) + +Note that some files may be missing if they are not supported +by your particular laptop model. + +Example usage: + # echo "1" > /sys/devices/platform/sony-laptop/brightness_default +sets the lowest screen brightness for the next and later reboots, + # echo "8" > /sys/devices/platform/sony-laptop/brightness_default +sets the highest screen brightness for the next and later reboots, + # cat /sys/devices/platform/sony-laptop/brightness_default +retrieves the value. + + # echo "0" > /sys/devices/platform/sony-laptop/audiopower +powers off the sound card, + # echo "1" > /sys/devices/platform/sony-laptop/audiopower +powers on the sound card. + +Development: +------------ + +If you want to help with the development of this driver (and +you are not afraid of any side effects doing strange things with +your ACPI BIOS could have on your laptop), load the driver and +pass the option 'debug=1'. + +REPEAT: DON'T DO THIS IF YOU DON'T LIKE RISKY BUSINESS. + +In your kernel logs you will find the list of all ACPI methods +the SNC device has on your laptop. You can see the GCDP/GCDP methods +used to pwer on/off the CD drive, but there are others. + +I HAVE NO IDEA WHAT THOSE METHODS DO. + +The sony-laptop driver creates, for some of those methods (the most +current ones found on several Vaio models), an entry under +/sys/devices/platform/sony-laptop, just like the 'cdpower' one. +You can create other entries corresponding to your own laptop methods by +further editing the source (see the 'sony_acpi_values' table, and add a new +entry to this table with your get/set method names using the +HANDLE_NAMES macro). + +Your mission, should you accept it, is to try finding out what +those entries are for, by reading/writing random values from/to those +files and find out what is the impact on your laptop. + +Should you find anything interesting, please report it back to me, +I will not disavow all knowledge of your actions :) + +Bugs/Limitations: +----------------- + +* This driver is not based on official documentation from Sony + (because there is none), so there is no guarantee this driver + will work at all, or do the right thing. Although this hasn't + happened to me, this driver could do very bad things to your + laptop, including permanent damage. + +* The sony-laptop and sonypi drivers do not interact at all. In the + future, sonypi could use sony-laptop to do (part of) its business. + +* spicctrl, which is the userspace tool used to communicate with the + sonypi driver (through /dev/sonypi) does not try to use the + sony-laptop driver. In the future, spicctrl could try sonypi first, + and if it isn't present, try sony-laptop instead. + diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 009aff82d06b..d7e8969fc319 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -93,14 +93,14 @@ config SONY_LAPTOP depends on X86 && ACPI select BACKLIGHT_CLASS_DEVICE ---help--- - This mini-driver drives the ACPI SNC device present in the - ACPI BIOS of the Sony Vaio laptops. + This mini-driver drives the SNC device present in the ACPI BIOS of + the Sony Vaio laptops. - It gives access to some extra laptop functionalities. In - its current form, the only thing this driver does is letting - the user set or query the screen brightness and remove/apply - power to some devices. + It gives access to some extra laptop functionalities. In its current + form, this driver let the user set or query the screen brightness + through the backlight subsystem and remove/apply power to some + devices. - Read for more information. + Read for more information. endmenu -- cgit v1.2.3 From 0c6022d453ecebdace0ce15434c7108e158149ca Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Tue, 13 Feb 2007 13:02:52 +0100 Subject: [ARM] 4177/1: S3C24XX: Add DMA channel allocation order Allow the CPU code, and any board specific initialisation code to change the allocation order of the DMA channels, or stop a peripheral allocating any DMA at-all. This is due to the scarce mapping of DMA channels on some earlier S3C24XX cpus, where the selection changes depending on the channel in use. Signed-off-by: Ben Dooks Signed-off-by: Russell King --- Documentation/arm/Samsung-S3C24XX/DMA.txt | 46 +++++++++++++++++++++++++++++ arch/arm/plat-s3c24xx/dma.c | 49 +++++++++++++++++++++++++++++-- include/asm-arm/plat-s3c24xx/dma.h | 25 ++++++++++++++++ 3 files changed, 117 insertions(+), 3 deletions(-) create mode 100644 Documentation/arm/Samsung-S3C24XX/DMA.txt (limited to 'Documentation') diff --git a/Documentation/arm/Samsung-S3C24XX/DMA.txt b/Documentation/arm/Samsung-S3C24XX/DMA.txt new file mode 100644 index 000000000000..37f4edcc5d87 --- /dev/null +++ b/Documentation/arm/Samsung-S3C24XX/DMA.txt @@ -0,0 +1,46 @@ + S3C2410 DMA + =========== + +Introduction +------------ + + The kernel provides an interface to manage DMA transfers + using the DMA channels in the cpu, so that the central + duty of managing channel mappings, and programming the + channel generators is in one place. + + +DMA Channel Ordering +-------------------- + + Many of the range do not have connections for the DMA + channels to all sources, which means that some devices + have a restricted number of channels that can be used. + + To allow flexibilty for each cpu type and board, the + dma code can be given an dma ordering structure which + allows the order of channel search to be specified, as + well as allowing the prohibition of certain claims. + + struct s3c24xx_dma_order has a list of channels, and + each channel within has a slot for a list of dma + channel numbers. The slots are searched in order, for + the presence of a dma channel number with DMA_CH_VALID + orred in. + + If the order has the flag DMA_CH_NEVER set, then after + checking the channel list, the system will return no + found channel, thus denying the request. + + A board support file can call s3c24xx_dma_order_set() + to register an complete ordering set. The routine will + copy the data, so the original can be discared with + __initdata. + + +Authour +------- + +Ben Dooks, +Copyright (c) 2007 Ben Dooks, Simtec Electronics +Licensed under the GPL v2 diff --git a/arch/arm/plat-s3c24xx/dma.c b/arch/arm/plat-s3c24xx/dma.c index c784e1f816bb..44e39438b9d8 100644 --- a/arch/arm/plat-s3c24xx/dma.c +++ b/arch/arm/plat-s3c24xx/dma.c @@ -1354,18 +1354,22 @@ static inline int is_channel_valid(unsigned int channel) return (channel & DMA_CH_VALID); } +static struct s3c24xx_dma_order *dma_order; + + /* s3c2410_dma_map_channel() * * turn the virtual channel number into a real, and un-used hardware * channel. * - * currently this code uses first-free channel from the specified harware - * map, not taking into account anything that the board setup code may - * have to say about the likely peripheral set to be in use. + * first, try the dma ordering given to us by either the relevant + * dma code, or the board. Then just find the first usable free + * channel */ struct s3c2410_dma_chan *s3c2410_dma_map_channel(int channel) { + struct s3c24xx_dma_order_ch *ord = NULL; struct s3c24xx_dma_map *ch_map; struct s3c2410_dma_chan *dmach; int ch; @@ -1375,6 +1379,27 @@ struct s3c2410_dma_chan *s3c2410_dma_map_channel(int channel) ch_map = dma_sel.map + channel; + /* first, try the board mapping */ + + if (dma_order) { + ord = &dma_order->channels[channel]; + + for (ch = 0; ch < S3C2410_DMA_CHANNELS; ch++) { + if (!is_channel_valid(ord->list[ch])) + continue; + + if (s3c2410_chans[ord->list[ch]].in_use == 0) { + ch = ord->list[ch] & ~DMA_CH_VALID; + goto found; + } + } + + if (ord->flags & DMA_CH_NEVER) + return NULL; + } + + /* second, search the channel map for first free */ + for (ch = 0; ch < S3C2410_DMA_CHANNELS; ch++) { if (!is_channel_valid(ch_map->channels[ch])) continue; @@ -1390,6 +1415,7 @@ struct s3c2410_dma_chan *s3c2410_dma_map_channel(int channel) /* update our channel mapping */ + found: dmach = &s3c2410_chans[ch]; dma_chan_map[channel] = dmach; @@ -1439,3 +1465,20 @@ int __init s3c24xx_dma_init_map(struct s3c24xx_dma_selection *sel) return 0; } + +int __init s3c24xx_dma_order_set(struct s3c24xx_dma_order *ord) +{ + struct s3c24xx_dma_order *nord = dma_order; + + if (nord == NULL) + nord = kmalloc(sizeof(struct s3c24xx_dma_order), GFP_KERNEL); + + if (nord == NULL) { + printk(KERN_ERR "no memory to store dma channel order\n"); + return -ENOMEM; + } + + dma_order = nord; + memcpy(nord, ord, sizeof(struct s3c24xx_dma_order)); + return 0; +} diff --git a/include/asm-arm/plat-s3c24xx/dma.h b/include/asm-arm/plat-s3c24xx/dma.h index 421b567fa019..15e140c2d4fc 100644 --- a/include/asm-arm/plat-s3c24xx/dma.h +++ b/include/asm-arm/plat-s3c24xx/dma.h @@ -14,6 +14,7 @@ extern struct sysdev_class dma_sysclass; extern struct s3c2410_dma_chan s3c2410_chans[S3C2410_DMA_CHANNELS]; #define DMA_CH_VALID (1<<31) +#define DMA_CH_NEVER (1<<30) struct s3c24xx_dma_addr { unsigned long from; @@ -43,3 +44,27 @@ struct s3c24xx_dma_selection { }; extern int s3c24xx_dma_init_map(struct s3c24xx_dma_selection *sel); + +/* struct s3c24xx_dma_order_ch + * + * channel map for one of the `enum dma_ch` dma channels. the list + * entry contains a set of low-level channel numbers, orred with + * DMA_CH_VALID, which are checked in the order in the array. +*/ + +struct s3c24xx_dma_order_ch { + unsigned int list[S3C2410_DMA_CHANNELS]; /* list of channels */ + unsigned int flags; /* flags */ +}; + +/* struct s3c24xx_dma_order + * + * information provided by either the core or the board to give the + * dma system a hint on how to allocate channels +*/ + +struct s3c24xx_dma_order { + struct s3c24xx_dma_order_ch channels[DMACH_MAX]; +}; + +extern int s3c24xx_dma_order_set(struct s3c24xx_dma_order *map); -- cgit v1.2.3 From 9e2ad159a9ba621c704c68703dcd86a4045a2523 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Tue, 13 Feb 2007 13:20:08 +0100 Subject: [ARM] 4180/1: S3C24XX: Update docs for S3C2412 and S3C2413 The S3C2412 and S3C2413 are supported, so document this as so Signed-off-by: Ben Dooks Signed-off-by: Russell King --- Documentation/arm/Samsung-S3C24XX/Overview.txt | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'Documentation') diff --git a/Documentation/arm/Samsung-S3C24XX/Overview.txt b/Documentation/arm/Samsung-S3C24XX/Overview.txt index 28d014714ab8..f42347162c1c 100644 --- a/Documentation/arm/Samsung-S3C24XX/Overview.txt +++ b/Documentation/arm/Samsung-S3C24XX/Overview.txt @@ -8,13 +8,10 @@ Introduction The Samsung S3C24XX range of ARM9 System-on-Chip CPUs are supported by the 's3c2410' architecture of ARM Linux. Currently the S3C2410, - S3C2440 and S3C2442 devices are supported. + S3C2412, S3C2413, S3C2440 and S3C2442 devices are supported. Support for the S3C2400 series is in progress. - Support for the S3C2412 and S3C2413 CPUs is being merged. - - Configuration ------------- -- cgit v1.2.3 From 961314d37ea441673d12e0a57b2dab2eeac520a5 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Tue, 13 Feb 2007 13:29:46 +0100 Subject: [ARM] 4181/1: S3C24XX: Document new layout Update Documentation/arm/Samsung-S3C24XX/Overview.txt with the new directory layout. Signed-off-by: Ben Dooks Signed-off-by: Russell King --- Documentation/arm/Samsung-S3C24XX/Overview.txt | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'Documentation') diff --git a/Documentation/arm/Samsung-S3C24XX/Overview.txt b/Documentation/arm/Samsung-S3C24XX/Overview.txt index f42347162c1c..c31b76fa66c4 100644 --- a/Documentation/arm/Samsung-S3C24XX/Overview.txt +++ b/Documentation/arm/Samsung-S3C24XX/Overview.txt @@ -23,6 +23,22 @@ Configuration please check the machine specific documentation. +Layout +------ + + The core support files are located in the platform code contained in + arch/arm/plat-s3c24xx with headers in include/asm-arm/plat-s3c24xx. + This directory should be kept to items shared between the platform + code (arch/arm/plat-s3c24xx) and the arch/arm/mach-s3c24* code. + + Each cpu has a directory with the support files for it, and the + machines that carry the device. For example S3C2410 is contained + in arch/arm/mach-s3c2410 and S3C2440 in arch/arm/mach-s3c2440 + + Register, kernel and platform data definitions are held in the + include/asm-arm/arch-s3c2410 directory. + + Machines -------- -- cgit v1.2.3 From f8d0c19a93cea3a26a90f2462295e1e01a4cd250 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Wed, 14 Feb 2007 21:15:02 +0100 Subject: hwmon/it87: Add PWM base frequency control Let the user select the base PWM frequency when using the it87 hardware monitoring driver. Different frequencies can give better control on some fans. Also update the documentation to mention the PWM frequency control files, with misc cleanups to the PWM section. Signed-off-by: Jean Delvare --- Documentation/hwmon/it87 | 10 +++++++ Documentation/hwmon/sysfs-interface | 15 ++++++---- drivers/hwmon/it87.c | 56 +++++++++++++++++++++++++++++++++++-- 3 files changed, 74 insertions(+), 7 deletions(-) (limited to 'Documentation') diff --git a/Documentation/hwmon/it87 b/Documentation/hwmon/it87 index 74a80992d237..c0528d6f9ace 100644 --- a/Documentation/hwmon/it87 +++ b/Documentation/hwmon/it87 @@ -135,6 +135,16 @@ Give 0 for unused sensor. Any other value is invalid. To configure this at startup, consult lm_sensors's /etc/sensors.conf. (2 = thermistor; 3 = thermal diode) + +Fan speed control +----------------- + The fan speed control features are limited to manual PWM mode. Automatic "Smart Guardian" mode control handling is not implemented. However if you want to go for "manual mode" just write 1 to pwmN_enable. + +If you are only able to control the fan speed with very small PWM values, +try lowering the PWM base frequency (pwm1_freq). Depending on the fan, +it may give you a somewhat greater control range. The same frequency is +used to drive all fan outputs, which is why pwm2_freq and pwm3_freq are +read-only. diff --git a/Documentation/hwmon/sysfs-interface b/Documentation/hwmon/sysfs-interface index efef3b962cd3..d73d2e8c7534 100644 --- a/Documentation/hwmon/sysfs-interface +++ b/Documentation/hwmon/sysfs-interface @@ -166,16 +166,21 @@ pwm[1-*] Pulse width modulation fan control. pwm[1-*]_enable Switch PWM on and off. - Not always present even if fan*_pwm is. + Not always present even if pwmN is. 0: turn off 1: turn on in manual mode 2+: turn on in automatic mode - Check individual chip documentation files for automatic mode details. + Check individual chip documentation files for automatic mode + details. RW -pwm[1-*]_mode - 0: DC mode - 1: PWM mode +pwm[1-*]_mode 0: DC mode (direct current) + 1: PWM mode (pulse-width modulation) + RW + +pwm[1-*]_freq Base PWM frequency in Hz. + Only possibly available when pwmN_mode is PWM, but not always + present even then. RW pwm[1-*]_auto_channels_temp diff --git a/drivers/hwmon/it87.c b/drivers/hwmon/it87.c index 1ed8b7e2c35d..bb16668ad2bd 100644 --- a/drivers/hwmon/it87.c +++ b/drivers/hwmon/it87.c @@ -202,6 +202,17 @@ static int DIV_TO_REG(int val) } #define DIV_FROM_REG(val) (1 << (val)) +static const unsigned int pwm_freq[8] = { + 48000000 / 128, + 24000000 / 128, + 12000000 / 128, + 8000000 / 128, + 6000000 / 128, + 3000000 / 128, + 1500000 / 128, + 750000 / 128, +}; + /* For each registered IT87, we need to keep some data in memory. That data is pointed to by it87_list[NR]->data. The structure itself is @@ -232,6 +243,7 @@ struct it87_data { u8 vrm; u32 alarms; /* Register encoding, combined */ u8 fan_main_ctrl; /* Register value */ + u8 fan_ctl; /* Register value */ u8 manual_pwm_ctl[3]; /* manual PWM value set by user */ }; @@ -519,6 +531,14 @@ static ssize_t show_pwm(struct device *dev, struct device_attribute *attr, struct it87_data *data = it87_update_device(dev); return sprintf(buf,"%d\n", data->manual_pwm_ctl[nr]); } +static ssize_t show_pwm_freq(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct it87_data *data = it87_update_device(dev); + int index = (data->fan_ctl >> 4) & 0x07; + + return sprintf(buf, "%u\n", pwm_freq[index]); +} static ssize_t set_fan_min(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { @@ -639,6 +659,28 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *attr, mutex_unlock(&data->update_lock); return count; } +static ssize_t set_pwm_freq(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct it87_data *data = i2c_get_clientdata(client); + unsigned long val = simple_strtoul(buf, NULL, 10); + int i; + + /* Search for the nearest available frequency */ + for (i = 0; i < 7; i++) { + if (val > (pwm_freq[i] + pwm_freq[i+1]) / 2) + break; + } + + mutex_lock(&data->update_lock); + data->fan_ctl = it87_read_value(client, IT87_REG_FAN_CTL) & 0x8f; + data->fan_ctl |= i << 4; + it87_write_value(client, IT87_REG_FAN_CTL, data->fan_ctl); + mutex_unlock(&data->update_lock); + + return count; +} #define show_fan_offset(offset) \ static SENSOR_DEVICE_ATTR(fan##offset##_input, S_IRUGO, \ @@ -656,7 +698,10 @@ show_fan_offset(3); static SENSOR_DEVICE_ATTR(pwm##offset##_enable, S_IRUGO | S_IWUSR, \ show_pwm_enable, set_pwm_enable, offset - 1); \ static SENSOR_DEVICE_ATTR(pwm##offset, S_IRUGO | S_IWUSR, \ - show_pwm, set_pwm, offset - 1); + show_pwm, set_pwm, offset - 1); \ +static DEVICE_ATTR(pwm##offset##_freq, \ + (offset == 1 ? S_IRUGO | S_IWUSR : S_IRUGO), \ + show_pwm_freq, (offset == 1 ? set_pwm_freq : NULL)); show_pwm_offset(1); show_pwm_offset(2); @@ -1021,7 +1066,13 @@ static int it87_detect(struct i2c_adapter *adapter) || (err = device_create_file(&new_client->dev, &sensor_dev_attr_pwm2.dev_attr)) || (err = device_create_file(&new_client->dev, - &sensor_dev_attr_pwm3.dev_attr))) + &sensor_dev_attr_pwm3.dev_attr)) + || (err = device_create_file(&new_client->dev, + &dev_attr_pwm1_freq)) + || (err = device_create_file(&new_client->dev, + &dev_attr_pwm2_freq)) + || (err = device_create_file(&new_client->dev, + &dev_attr_pwm3_freq))) goto ERROR4; } @@ -1316,6 +1367,7 @@ static struct it87_data *it87_update_device(struct device *dev) (it87_read_value(client, IT87_REG_ALARM2) << 8) | (it87_read_value(client, IT87_REG_ALARM3) << 16); data->fan_main_ctrl = it87_read_value(client, IT87_REG_FAN_MAIN_CTRL); + data->fan_ctl = it87_read_value(client, IT87_REG_FAN_CTL); data->sensor = it87_read_value(client, IT87_REG_TEMP_ENABLE); /* The 8705 does not have VID capability */ -- cgit v1.2.3 From 657c93b10fac97467cdf1d0424a209ce2e81991a Mon Sep 17 00:00:00 2001 From: David Hubbard Date: Wed, 14 Feb 2007 21:15:04 +0100 Subject: hwmon/w83627ehf: Add support for the W83627DHG chip Signed-off-by: David Hubbard Signed-off-by: Jean Delvare --- Documentation/hwmon/w83627ehf | 54 +++++++++++++++++++++++++++++++++++++------ drivers/hwmon/w83627ehf.c | 54 ++++++++++++++++++++++++++++++++++--------- 2 files changed, 90 insertions(+), 18 deletions(-) (limited to 'Documentation') diff --git a/Documentation/hwmon/w83627ehf b/Documentation/hwmon/w83627ehf index 8a15a7408753..030fac6cec7a 100644 --- a/Documentation/hwmon/w83627ehf +++ b/Documentation/hwmon/w83627ehf @@ -2,26 +2,29 @@ Kernel driver w83627ehf ======================= Supported chips: - * Winbond W83627EHF/EHG (ISA access ONLY) + * Winbond W83627EHF/EHG/DHG (ISA access ONLY) Prefix: 'w83627ehf' Addresses scanned: ISA address retrieved from Super I/O registers - Datasheet: http://www.winbond-usa.com/products/winbond_products/pdfs/PCIC/W83627EHF_%20W83627EHGb.pdf + Datasheet: + http://www.winbond-usa.com/products/winbond_products/pdfs/PCIC/W83627EHF_%20W83627EHGb.pdf + DHG datasheet confidential. Authors: Jean Delvare Yuan Mu (Winbond) Rudolf Marek + David Hubbard Description ----------- -This driver implements support for the Winbond W83627EHF and W83627EHG -super I/O chips. We will refer to them collectively as Winbond chips. +This driver implements support for the Winbond W83627EHF, W83627EHG, and +W83627DHG super I/O chips. We will refer to them collectively as Winbond chips. The chips implement three temperature sensors, five fan rotation -speed sensors, ten analog voltage sensors, alarms with beep warnings (control -unimplemented), and some automatic fan regulation strategies (plus manual -fan control mode). +speed sensors, ten analog voltage sensors (only nine for the 627DHG), alarms +with beep warnings (control unimplemented), and some automatic fan regulation +strategies (plus manual fan control mode). Temperatures are measured in degrees Celsius and measurement resolution is 1 degC for temp1 and 0.5 degC for temp2 and temp3. An alarm is triggered when @@ -55,6 +58,9 @@ prog -> pwm4 (the programmable setting is not supported by the driver) /sys files ---------- +name - this is a standard hwmon device entry. For the W83627EHF and W83627EHG, + it is set to "w83627ehf" and for the W83627DHG it is set to "w83627dhg" + pwm[1-4] - this file stores PWM duty cycle or DC value (fan speed) in range: 0 (stop) to 255 (full) @@ -83,3 +89,37 @@ pwm[1-4]_stop_time - how many milliseconds [ms] must elapse to switch Note: last two functions are influenced by other control bits, not yet exported by the driver, so a change might not have any effect. + +Implementation Details +---------------------- + +Future driver development should bear in mind that the following registers have +different functions on the 627EHF and the 627DHG. Some registers also have +different power-on default values, but BIOS should already be loading +appropriate defaults. Note that bank selection must be performed as is currently +done in the driver for all register addresses. + +0x49: only on DHG, selects temperature source for AUX fan, CPU fan0 +0x4a: not completely documented for the EHF and the DHG documentation assigns + different behavior to bits 7 and 6, including extending the temperature + input selection to SmartFan I, not just SmartFan III. Testing on the EHF + will reveal whether they are compatible or not. + +0x58: Chip ID: 0xa1=EHF 0xc1=DHG +0x5e: only on DHG, has bits to enable "current mode" temperature detection and + critical temperature protection +0x45b: only on EHF, bit 3, vin4 alarm (EHF supports 10 inputs, only 9 on DHG) +0x552: only on EHF, vin4 +0x558: only on EHF, vin4 high limit +0x559: only on EHF, vin4 low limit +0x6b: only on DHG, SYS fan critical temperature +0x6c: only on DHG, CPU fan0 critical temperature +0x6d: only on DHG, AUX fan critical temperature +0x6e: only on DHG, CPU fan1 critical temperature + +0x50-0x55 and 0x650-0x657 are marked "Test Register" for the EHF, but "Reserved + Register" for the DHG + +The DHG also supports PECI, where the DHG queries Intel CPU temperatures, and +the ICH8 southbridge gets that data via PECI from the DHG, so that the +southbridge drives the fans. And the DHG supports SST, a one-wire serial bus. diff --git a/drivers/hwmon/w83627ehf.c b/drivers/hwmon/w83627ehf.c index 212a1558c63b..da5828f2dfc2 100644 --- a/drivers/hwmon/w83627ehf.c +++ b/drivers/hwmon/w83627ehf.c @@ -32,8 +32,10 @@ Supports the following chips: - Chip #vin #fan #pwm #temp chip_id man_id - w83627ehf 10 5 4 3 0x88,0xa1 0x5ca3 + Chip #vin #fan #pwm #temp chip IDs man ID + w83627ehf 10 5 4 3 0x8850 0x88 0x5ca3 + 0x8860 0xa1 + w83627dhg 9 5 4 3 0xa020 0xc1 0x5ca3 */ #include @@ -55,8 +57,18 @@ static unsigned short address; * Super-I/O constants and functions */ +/* + * The three following globals are initialized in w83627ehf_find(), before + * the i2c-isa device is created. Otherwise, they could be stored in + * w83627ehf_data. This is ugly, but necessary, and when the driver is next + * updated to become a platform driver, the globals will disappear. + */ static int REG; /* The register to read/write */ static int VAL; /* The value to read/write */ +/* The w83627ehf/ehg have 10 voltage inputs, but the w83627dhg has 9. This + * value is also used in w83627ehf_detect() to export a device name in sysfs + * (e.g. w83627ehf or w83627dhg) */ +static int w83627ehf_num_in; #define W83627EHF_LD_HWM 0x0b @@ -65,8 +77,10 @@ static int VAL; /* The value to read/write */ #define SIO_REG_ENABLE 0x30 /* Logical device enable */ #define SIO_REG_ADDR 0x60 /* Logical device address (2 bytes) */ -#define SIO_W83627EHF_ID 0x8840 -#define SIO_ID_MASK 0xFFC0 +#define SIO_W83627EHF_ID 0x8850 +#define SIO_W83627EHG_ID 0x8860 +#define SIO_W83627DHG_ID 0xa020 +#define SIO_ID_MASK 0xFFF0 static inline void superio_outb(int reg, int val) @@ -115,8 +129,12 @@ superio_exit(void) #define W83627EHF_REG_BANK 0x4E #define W83627EHF_REG_CONFIG 0x40 -#define W83627EHF_REG_CHIP_ID 0x49 -#define W83627EHF_REG_MAN_ID 0x4F + +/* Not currently used: + * REG_MAN_ID has the value 0x5ca3 for all supported chips. + * REG_CHIP_ID == 0x88/0xa1/0xc1 depending on chip model. + * REG_MAN_ID is at port 0x4f + * REG_CHIP_ID is at port 0x58 */ static const u16 W83627EHF_REG_FAN[] = { 0x28, 0x29, 0x2a, 0x3f, 0x553 }; static const u16 W83627EHF_REG_FAN_MIN[] = { 0x3b, 0x3c, 0x3d, 0x3e, 0x55c }; @@ -429,7 +447,7 @@ static struct w83627ehf_data *w83627ehf_update_device(struct device *dev) } /* Measured voltages and limits */ - for (i = 0; i < 10; i++) { + for (i = 0; i < w83627ehf_num_in; i++) { data->in[i] = w83627ehf_read_value(client, W83627EHF_REG_IN(i)); data->in_min[i] = w83627ehf_read_value(client, @@ -1121,7 +1139,7 @@ static void w83627ehf_device_remove_files(struct device *dev) device_remove_file(dev, &sda_sf3_arrays[i].dev_attr); for (i = 0; i < ARRAY_SIZE(sda_sf3_arrays_fan4); i++) device_remove_file(dev, &sda_sf3_arrays_fan4[i].dev_attr); - for (i = 0; i < 10; i++) { + for (i = 0; i < w83627ehf_num_in; i++) { device_remove_file(dev, &sda_in_input[i].dev_attr); device_remove_file(dev, &sda_in_alarm[i].dev_attr); device_remove_file(dev, &sda_in_min[i].dev_attr); @@ -1196,7 +1214,11 @@ static int w83627ehf_detect(struct i2c_adapter *adapter) client->flags = 0; dev = &client->dev; - strlcpy(client->name, "w83627ehf", I2C_NAME_SIZE); + if (w83627ehf_num_in == 9) + strlcpy(client->name, "w83627dhg", I2C_NAME_SIZE); + else /* just say ehf. 627EHG is 627EHF in lead-free packaging. */ + strlcpy(client->name, "w83627ehf", I2C_NAME_SIZE); + data->valid = 0; mutex_init(&data->update_lock); @@ -1246,7 +1268,7 @@ static int w83627ehf_detect(struct i2c_adapter *adapter) goto exit_remove; } - for (i = 0; i < 10; i++) + for (i = 0; i < w83627ehf_num_in; i++) if ((err = device_create_file(dev, &sda_in_input[i].dev_attr)) || (err = device_create_file(dev, &sda_in_alarm[i].dev_attr)) @@ -1340,7 +1362,17 @@ static int __init w83627ehf_find(int sioaddr, unsigned short *addr) val = (superio_inb(SIO_REG_DEVID) << 8) | superio_inb(SIO_REG_DEVID + 1); - if ((val & SIO_ID_MASK) != SIO_W83627EHF_ID) { + switch (val & SIO_ID_MASK) { + case SIO_W83627DHG_ID: + w83627ehf_num_in = 9; + break; + case SIO_W83627EHF_ID: + case SIO_W83627EHG_ID: + w83627ehf_num_in = 10; + break; + default: + printk(KERN_WARNING "w83627ehf: unsupported chip ID: 0x%04x\n", + val); superio_exit(); return -ENODEV; } -- cgit v1.2.3 From 32aed2a5ce31dc8f42811a0e74020f230241165a Mon Sep 17 00:00:00 2001 From: Timur Tabi Date: Wed, 14 Feb 2007 15:29:07 -0600 Subject: [POWERPC] Delete boot-cpu property from all DTS files The 'linux,boot-cpu' property is obsolete, so remove it from all of the DTS files and from booting-without-of.txt. The boot CPU is actually defined in the device tree header, and U-Boot sets that field. The device tree compiler also complains if the property exists. Signed-off-by: Timur Tabi Signed-off-by: Stuart Yoder Acked-by: David Gibson Signed-off-by: Paul Mackerras --- Documentation/powerpc/booting-without-of.txt | 5 ++--- arch/powerpc/boot/dts/kuroboxHD.dts | 1 - arch/powerpc/boot/dts/kuroboxHG.dts | 1 - arch/powerpc/boot/dts/mpc7448hpc2.dts | 1 - arch/powerpc/boot/dts/mpc8272ads.dts | 1 - arch/powerpc/boot/dts/mpc8323emds.dts | 1 - arch/powerpc/boot/dts/mpc8360emds.dts | 1 - arch/powerpc/boot/dts/mpc8560ads.dts | 1 - arch/powerpc/boot/dts/mpc8641_hpcn.dts | 1 - arch/powerpc/boot/dts/mpc866ads.dts | 1 - arch/powerpc/boot/dts/mpc885ads.dts | 1 - 11 files changed, 2 insertions(+), 13 deletions(-) (limited to 'Documentation') diff --git a/Documentation/powerpc/booting-without-of.txt b/Documentation/powerpc/booting-without-of.txt index 3b514672b80e..1e1abaf83712 100644 --- a/Documentation/powerpc/booting-without-of.txt +++ b/Documentation/powerpc/booting-without-of.txt @@ -497,7 +497,7 @@ looks like in practice. | |- device_type = "cpu" | |- reg = <0> | |- clock-frequency = <5f5e1000> - | |- linux,boot-cpu + | |- 64-bit | |- linux,phandle = <2> | o memory@0 @@ -519,7 +519,7 @@ physical memory layout. It also includes misc information passed through /chosen, like in this example, the platform type (mandatory) and the kernel command line arguments (optional). -The /cpus/PowerPC,970@0/linux,boot-cpu property is an example of a +The /cpus/PowerPC,970@0/64-bit property is an example of a property without a value. All other properties have a value. The significance of the #address-cells and #size-cells properties will be explained in chapter IV which defines precisely the required nodes and @@ -778,7 +778,6 @@ address which can extend beyond that limit. bytes - d-cache-size : one cell, size of L1 data cache in bytes - i-cache-size : one cell, size of L1 instruction cache in bytes - - linux, boot-cpu : Should be defined if this cpu is the boot cpu. Recommended properties: diff --git a/arch/powerpc/boot/dts/kuroboxHD.dts b/arch/powerpc/boot/dts/kuroboxHD.dts index 096e94ac415f..b89791802e86 100644 --- a/arch/powerpc/boot/dts/kuroboxHD.dts +++ b/arch/powerpc/boot/dts/kuroboxHD.dts @@ -35,7 +35,6 @@ build with: "dtc -f -I dts -O dtb -o kuroboxHD.dtb -V 16 kuroboxHD.dts" PowerPC,603e { /* Really 8241 */ linux,phandle = <2100>; - linux,boot-cpu; device_type = "cpu"; reg = <0>; clock-frequency = ; /* Fixed by bootwrapper */ diff --git a/arch/powerpc/boot/dts/kuroboxHG.dts b/arch/powerpc/boot/dts/kuroboxHG.dts index d06b0b018899..753102752d8b 100644 --- a/arch/powerpc/boot/dts/kuroboxHG.dts +++ b/arch/powerpc/boot/dts/kuroboxHG.dts @@ -35,7 +35,6 @@ build with: "dtc -f -I dts -O dtb -o kuroboxHG.dtb -V 16 kuroboxHG.dts" PowerPC,603e { /* Really 8241 */ linux,phandle = <2100>; - linux,boot-cpu; device_type = "cpu"; reg = <0>; clock-frequency = ; /* Fixed by bootwrapper */ diff --git a/arch/powerpc/boot/dts/mpc7448hpc2.dts b/arch/powerpc/boot/dts/mpc7448hpc2.dts index c4d9562cbaad..41d0720c5900 100644 --- a/arch/powerpc/boot/dts/mpc7448hpc2.dts +++ b/arch/powerpc/boot/dts/mpc7448hpc2.dts @@ -36,7 +36,6 @@ bus-frequency = <0>; // From U-Boot 32-bit; linux,phandle = <201>; - linux,boot-cpu; }; }; diff --git a/arch/powerpc/boot/dts/mpc8272ads.dts b/arch/powerpc/boot/dts/mpc8272ads.dts index 26b44f7513dc..260b2e447779 100644 --- a/arch/powerpc/boot/dts/mpc8272ads.dts +++ b/arch/powerpc/boot/dts/mpc8272ads.dts @@ -34,7 +34,6 @@ clock-frequency = <0>; 32-bit; linux,phandle = <201>; - linux,boot-cpu; }; }; diff --git a/arch/powerpc/boot/dts/mpc8323emds.dts b/arch/powerpc/boot/dts/mpc8323emds.dts index fa7ef24d205b..57a3665f82ed 100644 --- a/arch/powerpc/boot/dts/mpc8323emds.dts +++ b/arch/powerpc/boot/dts/mpc8323emds.dts @@ -34,7 +34,6 @@ clock-frequency = <0>; 32-bit; linux,phandle = <201>; - linux,boot-cpu; }; }; diff --git a/arch/powerpc/boot/dts/mpc8360emds.dts b/arch/powerpc/boot/dts/mpc8360emds.dts index 9022192155b9..303bd668deb7 100644 --- a/arch/powerpc/boot/dts/mpc8360emds.dts +++ b/arch/powerpc/boot/dts/mpc8360emds.dts @@ -39,7 +39,6 @@ clock-frequency = <1F78A400>; 32-bit; linux,phandle = <201>; - linux,boot-cpu; }; }; diff --git a/arch/powerpc/boot/dts/mpc8560ads.dts b/arch/powerpc/boot/dts/mpc8560ads.dts index 119bd5d3a834..c74d6ebc5c8a 100644 --- a/arch/powerpc/boot/dts/mpc8560ads.dts +++ b/arch/powerpc/boot/dts/mpc8560ads.dts @@ -35,7 +35,6 @@ clock-frequency = <312c8040>; 32-bit; linux,phandle = <201>; - linux,boot-cpu; }; }; diff --git a/arch/powerpc/boot/dts/mpc8641_hpcn.dts b/arch/powerpc/boot/dts/mpc8641_hpcn.dts index f0c7731743ea..8c75e4e1258f 100644 --- a/arch/powerpc/boot/dts/mpc8641_hpcn.dts +++ b/arch/powerpc/boot/dts/mpc8641_hpcn.dts @@ -32,7 +32,6 @@ bus-frequency = <0>; // From uboot clock-frequency = <0>; // From uboot 32-bit; - linux,boot-cpu; }; PowerPC,8641@1 { device_type = "cpu"; diff --git a/arch/powerpc/boot/dts/mpc866ads.dts b/arch/powerpc/boot/dts/mpc866ads.dts index 5d4005239b83..2b56b5df451a 100644 --- a/arch/powerpc/boot/dts/mpc866ads.dts +++ b/arch/powerpc/boot/dts/mpc866ads.dts @@ -37,7 +37,6 @@ interrupts = ; // decrementer interrupt interrupt-parent = ; linux,phandle = <201>; - linux,boot-cpu; }; }; diff --git a/arch/powerpc/boot/dts/mpc885ads.dts b/arch/powerpc/boot/dts/mpc885ads.dts index cf1a19f962c5..faecd08c54da 100644 --- a/arch/powerpc/boot/dts/mpc885ads.dts +++ b/arch/powerpc/boot/dts/mpc885ads.dts @@ -37,7 +37,6 @@ interrupts = ; // decrementer interrupt interrupt-parent = ; linux,phandle = <201>; - linux,boot-cpu; }; }; -- cgit v1.2.3 From 11ef697b37e3c85ce1ac21f7711babf1f5b12784 Mon Sep 17 00:00:00 2001 From: Kristen Carlson Accardi Date: Thu, 28 Sep 2006 11:29:01 -0700 Subject: [PATCH] libata: ACPI and _GTF support _GTF is an acpi method that is used to reinitialize the drive. It returns a task file containing ata commands that are sent back to the drive to restore it to boot up defaults. Signed-off-by: Kristen Carlson Accardi Signed-off-by: Jeff Garzik (cherry picked from 9c69cab24b51a89664f4c0dfaf8a436d32117624 commit) --- Documentation/kernel-parameters.txt | 5 + drivers/ata/Kconfig | 13 + drivers/ata/Makefile | 2 +- drivers/ata/libata-acpi.c | 602 ++++++++++++++++++++++++++++++++++++ drivers/ata/libata-core.c | 7 + drivers/ata/libata.h | 10 + include/linux/libata.h | 5 + 7 files changed, 643 insertions(+), 1 deletion(-) create mode 100644 drivers/ata/libata-acpi.c (limited to 'Documentation') diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index abd575cfc759..5bc8970b8349 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -48,6 +48,7 @@ parameter is applicable: ISAPNP ISA PnP code is enabled. ISDN Appropriate ISDN support is enabled. JOY Appropriate joystick support is enabled. + LIBATA Libata driver is enabled LP Printer support is enabled. LOOP Loopback device support is enabled. M68k M68k architecture is enabled. @@ -1038,6 +1039,10 @@ and is between 256 and 4096 characters. It is defined in the file emulation library even if a 387 maths coprocessor is present. + noacpi [LIBATA] Disables use of ACPI in libata suspend/resume + when set. + Format: + noaliencache [MM, NUMA] Disables the allcoation of alien caches in the slab allocator. Saves per-node memory, but will impact performance on real NUMA hardware. diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig index 3747457fee7a..4af0a4bb5780 100644 --- a/drivers/ata/Kconfig +++ b/drivers/ata/Kconfig @@ -161,6 +161,19 @@ config SATA_INTEL_COMBINED depends on IDE=y && !BLK_DEV_IDE_SATA && (SATA_AHCI || ATA_PIIX) default y +config SATA_ACPI + bool + depends on ACPI && PCI + default y + help + This option adds support for SATA-related ACPI objects. + These ACPI objects add the ability to retrieve taskfiles + from the ACPI BIOS and write them to the disk controller. + These objects may be related to performance, security, + power management, or other areas. + You can disable this at kernel boot time by using the + option libata.noacpi=1 + config PATA_ALI tristate "ALi PATA support (Experimental)" depends on PCI && EXPERIMENTAL diff --git a/drivers/ata/Makefile b/drivers/ata/Makefile index cd096f0c78a1..74298afbbaa7 100644 --- a/drivers/ata/Makefile +++ b/drivers/ata/Makefile @@ -66,4 +66,4 @@ obj-$(CONFIG_ATA_GENERIC) += ata_generic.o obj-$(CONFIG_PATA_LEGACY) += pata_legacy.o libata-objs := libata-core.o libata-scsi.o libata-sff.o libata-eh.o - +libata-$(CONFIG_SATA_ACPI) += libata-acpi.o diff --git a/drivers/ata/libata-acpi.c b/drivers/ata/libata-acpi.c new file mode 100644 index 000000000000..7b740fe1721b --- /dev/null +++ b/drivers/ata/libata-acpi.c @@ -0,0 +1,602 @@ +/* + * libata-acpi.c + * Provides ACPI support for PATA/SATA. + * + * Copyright (C) 2006 Intel Corp. + * Copyright (C) 2006 Randy Dunlap + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "libata.h" + +#include +#include +#include +#include +#include +#include +#include + +#define SATA_ROOT_PORT(x) (((x) >> 16) & 0xffff) +#define SATA_PORT_NUMBER(x) ((x) & 0xffff) /* or NO_PORT_MULT */ +#define NO_PORT_MULT 0xffff +#define SATA_ADR_RSVD 0xffffffff + +#define REGS_PER_GTF 7 +struct taskfile_array { + u8 tfa[REGS_PER_GTF]; /* regs. 0x1f1 - 0x1f7 */ +}; + + +/** + * sata_get_dev_handle - finds acpi_handle and PCI device.function + * @dev: device to locate + * @handle: returned acpi_handle for @dev + * @pcidevfn: return PCI device.func for @dev + * + * This function is somewhat SATA-specific. Or at least the + * PATA & SATA versions of this function are different, + * so it's not entirely generic code. + * + * Returns 0 on success, <0 on error. + */ +static int sata_get_dev_handle(struct device *dev, acpi_handle *handle, + acpi_integer *pcidevfn) +{ + struct pci_dev *pci_dev; + acpi_integer addr; + + pci_dev = to_pci_dev(dev); /* NOTE: PCI-specific */ + /* Please refer to the ACPI spec for the syntax of _ADR. */ + addr = (PCI_SLOT(pci_dev->devfn) << 16) | PCI_FUNC(pci_dev->devfn); + *pcidevfn = addr; + *handle = acpi_get_child(DEVICE_ACPI_HANDLE(dev->parent), addr); + if (!*handle) + return -ENODEV; + return 0; +} + +/** + * pata_get_dev_handle - finds acpi_handle and PCI device.function + * @dev: device to locate + * @handle: returned acpi_handle for @dev + * @pcidevfn: return PCI device.func for @dev + * + * The PATA and SATA versions of this function are different. + * + * Returns 0 on success, <0 on error. + */ +static int pata_get_dev_handle(struct device *dev, acpi_handle *handle, + acpi_integer *pcidevfn) +{ + unsigned int bus, devnum, func; + acpi_integer addr; + acpi_handle dev_handle, parent_handle; + struct acpi_buffer buffer = {.length = ACPI_ALLOCATE_BUFFER, + .pointer = NULL}; + acpi_status status; + struct acpi_device_info *dinfo = NULL; + int ret = -ENODEV; + struct pci_dev *pdev = to_pci_dev(dev); + + bus = pdev->bus->number; + devnum = PCI_SLOT(pdev->devfn); + func = PCI_FUNC(pdev->devfn); + + dev_handle = DEVICE_ACPI_HANDLE(dev); + parent_handle = DEVICE_ACPI_HANDLE(dev->parent); + + status = acpi_get_object_info(parent_handle, &buffer); + if (ACPI_FAILURE(status)) + goto err; + + dinfo = buffer.pointer; + if (dinfo && (dinfo->valid & ACPI_VALID_ADR) && + dinfo->address == bus) { + /* ACPI spec for _ADR for PCI bus: */ + addr = (acpi_integer)(devnum << 16 | func); + *pcidevfn = addr; + *handle = dev_handle; + } else { + goto err; + } + + if (!*handle) + goto err; + ret = 0; +err: + kfree(dinfo); + return ret; +} + +struct walk_info { /* can be trimmed some */ + struct device *dev; + struct acpi_device *adev; + acpi_handle handle; + acpi_integer pcidevfn; + unsigned int drivenum; + acpi_handle obj_handle; + struct ata_port *ataport; + struct ata_device *atadev; + u32 sata_adr; + int status; + char basepath[ACPI_PATHNAME_MAX]; + int basepath_len; +}; + +static acpi_status get_devices(acpi_handle handle, + u32 level, void *context, void **return_value) +{ + acpi_status status; + struct walk_info *winfo = context; + struct acpi_buffer namebuf = {ACPI_ALLOCATE_BUFFER, NULL}; + char *pathname; + struct acpi_buffer buffer; + struct acpi_device_info *dinfo; + + status = acpi_get_name(handle, ACPI_FULL_PATHNAME, &namebuf); + if (status) + goto ret; + pathname = namebuf.pointer; + + buffer.length = ACPI_ALLOCATE_BUFFER; + buffer.pointer = NULL; + status = acpi_get_object_info(handle, &buffer); + if (ACPI_FAILURE(status)) + goto out2; + + dinfo = buffer.pointer; + + /* find full device path name for pcidevfn */ + if (dinfo && (dinfo->valid & ACPI_VALID_ADR) && + dinfo->address == winfo->pcidevfn) { + if (ata_msg_probe(winfo->ataport)) + ata_dev_printk(winfo->atadev, KERN_DEBUG, + ":%s: matches pcidevfn (0x%llx)\n", + pathname, winfo->pcidevfn); + strlcpy(winfo->basepath, pathname, + sizeof(winfo->basepath)); + winfo->basepath_len = strlen(pathname); + goto out; + } + + /* if basepath is not yet known, ignore this object */ + if (!winfo->basepath_len) + goto out; + + /* if this object is in scope of basepath, maybe use it */ + if (strncmp(pathname, winfo->basepath, + winfo->basepath_len) == 0) { + if (!(dinfo->valid & ACPI_VALID_ADR)) + goto out; + if (ata_msg_probe(winfo->ataport)) + ata_dev_printk(winfo->atadev, KERN_DEBUG, + "GOT ONE: (%s) root_port = 0x%llx," + " port_num = 0x%llx\n", pathname, + SATA_ROOT_PORT(dinfo->address), + SATA_PORT_NUMBER(dinfo->address)); + /* heuristics: */ + if (SATA_PORT_NUMBER(dinfo->address) != NO_PORT_MULT) + if (ata_msg_probe(winfo->ataport)) + ata_dev_printk(winfo->atadev, + KERN_DEBUG, "warning: don't" + " know how to handle SATA port" + " multiplier\n"); + if (SATA_ROOT_PORT(dinfo->address) == + winfo->ataport->port_no && + SATA_PORT_NUMBER(dinfo->address) == NO_PORT_MULT) { + if (ata_msg_probe(winfo->ataport)) + ata_dev_printk(winfo->atadev, + KERN_DEBUG, + "THIS ^^^^^ is the requested" + " SATA drive (handle = 0x%p)\n", + handle); + winfo->sata_adr = dinfo->address; + winfo->obj_handle = handle; + } + } +out: + kfree(dinfo); +out2: + kfree(pathname); + +ret: + return status; +} + +/* Get the SATA drive _ADR object. */ +static int get_sata_adr(struct device *dev, acpi_handle handle, + acpi_integer pcidevfn, unsigned int drive, + struct ata_port *ap, + struct ata_device *atadev, u32 *dev_adr) +{ + acpi_status status; + struct walk_info *winfo; + int err = -ENOMEM; + + winfo = kzalloc(sizeof(struct walk_info), GFP_KERNEL); + if (!winfo) + goto out; + + winfo->dev = dev; + winfo->atadev = atadev; + winfo->ataport = ap; + if (acpi_bus_get_device(handle, &winfo->adev) < 0) + if (ata_msg_probe(ap)) + ata_dev_printk(winfo->atadev, KERN_DEBUG, + "acpi_bus_get_device failed\n"); + winfo->handle = handle; + winfo->pcidevfn = pcidevfn; + winfo->drivenum = drive; + + status = acpi_get_devices(NULL, get_devices, winfo, NULL); + if (ACPI_FAILURE(status)) { + if (ata_msg_probe(ap)) + ata_dev_printk(winfo->atadev, KERN_DEBUG, + "%s: acpi_get_devices failed\n", + __FUNCTION__); + err = -ENODEV; + } else { + *dev_adr = winfo->sata_adr; + atadev->obj_handle = winfo->obj_handle; + err = 0; + } + kfree(winfo); +out: + return err; +} + +/** + * do_drive_get_GTF - get the drive bootup default taskfile settings + * @ap: the ata_port for the drive + * @ix: target ata_device (drive) index + * @gtf_length: number of bytes of _GTF data returned at @gtf_address + * @gtf_address: buffer containing _GTF taskfile arrays + * + * This applies to both PATA and SATA drives. + * + * The _GTF method has no input parameters. + * It returns a variable number of register set values (registers + * hex 1F1..1F7, taskfiles). + * The is not known in advance, so have ACPI-CA + * allocate the buffer as needed and return it, then free it later. + * + * The returned @gtf_length and @gtf_address are only valid if the + * function return value is 0. + */ +static int do_drive_get_GTF(struct ata_port *ap, int ix, + unsigned int *gtf_length, unsigned long *gtf_address, + unsigned long *obj_loc) +{ + acpi_status status; + acpi_handle dev_handle = NULL; + acpi_handle chan_handle, drive_handle; + acpi_integer pcidevfn = 0; + u32 dev_adr; + struct acpi_buffer output; + union acpi_object *out_obj; + struct device *dev = ap->host->dev; + struct ata_device *atadev = &ap->device[ix]; + int err = -ENODEV; + + *gtf_length = 0; + *gtf_address = 0UL; + *obj_loc = 0UL; + + if (noacpi) + return 0; + + if (ata_msg_probe(ap)) + ata_dev_printk(atadev, KERN_DEBUG, + "%s: ENTER: ap->id: %d, port#: %d\n", + __FUNCTION__, ap->id, ap->port_no); + + if (!ata_dev_enabled(atadev) || (ap->flags & ATA_FLAG_DISABLED)) { + if (ata_msg_probe(ap)) + ata_dev_printk(atadev, KERN_DEBUG, "%s: ERR: " + "ata_dev_present: %d, PORT_DISABLED: %lu\n", + __FUNCTION__, ata_dev_enabled(atadev), + ap->flags & ATA_FLAG_DISABLED); + goto out; + } + + /* Don't continue if device has no _ADR method. + * _GTF is intended for known motherboard devices. */ + if (!(ap->cbl == ATA_CBL_SATA)) { + err = pata_get_dev_handle(dev, &dev_handle, &pcidevfn); + if (err < 0) { + if (ata_msg_probe(ap)) + ata_dev_printk(atadev, KERN_DEBUG, + "%s: pata_get_dev_handle failed (%d)\n", + __FUNCTION__, err); + goto out; + } + } else { + err = sata_get_dev_handle(dev, &dev_handle, &pcidevfn); + if (err < 0) { + if (ata_msg_probe(ap)) + ata_dev_printk(atadev, KERN_DEBUG, + "%s: sata_get_dev_handle failed (%d\n", + __FUNCTION__, err); + goto out; + } + } + + /* Get this drive's _ADR info. if not already known. */ + if (!atadev->obj_handle) { + if (!(ap->cbl == ATA_CBL_SATA)) { + /* get child objects of dev_handle == channel objects, + * + _their_ children == drive objects */ + /* channel is ap->port_no */ + chan_handle = acpi_get_child(dev_handle, + ap->port_no); + if (ata_msg_probe(ap)) + ata_dev_printk(atadev, KERN_DEBUG, + "%s: chan adr=%d: chan_handle=0x%p\n", + __FUNCTION__, ap->port_no, + chan_handle); + if (!chan_handle) { + err = -ENODEV; + goto out; + } + /* TBD: could also check ACPI object VALID bits */ + drive_handle = acpi_get_child(chan_handle, ix); + if (!drive_handle) { + err = -ENODEV; + goto out; + } + dev_adr = ix; + atadev->obj_handle = drive_handle; + } else { /* for SATA mode */ + dev_adr = SATA_ADR_RSVD; + err = get_sata_adr(dev, dev_handle, pcidevfn, 0, + ap, atadev, &dev_adr); + } + if (err < 0 || dev_adr == SATA_ADR_RSVD || + !atadev->obj_handle) { + if (ata_msg_probe(ap)) + ata_dev_printk(atadev, KERN_DEBUG, + "%s: get_sata/pata_adr failed: " + "err=%d, dev_adr=%u, obj_handle=0x%p\n", + __FUNCTION__, err, dev_adr, + atadev->obj_handle); + goto out; + } + } + + /* Setting up output buffer */ + output.length = ACPI_ALLOCATE_BUFFER; + output.pointer = NULL; /* ACPI-CA sets this; save/free it later */ + + /* _GTF has no input parameters */ + err = -EIO; + status = acpi_evaluate_object(atadev->obj_handle, "_GTF", + NULL, &output); + if (ACPI_FAILURE(status)) { + if (ata_msg_probe(ap)) + ata_dev_printk(atadev, KERN_DEBUG, + "%s: Run _GTF error: status = 0x%x\n", + __FUNCTION__, status); + goto out; + } + + if (!output.length || !output.pointer) { + if (ata_msg_probe(ap)) + ata_dev_printk(atadev, KERN_DEBUG, "%s: Run _GTF: " + "length or ptr is NULL (0x%llx, 0x%p)\n", + __FUNCTION__, + (unsigned long long)output.length, + output.pointer); + kfree(output.pointer); + goto out; + } + + out_obj = output.pointer; + if (out_obj->type != ACPI_TYPE_BUFFER) { + kfree(output.pointer); + if (ata_msg_probe(ap)) + ata_dev_printk(atadev, KERN_DEBUG, "%s: Run _GTF: " + "error: expected object type of " + " ACPI_TYPE_BUFFER, got 0x%x\n", + __FUNCTION__, out_obj->type); + err = -ENOENT; + goto out; + } + + if (!out_obj->buffer.length || !out_obj->buffer.pointer || + out_obj->buffer.length % REGS_PER_GTF) { + if (ata_msg_drv(ap)) + ata_dev_printk(atadev, KERN_ERR, + "%s: unexpected GTF length (%d) or addr (0x%p)\n", + __FUNCTION__, out_obj->buffer.length, + out_obj->buffer.pointer); + err = -ENOENT; + goto out; + } + + *gtf_length = out_obj->buffer.length; + *gtf_address = (unsigned long)out_obj->buffer.pointer; + *obj_loc = (unsigned long)out_obj; + if (ata_msg_probe(ap)) + ata_dev_printk(atadev, KERN_DEBUG, "%s: returning " + "gtf_length=%d, gtf_address=0x%lx, obj_loc=0x%lx\n", + __FUNCTION__, *gtf_length, *gtf_address, *obj_loc); + err = 0; +out: + return err; +} + +/** + * taskfile_load_raw - send taskfile registers to host controller + * @ap: Port to which output is sent + * @gtf: raw ATA taskfile register set (0x1f1 - 0x1f7) + * + * Outputs ATA taskfile to standard ATA host controller using MMIO + * or PIO as indicated by the ATA_FLAG_MMIO flag. + * Writes the control, feature, nsect, lbal, lbam, and lbah registers. + * Optionally (ATA_TFLAG_LBA48) writes hob_feature, hob_nsect, + * hob_lbal, hob_lbam, and hob_lbah. + * + * This function waits for idle (!BUSY and !DRQ) after writing + * registers. If the control register has a new value, this + * function also waits for idle after writing control and before + * writing the remaining registers. + * + * LOCKING: TBD: + * Inherited from caller. + */ +static void taskfile_load_raw(struct ata_port *ap, + struct ata_device *atadev, + const struct taskfile_array *gtf) +{ + if (ata_msg_probe(ap)) + ata_dev_printk(atadev, KERN_DEBUG, "%s: (0x1f1-1f7): hex: " + "%02x %02x %02x %02x %02x %02x %02x\n", + __FUNCTION__, + gtf->tfa[0], gtf->tfa[1], gtf->tfa[2], + gtf->tfa[3], gtf->tfa[4], gtf->tfa[5], gtf->tfa[6]); + + if ((gtf->tfa[0] == 0) && (gtf->tfa[1] == 0) && (gtf->tfa[2] == 0) + && (gtf->tfa[3] == 0) && (gtf->tfa[4] == 0) && (gtf->tfa[5] == 0) + && (gtf->tfa[6] == 0)) + return; + + if (ap->ops->qc_issue) { + struct ata_taskfile tf; + unsigned int err; + + ata_tf_init(atadev, &tf); + + /* convert gtf to tf */ + tf.flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE; /* TBD */ + tf.protocol = atadev->class == ATA_DEV_ATAPI ? + ATA_PROT_ATAPI_NODATA : ATA_PROT_NODATA; + tf.feature = gtf->tfa[0]; /* 0x1f1 */ + tf.nsect = gtf->tfa[1]; /* 0x1f2 */ + tf.lbal = gtf->tfa[2]; /* 0x1f3 */ + tf.lbam = gtf->tfa[3]; /* 0x1f4 */ + tf.lbah = gtf->tfa[4]; /* 0x1f5 */ + tf.device = gtf->tfa[5]; /* 0x1f6 */ + tf.command = gtf->tfa[6]; /* 0x1f7 */ + + err = ata_exec_internal(atadev, &tf, NULL, DMA_NONE, NULL, 0); + if (err && ata_msg_probe(ap)) + ata_dev_printk(atadev, KERN_ERR, + "%s: ata_exec_internal failed: %u\n", + __FUNCTION__, err); + } else + if (ata_msg_warn(ap)) + ata_dev_printk(atadev, KERN_WARNING, + "%s: SATA driver is missing qc_issue function" + " entry points\n", + __FUNCTION__); +} + +/** + * do_drive_set_taskfiles - write the drive taskfile settings from _GTF + * @ap: the ata_port for the drive + * @atadev: target ata_device + * @gtf_length: total number of bytes of _GTF taskfiles + * @gtf_address: location of _GTF taskfile arrays + * + * This applies to both PATA and SATA drives. + * + * Write {gtf_address, length gtf_length} in groups of + * REGS_PER_GTF bytes. + */ +static int do_drive_set_taskfiles(struct ata_port *ap, + struct ata_device *atadev, unsigned int gtf_length, + unsigned long gtf_address) +{ + int err = -ENODEV; + int gtf_count = gtf_length / REGS_PER_GTF; + int ix; + struct taskfile_array *gtf; + + if (ata_msg_probe(ap)) + ata_dev_printk(atadev, KERN_DEBUG, + "%s: ENTER: ap->id: %d, port#: %d\n", + __FUNCTION__, ap->id, ap->port_no); + + if (noacpi || !(ap->cbl == ATA_CBL_SATA)) + return 0; + + if (!ata_dev_enabled(atadev) || (ap->flags & ATA_FLAG_DISABLED)) + goto out; + if (!gtf_count) /* shouldn't be here */ + goto out; + + if (gtf_length % REGS_PER_GTF) { + if (ata_msg_drv(ap)) + ata_dev_printk(atadev, KERN_ERR, + "%s: unexpected GTF length (%d)\n", + __FUNCTION__, gtf_length); + goto out; + } + + for (ix = 0; ix < gtf_count; ix++) { + gtf = (struct taskfile_array *) + (gtf_address + ix * REGS_PER_GTF); + + /* send all TaskFile registers (0x1f1-0x1f7) *in*that*order* */ + taskfile_load_raw(ap, atadev, gtf); + } + + err = 0; +out: + return err; +} + +/** + * ata_acpi_exec_tfs - get then write drive taskfile settings + * @ap: the ata_port for the drive + * + * This applies to both PATA and SATA drives. + */ +int ata_acpi_exec_tfs(struct ata_port *ap) +{ + int ix; + int ret =0; + unsigned int gtf_length; + unsigned long gtf_address; + unsigned long obj_loc; + + if (noacpi) + return 0; + + for (ix = 0; ix < ATA_MAX_DEVICES; ix++) { + if (!ata_dev_enabled(&ap->device[ix])) + continue; + + ret = do_drive_get_GTF(ap, ix, + >f_length, >f_address, &obj_loc); + if (ret < 0) { + if (ata_msg_probe(ap)) + ata_port_printk(ap, KERN_DEBUG, + "%s: get_GTF error (%d)\n", + __FUNCTION__, ret); + break; + } + + ret = do_drive_set_taskfiles(ap, &ap->device[ix], + gtf_length, gtf_address); + kfree((void *)obj_loc); + if (ret < 0) { + if (ata_msg_probe(ap)) + ata_port_printk(ap, KERN_DEBUG, + "%s: set_taskfiles error (%d)\n", + __FUNCTION__, ret); + break; + } + } + + return ret; +} + diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index 2cf8251728d2..785bf50488e9 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -93,6 +93,10 @@ static int ata_probe_timeout = ATA_TMOUT_INTERNAL / HZ; module_param(ata_probe_timeout, int, 0444); MODULE_PARM_DESC(ata_probe_timeout, "Set ATA probing timeout (seconds)"); +int noacpi; +module_param(noacpi, int, 0444); +MODULE_PARM_DESC(noacpi, "Disables the use of ACPI in suspend/resume when set"); + MODULE_AUTHOR("Jeff Garzik"); MODULE_DESCRIPTION("Library module for ATA devices"); MODULE_LICENSE("GPL"); @@ -1778,6 +1782,9 @@ int ata_bus_probe(struct ata_port *ap) /* reset and determine device classes */ ap->ops->phy_reset(ap); + /* retrieve and execute the ATA task file of _GTF */ + ata_acpi_exec_tfs(ap); + for (i = 0; i < ATA_MAX_DEVICES; i++) { dev = &ap->device[i]; diff --git a/drivers/ata/libata.h b/drivers/ata/libata.h index 06ccf230e3c2..ea02903e8503 100644 --- a/drivers/ata/libata.h +++ b/drivers/ata/libata.h @@ -47,6 +47,7 @@ extern struct workqueue_struct *ata_aux_wq; extern int atapi_enabled; extern int atapi_dmadir; extern int libata_fua; +extern int noacpi; extern struct ata_queued_cmd *ata_qc_new_init(struct ata_device *dev); extern int ata_build_rw_tf(struct ata_taskfile *tf, struct ata_device *dev, u64 block, u32 n_block, unsigned int tf_flags, @@ -87,6 +88,15 @@ extern void ata_port_init(struct ata_port *ap, struct ata_host *host, extern struct ata_probe_ent *ata_probe_ent_alloc(struct device *dev, const struct ata_port_info *port); +/* libata-acpi.c */ +#ifdef CONFIG_SATA_ACPI +extern int ata_acpi_exec_tfs(struct ata_port *ap); +#else +static inline int ata_acpi_exec_tfs(struct ata_port *ap) +{ + return 0; +} +#endif /* libata-scsi.c */ extern struct scsi_transport_template ata_scsi_transport_template; diff --git a/include/linux/libata.h b/include/linux/libata.h index b870b20df43c..86762a9f52ba 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -35,6 +35,7 @@ #include #include #include +#include /* * Define if arch has non-standard setup. This is a _PCI_ standard @@ -495,6 +496,10 @@ struct ata_device { /* error history */ struct ata_ering ering; unsigned int horkage; /* List of broken features */ +#ifdef CONFIG_SATA_ACPI + /* ACPI objects info */ + acpi_handle obj_handle; +#endif }; /* Offset into struct ata_device. Fields above it are maintained -- cgit v1.2.3 From 143a42d16a18303d5c8d625730546f8b515b5d54 Mon Sep 17 00:00:00 2001 From: Stuart Yoder Date: Fri, 16 Feb 2007 11:30:29 -0600 Subject: [POWERPC] powerpc: remove references to the obsolete linux,platform property Remove references to the linux,platform property from booting-without-of.txt since it is obsolete. Signed-off-by: Stuart Yoder Signed-off-by: Paul Mackerras --- Documentation/powerpc/booting-without-of.txt | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) (limited to 'Documentation') diff --git a/Documentation/powerpc/booting-without-of.txt b/Documentation/powerpc/booting-without-of.txt index 1e1abaf83712..b41397d6430a 100644 --- a/Documentation/powerpc/booting-without-of.txt +++ b/Documentation/powerpc/booting-without-of.txt @@ -509,7 +509,6 @@ looks like in practice. o chosen |- name = "chosen" |- bootargs = "root=/dev/sda2" - |- linux,platform = <00000600> |- linux,phandle = <4> This tree is almost a minimal tree. It pretty much contains the @@ -733,8 +732,7 @@ address which can extend beyond that limit. that typically get driven by the same platform code in the kernel, you would use a different "model" property but put a value in "compatible". The kernel doesn't directly use that - value (see /chosen/linux,platform for how the kernel chooses a - platform type) but it is generally useful. + value but it is generally useful. The root node is also generally where you add additional properties specific to your board like the serial number if any, that sort of @@ -842,11 +840,6 @@ address which can extend beyond that limit. the prom_init() trampoline when booting with an OF client interface, but that you have to provide yourself when using the flattened format. - Required properties: - - - linux,platform : This is your platform number as assigned by the - architecture maintainers - Recommended properties: - bootargs : This zero-terminated string is passed as the kernel -- cgit v1.2.3 From 26ba05e4c66ad3fafe08412ffcf8c328cc4640b0 Mon Sep 17 00:00:00 2001 From: Grant Grundler Date: Sun, 11 Feb 2007 00:04:04 -0700 Subject: PCI: pci.txt fix __devexit() usage Marin Mitov spotted a brainfart where I had failed to update copied text with *_remove and __devexit(). Marin made a good comment in his email to me: | mydriver_probe() is _always_ executed, while mydriver_remove() is not. | See: include/linux/init.h Which says: /* Functions marked as __devexit may be discarded at kernel link time, depending on config options. Newer versions of binutils detect references from retained sections to discarded sections and flag an error. Pointers to __devexit functions must use __devexit_p(function_name), the wrapper will insert either the function_name or NULL, depending on the config options. */ Signed-off-by: Grant Grundler Signed-off-by: Greg Kroah-Hartman --- Documentation/pci.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Documentation') diff --git a/Documentation/pci.txt b/Documentation/pci.txt index fd5028eca13e..cdf2f3c0ab14 100644 --- a/Documentation/pci.txt +++ b/Documentation/pci.txt @@ -205,8 +205,8 @@ Tips on when/where to use the above attributes: exclusively called by the probe() routine, can be marked __devinit. Ditto for remove() and __devexit. - o If mydriver_probe() is marked with __devinit(), then all address - references to mydriver_probe must use __devexit_p(mydriver_probe) + o If mydriver_remove() is marked with __devexit(), then all address + references to mydriver_remove must use __devexit_p(mydriver_remove) (in the struct pci_driver declaration for example). __devexit_p() will generate the function name _or_ NULL if the function will be discarded. For an example, see drivers/net/tg3.c. -- cgit v1.2.3 From 4516a618a76eae6eb1b37259ad49f39b7b7f33d8 Mon Sep 17 00:00:00 2001 From: Atsushi Nemoto Date: Mon, 5 Feb 2007 16:36:06 -0800 Subject: PCI: Make CARDBUS_MEM_SIZE and CARDBUS_IO_SIZE boot options CARDBUS_MEM_SIZE was increased to 64MB on 2.6.20-rc2, but larger size might result in allocation failure for the reserving itself on some platforms (for example typical 32bit MIPS). Make it (and CARDBUS_IO_SIZE too) customizable by "pci=" option for such platforms. Signed-off-by: Atsushi Nemoto Cc: Daniel Ritz Cc: Ralf Baechle Cc: Ivan Kokshaysky Cc: Dominik Brodowski Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- Documentation/kernel-parameters.txt | 6 ++++++ drivers/pci/pci.c | 10 ++++++++++ drivers/pci/setup-bus.c | 27 ++++++++++----------------- include/linux/pci.h | 3 +++ 4 files changed, 29 insertions(+), 17 deletions(-) (limited to 'Documentation') diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index abd575cfc759..ce1f2c85e20f 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -1275,6 +1275,12 @@ and is between 256 and 4096 characters. It is defined in the file This sorting is done to get a device order compatible with older (<= 2.4) kernels. nobfsort Don't sort PCI devices into breadth-first order. + cbiosize=nn[KMG] The fixed amount of bus space which is + reserved for the CardBus bridge's IO window. + The default value is 256 bytes. + cbmemsize=nn[KMG] The fixed amount of bus space which is + reserved for the CardBus bridge's memory + window. The default value is 64 megabytes. pcmv= [HW,PCMCIA] BadgePAD 4 diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index a7b869e24650..1e74e1ee8bd8 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -21,6 +21,12 @@ unsigned int pci_pm_d3_delay = 10; +#define DEFAULT_CARDBUS_IO_SIZE (256) +#define DEFAULT_CARDBUS_MEM_SIZE (64*1024*1024) +/* pci=cbmemsize=nnM,cbiosize=nn can override this */ +unsigned long pci_cardbus_io_size = DEFAULT_CARDBUS_IO_SIZE; +unsigned long pci_cardbus_mem_size = DEFAULT_CARDBUS_MEM_SIZE; + /** * pci_bus_max_busnr - returns maximum PCI bus number of given bus' children * @bus: pointer to PCI bus structure to search @@ -1333,6 +1339,10 @@ static int __devinit pci_setup(char *str) if (*str && (str = pcibios_setup(str)) && *str) { if (!strcmp(str, "nomsi")) { pci_no_msi(); + } else if (!strncmp(str, "cbiosize=", 9)) { + pci_cardbus_io_size = memparse(str + 9, &str); + } else if (!strncmp(str, "cbmemsize=", 10)) { + pci_cardbus_mem_size = memparse(str + 10, &str); } else { printk(KERN_ERR "PCI: Unknown option `%s'\n", str); diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index 89f3036f0de8..3554f3948814 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -36,13 +36,6 @@ #define ROUND_UP(x, a) (((x) + (a) - 1) & ~((a) - 1)) -/* - * FIXME: IO should be max 256 bytes. However, since we may - * have a P2P bridge below a cardbus bridge, we need 4K. - */ -#define CARDBUS_IO_SIZE (256) -#define CARDBUS_MEM_SIZE (64*1024*1024) - static void __devinit pbus_assign_resources_sorted(struct pci_bus *bus) { @@ -415,12 +408,12 @@ pci_bus_size_cardbus(struct pci_bus *bus) * Reserve some resources for CardBus. We reserve * a fixed amount of bus space for CardBus bridges. */ - b_res[0].start = CARDBUS_IO_SIZE; - b_res[0].end = b_res[0].start + CARDBUS_IO_SIZE - 1; + b_res[0].start = pci_cardbus_io_size; + b_res[0].end = b_res[0].start + pci_cardbus_io_size - 1; b_res[0].flags |= IORESOURCE_IO; - b_res[1].start = CARDBUS_IO_SIZE; - b_res[1].end = b_res[1].start + CARDBUS_IO_SIZE - 1; + b_res[1].start = pci_cardbus_io_size; + b_res[1].end = b_res[1].start + pci_cardbus_io_size - 1; b_res[1].flags |= IORESOURCE_IO; /* @@ -440,16 +433,16 @@ pci_bus_size_cardbus(struct pci_bus *bus) * twice the size. */ if (ctrl & PCI_CB_BRIDGE_CTL_PREFETCH_MEM0) { - b_res[2].start = CARDBUS_MEM_SIZE; - b_res[2].end = b_res[2].start + CARDBUS_MEM_SIZE - 1; + b_res[2].start = pci_cardbus_mem_size; + b_res[2].end = b_res[2].start + pci_cardbus_mem_size - 1; b_res[2].flags |= IORESOURCE_MEM | IORESOURCE_PREFETCH; - b_res[3].start = CARDBUS_MEM_SIZE; - b_res[3].end = b_res[3].start + CARDBUS_MEM_SIZE - 1; + b_res[3].start = pci_cardbus_mem_size; + b_res[3].end = b_res[3].start + pci_cardbus_mem_size - 1; b_res[3].flags |= IORESOURCE_MEM; } else { - b_res[3].start = CARDBUS_MEM_SIZE * 2; - b_res[3].end = b_res[3].start + CARDBUS_MEM_SIZE * 2 - 1; + b_res[3].start = pci_cardbus_mem_size * 2; + b_res[3].end = b_res[3].start + pci_cardbus_mem_size * 2 - 1; b_res[3].flags |= IORESOURCE_MEM; } } diff --git a/include/linux/pci.h b/include/linux/pci.h index 98c8765a488e..2c4b6842dfb9 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -854,5 +854,8 @@ extern int pci_pci_problems; #define PCIPCI_ALIMAGIK 32 /* Need low latency setting */ #define PCIAGP_FAIL 64 /* No PCI to AGP DMA */ +extern unsigned long pci_cardbus_io_size; +extern unsigned long pci_cardbus_mem_size; + #endif /* __KERNEL__ */ #endif /* LINUX_PCI_H */ -- cgit v1.2.3 From 5ee6edbcde4d3b14e4e03d4b331df1099a34aa8d Mon Sep 17 00:00:00 2001 From: Len Brown Date: Sat, 10 Feb 2007 01:18:25 -0500 Subject: ACPI: hotkey: remove driver, per feature-removal-schedule.txt Signed-off-by: Len Brown --- Documentation/acpi-hotkey.txt | 38 - Documentation/feature-removal-schedule.txt | 23 - drivers/acpi/Kconfig | 9 - drivers/acpi/Makefile | 1 - drivers/acpi/asus_acpi.c | 4 - drivers/acpi/hotkey.c | 1042 ---------------------------- drivers/acpi/ibm_acpi.c | 5 - drivers/acpi/osl.c | 11 - drivers/acpi/toshiba_acpi.c | 4 - drivers/misc/asus-laptop.c | 5 - include/acpi/acpi_drivers.h | 6 - 11 files changed, 1148 deletions(-) delete mode 100644 Documentation/acpi-hotkey.txt delete mode 100644 drivers/acpi/hotkey.c (limited to 'Documentation') diff --git a/Documentation/acpi-hotkey.txt b/Documentation/acpi-hotkey.txt deleted file mode 100644 index 38040fa37649..000000000000 --- a/Documentation/acpi-hotkey.txt +++ /dev/null @@ -1,38 +0,0 @@ -driver/acpi/hotkey.c implement: -1. /proc/acpi/hotkey/event_config -(event based hotkey or event config interface): -a. add a event based hotkey(event) : -echo "0:bus::action:method:num:num" > event_config - -b. delete a event based hotkey(event): -echo "1:::::num:num" > event_config - -c. modify a event based hotkey(event): -echo "2:bus::action:method:num:num" > event_config - -2. /proc/acpi/hotkey/poll_config -(polling based hotkey or event config interface): -a.add a polling based hotkey(event) : -echo "0:bus:method:action:method:num" > poll_config -this adding command will create a proc file -/proc/acpi/hotkey/method, which is used to get -result of polling. - -b.delete a polling based hotkey(event): -echo "1:::::num" > event_config - -c.modify a polling based hotkey(event): -echo "2:bus:method:action:method:num" > poll_config - -3./proc/acpi/hotkey/action -(interface to call aml method associated with a -specific hotkey(event)) -echo "event_num:event_type:event_argument" > - /proc/acpi/hotkey/action. -The result of the execution of this aml method is -attached to /proc/acpi/hotkey/poll_method, which is dynamically -created. Please use command "cat /proc/acpi/hotkey/polling_method" -to retrieve it. - -Note: Use cmdline "acpi_generic_hotkey" to over-ride -platform-specific with generic driver. diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt index fa844fd7bded..7dec8e0193c0 100644 --- a/Documentation/feature-removal-schedule.txt +++ b/Documentation/feature-removal-schedule.txt @@ -246,29 +246,6 @@ Who: Venkatesh Pallipadi --------------------------- -<<<<<<< test:Documentation/feature-removal-schedule.txt -What: ACPI hotkey driver (CONFIG_ACPI_HOTKEY) -When: 2.6.21 -Why: hotkey.c was an attempt to consolidate multiple drivers that use - ACPI to implement hotkeys. However, hotkeys are not documented - in the ACPI specification, so the drivers used undocumented - vendor-specific hooks and turned out to be more different than - the same. - - Further, the keys and the features supplied by each platform - are different, so there will always be a need for - platform-specific drivers. - - So the new plan is to delete hotkey.c and instead, work on the - platform specific drivers to try to make them look the same - to the user when they supply the same features. - - hotkey.c has always depended on CONFIG_EXPERIMENTAL - -Who: Len Brown - ---------------------------- - What: /sys/firmware/acpi/namespace When: 2.6.21 Why: The ACPI namespace is effectively the symbol list for diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index 20eacc2c9e0e..4f5bfb1e629a 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -132,15 +132,6 @@ config ACPI_VIDEO Note that this is an ref. implementation only. It may or may not work for your integrated video device. -config ACPI_HOTKEY - tristate "Generic Hotkey (EXPERIMENTAL)" - depends on EXPERIMENTAL - depends on X86 - default n - help - Experimental consolidated hotkey driver. - If you are unsure, say N. - config ACPI_FAN tristate "Fan" default y diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index 856c32bccacb..5956e9f64a8b 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile @@ -46,7 +46,6 @@ obj-$(CONFIG_ACPI_FAN) += fan.o obj-$(CONFIG_ACPI_DOCK) += dock.o obj-$(CONFIG_ACPI_BAY) += bay.o obj-$(CONFIG_ACPI_VIDEO) += video.o -obj-$(CONFIG_ACPI_HOTKEY) += hotkey.o obj-y += pci_root.o pci_link.o pci_irq.o pci_bind.o obj-$(CONFIG_ACPI_POWER) += power.o obj-$(CONFIG_ACPI_PROCESSOR) += processor.o diff --git a/drivers/acpi/asus_acpi.c b/drivers/acpi/asus_acpi.c index 31ad70a6e22e..ab8c5cb8384d 100644 --- a/drivers/acpi/asus_acpi.c +++ b/drivers/acpi/asus_acpi.c @@ -1365,10 +1365,6 @@ static int __init asus_acpi_init(void) if (acpi_disabled) return -ENODEV; - if (!acpi_specific_hotkey_enabled) { - printk(KERN_ERR "Using generic hotkey driver\n"); - return -ENODEV; - } asus_proc_dir = proc_mkdir(PROC_ASUS, acpi_root_dir); if (!asus_proc_dir) { printk(KERN_ERR "Asus ACPI: Unable to create /proc entry\n"); diff --git a/drivers/acpi/hotkey.c b/drivers/acpi/hotkey.c deleted file mode 100644 index 8edfb92f7ede..000000000000 --- a/drivers/acpi/hotkey.c +++ /dev/null @@ -1,1042 +0,0 @@ -/* - * hotkey.c - ACPI Hotkey Driver ($Revision: 0.2 $) - * - * Copyright (C) 2004 Luming Yu - * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * - * 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. - * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define HOTKEY_ACPI_VERSION "0.1" - -#define HOTKEY_PROC "hotkey" -#define HOTKEY_EV_CONFIG "event_config" -#define HOTKEY_PL_CONFIG "poll_config" -#define HOTKEY_ACTION "action" -#define HOTKEY_INFO "info" - -#define ACPI_HOTK_NAME "Generic Hotkey Driver" -#define ACPI_HOTK_CLASS "Hotkey" -#define ACPI_HOTK_DEVICE_NAME "Hotkey" -#define ACPI_HOTK_HID "Unknown?" -#define ACPI_HOTKEY_COMPONENT 0x20000000 - -#define ACPI_HOTKEY_EVENT 0x1 -#define ACPI_HOTKEY_POLLING 0x2 -#define ACPI_UNDEFINED_EVENT 0xf - -#define RESULT_STR_LEN 80 - -#define ACTION_METHOD 0 -#define POLL_METHOD 1 - -#define IS_EVENT(e) ((e) <= 10000 && (e) >0) -#define IS_POLL(e) ((e) > 10000) -#define IS_OTHERS(e) ((e)<=0 || (e)>=20000) -#define _COMPONENT ACPI_HOTKEY_COMPONENT -ACPI_MODULE_NAME("acpi_hotkey") - - MODULE_AUTHOR("luming.yu@intel.com"); -MODULE_DESCRIPTION(ACPI_HOTK_NAME); -MODULE_LICENSE("GPL"); - -/* standardized internal hotkey number/event */ -enum { - /* Video Extension event */ - HK_EVENT_CYCLE_OUTPUT_DEVICE = 0x80, - HK_EVENT_OUTPUT_DEVICE_STATUS_CHANGE, - HK_EVENT_CYCLE_DISPLAY_OUTPUT, - HK_EVENT_NEXT_DISPLAY_OUTPUT, - HK_EVENT_PREVIOUS_DISPLAY_OUTPUT, - HK_EVENT_CYCLE_BRIGHTNESS, - HK_EVENT_INCREASE_BRIGHTNESS, - HK_EVENT_DECREASE_BRIGHTNESS, - HK_EVENT_ZERO_BRIGHTNESS, - HK_EVENT_DISPLAY_DEVICE_OFF, - - /* Snd Card event */ - HK_EVENT_VOLUME_MUTE, - HK_EVENT_VOLUME_INCLREASE, - HK_EVENT_VOLUME_DECREASE, - - /* running state control */ - HK_EVENT_ENTERRING_S3, - HK_EVENT_ENTERRING_S4, - HK_EVENT_ENTERRING_S5, -}; - -enum conf_entry_enum { - bus_handle = 0, - bus_method = 1, - action_handle = 2, - method = 3, - LAST_CONF_ENTRY -}; - -/* procdir we use */ -static struct proc_dir_entry *hotkey_proc_dir; -static struct proc_dir_entry *hotkey_config; -static struct proc_dir_entry *hotkey_poll_config; -static struct proc_dir_entry *hotkey_action; -static struct proc_dir_entry *hotkey_info; - -/* linkage for all type of hotkey */ -struct acpi_hotkey_link { - struct list_head entries; - int hotkey_type; /* event or polling based hotkey */ - int hotkey_standard_num; /* standardized hotkey(event) number */ -}; - -/* event based hotkey */ -struct acpi_event_hotkey { - struct acpi_hotkey_link hotkey_link; - int flag; - acpi_handle bus_handle; /* bus to install notify handler */ - int external_hotkey_num; /* external hotkey/event number */ - acpi_handle action_handle; /* acpi handle attached aml action method */ - char *action_method; /* action method */ -}; - -/* - * There are two ways to poll status - * 1. directy call read_xxx method, without any arguments passed in - * 2. call write_xxx method, with arguments passed in, you need - * the result is saved in acpi_polling_hotkey.poll_result. - * anthoer read command through polling interface. - * - */ - -/* polling based hotkey */ -struct acpi_polling_hotkey { - struct acpi_hotkey_link hotkey_link; - int flag; - acpi_handle poll_handle; /* acpi handle attached polling method */ - char *poll_method; /* poll method */ - acpi_handle action_handle; /* acpi handle attached action method */ - char *action_method; /* action method */ - union acpi_object *poll_result; /* polling_result */ - struct proc_dir_entry *proc; -}; - -/* hotkey object union */ -union acpi_hotkey { - struct list_head entries; - struct acpi_hotkey_link link; - struct acpi_event_hotkey event_hotkey; - struct acpi_polling_hotkey poll_hotkey; -}; - -/* hotkey object list */ -struct acpi_hotkey_list { - struct list_head *entries; - int count; -}; - -static int auto_hotkey_add(struct acpi_device *device); -static int auto_hotkey_remove(struct acpi_device *device, int type); - -static struct acpi_driver hotkey_driver = { - .name = ACPI_HOTK_NAME, - .class = ACPI_HOTK_CLASS, - .ids = ACPI_HOTK_HID, - .ops = { - .add = auto_hotkey_add, - .remove = auto_hotkey_remove, - }, -}; - -static void free_hotkey_device(union acpi_hotkey *key); -static void free_hotkey_buffer(union acpi_hotkey *key); -static void free_poll_hotkey_buffer(union acpi_hotkey *key); -static int hotkey_open_config(struct inode *inode, struct file *file); -static int hotkey_poll_open_config(struct inode *inode, struct file *file); -static ssize_t hotkey_write_config(struct file *file, - const char __user * buffer, - size_t count, loff_t * data); -static int hotkey_info_open_fs(struct inode *inode, struct file *file); -static int hotkey_action_open_fs(struct inode *inode, struct file *file); -static ssize_t hotkey_execute_aml_method(struct file *file, - const char __user * buffer, - size_t count, loff_t * data); -static int hotkey_config_seq_show(struct seq_file *seq, void *offset); -static int hotkey_poll_config_seq_show(struct seq_file *seq, void *offset); -static int hotkey_polling_open_fs(struct inode *inode, struct file *file); -static union acpi_hotkey *get_hotkey_by_event(struct - acpi_hotkey_list - *hotkey_list, int event); - -/* event based config */ -static const struct file_operations hotkey_config_fops = { - .open = hotkey_open_config, - .read = seq_read, - .write = hotkey_write_config, - .llseek = seq_lseek, - .release = single_release, -}; - -/* polling based config */ -static const struct file_operations hotkey_poll_config_fops = { - .open = hotkey_poll_open_config, - .read = seq_read, - .write = hotkey_write_config, - .llseek = seq_lseek, - .release = single_release, -}; - -/* hotkey driver info */ -static const struct file_operations hotkey_info_fops = { - .open = hotkey_info_open_fs, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -/* action */ -static const struct file_operations hotkey_action_fops = { - .open = hotkey_action_open_fs, - .read = seq_read, - .write = hotkey_execute_aml_method, - .llseek = seq_lseek, - .release = single_release, -}; - -/* polling results */ -static const struct file_operations hotkey_polling_fops = { - .open = hotkey_polling_open_fs, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -struct acpi_hotkey_list global_hotkey_list; /* link all ev or pl hotkey */ -struct list_head hotkey_entries; /* head of the list of hotkey_list */ - -static int hotkey_info_seq_show(struct seq_file *seq, void *offset) -{ - - seq_printf(seq, "Hotkey generic driver ver: %s\n", HOTKEY_ACPI_VERSION); - - return 0; -} - -static int hotkey_info_open_fs(struct inode *inode, struct file *file) -{ - return single_open(file, hotkey_info_seq_show, PDE(inode)->data); -} - -static char *format_result(union acpi_object *object) -{ - char *buf; - - buf = kzalloc(RESULT_STR_LEN, GFP_KERNEL); - if (!buf) - return NULL; - /* Now, just support integer type */ - if (object->type == ACPI_TYPE_INTEGER) - sprintf(buf, "%d\n", (u32) object->integer.value); - return buf; -} - -static int hotkey_polling_seq_show(struct seq_file *seq, void *offset) -{ - struct acpi_polling_hotkey *poll_hotkey = seq->private; - char *buf; - - - if (poll_hotkey->poll_result) { - buf = format_result(poll_hotkey->poll_result); - if (buf) - seq_printf(seq, "%s", buf); - kfree(buf); - } - return 0; -} - -static int hotkey_polling_open_fs(struct inode *inode, struct file *file) -{ - return single_open(file, hotkey_polling_seq_show, PDE(inode)->data); -} - -static int hotkey_action_open_fs(struct inode *inode, struct file *file) -{ - return single_open(file, hotkey_info_seq_show, PDE(inode)->data); -} - -/* Mapping external hotkey number to standardized hotkey event num */ -static int hotkey_get_internal_event(int event, struct acpi_hotkey_list *list) -{ - struct list_head *entries; - int val = -1; - - - list_for_each(entries, list->entries) { - union acpi_hotkey *key = - container_of(entries, union acpi_hotkey, entries); - if (key->link.hotkey_type == ACPI_HOTKEY_EVENT - && key->event_hotkey.external_hotkey_num == event) { - val = key->link.hotkey_standard_num; - break; - } - } - - return val; -} - -static void -acpi_hotkey_notify_handler(acpi_handle handle, u32 event, void *data) -{ - struct acpi_device *device = NULL; - u32 internal_event; - - - if (acpi_bus_get_device(handle, &device)) - return; - - internal_event = hotkey_get_internal_event(event, &global_hotkey_list); - acpi_bus_generate_event(device, internal_event, 0); - - return; -} - -/* Need to invent automatically hotkey add method */ -static int auto_hotkey_add(struct acpi_device *device) -{ - /* Implement me */ - return 0; -} - -/* Need to invent automatically hotkey remove method */ -static int auto_hotkey_remove(struct acpi_device *device, int type) -{ - /* Implement me */ - return 0; -} - -/* Create a proc file for each polling method */ -static int create_polling_proc(union acpi_hotkey *device) -{ - struct proc_dir_entry *proc; - char proc_name[80]; - mode_t mode; - - mode = S_IFREG | S_IRUGO | S_IWUGO; - - sprintf(proc_name, "%d", device->link.hotkey_standard_num); - /* - strcat(proc_name, device->poll_hotkey.poll_method); - */ - proc = create_proc_entry(proc_name, mode, hotkey_proc_dir); - - if (!proc) { - return -ENODEV; - } else { - proc->proc_fops = &hotkey_polling_fops; - proc->owner = THIS_MODULE; - proc->data = device; - proc->uid = 0; - proc->gid = 0; - device->poll_hotkey.proc = proc; - } - return 0; -} - -static int hotkey_add(union acpi_hotkey *device) -{ - int status = 0; - struct acpi_device *dev = NULL; - - - if (device->link.hotkey_type == ACPI_HOTKEY_EVENT) { - acpi_bus_get_device(device->event_hotkey.bus_handle, &dev); - status = acpi_install_notify_handler(dev->handle, - ACPI_DEVICE_NOTIFY, - acpi_hotkey_notify_handler, - dev); - } else /* Add polling hotkey */ - create_polling_proc(device); - - global_hotkey_list.count++; - - list_add_tail(&device->link.entries, global_hotkey_list.entries); - - return status; -} - -static int hotkey_remove(union acpi_hotkey *device) -{ - struct list_head *entries, *next; - - - list_for_each_safe(entries, next, global_hotkey_list.entries) { - union acpi_hotkey *key = - container_of(entries, union acpi_hotkey, entries); - if (key->link.hotkey_standard_num == - device->link.hotkey_standard_num) { - list_del(&key->link.entries); - free_hotkey_device(key); - global_hotkey_list.count--; - break; - } - } - kfree(device); - return 0; -} - -static int hotkey_update(union acpi_hotkey *key) -{ - struct list_head *entries; - - - list_for_each(entries, global_hotkey_list.entries) { - union acpi_hotkey *tmp = - container_of(entries, union acpi_hotkey, entries); - if (tmp->link.hotkey_standard_num == - key->link.hotkey_standard_num) { - if (key->link.hotkey_type == ACPI_HOTKEY_EVENT) { - free_hotkey_buffer(tmp); - tmp->event_hotkey.bus_handle = - key->event_hotkey.bus_handle; - tmp->event_hotkey.external_hotkey_num = - key->event_hotkey.external_hotkey_num; - tmp->event_hotkey.action_handle = - key->event_hotkey.action_handle; - tmp->event_hotkey.action_method = - key->event_hotkey.action_method; - kfree(key); - } else { - /* - char proc_name[80]; - - sprintf(proc_name, "%d", tmp->link.hotkey_standard_num); - strcat(proc_name, tmp->poll_hotkey.poll_method); - remove_proc_entry(proc_name,hotkey_proc_dir); - */ - free_poll_hotkey_buffer(tmp); - tmp->poll_hotkey.poll_handle = - key->poll_hotkey.poll_handle; - tmp->poll_hotkey.poll_method = - key->poll_hotkey.poll_method; - tmp->poll_hotkey.action_handle = - key->poll_hotkey.action_handle; - tmp->poll_hotkey.action_method = - key->poll_hotkey.action_method; - tmp->poll_hotkey.poll_result = - key->poll_hotkey.poll_result; - /* - create_polling_proc(tmp); - */ - kfree(key); - } - return 0; - break; - } - } - - return -ENODEV; -} - -static void free_hotkey_device(union acpi_hotkey *key) -{ - struct acpi_device *dev; - - - if (key->link.hotkey_type == ACPI_HOTKEY_EVENT) { - acpi_bus_get_device(key->event_hotkey.bus_handle, &dev); - if (dev->handle) - acpi_remove_notify_handler(dev->handle, - ACPI_DEVICE_NOTIFY, - acpi_hotkey_notify_handler); - free_hotkey_buffer(key); - } else { - char proc_name[80]; - - sprintf(proc_name, "%d", key->link.hotkey_standard_num); - /* - strcat(proc_name, key->poll_hotkey.poll_method); - */ - remove_proc_entry(proc_name, hotkey_proc_dir); - free_poll_hotkey_buffer(key); - } - kfree(key); - return; -} - -static void free_hotkey_buffer(union acpi_hotkey *key) -{ - /* key would never be null, action method could be */ - kfree(key->event_hotkey.action_method); -} - -static void free_poll_hotkey_buffer(union acpi_hotkey *key) -{ - /* key would never be null, others could be*/ - kfree(key->poll_hotkey.action_method); - kfree(key->poll_hotkey.poll_method); - kfree(key->poll_hotkey.poll_result); -} -static int -init_hotkey_device(union acpi_hotkey *key, char **config_entry, - int std_num, int external_num) -{ - acpi_handle tmp_handle; - acpi_status status = AE_OK; - - if (std_num < 0 || IS_POLL(std_num) || !key) - goto do_fail; - - if (!config_entry[bus_handle] || !config_entry[action_handle] - || !config_entry[method]) - goto do_fail; - - key->link.hotkey_type = ACPI_HOTKEY_EVENT; - key->link.hotkey_standard_num = std_num; - key->event_hotkey.flag = 0; - key->event_hotkey.action_method = config_entry[method]; - - status = acpi_get_handle(NULL, config_entry[bus_handle], - &(key->event_hotkey.bus_handle)); - if (ACPI_FAILURE(status)) - goto do_fail_zero; - key->event_hotkey.external_hotkey_num = external_num; - status = acpi_get_handle(NULL, config_entry[action_handle], - &(key->event_hotkey.action_handle)); - if (ACPI_FAILURE(status)) - goto do_fail_zero; - status = acpi_get_handle(key->event_hotkey.action_handle, - config_entry[method], &tmp_handle); - if (ACPI_FAILURE(status)) - goto do_fail_zero; - return AE_OK; -do_fail_zero: - key->event_hotkey.action_method = NULL; -do_fail: - return -ENODEV; -} - -static int -init_poll_hotkey_device(union acpi_hotkey *key, char **config_entry, - int std_num) -{ - acpi_status status = AE_OK; - acpi_handle tmp_handle; - - if (std_num < 0 || IS_EVENT(std_num) || !key) - goto do_fail; - if (!config_entry[bus_handle] ||!config_entry[bus_method] || - !config_entry[action_handle] || !config_entry[method]) - goto do_fail; - - key->link.hotkey_type = ACPI_HOTKEY_POLLING; - key->link.hotkey_standard_num = std_num; - key->poll_hotkey.flag = 0; - key->poll_hotkey.poll_method = config_entry[bus_method]; - key->poll_hotkey.action_method = config_entry[method]; - - status = acpi_get_handle(NULL, config_entry[bus_handle], - &(key->poll_hotkey.poll_handle)); - if (ACPI_FAILURE(status)) - goto do_fail_zero; - status = acpi_get_handle(key->poll_hotkey.poll_handle, - config_entry[bus_method], &tmp_handle); - if (ACPI_FAILURE(status)) - goto do_fail_zero; - status = - acpi_get_handle(NULL, config_entry[action_handle], - &(key->poll_hotkey.action_handle)); - if (ACPI_FAILURE(status)) - goto do_fail_zero; - status = acpi_get_handle(key->poll_hotkey.action_handle, - config_entry[method], &tmp_handle); - if (ACPI_FAILURE(status)) - goto do_fail_zero; - key->poll_hotkey.poll_result = - kmalloc(sizeof(union acpi_object), GFP_KERNEL); - if (!key->poll_hotkey.poll_result) - goto do_fail_zero; - return AE_OK; - -do_fail_zero: - key->poll_hotkey.poll_method = NULL; - key->poll_hotkey.action_method = NULL; -do_fail: - return -ENODEV; -} - -static int hotkey_open_config(struct inode *inode, struct file *file) -{ - return (single_open - (file, hotkey_config_seq_show, PDE(inode)->data)); -} - -static int hotkey_poll_open_config(struct inode *inode, struct file *file) -{ - return (single_open - (file, hotkey_poll_config_seq_show, PDE(inode)->data)); -} - -static int hotkey_config_seq_show(struct seq_file *seq, void *offset) -{ - struct acpi_hotkey_list *hotkey_list = &global_hotkey_list; - struct list_head *entries; - char bus_name[ACPI_PATHNAME_MAX] = { 0 }; - char action_name[ACPI_PATHNAME_MAX] = { 0 }; - struct acpi_buffer bus = { ACPI_PATHNAME_MAX, bus_name }; - struct acpi_buffer act = { ACPI_PATHNAME_MAX, action_name }; - - - list_for_each(entries, hotkey_list->entries) { - union acpi_hotkey *key = - container_of(entries, union acpi_hotkey, entries); - if (key->link.hotkey_type == ACPI_HOTKEY_EVENT) { - acpi_get_name(key->event_hotkey.bus_handle, - ACPI_NAME_TYPE_MAX, &bus); - acpi_get_name(key->event_hotkey.action_handle, - ACPI_NAME_TYPE_MAX, &act); - seq_printf(seq, "%s:%s:%s:%d:%d\n", bus_name, - action_name, - key->event_hotkey.action_method, - key->link.hotkey_standard_num, - key->event_hotkey.external_hotkey_num); - } - } - seq_puts(seq, "\n"); - return 0; -} - -static int hotkey_poll_config_seq_show(struct seq_file *seq, void *offset) -{ - struct acpi_hotkey_list *hotkey_list = &global_hotkey_list; - struct list_head *entries; - char bus_name[ACPI_PATHNAME_MAX] = { 0 }; - char action_name[ACPI_PATHNAME_MAX] = { 0 }; - struct acpi_buffer bus = { ACPI_PATHNAME_MAX, bus_name }; - struct acpi_buffer act = { ACPI_PATHNAME_MAX, action_name }; - - - list_for_each(entries, hotkey_list->entries) { - union acpi_hotkey *key = - container_of(entries, union acpi_hotkey, entries); - if (key->link.hotkey_type == ACPI_HOTKEY_POLLING) { - acpi_get_name(key->poll_hotkey.poll_handle, - ACPI_NAME_TYPE_MAX, &bus); - acpi_get_name(key->poll_hotkey.action_handle, - ACPI_NAME_TYPE_MAX, &act); - seq_printf(seq, "%s:%s:%s:%s:%d\n", bus_name, - key->poll_hotkey.poll_method, - action_name, - key->poll_hotkey.action_method, - key->link.hotkey_standard_num); - } - } - seq_puts(seq, "\n"); - return 0; -} - -static int -get_parms(char *config_record, int *cmd, char **config_entry, - int *internal_event_num, int *external_event_num) -{ -/* the format of *config_record = - * "1:\d+:*" : "cmd:internal_event_num" - * "\d+:\w+:\w+:\w+:\w+:\d+:\d+" : - * "cmd:bus_handle:bus_method:action_handle:method:internal_event_num:external_event_num" - */ - char *tmp, *tmp1, count; - int i; - - sscanf(config_record, "%d", cmd); - if (*cmd == 1) { - if (sscanf(config_record, "%d:%d", cmd, internal_event_num) != - 2) - goto do_fail; - else - return (6); - } - tmp = strchr(config_record, ':'); - if (!tmp) - goto do_fail; - tmp++; - for (i = 0; i < LAST_CONF_ENTRY; i++) { - tmp1 = strchr(tmp, ':'); - if (!tmp1) { - goto do_fail; - } - count = tmp1 - tmp; - config_entry[i] = kzalloc(count + 1, GFP_KERNEL); - if (!config_entry[i]) - goto handle_failure; - strncpy(config_entry[i], tmp, count); - tmp = tmp1 + 1; - } - if (sscanf(tmp, "%d:%d", internal_event_num, external_event_num) <= 0) - goto handle_failure; - if (!IS_OTHERS(*internal_event_num)) { - return 6; - } -handle_failure: - while (i-- > 0) - kfree(config_entry[i]); -do_fail: - return -1; -} - -/* count is length for one input record */ -static ssize_t hotkey_write_config(struct file *file, - const char __user * buffer, - size_t count, loff_t * data) -{ - char *config_record = NULL; - char *config_entry[LAST_CONF_ENTRY]; - int cmd, internal_event_num, external_event_num; - int ret = 0; - union acpi_hotkey *key = kzalloc(sizeof(union acpi_hotkey), GFP_KERNEL); - - if (!key) - return -ENOMEM; - - config_record = kzalloc(count + 1, GFP_KERNEL); - if (!config_record) { - kfree(key); - return -ENOMEM; - } - - if (copy_from_user(config_record, buffer, count)) { - kfree(config_record); - kfree(key); - printk(KERN_ERR PREFIX "Invalid data\n"); - return -EINVAL; - } - ret = get_parms(config_record, &cmd, config_entry, - &internal_event_num, &external_event_num); - kfree(config_record); - if (ret != 6) { - printk(KERN_ERR PREFIX "Invalid data format ret=%d\n", ret); - return -EINVAL; - } - - if (cmd == 1) { - union acpi_hotkey *tmp = NULL; - tmp = get_hotkey_by_event(&global_hotkey_list, - internal_event_num); - if (!tmp) - printk(KERN_ERR PREFIX "Invalid key\n"); - else - memcpy(key, tmp, sizeof(union acpi_hotkey)); - goto cont_cmd; - } - if (IS_EVENT(internal_event_num)) { - if (init_hotkey_device(key, config_entry, - internal_event_num, external_event_num)) - goto init_hotkey_fail; - } else { - if (init_poll_hotkey_device(key, config_entry, - internal_event_num)) - goto init_poll_hotkey_fail; - } -cont_cmd: - switch (cmd) { - case 0: - if (get_hotkey_by_event(&global_hotkey_list, - key->link.hotkey_standard_num)) - goto fail_out; - else - hotkey_add(key); - break; - case 1: - hotkey_remove(key); - break; - case 2: - /* key is kfree()ed if matched*/ - if (hotkey_update(key)) - goto fail_out; - break; - default: - goto fail_out; - break; - } - return count; - -init_poll_hotkey_fail: /* failed init_poll_hotkey_device */ - kfree(config_entry[bus_method]); - config_entry[bus_method] = NULL; -init_hotkey_fail: /* failed init_hotkey_device */ - kfree(config_entry[method]); -fail_out: - kfree(config_entry[bus_handle]); - kfree(config_entry[action_handle]); - /* No double free since elements =NULL for error cases */ - if (IS_EVENT(internal_event_num)) { - if (config_entry[bus_method]) - kfree(config_entry[bus_method]); - free_hotkey_buffer(key); /* frees [method] */ - } else - free_poll_hotkey_buffer(key); /* frees [bus_method]+[method] */ - kfree(key); - printk(KERN_ERR PREFIX "invalid key\n"); - return -EINVAL; -} - -/* - * This function evaluates an ACPI method, given an int as parameter, the - * method is searched within the scope of the handle, can be NULL. The output - * of the method is written is output, which can also be NULL - * - * returns 1 if write is successful, 0 else. - */ -static int write_acpi_int(acpi_handle handle, const char *method, int val, - struct acpi_buffer *output) -{ - struct acpi_object_list params; /* list of input parameters (an int here) */ - union acpi_object in_obj; /* the only param we use */ - acpi_status status; - - params.count = 1; - params.pointer = &in_obj; - in_obj.type = ACPI_TYPE_INTEGER; - in_obj.integer.value = val; - - status = acpi_evaluate_object(handle, (char *)method, ¶ms, output); - - return (status == AE_OK); -} - -static int read_acpi_int(acpi_handle handle, const char *method, - union acpi_object *val) -{ - struct acpi_buffer output; - union acpi_object out_obj; - acpi_status status; - - output.length = sizeof(out_obj); - output.pointer = &out_obj; - - status = acpi_evaluate_object(handle, (char *)method, NULL, &output); - if (val) { - val->integer.value = out_obj.integer.value; - val->type = out_obj.type; - } else - printk(KERN_ERR PREFIX "null val pointer\n"); - return ((status == AE_OK) - && (out_obj.type == ACPI_TYPE_INTEGER)); -} - -static union acpi_hotkey *get_hotkey_by_event(struct - acpi_hotkey_list - *hotkey_list, int event) -{ - struct list_head *entries; - - list_for_each(entries, hotkey_list->entries) { - union acpi_hotkey *key = - container_of(entries, union acpi_hotkey, entries); - if (key->link.hotkey_standard_num == event) { - return (key); - } - } - return (NULL); -} - -/* - * user call AML method interface: - * Call convention: - * echo "event_num: arg type : value" - * example: echo "1:1:30" > /proc/acpi/action - * Just support 1 integer arg passing to AML method - */ - -static ssize_t hotkey_execute_aml_method(struct file *file, - const char __user * buffer, - size_t count, loff_t * data) -{ - struct acpi_hotkey_list *hotkey_list = &global_hotkey_list; - char *arg; - int event, method_type, type, value; - union acpi_hotkey *key; - - - arg = kzalloc(count + 1, GFP_KERNEL); - if (!arg) - return -ENOMEM; - - if (copy_from_user(arg, buffer, count)) { - kfree(arg); - printk(KERN_ERR PREFIX "Invalid argument 2\n"); - return -EINVAL; - } - - if (sscanf(arg, "%d:%d:%d:%d", &event, &method_type, &type, &value) != - 4) { - kfree(arg); - printk(KERN_ERR PREFIX "Invalid argument 3\n"); - return -EINVAL; - } - kfree(arg); - if (type == ACPI_TYPE_INTEGER) { - key = get_hotkey_by_event(hotkey_list, event); - if (!key) - goto do_fail; - if (IS_EVENT(event)) - write_acpi_int(key->event_hotkey.action_handle, - key->event_hotkey.action_method, value, - NULL); - else if (IS_POLL(event)) { - if (method_type == POLL_METHOD) - read_acpi_int(key->poll_hotkey.poll_handle, - key->poll_hotkey.poll_method, - key->poll_hotkey.poll_result); - else if (method_type == ACTION_METHOD) - write_acpi_int(key->poll_hotkey.action_handle, - key->poll_hotkey.action_method, - value, NULL); - else - goto do_fail; - - } - } else { - printk(KERN_WARNING "Not supported\n"); - return -EINVAL; - } - return count; - do_fail: - return -EINVAL; - -} - -static int __init hotkey_init(void) -{ - int result; - mode_t mode = S_IFREG | S_IRUGO | S_IWUGO; - - - if (acpi_disabled) - return -ENODEV; - - if (acpi_specific_hotkey_enabled) { - printk("Using specific hotkey driver\n"); - return -ENODEV; - } - - hotkey_proc_dir = proc_mkdir(HOTKEY_PROC, acpi_root_dir); - if (!hotkey_proc_dir) { - return (-ENODEV); - } - hotkey_proc_dir->owner = THIS_MODULE; - - hotkey_config = - create_proc_entry(HOTKEY_EV_CONFIG, mode, hotkey_proc_dir); - if (!hotkey_config) { - goto do_fail1; - } else { - hotkey_config->proc_fops = &hotkey_config_fops; - hotkey_config->data = &global_hotkey_list; - hotkey_config->owner = THIS_MODULE; - hotkey_config->uid = 0; - hotkey_config->gid = 0; - } - - hotkey_poll_config = - create_proc_entry(HOTKEY_PL_CONFIG, mode, hotkey_proc_dir); - if (!hotkey_poll_config) { - goto do_fail2; - } else { - hotkey_poll_config->proc_fops = &hotkey_poll_config_fops; - hotkey_poll_config->data = &global_hotkey_list; - hotkey_poll_config->owner = THIS_MODULE; - hotkey_poll_config->uid = 0; - hotkey_poll_config->gid = 0; - } - - hotkey_action = create_proc_entry(HOTKEY_ACTION, mode, hotkey_proc_dir); - if (!hotkey_action) { - goto do_fail3; - } else { - hotkey_action->proc_fops = &hotkey_action_fops; - hotkey_action->owner = THIS_MODULE; - hotkey_action->uid = 0; - hotkey_action->gid = 0; - } - - hotkey_info = create_proc_entry(HOTKEY_INFO, mode, hotkey_proc_dir); - if (!hotkey_info) { - goto do_fail4; - } else { - hotkey_info->proc_fops = &hotkey_info_fops; - hotkey_info->owner = THIS_MODULE; - hotkey_info->uid = 0; - hotkey_info->gid = 0; - } - - result = acpi_bus_register_driver(&hotkey_driver); - if (result < 0) - goto do_fail5; - global_hotkey_list.count = 0; - global_hotkey_list.entries = &hotkey_entries; - - INIT_LIST_HEAD(&hotkey_entries); - - return (0); - - do_fail5: - remove_proc_entry(HOTKEY_INFO, hotkey_proc_dir); - do_fail4: - remove_proc_entry(HOTKEY_ACTION, hotkey_proc_dir); - do_fail3: - remove_proc_entry(HOTKEY_PL_CONFIG, hotkey_proc_dir); - do_fail2: - remove_proc_entry(HOTKEY_EV_CONFIG, hotkey_proc_dir); - do_fail1: - remove_proc_entry(HOTKEY_PROC, acpi_root_dir); - return (-ENODEV); -} - -static void __exit hotkey_exit(void) -{ - struct list_head *entries, *next; - - - list_for_each_safe(entries, next, global_hotkey_list.entries) { - union acpi_hotkey *key = - container_of(entries, union acpi_hotkey, entries); - - acpi_os_wait_events_complete(NULL); - list_del(&key->link.entries); - global_hotkey_list.count--; - free_hotkey_device(key); - } - acpi_bus_unregister_driver(&hotkey_driver); - remove_proc_entry(HOTKEY_EV_CONFIG, hotkey_proc_dir); - remove_proc_entry(HOTKEY_PL_CONFIG, hotkey_proc_dir); - remove_proc_entry(HOTKEY_ACTION, hotkey_proc_dir); - remove_proc_entry(HOTKEY_INFO, hotkey_proc_dir); - remove_proc_entry(HOTKEY_PROC, acpi_root_dir); - return; -} - -module_init(hotkey_init); -module_exit(hotkey_exit); diff --git a/drivers/acpi/ibm_acpi.c b/drivers/acpi/ibm_acpi.c index c6144ca66638..4def52ca69e5 100644 --- a/drivers/acpi/ibm_acpi.c +++ b/drivers/acpi/ibm_acpi.c @@ -2696,11 +2696,6 @@ static int __init acpi_ibm_init(void) if (acpi_disabled) return -ENODEV; - if (!acpi_specific_hotkey_enabled) { - printk(IBM_ERR "using generic hotkey driver\n"); - return -ENODEV; - } - /* ec is required because many other handles are relative to it */ IBM_HANDLE_INIT(ec); if (!ec_handle) { diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index 0f6f3bcbc8eb..368e111100bd 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c @@ -68,9 +68,6 @@ EXPORT_SYMBOL(acpi_in_debugger); extern char line_buf[80]; #endif /*ENABLE_DEBUGGER */ -int acpi_specific_hotkey_enabled = TRUE; -EXPORT_SYMBOL(acpi_specific_hotkey_enabled); - static unsigned int acpi_irq_irq; static acpi_osd_handler acpi_irq_handler; static void *acpi_irq_context; @@ -1012,14 +1009,6 @@ static int __init acpi_wake_gpes_always_on_setup(char *str) __setup("acpi_wake_gpes_always_on", acpi_wake_gpes_always_on_setup); -static int __init acpi_hotkey_setup(char *str) -{ - acpi_specific_hotkey_enabled = FALSE; - return 1; -} - -__setup("acpi_generic_hotkey", acpi_hotkey_setup); - /* * max_cstate is defined in the base kernel so modules can * change it w/o depending on the state of the processor module. diff --git a/drivers/acpi/toshiba_acpi.c b/drivers/acpi/toshiba_acpi.c index d9b651ffcdc0..56ec5503a124 100644 --- a/drivers/acpi/toshiba_acpi.c +++ b/drivers/acpi/toshiba_acpi.c @@ -561,10 +561,6 @@ static int __init toshiba_acpi_init(void) if (acpi_disabled) return -ENODEV; - if (!acpi_specific_hotkey_enabled) { - printk(MY_INFO "Using generic hotkey driver\n"); - return -ENODEV; - } /* simple device detection: look for HCI method */ if (is_valid_acpi_path(METHOD_HCI_1)) method_hci = METHOD_HCI_1; diff --git a/drivers/misc/asus-laptop.c b/drivers/misc/asus-laptop.c index 861c39935f99..e4e2b707a353 100644 --- a/drivers/misc/asus-laptop.c +++ b/drivers/misc/asus-laptop.c @@ -1088,11 +1088,6 @@ static int __init asus_laptop_init(void) if (acpi_disabled) return -ENODEV; - if (!acpi_specific_hotkey_enabled) { - printk(ASUS_ERR "Using generic hotkey driver\n"); - return -ENODEV; - } - result = acpi_bus_register_driver(&asus_hotk_driver); if (result < 0) return result; diff --git a/include/acpi/acpi_drivers.h b/include/acpi/acpi_drivers.h index 4dc8a5043ef0..0e21efb6f649 100644 --- a/include/acpi/acpi_drivers.h +++ b/include/acpi/acpi_drivers.h @@ -105,12 +105,6 @@ int acpi_ec_ecdt_probe(void); int acpi_processor_set_thermal_limit(acpi_handle handle, int type); -/* -------------------------------------------------------------------------- - Hot Keys - -------------------------------------------------------------------------- */ - -extern int acpi_specific_hotkey_enabled; - /*-------------------------------------------------------------------------- Dock Station -------------------------------------------------------------------------- */ -- cgit v1.2.3 From d08df601a30df9e36c29f3214315f4f0c8784c68 Mon Sep 17 00:00:00 2001 From: "Robert P. J. Day" Date: Sat, 17 Feb 2007 19:07:33 +0100 Subject: Various typo fixes. Correct mis-spellings of "algorithm", "appear", "consistent" and (shame, shame) "kernel". Signed-off-by: Robert P. J. Day Signed-off-by: Adrian Bunk --- Documentation/video4linux/bttv/Insmod-options | 2 +- arch/m32r/lib/usercopy.c | 4 ++-- arch/m68knommu/platform/5307/timers.c | 2 +- drivers/infiniband/hw/ipath/ipath_iba6110.c | 2 +- drivers/infiniband/hw/ipath/ipath_iba6120.c | 2 +- drivers/isdn/i4l/isdn_ppp.c | 2 +- drivers/net/e1000/e1000_hw.h | 2 +- drivers/net/wireless/wavelan_cs.c | 8 ++++---- drivers/sbus/char/vfc_i2c.c | 2 +- include/linux/seqlock.h | 2 +- lib/textsearch.c | 2 +- net/ipv4/tcp_cong.c | 2 +- 12 files changed, 16 insertions(+), 16 deletions(-) (limited to 'Documentation') diff --git a/Documentation/video4linux/bttv/Insmod-options b/Documentation/video4linux/bttv/Insmod-options index bb7c2cac7917..5ef75787f83a 100644 --- a/Documentation/video4linux/bttv/Insmod-options +++ b/Documentation/video4linux/bttv/Insmod-options @@ -57,7 +57,7 @@ bttv.o i2c_udelay= Allow reduce I2C speed. Default is 5 usecs (meaning 66,67 Kbps). The default is the maximum supported speed by kernel bitbang - algoritm. You may use lower numbers, if I2C + algorithm. You may use lower numbers, if I2C messages are lost (16 is known to work on all supported cards). diff --git a/arch/m32r/lib/usercopy.c b/arch/m32r/lib/usercopy.c index 896cef1aca5f..82abd159dbef 100644 --- a/arch/m32r/lib/usercopy.c +++ b/arch/m32r/lib/usercopy.c @@ -293,7 +293,7 @@ long strnlen_user(const char __user *s, long n) : "0" (n), "1" (s), "r" (n & 3), "r" (mask), "r"(0x01010101) : "r0", "r1", "cbit"); - /* NOTE: strnlen_user() algorism: + /* NOTE: strnlen_user() algorithm: * { * char *p; * for (p = s; n-- && *p != '\0'; ++p) @@ -369,7 +369,7 @@ long strnlen_user(const char __user *s, long n) : "0" (n), "1" (s), "r" (n & 3), "r" (mask), "r"(0x01010101) : "r0", "r1", "r2", "r3", "cbit"); - /* NOTE: strnlen_user() algorism: + /* NOTE: strnlen_user() algorithm: * { * char *p; * for (p = s; n-- && *p != '\0'; ++p) diff --git a/arch/m68knommu/platform/5307/timers.c b/arch/m68knommu/platform/5307/timers.c index 87b112b363a6..92e58070b016 100644 --- a/arch/m68knommu/platform/5307/timers.c +++ b/arch/m68knommu/platform/5307/timers.c @@ -104,7 +104,7 @@ unsigned long coldfire_timer_offset(void) /* * Choose a reasonably fast profile timer. Make it an odd value to - * try and get good coverage of kernal operations. + * try and get good coverage of kernel operations. */ #define PROFILEHZ 1013 diff --git a/drivers/infiniband/hw/ipath/ipath_iba6110.c b/drivers/infiniband/hw/ipath/ipath_iba6110.c index 7468477ba837..993482545021 100644 --- a/drivers/infiniband/hw/ipath/ipath_iba6110.c +++ b/drivers/infiniband/hw/ipath/ipath_iba6110.c @@ -1534,7 +1534,7 @@ static int ipath_ht_early_init(struct ipath_devdata *dd) * @kbase: ipath_base_info pointer * * We set the PCIE flag because the lower bandwidth on PCIe vs - * HyperTransport can affect some user packet algorithims. + * HyperTransport can affect some user packet algorithms. */ static int ipath_ht_get_base_info(struct ipath_portdata *pd, void *kbase) { diff --git a/drivers/infiniband/hw/ipath/ipath_iba6120.c b/drivers/infiniband/hw/ipath/ipath_iba6120.c index ae8bf9950c6d..05918e1e7c36 100644 --- a/drivers/infiniband/hw/ipath/ipath_iba6120.c +++ b/drivers/infiniband/hw/ipath/ipath_iba6120.c @@ -1293,7 +1293,7 @@ int __attribute__((weak)) ipath_unordered_wc(void) * @kbase: ipath_base_info pointer * * We set the PCIE flag because the lower bandwidth on PCIe vs - * HyperTransport can affect some user packet algorithims. + * HyperTransport can affect some user packet algorithms. */ static int ipath_pe_get_base_info(struct ipath_portdata *pd, void *kbase) { diff --git a/drivers/isdn/i4l/isdn_ppp.c b/drivers/isdn/i4l/isdn_ppp.c index 4e3f127e4003..1b2df80c3bce 100644 --- a/drivers/isdn/i4l/isdn_ppp.c +++ b/drivers/isdn/i4l/isdn_ppp.c @@ -1680,7 +1680,7 @@ static void isdn_ppp_mp_receive(isdn_net_dev * net_dev, isdn_net_local * lp, * - we hit a gap in the sequence, so no reassembly/processing is * possible ('start' would be set to NULL) * - * algorightm for this code is derived from code in the book + * algorithm for this code is derived from code in the book * 'PPP Design And Debugging' by James Carlson (Addison-Wesley) */ while (start != NULL || newfrag != NULL) { diff --git a/drivers/net/e1000/e1000_hw.h b/drivers/net/e1000/e1000_hw.h index d67105883341..bd000b802ee7 100644 --- a/drivers/net/e1000/e1000_hw.h +++ b/drivers/net/e1000/e1000_hw.h @@ -3253,7 +3253,7 @@ struct e1000_host_command_info { #define IFE_PMC_AUTO_MDIX 0x0080 /* 1=enable MDI/MDI-X feature, default 0=disabled */ #define IFE_PMC_FORCE_MDIX 0x0040 /* 1=force MDIX-X, 0=force MDI */ #define IFE_PMC_MDIX_STATUS 0x0020 /* 1=MDI-X, 0=MDI */ -#define IFE_PMC_AUTO_MDIX_COMPLETE 0x0010 /* Resolution algorthm is completed */ +#define IFE_PMC_AUTO_MDIX_COMPLETE 0x0010 /* Resolution algorithm is completed */ #define IFE_PMC_MDIX_MODE_SHIFT 6 #define IFE_PHC_MDIX_RESET_ALL_MASK 0x0000 /* Disable auto MDI-X */ diff --git a/drivers/net/wireless/wavelan_cs.c b/drivers/net/wireless/wavelan_cs.c index 5eb81638e846..b04239792f63 100644 --- a/drivers/net/wireless/wavelan_cs.c +++ b/drivers/net/wireless/wavelan_cs.c @@ -1168,7 +1168,7 @@ wv_mmc_show(struct net_device * dev) m.mmr_unused0[6], m.mmr_unused0[7]); #endif /* DEBUG_SHOW_UNUSED */ - printk(KERN_DEBUG "Encryption algorythm: %02X - Status: %02X\n", + printk(KERN_DEBUG "Encryption algorithm: %02X - Status: %02X\n", m.mmr_des_avail, m.mmr_des_status); #ifdef DEBUG_SHOW_UNUSED printk(KERN_DEBUG "mmc_unused1[]: %02X:%02X:%02X:%02X:%02X\n", @@ -3590,9 +3590,9 @@ wv_82593_config(struct net_device * dev) cfblk.acloc = TRUE; /* Disable source addr insertion by i82593 */ cfblk.preamb_len = 0; /* 2 bytes preamble (SFD) */ cfblk.loopback = FALSE; - cfblk.lin_prio = 0; /* conform to 802.3 backoff algoritm */ - cfblk.exp_prio = 5; /* conform to 802.3 backoff algoritm */ - cfblk.bof_met = 1; /* conform to 802.3 backoff algoritm */ + cfblk.lin_prio = 0; /* conform to 802.3 backoff algorithm */ + cfblk.exp_prio = 5; /* conform to 802.3 backoff algorithm */ + cfblk.bof_met = 1; /* conform to 802.3 backoff algorithm */ cfblk.ifrm_spc = 0x20 >> 4; /* 32 bit times interframe spacing */ cfblk.slottim_low = 0x20 >> 5; /* 32 bit times slot time */ cfblk.slottim_hi = 0x0; diff --git a/drivers/sbus/char/vfc_i2c.c b/drivers/sbus/char/vfc_i2c.c index ceec30648f4f..9efed771f6c0 100644 --- a/drivers/sbus/char/vfc_i2c.c +++ b/drivers/sbus/char/vfc_i2c.c @@ -14,7 +14,7 @@ /* NOTE: It seems to me that the documentation regarding the pcd8584t/pcf8584 does not show the correct way to address the i2c bus. Based on the information on the I2C bus itself and the remainder of -the Phillips docs the following algorithims apper to be correct. I am +the Phillips docs the following algorithms appear to be correct. I am fairly certain that the flowcharts in the phillips docs are wrong. */ diff --git a/include/linux/seqlock.h b/include/linux/seqlock.h index 6b0648cfdffc..52c9eb9b6df2 100644 --- a/include/linux/seqlock.h +++ b/include/linux/seqlock.h @@ -2,7 +2,7 @@ #define __LINUX_SEQLOCK_H /* * Reader/writer consistent mechanism without starving writers. This type of - * lock for data where the reader wants a consitent set of information + * lock for data where the reader wants a consistent set of information * and is willing to retry if the information changes. Readers never * block but they may have to retry if a writer is in * progress. Writers do not wait for readers. diff --git a/lib/textsearch.c b/lib/textsearch.c index 9e2a002c5b54..88c98a2ec8d9 100644 --- a/lib/textsearch.c +++ b/lib/textsearch.c @@ -40,7 +40,7 @@ * configuration according to the specified parameters. * (3) User starts the search(es) by calling _find() or _next() to * fetch subsequent occurrences. A state variable is provided - * to the algorihtm to store persistent variables. + * to the algorithm to store persistent variables. * (4) Core eventually resets the search offset and forwards the find() * request to the algorithm. * (5) Algorithm calls get_next_block() provided by the user continously diff --git a/net/ipv4/tcp_cong.c b/net/ipv4/tcp_cong.c index c1b34f1edb32..5c8caf4a1244 100644 --- a/net/ipv4/tcp_cong.c +++ b/net/ipv4/tcp_cong.c @@ -29,7 +29,7 @@ static struct tcp_congestion_ops *tcp_ca_find(const char *name) } /* - * Attach new congestion control algorthim to the list + * Attach new congestion control algorithm to the list * of available options. */ int tcp_register_congestion_control(struct tcp_congestion_ops *ca) -- cgit v1.2.3 From 1b3c3714cb4767d00f507cc6854d3339d82c5b9d Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Sat, 17 Feb 2007 19:23:03 +0100 Subject: Fix typos concerning hierarchy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit heirarchical, hierachical -> hierarchical heirarchy, hierachy -> hierarchy Signed-off-by: Uwe Kleine-König Signed-off-by: Adrian Bunk --- Documentation/filesystems/sysfs-pci.txt | 2 +- Documentation/sh/new-machine.txt | 4 ++-- arch/powerpc/mm/numa.c | 2 +- drivers/media/dvb/dvb-core/dvb_frontend.c | 2 +- drivers/media/dvb/frontends/dib3000mb.c | 2 +- drivers/pci/pcie/aer/aerdrv.h | 2 +- drivers/scsi/scsi_transport_fc.c | 2 +- drivers/scsi/scsi_transport_sas.c | 4 ++-- fs/cifs/README | 2 +- fs/ocfs2/namei.c | 2 +- include/asm-ia64/pal.h | 14 +++++++------- 11 files changed, 19 insertions(+), 19 deletions(-) (limited to 'Documentation') diff --git a/Documentation/filesystems/sysfs-pci.txt b/Documentation/filesystems/sysfs-pci.txt index 7ba2baa165ff..5daa2aaec2c5 100644 --- a/Documentation/filesystems/sysfs-pci.txt +++ b/Documentation/filesystems/sysfs-pci.txt @@ -65,7 +65,7 @@ Accessing legacy resources through sysfs ---------------------------------------- Legacy I/O port and ISA memory resources are also provided in sysfs if the -underlying platform supports them. They're located in the PCI class heirarchy, +underlying platform supports them. They're located in the PCI class hierarchy, e.g. /sys/class/pci_bus/0000:17/ diff --git a/Documentation/sh/new-machine.txt b/Documentation/sh/new-machine.txt index 73988e0d112b..5482bf5d005b 100644 --- a/Documentation/sh/new-machine.txt +++ b/Documentation/sh/new-machine.txt @@ -17,7 +17,7 @@ of the board-specific code (with the exception of stboards) ended up in arch/sh/kernel/ directly, with board-specific headers ending up in include/asm-sh/. For the new kernel, things are broken out by board type, companion chip type, and CPU type. Looking at a tree view of this directory -heirarchy looks like the following: +hierarchy looks like the following: Board-specific code: @@ -108,7 +108,7 @@ overloading), and you can feel free to name the directory after the family member itself. There are a few things that each board is required to have, both in the -arch/sh/boards and the include/asm-sh/ heirarchy. In order to better +arch/sh/boards and the include/asm-sh/ hierarchy. In order to better explain this, we use some examples for adding an imaginary board. For setup code, we're required at the very least to provide definitions for get_system_type() and platform_setup(). For our imaginary board, this diff --git a/arch/powerpc/mm/numa.c b/arch/powerpc/mm/numa.c index 262790910ff2..e86c37c82cfd 100644 --- a/arch/powerpc/mm/numa.c +++ b/arch/powerpc/mm/numa.c @@ -154,7 +154,7 @@ EXPORT_SYMBOL_GPL(of_node_to_nid); * characteristics relative to its multiple connections. We ignore * this for now. We also assume that all cpu and memory sets have * their distances represented at a common level. This won't be - * true for heirarchical NUMA. + * true for hierarchical NUMA. * * In any case the ibm,associativity-reference-points should give * the correct depth for a normal NUMA system. diff --git a/drivers/media/dvb/dvb-core/dvb_frontend.c b/drivers/media/dvb/dvb-core/dvb_frontend.c index e85972222ab4..7c42d53a1cc7 100644 --- a/drivers/media/dvb/dvb-core/dvb_frontend.c +++ b/drivers/media/dvb/dvb-core/dvb_frontend.c @@ -915,7 +915,7 @@ static int dvb_frontend_ioctl(struct inode *inode, struct file *file, fetunesettings.parameters.inversion = INVERSION_AUTO; } if (fe->ops.info.type == FE_OFDM) { - /* without hierachical coding code_rate_LP is irrelevant, + /* without hierarchical coding code_rate_LP is irrelevant, * so we tolerate the otherwise invalid FEC_NONE setting */ if (fepriv->parameters.u.ofdm.hierarchy_information == HIERARCHY_NONE && fepriv->parameters.u.ofdm.code_rate_LP == FEC_NONE) diff --git a/drivers/media/dvb/frontends/dib3000mb.c b/drivers/media/dvb/frontends/dib3000mb.c index adbabfdb04a9..b6adea5ffeb8 100644 --- a/drivers/media/dvb/frontends/dib3000mb.c +++ b/drivers/media/dvb/frontends/dib3000mb.c @@ -239,7 +239,7 @@ static int dib3000mb_set_frontend(struct dvb_frontend* fe, default: return -EINVAL; } - deb_setf("hierachy: "); + deb_setf("hierarchy: "); switch (ofdm->hierarchy_information) { case HIERARCHY_NONE: deb_setf("none "); diff --git a/drivers/pci/pcie/aer/aerdrv.h b/drivers/pci/pcie/aer/aerdrv.h index 3c0a58f64dd8..bf655dbaf8e2 100644 --- a/drivers/pci/pcie/aer/aerdrv.h +++ b/drivers/pci/pcie/aer/aerdrv.h @@ -85,7 +85,7 @@ struct aer_rpc { struct mutex rpc_mutex; /* * only one thread could do * recovery on the same - * root port hierachy + * root port hierarchy */ wait_queue_head_t wait_release; }; diff --git a/drivers/scsi/scsi_transport_fc.c b/drivers/scsi/scsi_transport_fc.c index 0d3c10f2134c..58afdb401703 100644 --- a/drivers/scsi/scsi_transport_fc.c +++ b/drivers/scsi/scsi_transport_fc.c @@ -855,7 +855,7 @@ static FC_CLASS_DEVICE_ATTR(rport, fast_io_fail_tmo, S_IRUGO | S_IWUSR, /* * Note: in the target show function we recognize when the remote - * port is in the heirarchy and do not allow the driver to get + * port is in the hierarchy and do not allow the driver to get * involved in sysfs functions. The driver only gets involved if * it's the "old" style that doesn't use rports. */ diff --git a/drivers/scsi/scsi_transport_sas.c b/drivers/scsi/scsi_transport_sas.c index 6d39150e205b..b2ef71a86292 100644 --- a/drivers/scsi/scsi_transport_sas.c +++ b/drivers/scsi/scsi_transport_sas.c @@ -500,7 +500,7 @@ struct sas_phy *sas_phy_alloc(struct device *parent, int number) EXPORT_SYMBOL(sas_phy_alloc); /** - * sas_phy_add -- add a SAS PHY to the device hierachy + * sas_phy_add -- add a SAS PHY to the device hierarchy * @phy: The PHY to be added * * Publishes a SAS PHY to the rest of the system. @@ -1265,7 +1265,7 @@ struct sas_rphy *sas_expander_alloc(struct sas_port *parent, EXPORT_SYMBOL(sas_expander_alloc); /** - * sas_rphy_add -- add a SAS remote PHY to the device hierachy + * sas_rphy_add -- add a SAS remote PHY to the device hierarchy * @rphy: The remote PHY to be added * * Publishes a SAS remote PHY to the rest of the system. diff --git a/fs/cifs/README b/fs/cifs/README index 432e515431c4..080c5eba112b 100644 --- a/fs/cifs/README +++ b/fs/cifs/README @@ -1,5 +1,5 @@ The CIFS VFS support for Linux supports many advanced network filesystem -features such as heirarchical dfs like namespace, hardlinks, locking and more. +features such as hierarchical dfs like namespace, hardlinks, locking and more. It was designed to comply with the SNIA CIFS Technical Reference (which supersedes the 1992 X/Open SMB Standard) as well as to perform best practice practical interoperability with Windows 2000, Windows XP, Samba and equivalent diff --git a/fs/ocfs2/namei.c b/fs/ocfs2/namei.c index f7fa52bb3f6b..28dd757ff67d 100644 --- a/fs/ocfs2/namei.c +++ b/fs/ocfs2/namei.c @@ -1098,7 +1098,7 @@ static int ocfs2_rename(struct inode *old_dir, BUG(); } - /* Assume a directory heirarchy thusly: + /* Assume a directory hierarchy thusly: * a/b/c * a/d * a,b,c, and d are all directories. diff --git a/include/asm-ia64/pal.h b/include/asm-ia64/pal.h index bc768153f3c9..e43021a99a20 100644 --- a/include/asm-ia64/pal.h +++ b/include/asm-ia64/pal.h @@ -32,7 +32,7 @@ #define PAL_CACHE_FLUSH 1 /* flush i/d cache */ #define PAL_CACHE_INFO 2 /* get detailed i/d cache info */ #define PAL_CACHE_INIT 3 /* initialize i/d cache */ -#define PAL_CACHE_SUMMARY 4 /* get summary of cache heirarchy */ +#define PAL_CACHE_SUMMARY 4 /* get summary of cache hierarchy */ #define PAL_MEM_ATTRIB 5 /* list supported memory attributes */ #define PAL_PTCE_INFO 6 /* purge TLB info */ #define PAL_VM_INFO 7 /* return supported virtual memory features */ @@ -113,14 +113,14 @@ typedef s64 pal_status_t; */ #define PAL_STATUS_REQUIRES_MEMORY (-9) /* Call requires PAL memory buffer */ -/* Processor cache level in the heirarchy */ +/* Processor cache level in the hierarchy */ typedef u64 pal_cache_level_t; #define PAL_CACHE_LEVEL_L0 0 /* L0 */ #define PAL_CACHE_LEVEL_L1 1 /* L1 */ #define PAL_CACHE_LEVEL_L2 2 /* L2 */ -/* Processor cache type at a particular level in the heirarchy */ +/* Processor cache type at a particular level in the hierarchy */ typedef u64 pal_cache_type_t; #define PAL_CACHE_TYPE_INSTRUCTION 1 /* Instruction cache */ @@ -272,14 +272,14 @@ typedef struct pal_cache_protection_info_s { #define PAL_CACHE_PROT_METHOD_ECC 3 /* ECC protection */ -/* Processor cache line identification in the heirarchy */ +/* Processor cache line identification in the hierarchy */ typedef union pal_cache_line_id_u { u64 pclid_data; struct { u64 cache_type : 8, /* 7-0 cache type */ level : 8, /* 15-8 level of the * cache in the - * heirarchy. + * hierarchy. */ way : 8, /* 23-16 way in the set */ @@ -292,7 +292,7 @@ typedef union pal_cache_line_id_u { u64 cache_type : 8, /* 7-0 cache type */ level : 8, /* 15-8 level of the * cache in the - * heirarchy. + * hierarchy. */ way : 8, /* 23-16 way in the set */ @@ -978,7 +978,7 @@ ia64_pal_cache_read (pal_cache_line_id_u_t line_id, u64 physical_addr) return iprv.status; } -/* Return summary information about the heirarchy of caches controlled by the processor */ +/* Return summary information about the hierarchy of caches controlled by the processor */ static inline s64 ia64_pal_cache_summary (u64 *cache_levels, u64 *unique_caches) { -- cgit v1.2.3 From be7d2f775c788a1891f0f600537f130178448b20 Mon Sep 17 00:00:00 2001 From: Erik Hovland Date: Sat, 17 Feb 2007 19:29:21 +0100 Subject: trivial documentation patch for platform.txt Found a couple of typos in the Documentation/driver-model/platform.txt file. This patch fixes both of them. Signed-off-by: Erik Hovland Signed-off-by: Adrian Bunk --- Documentation/driver-model/platform.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Documentation') diff --git a/Documentation/driver-model/platform.txt b/Documentation/driver-model/platform.txt index 9f0bc3bfd776..f7c9262b2dc8 100644 --- a/Documentation/driver-model/platform.txt +++ b/Documentation/driver-model/platform.txt @@ -66,7 +66,7 @@ runtime memory footprint: Device Enumeration ~~~~~~~~~~~~~~~~~~ -As a rule, platform specific (and often board-specific) setup code wil +As a rule, platform specific (and often board-specific) setup code will register platform devices: int platform_device_register(struct platform_device *pdev); @@ -106,7 +106,7 @@ It's built from two components: * platform_device.id ... the device instance number, or else "-1" to indicate there's only one. -These are catenated, so name/id "serial"/0 indicates bus_id "serial.0", and +These are concatenated, so name/id "serial"/0 indicates bus_id "serial.0", and "serial/3" indicates bus_id "serial.3"; both would use the platform_driver named "serial". While "my_rtc"/-1 would be bus_id "my_rtc" (no instance id) and use the platform_driver called "my_rtc". -- cgit v1.2.3 From 78f92a82c20a9f66d215f6c6d96fb91c0763ce95 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Sat, 17 Feb 2007 19:58:30 +0100 Subject: doc: make doc. for maxcpus= more visible Some people are confused about maxcpus=1 and maxcpus=0, so put the documentation text from init/main.c into Documentation/kernel-parameters.txt also. Signed-off-by: Randy Dunlap Signed-off-by: Adrian Bunk --- Documentation/kernel-parameters.txt | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index abd575cfc759..f6a3961da349 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -863,7 +863,14 @@ and is between 256 and 4096 characters. It is defined in the file Format: <1-256> maxcpus= [SMP] Maximum number of processors that an SMP kernel - should make use of + should make use of. + Using "nosmp" or "maxcpus=0" will disable SMP + entirely (the MPS table probe still happens, though). + A command-line option of "maxcpus=", where + is an integer greater than 0, limits the maximum number + of CPUs activated in SMP mode to . + Using "maxcpus=1" on an SMP kernel is the trivial + case of an SMP kernel with only one CPU. max_addr=[KMG] [KNL,BOOT,ia64] All physical memory greater than or equal to this physical address is ignored. -- cgit v1.2.3 From 5c811e59ada9d31f79c8d340f28184084a3aea5b Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Sat, 17 Feb 2007 20:03:14 +0100 Subject: kbuild: more doc. cleanups Fix typos/spellos in kbuild/makefiles.txt. Signed-off-by: Randy Dunlap Signed-off-by: Adrian Bunk --- Documentation/kbuild/makefiles.txt | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) (limited to 'Documentation') diff --git a/Documentation/kbuild/makefiles.txt b/Documentation/kbuild/makefiles.txt index 4b3d6710c504..bb5306e9a5c3 100644 --- a/Documentation/kbuild/makefiles.txt +++ b/Documentation/kbuild/makefiles.txt @@ -34,7 +34,7 @@ This document describes the Linux kernel Makefiles. --- 6.1 Set variables to tweak the build to the architecture --- 6.2 Add prerequisites to archprepare: --- 6.3 List directories to visit when descending - --- 6.4 Architecture specific boot images + --- 6.4 Architecture-specific boot images --- 6.5 Building non-kbuild targets --- 6.6 Commands useful for building a boot image --- 6.7 Custom kbuild commands @@ -124,7 +124,7 @@ more details, with real examples. Example: obj-y += foo.o - This tell kbuild that there is one object in that directory, named + This tells kbuild that there is one object in that directory, named foo.o. foo.o will be built from foo.c or foo.S. If foo.o shall be built as a module, the variable obj-m is used. @@ -353,7 +353,7 @@ more details, with real examples. Special rules are used when the kbuild infrastructure does not provide the required support. A typical example is header files generated during the build process. - Another example are the architecture specific Makefiles which + Another example are the architecture-specific Makefiles which need special rules to prepare boot images etc. Special rules are written as normal Make rules. @@ -416,7 +416,7 @@ more details, with real examples. #arch/i386/kernel/Makefile vsyscall-flags += $(call ld-option, -Wl$(comma)--hash-style=sysv) - In the above example vsyscall-flags will be assigned the option + In the above example, vsyscall-flags will be assigned the option -Wl$(comma)--hash-style=sysv if it is supported by $(CC). The second argument is optional, and if supplied will be used if first argument is not supported. @@ -434,7 +434,7 @@ more details, with real examples. #arch/i386/Makefile cflags-y += $(call cc-option,-march=pentium-mmx,-march=i586) - In the above example cflags-y will be assigned the option + In the above example, cflags-y will be assigned the option -march=pentium-mmx if supported by $(CC), otherwise -march=i586. The second argument to cc-option is optional, and if omitted, cflags-y will be assigned no value if first option is not supported. @@ -750,10 +750,10 @@ When kbuild executes, the following steps are followed (roughly): located at the root of the obj tree. The very first objects linked are listed in head-y, assigned by arch/$(ARCH)/Makefile. -7) Finally, the architecture specific part does any required post processing +7) Finally, the architecture-specific part does any required post processing and builds the final bootimage. - This includes building boot records - - Preparing initrd images and thelike + - Preparing initrd images and the like --- 6.1 Set variables to tweak the build to the architecture @@ -880,7 +880,7 @@ When kbuild executes, the following steps are followed (roughly): $(head-y) lists objects to be linked first in vmlinux. $(libs-y) lists directories where a lib.a archive can be located. - The rest lists directories where a built-in.o object file can be + The rest list directories where a built-in.o object file can be located. $(init-y) objects will be located after $(head-y). @@ -888,7 +888,7 @@ When kbuild executes, the following steps are followed (roughly): $(core-y), $(libs-y), $(drivers-y) and $(net-y). The top level Makefile defines values for all generic directories, - and arch/$(ARCH)/Makefile only adds architecture specific directories. + and arch/$(ARCH)/Makefile only adds architecture-specific directories. Example: #arch/sparc64/Makefile @@ -897,7 +897,7 @@ When kbuild executes, the following steps are followed (roughly): drivers-$(CONFIG_OPROFILE) += arch/sparc64/oprofile/ ---- 6.4 Architecture specific boot images +--- 6.4 Architecture-specific boot images An arch Makefile specifies goals that take the vmlinux file, compress it, wrap it in bootstrapping code, and copy the resulting files @@ -924,7 +924,7 @@ When kbuild executes, the following steps are followed (roughly): "$(Q)$(MAKE) $(build)=" is the recommended way to invoke make in a subdirectory. - There are no rules for naming architecture specific targets, + There are no rules for naming architecture-specific targets, but executing "make help" will list all relevant targets. To support this, $(archhelp) must be defined. @@ -982,7 +982,7 @@ When kbuild executes, the following steps are followed (roughly): $(call if_changed,ld/objcopy/gzip) When the rule is evaluated, it is checked to see if any files - needs an update, or the command line has changed since the last + need an update, or the command line has changed since the last invocation. The latter will force a rebuild if any options to the executable have changed. Any target that utilises if_changed must be listed in $(targets), @@ -1089,7 +1089,7 @@ When kbuild executes, the following steps are followed (roughly): assignment. The kbuild infrastructure for *lds file are used in several - architecture specific files. + architecture-specific files. === 7 Kbuild Variables @@ -1133,7 +1133,7 @@ The top Makefile exports the following variables: This variable defines a place for the arch Makefiles to install the resident kernel image and System.map file. - Use this for architecture specific install targets. + Use this for architecture-specific install targets. INSTALL_MOD_PATH, MODLIB -- cgit v1.2.3 From 86aae08faa0069a559ba543ff3dab33fe95f891b Mon Sep 17 00:00:00 2001 From: James Nelson Date: Sat, 17 Feb 2007 20:15:38 +0100 Subject: Documentation/kernel-docs.txt update. Signed-off-by: James Nelson Signed-off-by: Adrian Bunk --- Documentation/kernel-docs.txt | 257 ++++++++++++++++++++---------------------- 1 file changed, 121 insertions(+), 136 deletions(-) (limited to 'Documentation') diff --git a/Documentation/kernel-docs.txt b/Documentation/kernel-docs.txt index b53bccbd9727..c68dafeda7a7 100644 --- a/Documentation/kernel-docs.txt +++ b/Documentation/kernel-docs.txt @@ -1,10 +1,10 @@ - Index of Documentation for People Interested in Writing and/or - - Understanding the Linux Kernel. - - Juan-Mariano de Goyeneche - + Index of Documentation for People Interested in Writing and/or + + Understanding the Linux Kernel. + + Juan-Mariano de Goyeneche + /* * The latest version of this document may be found at: * http://www.dit.upm.es/~jmseyas/linux/kernel/hackers-docs.html @@ -61,18 +61,18 @@ 13.-The Linux Kernel Sources, A.-Linux Data Structures, B.-The Alpha AXP Processor, C.-Useful Web and FTP Sites, D.-The GNU General Public License, Glossary". In short: a must have. - - * Title: "The Linux Kernel Hackers' Guide" - Author: Michael K.Johnson and others. - URL: http://www.tldp.org/LDP/khg/HyperNews/get/khg.html - Keywords: everything! - Description: No more Postscript book-like version. Only HTML now. - Many people have contributed. The interface is similar to web - available mailing lists archives. You can find some articles and - then some mails asking questions about them and/or complementing - previous contributions. A little bit anarchic in this aspect, but - with some valuable information in some cases. - + + * Title: "Linux Device Drivers, 2nd Edition" + Author: Alessandro Rubini and Jonathan Corbet. + URL: http://www.xml.com/ldd/chapter/book/index.html + Keywords: device drivers, modules, debugging, memory, hardware, + interrupt handling, char drivers, block drivers, kmod, mmap, DMA, + buses. + Description: O'Reilly's popular book, now also on-line under the + GNU Free Documentation License. + Notes: You can also buy it in paper-form from O'Reilly. See below + under BOOKS (Not on-line). + * Title: "Conceptual Architecture of the Linux Kernel" Author: Ivan T. Bowman. URL: http://plg.uwaterloo.ca/~itbowman/papers/CS746G-a1.html @@ -81,17 +81,17 @@ Description: Conceptual software arquitecture of the Linux kernel, automatically extracted from the source code. Very detailed. Good figures. Gives good overall kernel understanding. - + * Title: "Concrete Architecture of the Linux Kernel" Author: Ivan T. Bowman, Saheem Siddiqi, and Meyer C. Tanuan. URL: http://plg.uwaterloo.ca/~itbowman/papers/CS746G-a2.html - Keywords: concrete arquitecture, extracted design, reverse + Keywords: concrete architecture, extracted design, reverse engineering, system structure, dependencies. - Description: Concrete arquitecture of the Linux kernel, + Description: Concrete architecture of the Linux kernel, automatically extracted from the source code. Very detailed. Good figures. Gives good overall kernel understanding. This papers focus on lower details than its predecessor (files, variables...). - + * Title: "Linux as a Case Study: Its Extracted Software Architecture" Author: Ivan T. Bowman, Richard C. Holt and Neil V. Brewster. @@ -101,7 +101,7 @@ Description: Paper appeared at ICSE'99, Los Angeles, May 16-22, 1999. A mixture of the previous two documents from the same author. - + * Title: "Overview of the Virtual File System" Author: Richard Gooch. URL: http://www.atnf.csiro.au/~rgooch/linux/vfs.txt @@ -111,20 +111,20 @@ What is it, how it works, operations taken when opening a file or mounting a file system and description of important data structures explaining the purpose of each of their entries. - + * Title: "The Linux RAID-1, 4, 5 Code" Author: Ingo Molnar, Gadi Oxman and Miguel de Icaza. - URL: http://www2.linuxjournal.com/lj-issues/issue44/2391.html + URL: http://www.linuxjournal.com/article.php?sid=2391 Keywords: RAID, MD driver. Description: Linux Journal Kernel Korner article. Here is it's abstract: "A description of the implementation of the RAID-1, RAID-4 and RAID-5 personalities of the MD device driver in the Linux kernel, providing users with high performance and reliable, secondary-storage capability using software". - + * Title: "Dynamic Kernels: Modularized Device Drivers" Author: Alessandro Rubini. - URL: http://www2.linuxjournal.com/lj-issues/issue23/1219.html + URL: http://www.linuxjournal.com/article.php?sid=1219 Keywords: device driver, module, loading/unloading modules, allocating resources. Description: Linux Journal Kernel Korner article. Here is it's @@ -134,10 +134,10 @@ loadable modules. This installment presents an introduction to the topic, preparing the reader to understand next month's installment". - + * Title: "Dynamic Kernels: Discovery" Author: Alessandro Rubini. - URL: http://www2.linuxjournal.com/lj-issues/issue24/1220.html + URL: http://www.linuxjournal.com/article.php?sid=1220 Keywords: character driver, init_module, clean_up module, autodetection, mayor number, minor number, file operations, open(), close(). @@ -146,20 +146,20 @@ the actual code to create custom module implementing a character device driver. It describes the code for module initialization and cleanup, as well as the open() and close() system calls". - + * Title: "The Devil's in the Details" Author: Georg v. Zezschwitz and Alessandro Rubini. - URL: http://www2.linuxjournal.com/lj-issues/issue25/1221.html + URL: http://www.linuxjournal.com/article.php?sid=1221 Keywords: read(), write(), select(), ioctl(), blocking/non blocking mode, interrupt handler. Description: Linux Journal Kernel Korner article. Here is it's abstract: "This article, the third of four on writing character device drivers, introduces concepts of reading, writing, and using ioctl-calls". - + * Title: "Dissecting Interrupts and Browsing DMA" Author: Alessandro Rubini and Georg v. Zezschwitz. - URL: http://www2.linuxjournal.com/lj-issues/issue26/1222.html + URL: http://www.linuxjournal.com/article.php?sid=1222 Keywords: interrupts, irqs, DMA, bottom halves, task queues. Description: Linux Journal Kernel Korner article. Here is it's abstract: "This is the fourth in a series of articles about @@ -170,10 +170,10 @@ writing, and several different facilities have been provided for different situations. We also investigate the complex topic of DMA". - + * Title: "Device Drivers Concluded" Author: Georg v. Zezschwitz. - URL: http://www2.linuxjournal.com/lj-issues/issue28/1287.html + URL: http://www.linuxjournal.com/article.php?sid=1287 Keywords: address spaces, pages, pagination, page management, demand loading, swapping, memory protection, memory mapping, mmap, virtual memory areas (VMAs), vremap, PCI. @@ -182,10 +182,10 @@ five articles about character device drivers. In this final section, Georg deals with memory mapping devices, beginning with an overall description of the Linux memory management concepts". - + * Title: "Network Buffers And Memory Management" Author: Alan Cox. - URL: http://www2.linuxjournal.com/lj-issues/issue30/1312.html + URL: http://www.linuxjournal.com/article.php?sid=1312 Keywords: sk_buffs, network devices, protocol/link layer variables, network devices flags, transmit, receive, configuration, multicast. @@ -214,28 +214,26 @@ of the Coda filesystem. This version document is meant to describe the current interface (version 1.0) as well as improvements we envisage". - + * Title: "Programming PCI-Devices under Linux" Author: Claus Schroeter. URL: - ftp://ftp.llp.fu-berlin.de/pub/linux/LINUX-LAB/whitepapers/pcip.ps - .gz + ftp://ftp.llp.fu-berlin.de/pub/linux/LINUX-LAB/whitepapers/pcip.ps.gz Keywords: PCI, device, busmastering. Description: 6 pages tutorial on PCI programming under Linux. Gives the basic concepts on the architecture of the PCI subsystem, as long as basic functions and macros to read/write the devices and perform busmastering. - + * Title: "Writing Character Device Driver for Linux" Author: R. Baruch and C. Schroeter. URL: - ftp://ftp.llp.fu-berlin.de/pub/linux/LINUX-LAB/whitepapers/drivers - .ps.gz + ftp://ftp.llp.fu-berlin.de/pub/linux/LINUX-LAB/whitepapers/drivers.ps.gz Keywords: character device drivers, I/O, signals, DMA, accessing ports in user space, kernel environment. Description: 68 pages paper on writing character drivers. A little bit old (1.993, 1.994) although still useful. - + * Title: "Design and Implementation of the Second Extended Filesystem" Author: Rémy Card, Theodore Ts'o, Stephen Tweedie. @@ -249,14 +247,14 @@ e2fsck's passes description... A must read! Notes: This paper was first published in the Proceedings of the First Dutch International Symposium on Linux, ISBN 90-367-0385-9. - + * Title: "Analysis of the Ext2fs structure" Author: Louis-Dominique Dubeau. - URL: http://step.polymtl.ca/~ldd/ext2fs/ext2fs_toc.html + URL: http://www.nondot.org/sabre/os/files/FileSystems/ext2fs/ Keywords: ext2, filesystem, ext2fs. Description: Description of ext2's blocks, directories, inodes, bitmaps, invariants... - + * Title: "Journaling the Linux ext2fs Filesystem" Author: Stephen C. Tweedie. URL: @@ -265,7 +263,7 @@ Description: Excellent 8-pages paper explaining the journaling capabilities added to ext2 by the author, showing different problems faced and the alternatives chosen. - + * Title: "Kernel API changes from 2.0 to 2.2" Author: Richard Gooch. URL: @@ -273,7 +271,7 @@ Keywords: 2.2, changes. Description: Kernel functions/structures/variables which changed from 2.0.x to 2.2.x. - + * Title: "Kernel API changes from 2.2 to 2.4" Author: Richard Gooch. URL: @@ -345,17 +343,7 @@ Notes: Beware: the main page states: "This document may not be published, printed or used in excerpts without explicit permission of the author". Fortunately, it may still be read... - - * Title: "Tour Of the Linux Kernel Source" - Author: Vijo Cherian. - URL: http://www.geocities.com/vijoc/tolks/tolks.html - Keywords: . - Description: A classic of this page! Was lost for a while and is - back again. Thanks Vijo! TOLKS: the name says it all. A tour of - the sources, describing directories, files, variables, data - structures... It covers general stuff, device drivers, - filesystems, IPC and Networking Code. - + * Title: "Linux Kernel Mailing List Glossary" Author: various URL: http://kernelnewbies.org/glossary/ @@ -377,7 +365,17 @@ kernels, but most of it applies to 2.2 too; 2.0 is slightly different". Freely redistributable under the conditions of the GNU General Public License. - + + * Title: "Global spinlock list and usage" + Author: Rick Lindsley. + URL: http://lse.sourceforge.net/lockhier/global-spin-lock + Keywords: spinlock. + Description: This is an attempt to document both the existence and + usage of the spinlocks in the Linux 2.4.5 kernel. Comprehensive + list of spinlocks showing when they are used, which functions + access them, how each lock is acquired, under what conditions it + is held, whether interrupts can occur or not while it is held... + * Title: "Porting Linux 2.0 Drivers To Linux 2.2: Changes and New Features " Author: Alan Cox. @@ -385,70 +383,70 @@ Keywords: ports, porting. Description: Article from Linux Magazine on porting from 2.0 to 2.2 kernels. - + * Title: "Porting Device Drivers To Linux 2.2: part II" Author: Alan Cox. URL: http://www.linux-mag.com/1999-06/gear_01.html Keywords: ports, porting. Description: Second part on porting from 2.0 to 2.2 kernels. - + * Title: "How To Make Sure Your Driver Will Work On The Power Macintosh" Author: Paul Mackerras. URL: http://www.linux-mag.com/1999-07/gear_01.html Keywords: Mac, Power Macintosh, porting, drivers, compatibility. Description: The title says it all. - + * Title: "An Introduction to SCSI Drivers" Author: Alan Cox. URL: http://www.linux-mag.com/1999-08/gear_01.html Keywords: SCSI, device, driver. Description: The title says it all. - + * Title: "Advanced SCSI Drivers And Other Tales" Author: Alan Cox. URL: http://www.linux-mag.com/1999-09/gear_01.html Keywords: SCSI, device, driver, advanced. Description: The title says it all. - + * Title: "Writing Linux Mouse Drivers" Author: Alan Cox. URL: http://www.linux-mag.com/1999-10/gear_01.html Keywords: mouse, driver, gpm. Description: The title says it all. - + * Title: "More on Mouse Drivers" Author: Alan Cox. URL: http://www.linux-mag.com/1999-11/gear_01.html Keywords: mouse, driver, gpm, races, asynchronous I/O. Description: The title still says it all. - + * Title: "Writing Video4linux Radio Driver" Author: Alan Cox. URL: http://www.linux-mag.com/1999-12/gear_01.html Keywords: video4linux, driver, radio, radio devices. Description: The title says it all. - + * Title: "Video4linux Drivers, Part 1: Video-Capture Device" Author: Alan Cox. URL: http://www.linux-mag.com/2000-01/gear_01.html Keywords: video4linux, driver, video capture, capture devices, camera driver. Description: The title says it all. - + * Title: "Video4linux Drivers, Part 2: Video-capture Devices" Author: Alan Cox. URL: http://www.linux-mag.com/2000-02/gear_01.html Keywords: video4linux, driver, video capture, capture devices, camera driver, control, query capabilities, capability, facility. Description: The title says it all. - + * Title: "PCI Management in Linux 2.2" Author: Alan Cox. URL: http://www.linux-mag.com/2000-03/gear_01.html Keywords: PCI, bus, bus-mastering. Description: The title says it all. - + * Title: "Linux 2.4 Kernel Internals" Author: Tigran Aivazian and Christoph Hellwig. URL: http://www.moses.uklinux.net/patches/lki.html @@ -456,13 +454,11 @@ Description: A little book used for a short training course. Covers building the kernel image, booting (including SMP bootup), process management, VFS and more. - + * Title: "Linux IP Networking. A Guide to the Implementation and Modification of the Linux Protocol Stack." Author: Glenn Herrin. - URL: - http://kernelnewbies.org/documents/ipnetworking/linuxipnetworking. - html + URL: http://www.cs.unh.edu/cnrg/gherrin Keywords: network, networking, protocol, IP, UDP, TCP, connection, socket, receiving, transmitting, forwarding, routing, packets, modules, /proc, sk_buff, FIB, tags. @@ -495,7 +491,7 @@ drivers for the Linux PCMCIA Card Services interface. It also describes how to write user-mode utilities for communicating with Card Services. - + * Title: "The Linux Kernel NFSD Implementation" Author: Neil Brown. URL: @@ -591,47 +587,22 @@ Pages: 520. ISBN: 2-212-08932-5 Notes: French. - - * Title: "The Linux Kernel Book" - Author: Remy Card, Eric Dumas, Franck Mevel. - Publisher: John Wiley & Sons. - Date: 1998. - ISBN: 0-471-98141-9 - Notes: English translation. - - * Title: "Linux 2.0" - Author: Remy Card, Eric Dumas, Franck Mevel. - Publisher: Gestión 2000. - Date: 1997. - Pages: 501. - ISBN: 8-480-88208-5 - Notes: Spanish translation. - + * Title: "Unix internals -- the new frontiers" Author: Uresh Vahalia. Publisher: Prentice Hall. Date: 1996. Pages: 600. ISBN: 0-13-101908-2 - - * Title: "Linux Core Kernel Commentary. Guide to Insider's Knowledge - on the Core Kernel of the Linux Code" - Author: Scott Maxwell. - Publisher: Coriolis. - Date: 1999. - Pages: 592. - ISBN: 1-57610-469-9 - Notes: CD-ROM included. Line by line commentary of the kernel - code. - - * Title: "Linux IP Stacks Commentary" - Author: Stephen Satchell and HBJ Clifford. - Publisher: Coriolis. - Date: 2000. - Pages: ???. - ISBN: 1-57610-470-2 - Notes: Line by line source code commentary book. - + + * Title: "The Design and Implementation of the 4.4 BSD UNIX + Operating System" + Author: Marshall Kirk McKusick, Keith Bostic, Michael J. Karels, + John S. Quarterman. + Publisher: Addison-Wesley. + Date: 1996. + ISBN: 0-201-54979-4 + * Title: "Programming for the real world - POSIX.4" Author: Bill O. Gallmeister. Publisher: O'Reilly & Associates, Inc.. @@ -640,18 +611,32 @@ ISBN: I-56592-074-0 Notes: Though not being directly about Linux, Linux aims to be POSIX. Good reference. - - * Title: "Understanding the Linux Kernel" - Author: Daniel P. Bovet and Marco Cesati. - Publisher: O'Reilly & Associates, Inc.. - Date: 2000. - Pages: 702. - ISBN: 0-596-00002-2 - Notes: Further information in - http://www.oreilly.com/catalog/linuxkernel/ - + + * Title: "UNIX Systems for Modern Architectures: Symmetric + Multiprocesssing and Caching for Kernel Programmers" + Author: Curt Schimmel. + Publisher: Addison Wesley. + Date: June, 1994. + Pages: 432. + ISBN: 0-201-63338-8 + + * Title: "The Design and Implementation of the 4.3 BSD UNIX + Operating System" + Author: Samuel J. Leffler, Marshall Kirk McKusick, Michael J. + Karels, John S. Quarterman. + Publisher: Addison-Wesley. + Date: 1989 (reprinted with corrections on October, 1990). + ISBN: 0-201-06196-1 + + * Title: "The Design of the UNIX Operating System" + Author: Maurice J. Bach. + Publisher: Prentice Hall. + Date: 1986. + Pages: 471. + ISBN: 0-13-201757-1 + MISCELLANEOUS: - + * Name: linux/Documentation Author: Many. URL: Just look inside your kernel sources. @@ -660,7 +645,7 @@ inside the Documentation directory. Some pages from this document (including this document itself) have been moved there, and might be more up to date than the web version. - + * Name: "Linux Source Driver" URL: http://lsd.linux.cz Keywords: Browsing source code. @@ -671,7 +656,7 @@ you can search Linux kernel (fulltext, macros, types, functions and variables) and LSD can generate patches for you on the fly (files, directories or kernel)". - + * Name: "Linux Kernel Source Reference" Author: Thomas Graichen. URL: http://innominate.org/~graichen/projects/lksr/ @@ -681,27 +666,27 @@ sources of any version starting from 1.0 up to the (daily updated) current version available. Also you can check the differences between two versions of a file". - + * Name: "Cross-Referencing Linux" URL: http://lxr.linux.no/source/ Keywords: Browsing source code. Description: Another web-based Linux kernel source code browser. Lots of cross references to variables and functions. You can see where they are defined and where they are used. - + * Name: "Linux Weekly News" URL: http://lwn.net Keywords: latest kernel news. Description: The title says it all. There's a fixed kernel section summarizing developers' work, bug fixes, new features and versions produced during the week. Published every Thursday. - + * Name: "Kernel Traffic" - URL: http://www.kerneltraffic.org/kernel-traffic/ + URL: http://kt.zork.net/kernel-traffic/ Keywords: linux-kernel mailing list, weekly kernel news. Description: Weekly newsletter covering the most relevant discussions of the linux-kernel mailing list. - + * Name: "CuTTiNG.eDGe.LiNuX" URL: http://edge.kernelnotes.org Keywords: changelist. @@ -709,7 +694,7 @@ release. What's new, what's better, what's changed. Myrdraal reads the patches and describes them. Pointers to the patches are there, too. - + * Name: "New linux-kernel Mailing List FAQ" URL: http://www.tux.org/lkml/ Keywords: linux-kernel mailing list FAQ. @@ -719,7 +704,7 @@ it. Read it to see how to join the mailing list. Dozens of interesting questions regarding the list, Linux, developers (who is ...?), terms (what is...?) are answered here too. Just read it. - + * Name: "Linux Virtual File System" Author: Peter J. Braam. URL: http://www.coda.cs.cmu.edu/doc/talks/linuxvfs/ @@ -727,10 +712,10 @@ Description: Set of slides, presumably from a presentation on the Linux VFS layer. Covers version 2.1.x, with dentries and the dcache. - + * Name: "Gary's Encyclopedia - The Linux Kernel" Author: Gary (I suppose...). - URL: http://members.aa.net/~swear/pedia/kernel.html + URL: http://www.lisoleg.net/cgi-bin/lisoleg.pl?view=kernel.htm Keywords: links, not found here?. Description: Gary's Encyclopedia exists to allow the rapid finding of documentation and other information of interest to GNU/Linux @@ -738,7 +723,7 @@ categories. This link is for kernel-specific links, documents, sites... Look there if you could not find here what you were looking for. - + * Name: "The home page of Linux-MM" Author: The Linux-MM team. URL: http://linux-mm.org/ @@ -747,7 +732,7 @@ Description: Site devoted to Linux Memory Management development. Memory related patches, HOWTOs, links, mm developers... Don't miss it if you are interested in memory management development! - + * Name: "Kernel Newbies IRC Channel" URL: http://www.kernelnewbies.org Keywords: IRC, newbies, channel, asking doubts. -- cgit v1.2.3 From 419ee448ff76aef13526a99c2dc39ba3ae1f0970 Mon Sep 17 00:00:00 2001 From: Jeff Garzik Date: Sat, 17 Feb 2007 16:10:59 -0500 Subject: Remove JFFS (version 1), as scheduled. Unmaintained for years, few if any users. Signed-off-by: Jeff Garzik --- Documentation/feature-removal-schedule.txt | 7 - fs/Kconfig | 26 - fs/Makefile | 1 - fs/jffs/Makefile | 11 - fs/jffs/inode-v23.c | 1847 --------------- fs/jffs/intrep.c | 3449 ---------------------------- fs/jffs/intrep.h | 58 - fs/jffs/jffs_fm.c | 798 ------- fs/jffs/jffs_fm.h | 149 -- fs/jffs/jffs_proc.c | 261 --- fs/jffs/jffs_proc.h | 28 - include/linux/jffs.h | 224 -- 12 files changed, 6859 deletions(-) delete mode 100644 fs/jffs/Makefile delete mode 100644 fs/jffs/inode-v23.c delete mode 100644 fs/jffs/intrep.c delete mode 100644 fs/jffs/intrep.h delete mode 100644 fs/jffs/jffs_fm.c delete mode 100644 fs/jffs/jffs_fm.h delete mode 100644 fs/jffs/jffs_proc.c delete mode 100644 fs/jffs/jffs_proc.h delete mode 100644 include/linux/jffs.h (limited to 'Documentation') diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt index c585aa8d62b4..e1bc0c534ef9 100644 --- a/Documentation/feature-removal-schedule.txt +++ b/Documentation/feature-removal-schedule.txt @@ -306,13 +306,6 @@ Who: Len Brown --------------------------- -What: JFFS (version 1) -When: 2.6.21 -Why: Unmaintained for years, superceded by JFFS2 for years. -Who: Jeff Garzik - ---------------------------- - What: sk98lin network driver When: July 2007 Why: In kernel tree version of driver is unmaintained. Sk98lin driver diff --git a/fs/Kconfig b/fs/Kconfig index a722b5a3f752..3c4886b849f5 100644 --- a/fs/Kconfig +++ b/fs/Kconfig @@ -1189,32 +1189,6 @@ config EFS_FS To compile the EFS file system support as a module, choose M here: the module will be called efs. -config JFFS_FS - tristate "Journalling Flash File System (JFFS) support" - depends on MTD && BLOCK && BROKEN - help - JFFS is the Journalling Flash File System developed by Axis - Communications in Sweden, aimed at providing a crash/powerdown-safe - file system for disk-less embedded devices. Further information is - available at (). - - NOTE: This filesystem is deprecated and is scheduled for removal in - 2.6.21. See Documentation/feature-removal-schedule.txt - -config JFFS_FS_VERBOSE - int "JFFS debugging verbosity (0 = quiet, 3 = noisy)" - depends on JFFS_FS - default "0" - help - Determines the verbosity level of the JFFS debugging messages. - -config JFFS_PROC_FS - bool "JFFS stats available in /proc filesystem" - depends on JFFS_FS && PROC_FS - help - Enabling this option will cause statistics from mounted JFFS file systems - to be made available to the user in the /proc/fs/jffs/ directory. - config JFFS2_FS tristate "Journalling Flash File System v2 (JFFS2) support" select CRC32 diff --git a/fs/Makefile b/fs/Makefile index b9ffa63f77fc..9edf4112bee0 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -94,7 +94,6 @@ obj-$(CONFIG_HPFS_FS) += hpfs/ obj-$(CONFIG_NTFS_FS) += ntfs/ obj-$(CONFIG_UFS_FS) += ufs/ obj-$(CONFIG_EFS_FS) += efs/ -obj-$(CONFIG_JFFS_FS) += jffs/ obj-$(CONFIG_JFFS2_FS) += jffs2/ obj-$(CONFIG_AFFS_FS) += affs/ obj-$(CONFIG_ROMFS_FS) += romfs/ diff --git a/fs/jffs/Makefile b/fs/jffs/Makefile deleted file mode 100644 index 9c1c0bb59696..000000000000 --- a/fs/jffs/Makefile +++ /dev/null @@ -1,11 +0,0 @@ -# -# Makefile for the linux Journalling Flash FileSystem (JFFS) routines. -# -# $Id: Makefile,v 1.11 2001/09/25 20:59:41 dwmw2 Exp $ -# - -obj-$(CONFIG_JFFS_FS) += jffs.o - -jffs-y := jffs_fm.o intrep.o inode-v23.o -jffs-$(CONFIG_JFFS_PROC_FS) += jffs_proc.o -jffs-objs := $(jffs-y) diff --git a/fs/jffs/inode-v23.c b/fs/jffs/inode-v23.c deleted file mode 100644 index 9602b925da08..000000000000 --- a/fs/jffs/inode-v23.c +++ /dev/null @@ -1,1847 +0,0 @@ -/* - * JFFS -- Journalling Flash File System, Linux implementation. - * - * Copyright (C) 1999, 2000 Axis Communications AB. - * - * Created by Finn Hakansson . - * - * This 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. - * - * $Id: inode-v23.c,v 1.70 2001/10/02 09:16:02 dwmw2 Exp $ - * - * Ported to Linux 2.3.x and MTD: - * Copyright (C) 2000 Alexander Larsson (alex@cendio.se), Cendio Systems AB - * - * Copyright 2000, 2001 Red Hat, Inc. - */ - -/* inode.c -- Contains the code that is called from the VFS. */ - -/* TODO-ALEX: - * uid and gid are just 16 bit. - * jffs_file_write reads from user-space pointers without xx_from_user - * maybe other stuff do to. - */ - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "jffs_fm.h" -#include "intrep.h" -#ifdef CONFIG_JFFS_PROC_FS -#include "jffs_proc.h" -#endif - -static int jffs_remove(struct inode *dir, struct dentry *dentry, int type); - -static const struct super_operations jffs_ops; -static const struct file_operations jffs_file_operations; -static const struct inode_operations jffs_file_inode_operations; -static const struct file_operations jffs_dir_operations; -static const struct inode_operations jffs_dir_inode_operations; -static const struct address_space_operations jffs_address_operations; - -struct kmem_cache *node_cache = NULL; -struct kmem_cache *fm_cache = NULL; - -/* Called by the VFS at mount time to initialize the whole file system. */ -static int jffs_fill_super(struct super_block *sb, void *data, int silent) -{ - struct inode *root_inode; - struct jffs_control *c; - - sb->s_flags |= MS_NODIRATIME; - - D1(printk(KERN_NOTICE "JFFS: Trying to mount device %s.\n", - sb->s_id)); - - if (MAJOR(sb->s_dev) != MTD_BLOCK_MAJOR) { - printk(KERN_WARNING "JFFS: Trying to mount a " - "non-mtd device.\n"); - return -EINVAL; - } - - sb->s_blocksize = PAGE_CACHE_SIZE; - sb->s_blocksize_bits = PAGE_CACHE_SHIFT; - sb->s_fs_info = (void *) 0; - sb->s_maxbytes = 0xFFFFFFFF; - - /* Build the file system. */ - if (jffs_build_fs(sb) < 0) { - goto jffs_sb_err1; - } - - /* - * set up enough so that we can read an inode - */ - sb->s_magic = JFFS_MAGIC_SB_BITMASK; - sb->s_op = &jffs_ops; - - root_inode = iget(sb, JFFS_MIN_INO); - if (!root_inode) - goto jffs_sb_err2; - - /* Get the root directory of this file system. */ - if (!(sb->s_root = d_alloc_root(root_inode))) { - goto jffs_sb_err3; - } - - c = (struct jffs_control *) sb->s_fs_info; - -#ifdef CONFIG_JFFS_PROC_FS - /* Set up the jffs proc file system. */ - if (jffs_register_jffs_proc_dir(MINOR(sb->s_dev), c) < 0) { - printk(KERN_WARNING "JFFS: Failed to initialize the JFFS " - "proc file system for device %s.\n", - sb->s_id); - } -#endif - - /* Set the Garbage Collection thresholds */ - - /* GC if free space goes below 5% of the total size */ - c->gc_minfree_threshold = c->fmc->flash_size / 20; - - if (c->gc_minfree_threshold < c->fmc->sector_size) - c->gc_minfree_threshold = c->fmc->sector_size; - - /* GC if dirty space exceeds 33% of the total size. */ - c->gc_maxdirty_threshold = c->fmc->flash_size / 3; - - if (c->gc_maxdirty_threshold < c->fmc->sector_size) - c->gc_maxdirty_threshold = c->fmc->sector_size; - - - c->thread_pid = kernel_thread (jffs_garbage_collect_thread, - (void *) c, - CLONE_KERNEL); - D1(printk(KERN_NOTICE "JFFS: GC thread pid=%d.\n", (int) c->thread_pid)); - - D1(printk(KERN_NOTICE "JFFS: Successfully mounted device %s.\n", - sb->s_id)); - return 0; - -jffs_sb_err3: - iput(root_inode); -jffs_sb_err2: - jffs_cleanup_control((struct jffs_control *)sb->s_fs_info); -jffs_sb_err1: - printk(KERN_WARNING "JFFS: Failed to mount device %s.\n", - sb->s_id); - return -EINVAL; -} - - -/* This function is called when the file system is umounted. */ -static void -jffs_put_super(struct super_block *sb) -{ - struct jffs_control *c = (struct jffs_control *) sb->s_fs_info; - - D2(printk("jffs_put_super()\n")); - -#ifdef CONFIG_JFFS_PROC_FS - jffs_unregister_jffs_proc_dir(c); -#endif - - if (c->gc_task) { - D1(printk (KERN_NOTICE "jffs_put_super(): Telling gc thread to die.\n")); - send_sig(SIGKILL, c->gc_task, 1); - } - wait_for_completion(&c->gc_thread_comp); - - D1(printk (KERN_NOTICE "jffs_put_super(): Successfully waited on thread.\n")); - - jffs_cleanup_control((struct jffs_control *)sb->s_fs_info); - D1(printk(KERN_NOTICE "JFFS: Successfully unmounted device %s.\n", - sb->s_id)); -} - - -/* This function is called when user commands like chmod, chgrp and - chown are executed. System calls like trunc() results in a call - to this function. */ -static int -jffs_setattr(struct dentry *dentry, struct iattr *iattr) -{ - struct inode *inode = dentry->d_inode; - struct jffs_raw_inode raw_inode; - struct jffs_control *c; - struct jffs_fmcontrol *fmc; - struct jffs_file *f; - struct jffs_node *new_node; - int update_all; - int res = 0; - int recoverable = 0; - - lock_kernel(); - - if ((res = inode_change_ok(inode, iattr))) - goto out; - - c = (struct jffs_control *)inode->i_sb->s_fs_info; - fmc = c->fmc; - - D3(printk (KERN_NOTICE "notify_change(): down biglock\n")); - mutex_lock(&fmc->biglock); - - f = jffs_find_file(c, inode->i_ino); - - ASSERT(if (!f) { - printk("jffs_setattr(): Invalid inode number: %lu\n", - inode->i_ino); - D3(printk (KERN_NOTICE "notify_change(): up biglock\n")); - mutex_unlock(&fmc->biglock); - res = -EINVAL; - goto out; - }); - - D1(printk("***jffs_setattr(): file: \"%s\", ino: %u\n", - f->name, f->ino)); - - update_all = iattr->ia_valid & ATTR_FORCE; - - if ( (update_all || iattr->ia_valid & ATTR_SIZE) - && (iattr->ia_size + 128 < f->size) ) { - /* We're shrinking the file by more than 128 bytes. - We'll be able to GC and recover this space, so - allow it to go into the reserved space. */ - recoverable = 1; - } - - if (!(new_node = jffs_alloc_node())) { - D(printk("jffs_setattr(): Allocation failed!\n")); - D3(printk (KERN_NOTICE "notify_change(): up biglock\n")); - mutex_unlock(&fmc->biglock); - res = -ENOMEM; - goto out; - } - - new_node->data_offset = 0; - new_node->removed_size = 0; - raw_inode.magic = JFFS_MAGIC_BITMASK; - raw_inode.ino = f->ino; - raw_inode.pino = f->pino; - raw_inode.mode = f->mode; - raw_inode.uid = f->uid; - raw_inode.gid = f->gid; - raw_inode.atime = f->atime; - raw_inode.mtime = f->mtime; - raw_inode.ctime = f->ctime; - raw_inode.dsize = 0; - raw_inode.offset = 0; - raw_inode.rsize = 0; - raw_inode.dsize = 0; - raw_inode.nsize = f->nsize; - raw_inode.nlink = f->nlink; - raw_inode.spare = 0; - raw_inode.rename = 0; - raw_inode.deleted = 0; - - if (update_all || iattr->ia_valid & ATTR_MODE) { - raw_inode.mode = iattr->ia_mode; - inode->i_mode = iattr->ia_mode; - } - if (update_all || iattr->ia_valid & ATTR_UID) { - raw_inode.uid = iattr->ia_uid; - inode->i_uid = iattr->ia_uid; - } - if (update_all || iattr->ia_valid & ATTR_GID) { - raw_inode.gid = iattr->ia_gid; - inode->i_gid = iattr->ia_gid; - } - if (update_all || iattr->ia_valid & ATTR_SIZE) { - int len; - D1(printk("jffs_notify_change(): Changing size " - "to %lu bytes!\n", (long)iattr->ia_size)); - raw_inode.offset = iattr->ia_size; - - /* Calculate how many bytes need to be removed from - the end. */ - if (f->size < iattr->ia_size) { - len = 0; - } - else { - len = f->size - iattr->ia_size; - } - - raw_inode.rsize = len; - - /* The updated node will be a removal node, with - base at the new size and size of the nbr of bytes - to be removed. */ - new_node->data_offset = iattr->ia_size; - new_node->removed_size = len; - inode->i_size = iattr->ia_size; - inode->i_blocks = (inode->i_size + 511) >> 9; - - if (len) { - invalidate_mapping_pages(inode->i_mapping, 0, -1); - } - inode->i_ctime = CURRENT_TIME_SEC; - inode->i_mtime = inode->i_ctime; - } - if (update_all || iattr->ia_valid & ATTR_ATIME) { - raw_inode.atime = iattr->ia_atime.tv_sec; - inode->i_atime = iattr->ia_atime; - } - if (update_all || iattr->ia_valid & ATTR_MTIME) { - raw_inode.mtime = iattr->ia_mtime.tv_sec; - inode->i_mtime = iattr->ia_mtime; - } - if (update_all || iattr->ia_valid & ATTR_CTIME) { - raw_inode.ctime = iattr->ia_ctime.tv_sec; - inode->i_ctime = iattr->ia_ctime; - } - - /* Write this node to the flash. */ - if ((res = jffs_write_node(c, new_node, &raw_inode, f->name, NULL, recoverable, f)) < 0) { - D(printk("jffs_notify_change(): The write failed!\n")); - jffs_free_node(new_node); - D3(printk (KERN_NOTICE "n_c(): up biglock\n")); - mutex_unlock(&c->fmc->biglock); - goto out; - } - - jffs_insert_node(c, f, &raw_inode, NULL, new_node); - - mark_inode_dirty(inode); - D3(printk (KERN_NOTICE "n_c(): up biglock\n")); - mutex_unlock(&c->fmc->biglock); -out: - unlock_kernel(); - return res; -} /* jffs_notify_change() */ - - -static struct inode * -jffs_new_inode(const struct inode * dir, struct jffs_raw_inode *raw_inode, - int * err) -{ - struct super_block * sb; - struct inode * inode; - struct jffs_control *c; - struct jffs_file *f; - - sb = dir->i_sb; - inode = new_inode(sb); - if (!inode) { - *err = -ENOMEM; - return NULL; - } - - c = (struct jffs_control *)sb->s_fs_info; - - inode->i_ino = raw_inode->ino; - inode->i_mode = raw_inode->mode; - inode->i_nlink = raw_inode->nlink; - inode->i_uid = raw_inode->uid; - inode->i_gid = raw_inode->gid; - inode->i_size = raw_inode->dsize; - inode->i_atime.tv_sec = raw_inode->atime; - inode->i_mtime.tv_sec = raw_inode->mtime; - inode->i_ctime.tv_sec = raw_inode->ctime; - inode->i_ctime.tv_nsec = 0; - inode->i_mtime.tv_nsec = 0; - inode->i_atime.tv_nsec = 0; - inode->i_blocks = (inode->i_size + 511) >> 9; - - f = jffs_find_file(c, raw_inode->ino); - - inode->i_private = (void *)f; - insert_inode_hash(inode); - - return inode; -} - -/* Get statistics of the file system. */ -static int -jffs_statfs(struct dentry *dentry, struct kstatfs *buf) -{ - struct jffs_control *c = (struct jffs_control *) dentry->d_sb->s_fs_info; - struct jffs_fmcontrol *fmc; - - lock_kernel(); - - fmc = c->fmc; - - D2(printk("jffs_statfs()\n")); - - buf->f_type = JFFS_MAGIC_SB_BITMASK; - buf->f_bsize = PAGE_CACHE_SIZE; - buf->f_blocks = (fmc->flash_size / PAGE_CACHE_SIZE) - - (fmc->min_free_size / PAGE_CACHE_SIZE); - buf->f_bfree = (jffs_free_size1(fmc) + jffs_free_size2(fmc) + - fmc->dirty_size - fmc->min_free_size) - >> PAGE_CACHE_SHIFT; - buf->f_bavail = buf->f_bfree; - - /* Find out how many files there are in the filesystem. */ - buf->f_files = jffs_foreach_file(c, jffs_file_count); - buf->f_ffree = buf->f_bfree; - /* buf->f_fsid = 0; */ - buf->f_namelen = JFFS_MAX_NAME_LEN; - - unlock_kernel(); - - return 0; -} - - -/* Rename a file. */ -static int -jffs_rename(struct inode *old_dir, struct dentry *old_dentry, - struct inode *new_dir, struct dentry *new_dentry) -{ - struct jffs_raw_inode raw_inode; - struct jffs_control *c; - struct jffs_file *old_dir_f; - struct jffs_file *new_dir_f; - struct jffs_file *del_f; - struct jffs_file *f; - struct jffs_node *node; - struct inode *inode; - int result = 0; - __u32 rename_data = 0; - - D2(printk("***jffs_rename()\n")); - - D(printk("jffs_rename(): old_dir: 0x%p, old name: 0x%p, " - "new_dir: 0x%p, new name: 0x%p\n", - old_dir, old_dentry->d_name.name, - new_dir, new_dentry->d_name.name)); - - lock_kernel(); - c = (struct jffs_control *)old_dir->i_sb->s_fs_info; - ASSERT(if (!c) { - printk(KERN_ERR "jffs_rename(): The old_dir inode " - "didn't have a reference to a jffs_file struct\n"); - unlock_kernel(); - return -EIO; - }); - - result = -ENOTDIR; - if (!(old_dir_f = old_dir->i_private)) { - D(printk("jffs_rename(): Old dir invalid.\n")); - goto jffs_rename_end; - } - - /* Try to find the file to move. */ - result = -ENOENT; - if (!(f = jffs_find_child(old_dir_f, old_dentry->d_name.name, - old_dentry->d_name.len))) { - goto jffs_rename_end; - } - - /* Find the new directory. */ - result = -ENOTDIR; - if (!(new_dir_f = new_dir->i_private)) { - D(printk("jffs_rename(): New dir invalid.\n")); - goto jffs_rename_end; - } - D3(printk (KERN_NOTICE "rename(): down biglock\n")); - mutex_lock(&c->fmc->biglock); - /* Create a node and initialize as much as needed. */ - result = -ENOMEM; - if (!(node = jffs_alloc_node())) { - D(printk("jffs_rename(): Allocation failed: node == 0\n")); - goto jffs_rename_end; - } - node->data_offset = 0; - node->removed_size = 0; - - /* Initialize the raw inode. */ - raw_inode.magic = JFFS_MAGIC_BITMASK; - raw_inode.ino = f->ino; - raw_inode.pino = new_dir_f->ino; -/* raw_inode.version = f->highest_version + 1; */ - raw_inode.mode = f->mode; - raw_inode.uid = current->fsuid; - raw_inode.gid = current->fsgid; -#if 0 - raw_inode.uid = f->uid; - raw_inode.gid = f->gid; -#endif - raw_inode.atime = get_seconds(); - raw_inode.mtime = raw_inode.atime; - raw_inode.ctime = f->ctime; - raw_inode.offset = 0; - raw_inode.dsize = 0; - raw_inode.rsize = 0; - raw_inode.nsize = new_dentry->d_name.len; - raw_inode.nlink = f->nlink; - raw_inode.spare = 0; - raw_inode.rename = 0; - raw_inode.deleted = 0; - - /* See if there already exists a file with the same name as - new_name. */ - if ((del_f = jffs_find_child(new_dir_f, new_dentry->d_name.name, - new_dentry->d_name.len))) { - raw_inode.rename = 1; - raw_inode.dsize = sizeof(__u32); - rename_data = del_f->ino; - } - - /* Write the new node to the flash memory. */ - if ((result = jffs_write_node(c, node, &raw_inode, - new_dentry->d_name.name, - (unsigned char*)&rename_data, 0, f)) < 0) { - D(printk("jffs_rename(): Failed to write node to flash.\n")); - jffs_free_node(node); - goto jffs_rename_end; - } - raw_inode.dsize = 0; - - if (raw_inode.rename) { - /* The file with the same name must be deleted. */ - //FIXME deadlock down(&c->fmc->gclock); - if ((result = jffs_remove(new_dir, new_dentry, - del_f->mode)) < 0) { - /* This is really bad. */ - printk(KERN_ERR "JFFS: An error occurred in " - "rename().\n"); - } - // up(&c->fmc->gclock); - } - - if (old_dir_f != new_dir_f) { - /* Remove the file from its old position in the - filesystem tree. */ - jffs_unlink_file_from_tree(f); - } - - /* Insert the new node into the file system. */ - if ((result = jffs_insert_node(c, f, &raw_inode, - new_dentry->d_name.name, node)) < 0) { - D(printk(KERN_ERR "jffs_rename(): jffs_insert_node() " - "failed!\n")); - } - - if (old_dir_f != new_dir_f) { - /* Insert the file to its new position in the - file system. */ - jffs_insert_file_into_tree(f); - } - - /* This is a kind of update of the inode we're about to make - here. This is what they do in ext2fs. Kind of. */ - if ((inode = iget(new_dir->i_sb, f->ino))) { - inode->i_ctime = CURRENT_TIME_SEC; - mark_inode_dirty(inode); - iput(inode); - } - -jffs_rename_end: - D3(printk (KERN_NOTICE "rename(): up biglock\n")); - mutex_unlock(&c->fmc->biglock); - unlock_kernel(); - return result; -} /* jffs_rename() */ - - -/* Read the contents of a directory. Used by programs like `ls' - for instance. */ -static int -jffs_readdir(struct file *filp, void *dirent, filldir_t filldir) -{ - struct jffs_file *f; - struct dentry *dentry = filp->f_path.dentry; - struct inode *inode = dentry->d_inode; - struct jffs_control *c = (struct jffs_control *)inode->i_sb->s_fs_info; - int j; - int ddino; - lock_kernel(); - D3(printk (KERN_NOTICE "readdir(): down biglock\n")); - mutex_lock(&c->fmc->biglock); - - D2(printk("jffs_readdir(): inode: 0x%p, filp: 0x%p\n", inode, filp)); - if (filp->f_pos == 0) { - D3(printk("jffs_readdir(): \".\" %lu\n", inode->i_ino)); - if (filldir(dirent, ".", 1, filp->f_pos, inode->i_ino, DT_DIR) < 0) { - D3(printk (KERN_NOTICE "readdir(): up biglock\n")); - mutex_unlock(&c->fmc->biglock); - unlock_kernel(); - return 0; - } - filp->f_pos = 1; - } - if (filp->f_pos == 1) { - if (inode->i_ino == JFFS_MIN_INO) { - ddino = JFFS_MIN_INO; - } - else { - ddino = ((struct jffs_file *) - inode->i_private)->pino; - } - D3(printk("jffs_readdir(): \"..\" %u\n", ddino)); - if (filldir(dirent, "..", 2, filp->f_pos, ddino, DT_DIR) < 0) { - D3(printk (KERN_NOTICE "readdir(): up biglock\n")); - mutex_unlock(&c->fmc->biglock); - unlock_kernel(); - return 0; - } - filp->f_pos++; - } - f = ((struct jffs_file *)inode->i_private)->children; - - j = 2; - while(f && (f->deleted || j++ < filp->f_pos )) { - f = f->sibling_next; - } - - while (f) { - D3(printk("jffs_readdir(): \"%s\" ino: %u\n", - (f->name ? f->name : ""), f->ino)); - if (filldir(dirent, f->name, f->nsize, - filp->f_pos , f->ino, DT_UNKNOWN) < 0) { - D3(printk (KERN_NOTICE "readdir(): up biglock\n")); - mutex_unlock(&c->fmc->biglock); - unlock_kernel(); - return 0; - } - filp->f_pos++; - do { - f = f->sibling_next; - } while(f && f->deleted); - } - D3(printk (KERN_NOTICE "readdir(): up biglock\n")); - mutex_unlock(&c->fmc->biglock); - unlock_kernel(); - return filp->f_pos; -} /* jffs_readdir() */ - - -/* Find a file in a directory. If the file exists, return its - corresponding dentry. */ -static struct dentry * -jffs_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) -{ - struct jffs_file *d; - struct jffs_file *f; - struct jffs_control *c = (struct jffs_control *)dir->i_sb->s_fs_info; - int len; - int r = 0; - const char *name; - struct inode *inode = NULL; - - len = dentry->d_name.len; - name = dentry->d_name.name; - - lock_kernel(); - - D3({ - char *s = kmalloc(len + 1, GFP_KERNEL); - memcpy(s, name, len); - s[len] = '\0'; - printk("jffs_lookup(): dir: 0x%p, name: \"%s\"\n", dir, s); - kfree(s); - }); - - D3(printk (KERN_NOTICE "lookup(): down biglock\n")); - mutex_lock(&c->fmc->biglock); - - r = -ENAMETOOLONG; - if (len > JFFS_MAX_NAME_LEN) { - goto jffs_lookup_end; - } - - r = -EACCES; - if (!(d = (struct jffs_file *)dir->i_private)) { - D(printk("jffs_lookup(): No such inode! (%lu)\n", - dir->i_ino)); - goto jffs_lookup_end; - } - - /* Get the corresponding inode to the file. */ - - /* iget calls jffs_read_inode, so we need to drop the biglock - before calling iget. Unfortunately, the GC has a tendency - to sneak in here, because iget sometimes calls schedule (). - */ - - if ((len == 1) && (name[0] == '.')) { - D3(printk (KERN_NOTICE "lookup(): up biglock\n")); - mutex_unlock(&c->fmc->biglock); - if (!(inode = iget(dir->i_sb, d->ino))) { - D(printk("jffs_lookup(): . iget() ==> NULL\n")); - goto jffs_lookup_end_no_biglock; - } - D3(printk (KERN_NOTICE "lookup(): down biglock\n")); - mutex_lock(&c->fmc->biglock); - } else if ((len == 2) && (name[0] == '.') && (name[1] == '.')) { - D3(printk (KERN_NOTICE "lookup(): up biglock\n")); - mutex_unlock(&c->fmc->biglock); - if (!(inode = iget(dir->i_sb, d->pino))) { - D(printk("jffs_lookup(): .. iget() ==> NULL\n")); - goto jffs_lookup_end_no_biglock; - } - D3(printk (KERN_NOTICE "lookup(): down biglock\n")); - mutex_lock(&c->fmc->biglock); - } else if ((f = jffs_find_child(d, name, len))) { - D3(printk (KERN_NOTICE "lookup(): up biglock\n")); - mutex_unlock(&c->fmc->biglock); - if (!(inode = iget(dir->i_sb, f->ino))) { - D(printk("jffs_lookup(): iget() ==> NULL\n")); - goto jffs_lookup_end_no_biglock; - } - D3(printk (KERN_NOTICE "lookup(): down biglock\n")); - mutex_lock(&c->fmc->biglock); - } else { - D3(printk("jffs_lookup(): Couldn't find the file. " - "f = 0x%p, name = \"%s\", d = 0x%p, d->ino = %u\n", - f, name, d, d->ino)); - inode = NULL; - } - - d_add(dentry, inode); - D3(printk (KERN_NOTICE "lookup(): up biglock\n")); - mutex_unlock(&c->fmc->biglock); - unlock_kernel(); - return NULL; - -jffs_lookup_end: - D3(printk (KERN_NOTICE "lookup(): up biglock\n")); - mutex_unlock(&c->fmc->biglock); - -jffs_lookup_end_no_biglock: - unlock_kernel(); - return ERR_PTR(r); -} /* jffs_lookup() */ - - -/* Try to read a page of data from a file. */ -static int -jffs_do_readpage_nolock(struct file *file, struct page *page) -{ - void *buf; - unsigned long read_len; - int result; - struct inode *inode = (struct inode*)page->mapping->host; - struct jffs_file *f = (struct jffs_file *)inode->i_private; - struct jffs_control *c = (struct jffs_control *)inode->i_sb->s_fs_info; - int r; - loff_t offset; - - D2(printk("***jffs_readpage(): file = \"%s\", page->index = %lu\n", - (f->name ? f->name : ""), (long)page->index)); - - get_page(page); - /* Don't SetPageLocked(page), should be locked already */ - ClearPageUptodate(page); - ClearPageError(page); - - D3(printk (KERN_NOTICE "readpage(): down biglock\n")); - mutex_lock(&c->fmc->biglock); - - read_len = 0; - result = 0; - offset = page_offset(page); - - kmap(page); - buf = page_address(page); - if (offset < inode->i_size) { - read_len = min_t(long, inode->i_size - offset, PAGE_SIZE); - r = jffs_read_data(f, buf, offset, read_len); - if (r != read_len) { - result = -EIO; - D( - printk("***jffs_readpage(): Read error! " - "Wanted to read %lu bytes but only " - "read %d bytes.\n", read_len, r); - ); - } - - } - - /* This handles the case of partial or no read in above */ - if(read_len < PAGE_SIZE) - memset(buf + read_len, 0, PAGE_SIZE - read_len); - flush_dcache_page(page); - kunmap(page); - - D3(printk (KERN_NOTICE "readpage(): up biglock\n")); - mutex_unlock(&c->fmc->biglock); - - if (result) { - SetPageError(page); - }else { - SetPageUptodate(page); - } - - page_cache_release(page); - - D3(printk("jffs_readpage(): Leaving...\n")); - - return result; -} /* jffs_do_readpage_nolock() */ - -static int jffs_readpage(struct file *file, struct page *page) -{ - int ret = jffs_do_readpage_nolock(file, page); - unlock_page(page); - return ret; -} - -/* Create a new directory. */ -static int -jffs_mkdir(struct inode *dir, struct dentry *dentry, int mode) -{ - struct jffs_raw_inode raw_inode; - struct jffs_control *c; - struct jffs_node *node; - struct jffs_file *dir_f; - struct inode *inode; - int dir_mode; - int result = 0; - int err; - - D1({ - int len = dentry->d_name.len; - char *_name = kmalloc(len + 1, GFP_KERNEL); - memcpy(_name, dentry->d_name.name, len); - _name[len] = '\0'; - printk("***jffs_mkdir(): dir = 0x%p, name = \"%s\", " - "len = %d, mode = 0x%08x\n", dir, _name, len, mode); - kfree(_name); - }); - - lock_kernel(); - dir_f = dir->i_private; - - ASSERT(if (!dir_f) { - printk(KERN_ERR "jffs_mkdir(): No reference to a " - "jffs_file struct in inode.\n"); - unlock_kernel(); - return -EIO; - }); - - c = dir_f->c; - D3(printk (KERN_NOTICE "mkdir(): down biglock\n")); - mutex_lock(&c->fmc->biglock); - - dir_mode = S_IFDIR | (mode & (S_IRWXUGO|S_ISVTX) - & ~current->fs->umask); - if (dir->i_mode & S_ISGID) { - dir_mode |= S_ISGID; - } - - /* Create a node and initialize it as much as needed. */ - if (!(node = jffs_alloc_node())) { - D(printk("jffs_mkdir(): Allocation failed: node == 0\n")); - result = -ENOMEM; - goto jffs_mkdir_end; - } - node->data_offset = 0; - node->removed_size = 0; - - /* Initialize the raw inode. */ - raw_inode.magic = JFFS_MAGIC_BITMASK; - raw_inode.ino = c->next_ino++; - raw_inode.pino = dir_f->ino; - raw_inode.version = 1; - raw_inode.mode = dir_mode; - raw_inode.uid = current->fsuid; - raw_inode.gid = (dir->i_mode & S_ISGID) ? dir->i_gid : current->fsgid; - /* raw_inode.gid = current->fsgid; */ - raw_inode.atime = get_seconds(); - raw_inode.mtime = raw_inode.atime; - raw_inode.ctime = raw_inode.atime; - raw_inode.offset = 0; - raw_inode.dsize = 0; - raw_inode.rsize = 0; - raw_inode.nsize = dentry->d_name.len; - raw_inode.nlink = 1; - raw_inode.spare = 0; - raw_inode.rename = 0; - raw_inode.deleted = 0; - - /* Write the new node to the flash. */ - if ((result = jffs_write_node(c, node, &raw_inode, - dentry->d_name.name, NULL, 0, NULL)) < 0) { - D(printk("jffs_mkdir(): jffs_write_node() failed.\n")); - jffs_free_node(node); - goto jffs_mkdir_end; - } - - /* Insert the new node into the file system. */ - if ((result = jffs_insert_node(c, NULL, &raw_inode, dentry->d_name.name, - node)) < 0) { - goto jffs_mkdir_end; - } - - inode = jffs_new_inode(dir, &raw_inode, &err); - if (inode == NULL) { - result = err; - goto jffs_mkdir_end; - } - - inode->i_op = &jffs_dir_inode_operations; - inode->i_fop = &jffs_dir_operations; - - mark_inode_dirty(dir); - d_instantiate(dentry, inode); - - result = 0; -jffs_mkdir_end: - D3(printk (KERN_NOTICE "mkdir(): up biglock\n")); - mutex_unlock(&c->fmc->biglock); - unlock_kernel(); - return result; -} /* jffs_mkdir() */ - - -/* Remove a directory. */ -static int -jffs_rmdir(struct inode *dir, struct dentry *dentry) -{ - struct jffs_control *c = (struct jffs_control *)dir->i_sb->s_fs_info; - int ret; - D3(printk("***jffs_rmdir()\n")); - D3(printk (KERN_NOTICE "rmdir(): down biglock\n")); - lock_kernel(); - mutex_lock(&c->fmc->biglock); - ret = jffs_remove(dir, dentry, S_IFDIR); - D3(printk (KERN_NOTICE "rmdir(): up biglock\n")); - mutex_unlock(&c->fmc->biglock); - unlock_kernel(); - return ret; -} - - -/* Remove any kind of file except for directories. */ -static int -jffs_unlink(struct inode *dir, struct dentry *dentry) -{ - struct jffs_control *c = (struct jffs_control *)dir->i_sb->s_fs_info; - int ret; - - lock_kernel(); - D3(printk("***jffs_unlink()\n")); - D3(printk (KERN_NOTICE "unlink(): down biglock\n")); - mutex_lock(&c->fmc->biglock); - ret = jffs_remove(dir, dentry, 0); - D3(printk (KERN_NOTICE "unlink(): up biglock\n")); - mutex_unlock(&c->fmc->biglock); - unlock_kernel(); - return ret; -} - - -/* Remove a JFFS entry, i.e. plain files, directories, etc. Here we - shouldn't test for free space on the device. */ -static int -jffs_remove(struct inode *dir, struct dentry *dentry, int type) -{ - struct jffs_raw_inode raw_inode; - struct jffs_control *c; - struct jffs_file *dir_f; /* The file-to-remove's parent. */ - struct jffs_file *del_f; /* The file to remove. */ - struct jffs_node *del_node; - struct inode *inode = NULL; - int result = 0; - - D1({ - int len = dentry->d_name.len; - const char *name = dentry->d_name.name; - char *_name = kmalloc(len + 1, GFP_KERNEL); - memcpy(_name, name, len); - _name[len] = '\0'; - printk("***jffs_remove(): file = \"%s\", ino = %ld\n", _name, dentry->d_inode->i_ino); - kfree(_name); - }); - - dir_f = dir->i_private; - c = dir_f->c; - - result = -ENOENT; - if (!(del_f = jffs_find_child(dir_f, dentry->d_name.name, - dentry->d_name.len))) { - D(printk("jffs_remove(): jffs_find_child() failed.\n")); - goto jffs_remove_end; - } - - if (S_ISDIR(type)) { - struct jffs_file *child = del_f->children; - while(child) { - if( !child->deleted ) { - result = -ENOTEMPTY; - goto jffs_remove_end; - } - child = child->sibling_next; - } - } - else if (S_ISDIR(del_f->mode)) { - D(printk("jffs_remove(): node is a directory " - "but it shouldn't be.\n")); - result = -EPERM; - goto jffs_remove_end; - } - - inode = dentry->d_inode; - - result = -EIO; - if (del_f->ino != inode->i_ino) - goto jffs_remove_end; - - if (!inode->i_nlink) { - printk("Deleting nonexistent file inode: %lu, nlink: %d\n", - inode->i_ino, inode->i_nlink); - inode->i_nlink=1; - } - - /* Create a node for the deletion. */ - result = -ENOMEM; - if (!(del_node = jffs_alloc_node())) { - D(printk("jffs_remove(): Allocation failed!\n")); - goto jffs_remove_end; - } - del_node->data_offset = 0; - del_node->removed_size = 0; - - /* Initialize the raw inode. */ - raw_inode.magic = JFFS_MAGIC_BITMASK; - raw_inode.ino = del_f->ino; - raw_inode.pino = del_f->pino; -/* raw_inode.version = del_f->highest_version + 1; */ - raw_inode.mode = del_f->mode; - raw_inode.uid = current->fsuid; - raw_inode.gid = current->fsgid; - raw_inode.atime = get_seconds(); - raw_inode.mtime = del_f->mtime; - raw_inode.ctime = raw_inode.atime; - raw_inode.offset = 0; - raw_inode.dsize = 0; - raw_inode.rsize = 0; - raw_inode.nsize = 0; - raw_inode.nlink = del_f->nlink; - raw_inode.spare = 0; - raw_inode.rename = 0; - raw_inode.deleted = 1; - - /* Write the new node to the flash memory. */ - if (jffs_write_node(c, del_node, &raw_inode, NULL, NULL, 1, del_f) < 0) { - jffs_free_node(del_node); - result = -EIO; - goto jffs_remove_end; - } - - /* Update the file. This operation will make the file disappear - from the in-memory file system structures. */ - jffs_insert_node(c, del_f, &raw_inode, NULL, del_node); - - dir->i_ctime = dir->i_mtime = CURRENT_TIME_SEC; - mark_inode_dirty(dir); - inode->i_ctime = dir->i_ctime; - inode_dec_link_count(inode); - - d_delete(dentry); /* This also frees the inode */ - - result = 0; -jffs_remove_end: - return result; -} /* jffs_remove() */ - - -static int -jffs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t rdev) -{ - struct jffs_raw_inode raw_inode; - struct jffs_file *dir_f; - struct jffs_node *node = NULL; - struct jffs_control *c; - struct inode *inode; - int result = 0; - u16 data = old_encode_dev(rdev); - int err; - - D1(printk("***jffs_mknod()\n")); - - if (!old_valid_dev(rdev)) - return -EINVAL; - lock_kernel(); - dir_f = dir->i_private; - c = dir_f->c; - - D3(printk (KERN_NOTICE "mknod(): down biglock\n")); - mutex_lock(&c->fmc->biglock); - - /* Create and initialize a new node. */ - if (!(node = jffs_alloc_node())) { - D(printk("jffs_mknod(): Allocation failed!\n")); - result = -ENOMEM; - goto jffs_mknod_err; - } - node->data_offset = 0; - node->removed_size = 0; - - /* Initialize the raw inode. */ - raw_inode.magic = JFFS_MAGIC_BITMASK; - raw_inode.ino = c->next_ino++; - raw_inode.pino = dir_f->ino; - raw_inode.version = 1; - raw_inode.mode = mode; - raw_inode.uid = current->fsuid; - raw_inode.gid = (dir->i_mode & S_ISGID) ? dir->i_gid : current->fsgid; - /* raw_inode.gid = current->fsgid; */ - raw_inode.atime = get_seconds(); - raw_inode.mtime = raw_inode.atime; - raw_inode.ctime = raw_inode.atime; - raw_inode.offset = 0; - raw_inode.dsize = 2; - raw_inode.rsize = 0; - raw_inode.nsize = dentry->d_name.len; - raw_inode.nlink = 1; - raw_inode.spare = 0; - raw_inode.rename = 0; - raw_inode.deleted = 0; - - /* Write the new node to the flash. */ - if ((err = jffs_write_node(c, node, &raw_inode, dentry->d_name.name, - (unsigned char *)&data, 0, NULL)) < 0) { - D(printk("jffs_mknod(): jffs_write_node() failed.\n")); - result = err; - goto jffs_mknod_err; - } - - /* Insert the new node into the file system. */ - if ((err = jffs_insert_node(c, NULL, &raw_inode, dentry->d_name.name, - node)) < 0) { - result = err; - goto jffs_mknod_end; - } - - inode = jffs_new_inode(dir, &raw_inode, &err); - if (inode == NULL) { - result = err; - goto jffs_mknod_end; - } - - init_special_inode(inode, mode, rdev); - - d_instantiate(dentry, inode); - - goto jffs_mknod_end; - -jffs_mknod_err: - if (node) { - jffs_free_node(node); - } - -jffs_mknod_end: - D3(printk (KERN_NOTICE "mknod(): up biglock\n")); - mutex_unlock(&c->fmc->biglock); - unlock_kernel(); - return result; -} /* jffs_mknod() */ - - -static int -jffs_symlink(struct inode *dir, struct dentry *dentry, const char *symname) -{ - struct jffs_raw_inode raw_inode; - struct jffs_control *c; - struct jffs_file *dir_f; - struct jffs_node *node; - struct inode *inode; - - int symname_len = strlen(symname); - int err; - - lock_kernel(); - D1({ - int len = dentry->d_name.len; - char *_name = kmalloc(len + 1, GFP_KERNEL); - char *_symname = kmalloc(symname_len + 1, GFP_KERNEL); - memcpy(_name, dentry->d_name.name, len); - _name[len] = '\0'; - memcpy(_symname, symname, symname_len); - _symname[symname_len] = '\0'; - printk("***jffs_symlink(): dir = 0x%p, " - "dentry->dname.name = \"%s\", " - "symname = \"%s\"\n", dir, _name, _symname); - kfree(_name); - kfree(_symname); - }); - - dir_f = dir->i_private; - ASSERT(if (!dir_f) { - printk(KERN_ERR "jffs_symlink(): No reference to a " - "jffs_file struct in inode.\n"); - unlock_kernel(); - return -EIO; - }); - - c = dir_f->c; - - /* Create a node and initialize it as much as needed. */ - if (!(node = jffs_alloc_node())) { - D(printk("jffs_symlink(): Allocation failed: node = NULL\n")); - unlock_kernel(); - return -ENOMEM; - } - D3(printk (KERN_NOTICE "symlink(): down biglock\n")); - mutex_lock(&c->fmc->biglock); - - node->data_offset = 0; - node->removed_size = 0; - - /* Initialize the raw inode. */ - raw_inode.magic = JFFS_MAGIC_BITMASK; - raw_inode.ino = c->next_ino++; - raw_inode.pino = dir_f->ino; - raw_inode.version = 1; - raw_inode.mode = S_IFLNK | S_IRWXUGO; - raw_inode.uid = current->fsuid; - raw_inode.gid = (dir->i_mode & S_ISGID) ? dir->i_gid : current->fsgid; - raw_inode.atime = get_seconds(); - raw_inode.mtime = raw_inode.atime; - raw_inode.ctime = raw_inode.atime; - raw_inode.offset = 0; - raw_inode.dsize = symname_len; - raw_inode.rsize = 0; - raw_inode.nsize = dentry->d_name.len; - raw_inode.nlink = 1; - raw_inode.spare = 0; - raw_inode.rename = 0; - raw_inode.deleted = 0; - - /* Write the new node to the flash. */ - if ((err = jffs_write_node(c, node, &raw_inode, dentry->d_name.name, - (const unsigned char *)symname, 0, NULL)) < 0) { - D(printk("jffs_symlink(): jffs_write_node() failed.\n")); - jffs_free_node(node); - goto jffs_symlink_end; - } - - /* Insert the new node into the file system. */ - if ((err = jffs_insert_node(c, NULL, &raw_inode, dentry->d_name.name, - node)) < 0) { - goto jffs_symlink_end; - } - - inode = jffs_new_inode(dir, &raw_inode, &err); - if (inode == NULL) { - goto jffs_symlink_end; - } - err = 0; - inode->i_op = &page_symlink_inode_operations; - inode->i_mapping->a_ops = &jffs_address_operations; - - d_instantiate(dentry, inode); - jffs_symlink_end: - D3(printk (KERN_NOTICE "symlink(): up biglock\n")); - mutex_unlock(&c->fmc->biglock); - unlock_kernel(); - return err; -} /* jffs_symlink() */ - - -/* Create an inode inside a JFFS directory (dir) and return it. - * - * By the time this is called, we already have created - * the directory cache entry for the new file, but it - * is so far negative - it has no inode. - * - * If the create succeeds, we fill in the inode information - * with d_instantiate(). - */ -static int -jffs_create(struct inode *dir, struct dentry *dentry, int mode, - struct nameidata *nd) -{ - struct jffs_raw_inode raw_inode; - struct jffs_control *c; - struct jffs_node *node; - struct jffs_file *dir_f; /* JFFS representation of the directory. */ - struct inode *inode; - int err; - - lock_kernel(); - D1({ - int len = dentry->d_name.len; - char *s = kmalloc(len + 1, GFP_KERNEL); - memcpy(s, dentry->d_name.name, len); - s[len] = '\0'; - printk("jffs_create(): dir: 0x%p, name: \"%s\"\n", dir, s); - kfree(s); - }); - - dir_f = dir->i_private; - ASSERT(if (!dir_f) { - printk(KERN_ERR "jffs_create(): No reference to a " - "jffs_file struct in inode.\n"); - unlock_kernel(); - return -EIO; - }); - - c = dir_f->c; - - /* Create a node and initialize as much as needed. */ - if (!(node = jffs_alloc_node())) { - D(printk("jffs_create(): Allocation failed: node == 0\n")); - unlock_kernel(); - return -ENOMEM; - } - D3(printk (KERN_NOTICE "create(): down biglock\n")); - mutex_lock(&c->fmc->biglock); - - node->data_offset = 0; - node->removed_size = 0; - - /* Initialize the raw inode. */ - raw_inode.magic = JFFS_MAGIC_BITMASK; - raw_inode.ino = c->next_ino++; - raw_inode.pino = dir_f->ino; - raw_inode.version = 1; - raw_inode.mode = mode; - raw_inode.uid = current->fsuid; - raw_inode.gid = (dir->i_mode & S_ISGID) ? dir->i_gid : current->fsgid; - raw_inode.atime = get_seconds(); - raw_inode.mtime = raw_inode.atime; - raw_inode.ctime = raw_inode.atime; - raw_inode.offset = 0; - raw_inode.dsize = 0; - raw_inode.rsize = 0; - raw_inode.nsize = dentry->d_name.len; - raw_inode.nlink = 1; - raw_inode.spare = 0; - raw_inode.rename = 0; - raw_inode.deleted = 0; - - /* Write the new node to the flash. */ - if ((err = jffs_write_node(c, node, &raw_inode, - dentry->d_name.name, NULL, 0, NULL)) < 0) { - D(printk("jffs_create(): jffs_write_node() failed.\n")); - jffs_free_node(node); - goto jffs_create_end; - } - - /* Insert the new node into the file system. */ - if ((err = jffs_insert_node(c, NULL, &raw_inode, dentry->d_name.name, - node)) < 0) { - goto jffs_create_end; - } - - /* Initialize an inode. */ - inode = jffs_new_inode(dir, &raw_inode, &err); - if (inode == NULL) { - goto jffs_create_end; - } - err = 0; - inode->i_op = &jffs_file_inode_operations; - inode->i_fop = &jffs_file_operations; - inode->i_mapping->a_ops = &jffs_address_operations; - inode->i_mapping->nrpages = 0; - - d_instantiate(dentry, inode); - jffs_create_end: - D3(printk (KERN_NOTICE "create(): up biglock\n")); - mutex_unlock(&c->fmc->biglock); - unlock_kernel(); - return err; -} /* jffs_create() */ - - -/* Write, append or rewrite data to an existing file. */ -static ssize_t -jffs_file_write(struct file *filp, const char *buf, size_t count, - loff_t *ppos) -{ - struct jffs_raw_inode raw_inode; - struct jffs_control *c; - struct jffs_file *f; - struct jffs_node *node; - struct dentry *dentry = filp->f_path.dentry; - struct inode *inode = dentry->d_inode; - int recoverable = 0; - size_t written = 0; - __u32 thiscount = count; - loff_t pos = *ppos; - int err; - - inode = filp->f_path.dentry->d_inode; - - D2(printk("***jffs_file_write(): inode: 0x%p (ino: %lu), " - "filp: 0x%p, buf: 0x%p, count: %d\n", - inode, inode->i_ino, filp, buf, count)); - -#if 0 - if (inode->i_sb->s_flags & MS_RDONLY) { - D(printk("jffs_file_write(): MS_RDONLY\n")); - err = -EROFS; - goto out_isem; - } -#endif - err = -EINVAL; - - if (!S_ISREG(inode->i_mode)) { - D(printk("jffs_file_write(): inode->i_mode == 0x%08x\n", - inode->i_mode)); - goto out_isem; - } - - if (!(f = inode->i_private)) { - D(printk("jffs_file_write(): inode->i_private = 0x%p\n", - inode->i_private)); - goto out_isem; - } - - c = f->c; - - /* - * This will never trigger with sane page sizes. leave it in - * anyway, since I'm thinking about how to merge larger writes - * (the current idea is to poke a thread that does the actual - * I/O and starts by doing a mutex_lock(&inode->i_mutex). then we - * would need to get the page cache pages and have a list of - * I/O requests and do write-merging here. - * -- prumpf - */ - thiscount = min(c->fmc->max_chunk_size - sizeof(struct jffs_raw_inode), count); - - D3(printk (KERN_NOTICE "file_write(): down biglock\n")); - mutex_lock(&c->fmc->biglock); - - /* Urgh. POSIX says we can do short writes if we feel like it. - * In practice, we can't. Nothing will cope. So we loop until - * we're done. - * - * <_Anarchy_> posix and reality are not interconnected on this issue - */ - while (count) { - /* Things are going to be written so we could allocate and - initialize the necessary data structures now. */ - if (!(node = jffs_alloc_node())) { - D(printk("jffs_file_write(): node == 0\n")); - err = -ENOMEM; - goto out; - } - - node->data_offset = pos; - node->removed_size = 0; - - /* Initialize the raw inode. */ - raw_inode.magic = JFFS_MAGIC_BITMASK; - raw_inode.ino = f->ino; - raw_inode.pino = f->pino; - - raw_inode.mode = f->mode; - - raw_inode.uid = f->uid; - raw_inode.gid = f->gid; - raw_inode.atime = get_seconds(); - raw_inode.mtime = raw_inode.atime; - raw_inode.ctime = f->ctime; - raw_inode.offset = pos; - raw_inode.dsize = thiscount; - raw_inode.rsize = 0; - raw_inode.nsize = f->nsize; - raw_inode.nlink = f->nlink; - raw_inode.spare = 0; - raw_inode.rename = 0; - raw_inode.deleted = 0; - - if (pos < f->size) { - node->removed_size = raw_inode.rsize = min(thiscount, (__u32)(f->size - pos)); - - /* If this node is going entirely over the top of old data, - we can allow it to go into the reserved space, because - we know that GC can reclaim the space later. - */ - if (pos + thiscount < f->size) { - /* If all the data we're overwriting are _real_, - not just holes, then: - recoverable = 1; - */ - } - } - - /* Write the new node to the flash. */ - /* NOTE: We would be quite happy if jffs_write_node() wrote a - smaller node than we were expecting. There's no need for it - to waste the space at the end of the flash just because it's - a little smaller than what we asked for. But that's a whole - new can of worms which I'm not going to open this week. - -- dwmw2. - */ - if ((err = jffs_write_node(c, node, &raw_inode, f->name, - (const unsigned char *)buf, - recoverable, f)) < 0) { - D(printk("jffs_file_write(): jffs_write_node() failed.\n")); - jffs_free_node(node); - goto out; - } - - written += err; - buf += err; - count -= err; - pos += err; - - /* Insert the new node into the file system. */ - if ((err = jffs_insert_node(c, f, &raw_inode, NULL, node)) < 0) { - goto out; - } - - D3(printk("jffs_file_write(): new f_pos %ld.\n", (long)pos)); - - thiscount = min(c->fmc->max_chunk_size - sizeof(struct jffs_raw_inode), count); - } - out: - D3(printk (KERN_NOTICE "file_write(): up biglock\n")); - mutex_unlock(&c->fmc->biglock); - - /* Fix things in the real inode. */ - if (pos > inode->i_size) { - inode->i_size = pos; - inode->i_blocks = (inode->i_size + 511) >> 9; - } - inode->i_ctime = inode->i_mtime = CURRENT_TIME_SEC; - mark_inode_dirty(inode); - invalidate_mapping_pages(inode->i_mapping, 0, -1); - - out_isem: - return err; -} /* jffs_file_write() */ - -static int -jffs_prepare_write(struct file *filp, struct page *page, - unsigned from, unsigned to) -{ - /* FIXME: we should detect some error conditions here */ - - /* Bugger that. We should make sure the page is uptodate */ - if (!PageUptodate(page) && (from || to < PAGE_CACHE_SIZE)) - return jffs_do_readpage_nolock(filp, page); - - return 0; -} /* jffs_prepare_write() */ - -static int -jffs_commit_write(struct file *filp, struct page *page, - unsigned from, unsigned to) -{ - void *addr = page_address(page) + from; - /* XXX: PAGE_CACHE_SHIFT or PAGE_SHIFT */ - loff_t pos = page_offset(page) + from; - - return jffs_file_write(filp, addr, to-from, &pos); -} /* jffs_commit_write() */ - -/* This is our ioctl() routine. */ -static int -jffs_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, - unsigned long arg) -{ - struct jffs_control *c; - int ret = 0; - - D2(printk("***jffs_ioctl(): cmd = 0x%08x, arg = 0x%08lx\n", - cmd, arg)); - - if (!(c = (struct jffs_control *)inode->i_sb->s_fs_info)) { - printk(KERN_ERR "JFFS: Bad inode in ioctl() call. " - "(cmd = 0x%08x)\n", cmd); - return -EIO; - } - D3(printk (KERN_NOTICE "ioctl(): down biglock\n")); - mutex_lock(&c->fmc->biglock); - - switch (cmd) { - case JFFS_PRINT_HASH: - jffs_print_hash_table(c); - break; - case JFFS_PRINT_TREE: - jffs_print_tree(c->root, 0); - break; - case JFFS_GET_STATUS: - { - struct jffs_flash_status fst; - struct jffs_fmcontrol *fmc = c->fmc; - printk("Flash status -- "); - if (!access_ok(VERIFY_WRITE, - (struct jffs_flash_status __user *)arg, - sizeof(struct jffs_flash_status))) { - D(printk("jffs_ioctl(): Bad arg in " - "JFFS_GET_STATUS ioctl!\n")); - ret = -EFAULT; - break; - } - fst.size = fmc->flash_size; - fst.used = fmc->used_size; - fst.dirty = fmc->dirty_size; - fst.begin = fmc->head->offset; - fst.end = fmc->tail->offset + fmc->tail->size; - printk("size: %d, used: %d, dirty: %d, " - "begin: %d, end: %d\n", - fst.size, fst.used, fst.dirty, - fst.begin, fst.end); - if (copy_to_user((struct jffs_flash_status __user *)arg, - &fst, - sizeof(struct jffs_flash_status))) { - ret = -EFAULT; - } - } - break; - default: - ret = -ENOTTY; - } - D3(printk (KERN_NOTICE "ioctl(): up biglock\n")); - mutex_unlock(&c->fmc->biglock); - return ret; -} /* jffs_ioctl() */ - - -static const struct address_space_operations jffs_address_operations = { - .readpage = jffs_readpage, - .prepare_write = jffs_prepare_write, - .commit_write = jffs_commit_write, -}; - -static int jffs_fsync(struct file *f, struct dentry *d, int datasync) -{ - /* We currently have O_SYNC operations at all times. - Do nothing. - */ - return 0; -} - - -static const struct file_operations jffs_file_operations = -{ - .open = generic_file_open, - .llseek = generic_file_llseek, - .read = do_sync_read, - .aio_read = generic_file_aio_read, - .write = do_sync_write, - .aio_write = generic_file_aio_write, - .ioctl = jffs_ioctl, - .mmap = generic_file_readonly_mmap, - .fsync = jffs_fsync, - .sendfile = generic_file_sendfile, -}; - - -static const struct inode_operations jffs_file_inode_operations = -{ - .lookup = jffs_lookup, /* lookup */ - .setattr = jffs_setattr, -}; - - -static const struct file_operations jffs_dir_operations = -{ - .readdir = jffs_readdir, -}; - - -static const struct inode_operations jffs_dir_inode_operations = -{ - .create = jffs_create, - .lookup = jffs_lookup, - .unlink = jffs_unlink, - .symlink = jffs_symlink, - .mkdir = jffs_mkdir, - .rmdir = jffs_rmdir, - .mknod = jffs_mknod, - .rename = jffs_rename, - .setattr = jffs_setattr, -}; - - -/* Initialize an inode for the VFS. */ -static void -jffs_read_inode(struct inode *inode) -{ - struct jffs_file *f; - struct jffs_control *c; - - D3(printk("jffs_read_inode(): inode->i_ino == %lu\n", inode->i_ino)); - - if (!inode->i_sb) { - D(printk("jffs_read_inode(): !inode->i_sb ==> " - "No super block!\n")); - return; - } - c = (struct jffs_control *)inode->i_sb->s_fs_info; - D3(printk (KERN_NOTICE "read_inode(): down biglock\n")); - mutex_lock(&c->fmc->biglock); - if (!(f = jffs_find_file(c, inode->i_ino))) { - D(printk("jffs_read_inode(): No such inode (%lu).\n", - inode->i_ino)); - D3(printk (KERN_NOTICE "read_inode(): up biglock\n")); - mutex_unlock(&c->fmc->biglock); - return; - } - inode->i_private = f; - inode->i_mode = f->mode; - inode->i_nlink = f->nlink; - inode->i_uid = f->uid; - inode->i_gid = f->gid; - inode->i_size = f->size; - inode->i_atime.tv_sec = f->atime; - inode->i_mtime.tv_sec = f->mtime; - inode->i_ctime.tv_sec = f->ctime; - inode->i_atime.tv_nsec = - inode->i_mtime.tv_nsec = - inode->i_ctime.tv_nsec = 0; - - inode->i_blocks = (inode->i_size + 511) >> 9; - if (S_ISREG(inode->i_mode)) { - inode->i_op = &jffs_file_inode_operations; - inode->i_fop = &jffs_file_operations; - inode->i_mapping->a_ops = &jffs_address_operations; - } - else if (S_ISDIR(inode->i_mode)) { - inode->i_op = &jffs_dir_inode_operations; - inode->i_fop = &jffs_dir_operations; - } - else if (S_ISLNK(inode->i_mode)) { - inode->i_op = &page_symlink_inode_operations; - inode->i_mapping->a_ops = &jffs_address_operations; - } - else { - /* If the node is a device of some sort, then the number of - the device should be read from the flash memory and then - added to the inode's i_rdev member. */ - u16 val; - jffs_read_data(f, (char *)&val, 0, 2); - init_special_inode(inode, inode->i_mode, - old_decode_dev(val)); - } - - D3(printk (KERN_NOTICE "read_inode(): up biglock\n")); - mutex_unlock(&c->fmc->biglock); -} - - -static void -jffs_delete_inode(struct inode *inode) -{ - struct jffs_file *f; - struct jffs_control *c; - D3(printk("jffs_delete_inode(): inode->i_ino == %lu\n", - inode->i_ino)); - - truncate_inode_pages(&inode->i_data, 0); - lock_kernel(); - inode->i_size = 0; - inode->i_blocks = 0; - inode->i_private = NULL; - clear_inode(inode); - if (inode->i_nlink == 0) { - c = (struct jffs_control *) inode->i_sb->s_fs_info; - f = (struct jffs_file *) jffs_find_file (c, inode->i_ino); - jffs_possibly_delete_file(f); - } - - unlock_kernel(); -} - - -static void -jffs_write_super(struct super_block *sb) -{ - struct jffs_control *c = (struct jffs_control *)sb->s_fs_info; - lock_kernel(); - jffs_garbage_collect_trigger(c); - unlock_kernel(); -} - -static int jffs_remount(struct super_block *sb, int *flags, char *data) -{ - *flags |= MS_NODIRATIME; - return 0; -} - -static const struct super_operations jffs_ops = -{ - .read_inode = jffs_read_inode, - .delete_inode = jffs_delete_inode, - .put_super = jffs_put_super, - .write_super = jffs_write_super, - .statfs = jffs_statfs, - .remount_fs = jffs_remount, -}; - -static int jffs_get_sb(struct file_system_type *fs_type, - int flags, const char *dev_name, void *data, struct vfsmount *mnt) -{ - return get_sb_bdev(fs_type, flags, dev_name, data, jffs_fill_super, - mnt); -} - -static struct file_system_type jffs_fs_type = { - .owner = THIS_MODULE, - .name = "jffs", - .get_sb = jffs_get_sb, - .kill_sb = kill_block_super, - .fs_flags = FS_REQUIRES_DEV, -}; - -static int __init -init_jffs_fs(void) -{ - printk(KERN_INFO "JFFS version " JFFS_VERSION_STRING - ", (C) 1999, 2000 Axis Communications AB\n"); - -#ifdef CONFIG_JFFS_PROC_FS - jffs_proc_root = proc_mkdir("jffs", proc_root_fs); - if (!jffs_proc_root) { - printk(KERN_WARNING "cannot create /proc/jffs entry\n"); - } -#endif - fm_cache = kmem_cache_create("jffs_fm", sizeof(struct jffs_fm), - 0, - SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT|SLAB_MEM_SPREAD, - NULL, NULL); - if (!fm_cache) { - return -ENOMEM; - } - - node_cache = kmem_cache_create("jffs_node",sizeof(struct jffs_node), - 0, - SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT|SLAB_MEM_SPREAD, - NULL, NULL); - if (!node_cache) { - kmem_cache_destroy(fm_cache); - return -ENOMEM; - } - - return register_filesystem(&jffs_fs_type); -} - -static void __exit -exit_jffs_fs(void) -{ - unregister_filesystem(&jffs_fs_type); - kmem_cache_destroy(fm_cache); - kmem_cache_destroy(node_cache); -} - -module_init(init_jffs_fs) -module_exit(exit_jffs_fs) - -MODULE_DESCRIPTION("The Journalling Flash File System"); -MODULE_AUTHOR("Axis Communications AB."); -MODULE_LICENSE("GPL"); diff --git a/fs/jffs/intrep.c b/fs/jffs/intrep.c deleted file mode 100644 index 6dd18911b44c..000000000000 --- a/fs/jffs/intrep.c +++ /dev/null @@ -1,3449 +0,0 @@ -/* - * JFFS -- Journaling Flash File System, Linux implementation. - * - * Copyright (C) 1999, 2000 Axis Communications, Inc. - * - * Created by Finn Hakansson . - * - * This 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. - * - * $Id: intrep.c,v 1.102 2001/09/23 23:28:36 dwmw2 Exp $ - * - * Ported to Linux 2.3.x and MTD: - * Copyright (C) 2000 Alexander Larsson (alex@cendio.se), Cendio Systems AB - * - */ - -/* This file contains the code for the internal structure of the - Journaling Flash File System, JFFS. */ - -/* - * Todo list: - * - * memcpy_to_flash() and memcpy_from_flash() functions. - * - * Implementation of hard links. - * - * Organize the source code in a better way. Against the VFS we could - * have jffs_ext.c, and against the block device jffs_int.c. - * A better file-internal organization too. - * - * A better checksum algorithm. - * - * Consider endianness stuff. ntohl() etc. - * - * Are we handling the atime, mtime, ctime members of the inode right? - * - * Remove some duplicated code. Take a look at jffs_write_node() and - * jffs_rewrite_data() for instance. - * - * Implement more meaning of the nlink member in various data structures. - * nlink could be used in conjunction with hard links for instance. - * - * Better memory management. Allocate data structures in larger chunks - * if possible. - * - * If too much meta data is stored, a garbage collect should be issued. - * We have experienced problems with too much meta data with for instance - * log files. - * - * Improve the calls to jffs_ioctl(). We would like to retrieve more - * information to be able to debug (or to supervise) JFFS during run-time. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "intrep.h" -#include "jffs_fm.h" - -long no_jffs_node = 0; -static long no_jffs_file = 0; -#if defined(JFFS_MEMORY_DEBUG) && JFFS_MEMORY_DEBUG -long no_jffs_control = 0; -long no_jffs_raw_inode = 0; -long no_jffs_node_ref = 0; -long no_jffs_fm = 0; -long no_jffs_fmcontrol = 0; -long no_hash = 0; -long no_name = 0; -#endif - -static int jffs_scan_flash(struct jffs_control *c); -static int jffs_update_file(struct jffs_file *f, struct jffs_node *node); -static int jffs_build_file(struct jffs_file *f); -static int jffs_free_file(struct jffs_file *f); -static int jffs_free_node_list(struct jffs_file *f); -static int jffs_garbage_collect_now(struct jffs_control *c); -static int jffs_insert_file_into_hash(struct jffs_file *f); -static int jffs_remove_redundant_nodes(struct jffs_file *f); - -/* Is there enough space on the flash? */ -static inline int JFFS_ENOUGH_SPACE(struct jffs_control *c, __u32 space) -{ - struct jffs_fmcontrol *fmc = c->fmc; - - while (1) { - if ((fmc->flash_size - (fmc->used_size + fmc->dirty_size)) - >= fmc->min_free_size + space) { - return 1; - } - if (fmc->dirty_size < fmc->sector_size) - return 0; - - if (jffs_garbage_collect_now(c)) { - D1(printk("JFFS_ENOUGH_SPACE: jffs_garbage_collect_now() failed.\n")); - return 0; - } - } -} - -#if CONFIG_JFFS_FS_VERBOSE > 0 -static __u8 -flash_read_u8(struct mtd_info *mtd, loff_t from) -{ - size_t retlen; - __u8 ret; - int res; - - res = MTD_READ(mtd, from, 1, &retlen, &ret); - if (retlen != 1) { - printk("Didn't read a byte in flash_read_u8(). Returned %d\n", res); - return 0; - } - - return ret; -} - -static void -jffs_hexdump(struct mtd_info *mtd, loff_t pos, int size) -{ - char line[16]; - int j = 0; - - while (size > 0) { - int i; - - printk("%ld:", (long) pos); - for (j = 0; j < 16; j++) { - line[j] = flash_read_u8(mtd, pos++); - } - for (i = 0; i < j; i++) { - if (!(i & 1)) { - printk(" %.2x", line[i] & 0xff); - } - else { - printk("%.2x", line[i] & 0xff); - } - } - - /* Print empty space */ - for (; i < 16; i++) { - if (!(i & 1)) { - printk(" "); - } - else { - printk(" "); - } - } - printk(" "); - - for (i = 0; i < j; i++) { - if (isgraph(line[i])) { - printk("%c", line[i]); - } - else { - printk("."); - } - } - printk("\n"); - size -= 16; - } -} - -/* Print the contents of a node. */ -static void -jffs_print_node(struct jffs_node *n) -{ - D(printk("jffs_node: 0x%p\n", n)); - D(printk("{\n")); - D(printk(" 0x%08x, /* version */\n", n->version)); - D(printk(" 0x%08x, /* data_offset */\n", n->data_offset)); - D(printk(" 0x%08x, /* data_size */\n", n->data_size)); - D(printk(" 0x%08x, /* removed_size */\n", n->removed_size)); - D(printk(" 0x%08x, /* fm_offset */\n", n->fm_offset)); - D(printk(" 0x%02x, /* name_size */\n", n->name_size)); - D(printk(" 0x%p, /* fm, fm->offset: %u */\n", - n->fm, (n->fm ? n->fm->offset : 0))); - D(printk(" 0x%p, /* version_prev */\n", n->version_prev)); - D(printk(" 0x%p, /* version_next */\n", n->version_next)); - D(printk(" 0x%p, /* range_prev */\n", n->range_prev)); - D(printk(" 0x%p, /* range_next */\n", n->range_next)); - D(printk("}\n")); -} - -#endif - -/* Print the contents of a raw inode. */ -static void -jffs_print_raw_inode(struct jffs_raw_inode *raw_inode) -{ - D(printk("jffs_raw_inode: inode number: %u\n", raw_inode->ino)); - D(printk("{\n")); - D(printk(" 0x%08x, /* magic */\n", raw_inode->magic)); - D(printk(" 0x%08x, /* ino */\n", raw_inode->ino)); - D(printk(" 0x%08x, /* pino */\n", raw_inode->pino)); - D(printk(" 0x%08x, /* version */\n", raw_inode->version)); - D(printk(" 0x%08x, /* mode */\n", raw_inode->mode)); - D(printk(" 0x%04x, /* uid */\n", raw_inode->uid)); - D(printk(" 0x%04x, /* gid */\n", raw_inode->gid)); - D(printk(" 0x%08x, /* atime */\n", raw_inode->atime)); - D(printk(" 0x%08x, /* mtime */\n", raw_inode->mtime)); - D(printk(" 0x%08x, /* ctime */\n", raw_inode->ctime)); - D(printk(" 0x%08x, /* offset */\n", raw_inode->offset)); - D(printk(" 0x%08x, /* dsize */\n", raw_inode->dsize)); - D(printk(" 0x%08x, /* rsize */\n", raw_inode->rsize)); - D(printk(" 0x%02x, /* nsize */\n", raw_inode->nsize)); - D(printk(" 0x%02x, /* nlink */\n", raw_inode->nlink)); - D(printk(" 0x%02x, /* spare */\n", - raw_inode->spare)); - D(printk(" %u, /* rename */\n", - raw_inode->rename)); - D(printk(" %u, /* deleted */\n", - raw_inode->deleted)); - D(printk(" 0x%02x, /* accurate */\n", - raw_inode->accurate)); - D(printk(" 0x%08x, /* dchksum */\n", raw_inode->dchksum)); - D(printk(" 0x%04x, /* nchksum */\n", raw_inode->nchksum)); - D(printk(" 0x%04x, /* chksum */\n", raw_inode->chksum)); - D(printk("}\n")); -} - -#define flash_safe_acquire(arg) -#define flash_safe_release(arg) - - -static int -flash_safe_read(struct mtd_info *mtd, loff_t from, - u_char *buf, size_t count) -{ - size_t retlen; - int res; - - D3(printk(KERN_NOTICE "flash_safe_read(%p, %08x, %p, %08x)\n", - mtd, (unsigned int) from, buf, count)); - - res = mtd->read(mtd, from, count, &retlen, buf); - if (retlen != count) { - panic("Didn't read all bytes in flash_safe_read(). Returned %d\n", res); - } - return res?res:retlen; -} - - -static __u32 -flash_read_u32(struct mtd_info *mtd, loff_t from) -{ - size_t retlen; - __u32 ret; - int res; - - res = mtd->read(mtd, from, 4, &retlen, (unsigned char *)&ret); - if (retlen != 4) { - printk("Didn't read all bytes in flash_read_u32(). Returned %d\n", res); - return 0; - } - - return ret; -} - - -static int -flash_safe_write(struct mtd_info *mtd, loff_t to, - const u_char *buf, size_t count) -{ - size_t retlen; - int res; - - D3(printk(KERN_NOTICE "flash_safe_write(%p, %08x, %p, %08x)\n", - mtd, (unsigned int) to, buf, count)); - - res = mtd->write(mtd, to, count, &retlen, buf); - if (retlen != count) { - printk("Didn't write all bytes in flash_safe_write(). Returned %d\n", res); - } - return res?res:retlen; -} - - -static int -flash_safe_writev(struct mtd_info *mtd, const struct kvec *vecs, - unsigned long iovec_cnt, loff_t to) -{ - size_t retlen, retlen_a; - int i; - int res; - - D3(printk(KERN_NOTICE "flash_safe_writev(%p, %08x, %p)\n", - mtd, (unsigned int) to, vecs)); - - if (mtd->writev) { - res = mtd->writev(mtd, vecs, iovec_cnt, to, &retlen); - return res ? res : retlen; - } - /* Not implemented writev. Repeatedly use write - on the not so - unreasonable assumption that the mtd driver doesn't care how - many write cycles we use. */ - res=0; - retlen=0; - - for (i=0; !res && iwrite(mtd, to, vecs[i].iov_len, &retlen_a, - vecs[i].iov_base); - if (retlen_a != vecs[i].iov_len) { - printk("Didn't write all bytes in flash_safe_writev(). Returned %d\n", res); - if (i != iovec_cnt-1) - return -EIO; - } - /* If res is non-zero, retlen_a is undefined, but we don't - care because in that case it's not going to be - returned anyway. - */ - to += retlen_a; - retlen += retlen_a; - } - return res?res:retlen; -} - - -static int -flash_memset(struct mtd_info *mtd, loff_t to, - const u_char c, size_t size) -{ - static unsigned char pattern[64]; - int i; - - /* fill up pattern */ - - for(i = 0; i < 64; i++) - pattern[i] = c; - - /* write as many 64-byte chunks as we can */ - - while (size >= 64) { - flash_safe_write(mtd, to, pattern, 64); - size -= 64; - to += 64; - } - - /* and the rest */ - - if(size) - flash_safe_write(mtd, to, pattern, size); - - return size; -} - - -static void -intrep_erase_callback(struct erase_info *done) -{ - wait_queue_head_t *wait_q; - - wait_q = (wait_queue_head_t *)done->priv; - - wake_up(wait_q); -} - - -static int -flash_erase_region(struct mtd_info *mtd, loff_t start, - size_t size) -{ - struct erase_info *erase; - DECLARE_WAITQUEUE(wait, current); - wait_queue_head_t wait_q; - - erase = kmalloc(sizeof(struct erase_info), GFP_KERNEL); - if (!erase) - return -ENOMEM; - - init_waitqueue_head(&wait_q); - - erase->mtd = mtd; - erase->callback = intrep_erase_callback; - erase->addr = start; - erase->len = size; - erase->priv = (u_long)&wait_q; - - /* FIXME: Use TASK_INTERRUPTIBLE and deal with being interrupted */ - set_current_state(TASK_UNINTERRUPTIBLE); - add_wait_queue(&wait_q, &wait); - - if (mtd->erase(mtd, erase) < 0) { - set_current_state(TASK_RUNNING); - remove_wait_queue(&wait_q, &wait); - kfree(erase); - - printk(KERN_WARNING "flash: erase of region [0x%lx, 0x%lx] " - "totally failed\n", (long)start, (long)start + size); - - return -1; - } - - schedule(); /* Wait for flash to finish. */ - remove_wait_queue(&wait_q, &wait); - - kfree(erase); - - return 0; -} - -/* This routine calculates checksums in JFFS. */ -static __u32 -jffs_checksum(const void *data, int size) -{ - __u32 sum = 0; - __u8 *ptr = (__u8 *)data; - while (size-- > 0) { - sum += *ptr++; - } - D3(printk(", result: 0x%08x\n", sum)); - return sum; -} - - -static int -jffs_checksum_flash(struct mtd_info *mtd, loff_t start, int size, __u32 *result) -{ - __u32 sum = 0; - loff_t ptr = start; - __u8 *read_buf; - int i, length; - - /* Allocate read buffer */ - read_buf = kmalloc(sizeof(__u8) * 4096, GFP_KERNEL); - if (!read_buf) { - printk(KERN_NOTICE "kmalloc failed in jffs_checksum_flash()\n"); - return -ENOMEM; - } - /* Loop until checksum done */ - while (size) { - /* Get amount of data to read */ - if (size < 4096) - length = size; - else - length = 4096; - - /* Perform flash read */ - D3(printk(KERN_NOTICE "jffs_checksum_flash\n")); - flash_safe_read(mtd, ptr, &read_buf[0], length); - - /* Compute checksum */ - for (i=0; i < length ; i++) - sum += read_buf[i]; - - /* Update pointer and size */ - size -= length; - ptr += length; - } - - /* Free read buffer */ - kfree(read_buf); - - /* Return result */ - D3(printk("checksum result: 0x%08x\n", sum)); - *result = sum; - return 0; -} - -static __inline__ void jffs_fm_write_lock(struct jffs_fmcontrol *fmc) -{ - // down(&fmc->wlock); -} - -static __inline__ void jffs_fm_write_unlock(struct jffs_fmcontrol *fmc) -{ - // up(&fmc->wlock); -} - - -/* Create and initialize a new struct jffs_file. */ -static struct jffs_file * -jffs_create_file(struct jffs_control *c, - const struct jffs_raw_inode *raw_inode) -{ - struct jffs_file *f; - - if (!(f = kzalloc(sizeof(*f), GFP_KERNEL))) { - D(printk("jffs_create_file(): Failed!\n")); - return NULL; - } - no_jffs_file++; - f->ino = raw_inode->ino; - f->pino = raw_inode->pino; - f->nlink = raw_inode->nlink; - f->deleted = raw_inode->deleted; - f->c = c; - - return f; -} - - -/* Build a control block for the file system. */ -static struct jffs_control * -jffs_create_control(struct super_block *sb) -{ - struct jffs_control *c; - register int s = sizeof(struct jffs_control); - int i; - D(char *t = 0); - - D2(printk("jffs_create_control()\n")); - - if (!(c = kmalloc(s, GFP_KERNEL))) { - goto fail_control; - } - DJM(no_jffs_control++); - c->root = NULL; - c->gc_task = NULL; - c->hash_len = JFFS_HASH_SIZE; - s = sizeof(struct list_head) * c->hash_len; - if (!(c->hash = kmalloc(s, GFP_KERNEL))) { - goto fail_hash; - } - DJM(no_hash++); - for (i = 0; i < c->hash_len; i++) - INIT_LIST_HEAD(&c->hash[i]); - if (!(c->fmc = jffs_build_begin(c, MINOR(sb->s_dev)))) { - goto fail_fminit; - } - c->next_ino = JFFS_MIN_INO + 1; - c->delete_list = (struct jffs_delete_list *) 0; - return c; - -fail_fminit: - D(t = "c->fmc"); -fail_hash: - kfree(c); - DJM(no_jffs_control--); - D(t = t ? t : "c->hash"); -fail_control: - D(t = t ? t : "control"); - D(printk("jffs_create_control(): Allocation failed: (%s)\n", t)); - return (struct jffs_control *)0; -} - - -/* Clean up all data structures associated with the file system. */ -void -jffs_cleanup_control(struct jffs_control *c) -{ - D2(printk("jffs_cleanup_control()\n")); - - if (!c) { - D(printk("jffs_cleanup_control(): c == NULL !!!\n")); - return; - } - - while (c->delete_list) { - struct jffs_delete_list *delete_list_element; - delete_list_element = c->delete_list; - c->delete_list = c->delete_list->next; - kfree(delete_list_element); - } - - /* Free all files and nodes. */ - if (c->hash) { - jffs_foreach_file(c, jffs_free_node_list); - jffs_foreach_file(c, jffs_free_file); - kfree(c->hash); - DJM(no_hash--); - } - jffs_cleanup_fmcontrol(c->fmc); - kfree(c); - DJM(no_jffs_control--); - D3(printk("jffs_cleanup_control(): Leaving...\n")); -} - - -/* This function adds a virtual root node to the in-RAM representation. - Called by jffs_build_fs(). */ -static int -jffs_add_virtual_root(struct jffs_control *c) -{ - struct jffs_file *root; - struct jffs_node *node; - - D2(printk("jffs_add_virtual_root(): " - "Creating a virtual root directory.\n")); - - if (!(root = kzalloc(sizeof(struct jffs_file), GFP_KERNEL))) { - return -ENOMEM; - } - no_jffs_file++; - if (!(node = jffs_alloc_node())) { - kfree(root); - no_jffs_file--; - return -ENOMEM; - } - DJM(no_jffs_node++); - memset(node, 0, sizeof(struct jffs_node)); - node->ino = JFFS_MIN_INO; - root->ino = JFFS_MIN_INO; - root->mode = S_IFDIR | S_IRWXU | S_IRGRP - | S_IXGRP | S_IROTH | S_IXOTH; - root->atime = root->mtime = root->ctime = get_seconds(); - root->nlink = 1; - root->c = c; - root->version_head = root->version_tail = node; - jffs_insert_file_into_hash(root); - return 0; -} - - -/* This is where the file system is built and initialized. */ -int -jffs_build_fs(struct super_block *sb) -{ - struct jffs_control *c; - int err = 0; - - D2(printk("jffs_build_fs()\n")); - - if (!(c = jffs_create_control(sb))) { - return -ENOMEM; - } - c->building_fs = 1; - c->sb = sb; - if ((err = jffs_scan_flash(c)) < 0) { - if(err == -EAGAIN){ - /* scan_flash() wants us to try once more. A flipping - bits sector was detect in the middle of the scan flash. - Clean up old allocated memory before going in. - */ - D1(printk("jffs_build_fs: Cleaning up all control structures," - " reallocating them and trying mount again.\n")); - jffs_cleanup_control(c); - if (!(c = jffs_create_control(sb))) { - return -ENOMEM; - } - c->building_fs = 1; - c->sb = sb; - - if ((err = jffs_scan_flash(c)) < 0) { - goto jffs_build_fs_fail; - } - }else{ - goto jffs_build_fs_fail; - } - } - - /* Add a virtual root node if no one exists. */ - if (!jffs_find_file(c, JFFS_MIN_INO)) { - if ((err = jffs_add_virtual_root(c)) < 0) { - goto jffs_build_fs_fail; - } - } - - while (c->delete_list) { - struct jffs_file *f; - struct jffs_delete_list *delete_list_element; - - if ((f = jffs_find_file(c, c->delete_list->ino))) { - f->deleted = 1; - } - delete_list_element = c->delete_list; - c->delete_list = c->delete_list->next; - kfree(delete_list_element); - } - - /* Remove deleted nodes. */ - if ((err = jffs_foreach_file(c, jffs_possibly_delete_file)) < 0) { - printk(KERN_ERR "JFFS: Failed to remove deleted nodes.\n"); - goto jffs_build_fs_fail; - } - /* Remove redundant nodes. (We are not interested in the - return value in this case.) */ - jffs_foreach_file(c, jffs_remove_redundant_nodes); - /* Try to build a tree from all the nodes. */ - if ((err = jffs_foreach_file(c, jffs_insert_file_into_tree)) < 0) { - printk("JFFS: Failed to build tree.\n"); - goto jffs_build_fs_fail; - } - /* Compute the sizes of all files in the filesystem. Adjust if - necessary. */ - if ((err = jffs_foreach_file(c, jffs_build_file)) < 0) { - printk("JFFS: Failed to build file system.\n"); - goto jffs_build_fs_fail; - } - sb->s_fs_info = (void *)c; - c->building_fs = 0; - - D1(jffs_print_hash_table(c)); - D1(jffs_print_tree(c->root, 0)); - - return 0; - -jffs_build_fs_fail: - jffs_cleanup_control(c); - return err; -} /* jffs_build_fs() */ - - -/* - This checks for sectors that were being erased in their previous - lifetimes and for some reason or the other (power fail etc.), - the erase cycles never completed. - As the flash array would have reverted back to read status, - these sectors are detected by the symptom of the "flipping bits", - i.e. bits being read back differently from the same location in - flash if read multiple times. - The only solution to this is to re-erase the entire - sector. - Unfortunately detecting "flipping bits" is not a simple exercise - as a bit may be read back at 1 or 0 depending on the alignment - of the stars in the universe. - The level of confidence is in direct proportion to the number of - scans done. By power fail testing I (Vipin) have been able to - proove that reading twice is not enough. - Maybe 4 times? Change NUM_REREADS to a higher number if you want - a (even) higher degree of confidence in your mount process. - A higher number would of course slow down your mount. -*/ -static int check_partly_erased_sectors(struct jffs_fmcontrol *fmc){ - -#define NUM_REREADS 4 /* see note above */ -#define READ_AHEAD_BYTES 4096 /* must be a multiple of 4, - usually set to kernel page size */ - - __u8 *read_buf1; - __u8 *read_buf2; - - int err = 0; - int retlen; - int i; - int cnt; - __u32 offset; - loff_t pos = 0; - loff_t end = fmc->flash_size; - - - /* Allocate read buffers */ - read_buf1 = kmalloc(sizeof(__u8) * READ_AHEAD_BYTES, GFP_KERNEL); - if (!read_buf1) - return -ENOMEM; - - read_buf2 = kmalloc(sizeof(__u8) * READ_AHEAD_BYTES, GFP_KERNEL); - if (!read_buf2) { - kfree(read_buf1); - return -ENOMEM; - } - - CHECK_NEXT: - while(pos < end){ - - D1(printk("check_partly_erased_sector():checking sector which contains" - " offset 0x%x for flipping bits..\n", (__u32)pos)); - - retlen = flash_safe_read(fmc->mtd, pos, - &read_buf1[0], READ_AHEAD_BYTES); - retlen &= ~3; - - for(cnt = 0; cnt < NUM_REREADS; cnt++){ - (void)flash_safe_read(fmc->mtd, pos, - &read_buf2[0], READ_AHEAD_BYTES); - - for (i=0 ; i < retlen ; i+=4) { - /* buffers MUST match, double word for word! */ - if(*((__u32 *) &read_buf1[i]) != - *((__u32 *) &read_buf2[i]) - ){ - /* flipping bits detected, time to erase sector */ - /* This will help us log some statistics etc. */ - D1(printk("Flipping bits detected in re-read round:%i of %i\n", - cnt, NUM_REREADS)); - D1(printk("check_partly_erased_sectors:flipping bits detected" - " @offset:0x%x(0x%x!=0x%x)\n", - (__u32)pos+i, *((__u32 *) &read_buf1[i]), - *((__u32 *) &read_buf2[i]))); - - /* calculate start of present sector */ - offset = (((__u32)pos+i)/(__u32)fmc->sector_size) * (__u32)fmc->sector_size; - - D1(printk("check_partly_erased_sector():erasing sector starting 0x%x.\n", - offset)); - - if (flash_erase_region(fmc->mtd, - offset, fmc->sector_size) < 0) { - printk(KERN_ERR "JFFS: Erase of flash failed. " - "offset = %u, erase_size = %d\n", - offset , fmc->sector_size); - - err = -EIO; - goto returnBack; - - }else{ - D1(printk("JFFS: Erase of flash sector @0x%x successful.\n", - offset)); - /* skip ahead to the next sector */ - pos = (((__u32)pos+i)/(__u32)fmc->sector_size) * (__u32)fmc->sector_size; - pos += fmc->sector_size; - goto CHECK_NEXT; - } - } - } - } - pos += READ_AHEAD_BYTES; - } - - returnBack: - kfree(read_buf1); - kfree(read_buf2); - - D2(printk("check_partly_erased_sector():Done checking all sectors till offset 0x%x for flipping bits.\n", - (__u32)pos)); - - return err; - -}/* end check_partly_erased_sectors() */ - - - -/* Scan the whole flash memory in order to find all nodes in the - file systems. */ -static int -jffs_scan_flash(struct jffs_control *c) -{ - char name[JFFS_MAX_NAME_LEN + 2]; - struct jffs_raw_inode raw_inode; - struct jffs_node *node = NULL; - struct jffs_fmcontrol *fmc = c->fmc; - __u32 checksum; - __u8 tmp_accurate; - __u16 tmp_chksum; - __u32 deleted_file; - loff_t pos = 0; - loff_t start; - loff_t test_start; - loff_t end = fmc->flash_size; - __u8 *read_buf; - int i, len, retlen; - __u32 offset; - - __u32 free_chunk_size1; - __u32 free_chunk_size2; - - -#define NUMFREEALLOWED 2 /* 2 chunks of at least erase size space allowed */ - int num_free_space = 0; /* Flag err if more than TWO - free blocks found. This is NOT allowed - by the current jffs design. - */ - int num_free_spc_not_accp = 0; /* For debugging purposed keep count - of how much free space was rejected and - marked dirty - */ - - D1(printk("jffs_scan_flash(): start pos = 0x%lx, end = 0x%lx\n", - (long)pos, (long)end)); - - flash_safe_acquire(fmc->mtd); - - /* - check and make sure that any sector does not suffer - from the "partly erased, bit flipping syndrome" (TM Vipin :) - If so, offending sectors will be erased. - */ - if(check_partly_erased_sectors(fmc) < 0){ - - flash_safe_release(fmc->mtd); - return -EIO; /* bad, bad, bad error. Cannot continue.*/ - } - - /* Allocate read buffer */ - read_buf = kmalloc(sizeof(__u8) * 4096, GFP_KERNEL); - if (!read_buf) { - flash_safe_release(fmc->mtd); - return -ENOMEM; - } - - /* Start the scan. */ - while (pos < end) { - deleted_file = 0; - - /* Remember the position from where we started this scan. */ - start = pos; - - switch (flash_read_u32(fmc->mtd, pos)) { - case JFFS_EMPTY_BITMASK: - /* We have found 0xffffffff at this position. We have to - scan the rest of the flash till the end or till - something else than 0xffffffff is found. - Keep going till we do not find JFFS_EMPTY_BITMASK - anymore */ - - D1(printk("jffs_scan_flash(): 0xffffffff at pos 0x%lx.\n", - (long)pos)); - - while(pos < end){ - - len = end - pos < 4096 ? end - pos : 4096; - - retlen = flash_safe_read(fmc->mtd, pos, - &read_buf[0], len); - - retlen &= ~3; - - for (i=0 ; i < retlen ; i+=4, pos += 4) { - if(*((__u32 *) &read_buf[i]) != - JFFS_EMPTY_BITMASK) - break; - } - if (i == retlen) - continue; - else - break; - } - - D1(printk("jffs_scan_flash():0xffffffff ended at pos 0x%lx.\n", - (long)pos)); - - /* If some free space ends in the middle of a sector, - treat it as dirty rather than clean. - This is to handle the case where one thread - allocated space for a node, but didn't get to - actually _write_ it before power was lost, leaving - a gap in the log. Shifting all node writes into - a single kernel thread will fix the original problem. - */ - if ((__u32) pos % fmc->sector_size) { - /* If there was free space in previous - sectors, don't mark that dirty too - - only from the beginning of this sector - (or from start) - */ - - test_start = pos & ~(fmc->sector_size-1); /* end of last sector */ - - if (start < test_start) { - - /* free space started in the previous sector! */ - - if((num_free_space < NUMFREEALLOWED) && - ((unsigned int)(test_start - start) >= fmc->sector_size)){ - - /* - Count it in if we are still under NUMFREEALLOWED *and* it is - at least 1 erase sector in length. This will keep us from - picking any little ole' space as "free". - */ - - D1(printk("Reducing end of free space to 0x%x from 0x%x\n", - (unsigned int)test_start, (unsigned int)pos)); - - D1(printk("Free space accepted: Starting 0x%x for 0x%x bytes\n", - (unsigned int) start, - (unsigned int)(test_start - start))); - - /* below, space from "start" to "pos" will be marked dirty. */ - start = test_start; - - /* Being in here means that we have found at least an entire - erase sector size of free space ending on a sector boundary. - Keep track of free spaces accepted. - */ - num_free_space++; - }else{ - num_free_spc_not_accp++; - D1(printk("Free space (#%i) found but *Not* accepted: Starting" - " 0x%x for 0x%x bytes\n", - num_free_spc_not_accp, (unsigned int)start, - (unsigned int)((unsigned int)(pos & ~(fmc->sector_size-1)) - (unsigned int)start))); - - } - - } - if((((__u32)(pos - start)) != 0)){ - - D1(printk("Dirty space: Starting 0x%x for 0x%x bytes\n", - (unsigned int) start, (unsigned int) (pos - start))); - jffs_fmalloced(fmc, (__u32) start, - (__u32) (pos - start), NULL); - }else{ - /* "Flipping bits" detected. This means that our scan for them - did not catch this offset. See check_partly_erased_sectors() for - more info. - */ - - D1(printk("jffs_scan_flash():wants to allocate dirty flash " - "space for 0 bytes.\n")); - D1(printk("jffs_scan_flash(): Flipping bits! We will free " - "all allocated memory, erase this sector and remount\n")); - - /* calculate start of present sector */ - offset = (((__u32)pos)/(__u32)fmc->sector_size) * (__u32)fmc->sector_size; - - D1(printk("jffs_scan_flash():erasing sector starting 0x%x.\n", - offset)); - - if (flash_erase_region(fmc->mtd, - offset, fmc->sector_size) < 0) { - printk(KERN_ERR "JFFS: Erase of flash failed. " - "offset = %u, erase_size = %d\n", - offset , fmc->sector_size); - - flash_safe_release(fmc->mtd); - kfree(read_buf); - return -1; /* bad, bad, bad! */ - - } - flash_safe_release(fmc->mtd); - kfree(read_buf); - - return -EAGAIN; /* erased offending sector. Try mount one more time please. */ - } - }else{ - /* Being in here means that we have found free space that ends on an erase sector - boundary. - Count it in if we are still under NUMFREEALLOWED *and* it is at least 1 erase - sector in length. This will keep us from picking any little ole' space as "free". - */ - if((num_free_space < NUMFREEALLOWED) && - ((unsigned int)(pos - start) >= fmc->sector_size)){ - /* We really don't do anything to mark space as free, except *not* - mark it dirty and just advance the "pos" location pointer. - It will automatically be picked up as free space. - */ - num_free_space++; - D1(printk("Free space accepted: Starting 0x%x for 0x%x bytes\n", - (unsigned int) start, (unsigned int) (pos - start))); - }else{ - num_free_spc_not_accp++; - D1(printk("Free space (#%i) found but *Not* accepted: Starting " - "0x%x for 0x%x bytes\n", num_free_spc_not_accp, - (unsigned int) start, - (unsigned int) (pos - start))); - - /* Mark this space as dirty. We already have our free space. */ - D1(printk("Dirty space: Starting 0x%x for 0x%x bytes\n", - (unsigned int) start, (unsigned int) (pos - start))); - jffs_fmalloced(fmc, (__u32) start, - (__u32) (pos - start), NULL); - } - - } - if(num_free_space > NUMFREEALLOWED){ - printk(KERN_WARNING "jffs_scan_flash(): Found free space " - "number %i. Only %i free space is allowed.\n", - num_free_space, NUMFREEALLOWED); - } - continue; - - case JFFS_DIRTY_BITMASK: - /* We have found 0x00000000 at this position. Scan as far - as possible to find out how much is dirty. */ - D1(printk("jffs_scan_flash(): 0x00000000 at pos 0x%lx.\n", - (long)pos)); - for (; pos < end - && JFFS_DIRTY_BITMASK == flash_read_u32(fmc->mtd, pos); - pos += 4); - D1(printk("jffs_scan_flash(): 0x00 ended at " - "pos 0x%lx.\n", (long)pos)); - jffs_fmalloced(fmc, (__u32) start, - (__u32) (pos - start), NULL); - continue; - - case JFFS_MAGIC_BITMASK: - /* We have probably found a new raw inode. */ - break; - - default: - bad_inode: - /* We're f*cked. This is not solved yet. We have - to scan for the magic pattern. */ - D1(printk("*************** Dirty flash memory or " - "bad inode: " - "hexdump(pos = 0x%lx, len = 128):\n", - (long)pos)); - D1(jffs_hexdump(fmc->mtd, pos, 128)); - - for (pos += 4; pos < end; pos += 4) { - switch (flash_read_u32(fmc->mtd, pos)) { - case JFFS_MAGIC_BITMASK: - case JFFS_EMPTY_BITMASK: - /* handle these in the main switch() loop */ - goto cont_scan; - - default: - break; - } - } - - cont_scan: - /* First, mark as dirty the region - which really does contain crap. */ - jffs_fmalloced(fmc, (__u32) start, - (__u32) (pos - start), - NULL); - - continue; - }/* switch */ - - /* We have found the beginning of an inode. Create a - node for it unless there already is one available. */ - if (!node) { - if (!(node = jffs_alloc_node())) { - /* Free read buffer */ - kfree(read_buf); - - /* Release the flash device */ - flash_safe_release(fmc->mtd); - - return -ENOMEM; - } - DJM(no_jffs_node++); - } - - /* Read the next raw inode. */ - - flash_safe_read(fmc->mtd, pos, (u_char *) &raw_inode, - sizeof(struct jffs_raw_inode)); - - /* When we compute the checksum for the inode, we never - count the 'accurate' or the 'checksum' fields. */ - tmp_accurate = raw_inode.accurate; - tmp_chksum = raw_inode.chksum; - raw_inode.accurate = 0; - raw_inode.chksum = 0; - checksum = jffs_checksum(&raw_inode, - sizeof(struct jffs_raw_inode)); - raw_inode.accurate = tmp_accurate; - raw_inode.chksum = tmp_chksum; - - D3(printk("*** We have found this raw inode at pos 0x%lx " - "on the flash:\n", (long)pos)); - D3(jffs_print_raw_inode(&raw_inode)); - - if (checksum != raw_inode.chksum) { - D1(printk("jffs_scan_flash(): Bad checksum: " - "checksum = %u, " - "raw_inode.chksum = %u\n", - checksum, raw_inode.chksum)); - pos += sizeof(struct jffs_raw_inode); - jffs_fmalloced(fmc, (__u32) start, - (__u32) (pos - start), NULL); - /* Reuse this unused struct jffs_node. */ - continue; - } - - /* Check the raw inode read so far. Start with the - maximum length of the filename. */ - if (raw_inode.nsize > JFFS_MAX_NAME_LEN) { - printk(KERN_WARNING "jffs_scan_flash: Found a " - "JFFS node with name too large\n"); - goto bad_inode; - } - - if (raw_inode.rename && raw_inode.dsize != sizeof(__u32)) { - printk(KERN_WARNING "jffs_scan_flash: Found a " - "rename node with dsize %u.\n", - raw_inode.dsize); - jffs_print_raw_inode(&raw_inode); - goto bad_inode; - } - - /* The node's data segment should not exceed a - certain length. */ - if (raw_inode.dsize > fmc->max_chunk_size) { - printk(KERN_WARNING "jffs_scan_flash: Found a " - "JFFS node with dsize (0x%x) > max_chunk_size (0x%x)\n", - raw_inode.dsize, fmc->max_chunk_size); - goto bad_inode; - } - - pos += sizeof(struct jffs_raw_inode); - - /* This shouldn't be necessary because a node that - violates the flash boundaries shouldn't be written - in the first place. */ - if (pos >= end) { - goto check_node; - } - - /* Read the name. */ - *name = 0; - if (raw_inode.nsize) { - flash_safe_read(fmc->mtd, pos, name, raw_inode.nsize); - name[raw_inode.nsize] = '\0'; - pos += raw_inode.nsize - + JFFS_GET_PAD_BYTES(raw_inode.nsize); - D3(printk("name == \"%s\"\n", name)); - checksum = jffs_checksum(name, raw_inode.nsize); - if (checksum != raw_inode.nchksum) { - D1(printk("jffs_scan_flash(): Bad checksum: " - "checksum = %u, " - "raw_inode.nchksum = %u\n", - checksum, raw_inode.nchksum)); - jffs_fmalloced(fmc, (__u32) start, - (__u32) (pos - start), NULL); - /* Reuse this unused struct jffs_node. */ - continue; - } - if (pos >= end) { - goto check_node; - } - } - - /* Read the data, if it exists, in order to be sure it - matches the checksum. */ - if (raw_inode.dsize) { - if (raw_inode.rename) { - deleted_file = flash_read_u32(fmc->mtd, pos); - } - if (jffs_checksum_flash(fmc->mtd, pos, raw_inode.dsize, &checksum)) { - printk("jffs_checksum_flash() failed to calculate a checksum\n"); - jffs_fmalloced(fmc, (__u32) start, - (__u32) (pos - start), NULL); - /* Reuse this unused struct jffs_node. */ - continue; - } - pos += raw_inode.dsize - + JFFS_GET_PAD_BYTES(raw_inode.dsize); - - if (checksum != raw_inode.dchksum) { - D1(printk("jffs_scan_flash(): Bad checksum: " - "checksum = %u, " - "raw_inode.dchksum = %u\n", - checksum, raw_inode.dchksum)); - jffs_fmalloced(fmc, (__u32) start, - (__u32) (pos - start), NULL); - /* Reuse this unused struct jffs_node. */ - continue; - } - } - - check_node: - - /* Remember the highest inode number in the whole file - system. This information will be used when assigning - new files new inode numbers. */ - if (c->next_ino <= raw_inode.ino) { - c->next_ino = raw_inode.ino + 1; - } - - if (raw_inode.accurate) { - int err; - node->data_offset = raw_inode.offset; - node->data_size = raw_inode.dsize; - node->removed_size = raw_inode.rsize; - /* Compute the offset to the actual data in the - on-flash node. */ - node->fm_offset - = sizeof(struct jffs_raw_inode) - + raw_inode.nsize - + JFFS_GET_PAD_BYTES(raw_inode.nsize); - node->fm = jffs_fmalloced(fmc, (__u32) start, - (__u32) (pos - start), - node); - if (!node->fm) { - D(printk("jffs_scan_flash(): !node->fm\n")); - jffs_free_node(node); - DJM(no_jffs_node--); - - /* Free read buffer */ - kfree(read_buf); - - /* Release the flash device */ - flash_safe_release(fmc->mtd); - - return -ENOMEM; - } - if ((err = jffs_insert_node(c, NULL, &raw_inode, - name, node)) < 0) { - printk("JFFS: Failed to handle raw inode. " - "(err = %d)\n", err); - break; - } - if (raw_inode.rename) { - struct jffs_delete_list *dl - = (struct jffs_delete_list *) - kmalloc(sizeof(struct jffs_delete_list), - GFP_KERNEL); - if (!dl) { - D(printk("jffs_scan_flash: !dl\n")); - jffs_free_node(node); - DJM(no_jffs_node--); - - /* Release the flash device */ - flash_safe_release(fmc->flash_part); - - /* Free read buffer */ - kfree(read_buf); - - return -ENOMEM; - } - dl->ino = deleted_file; - dl->next = c->delete_list; - c->delete_list = dl; - node->data_size = 0; - } - D3(jffs_print_node(node)); - node = NULL; /* Don't free the node! */ - } - else { - jffs_fmalloced(fmc, (__u32) start, - (__u32) (pos - start), NULL); - D3(printk("jffs_scan_flash(): Just found an obsolete " - "raw_inode. Continuing the scan...\n")); - /* Reuse this unused struct jffs_node. */ - } - } - - if (node) { - jffs_free_node(node); - DJM(no_jffs_node--); - } - jffs_build_end(fmc); - - /* Free read buffer */ - kfree(read_buf); - - if(!num_free_space){ - printk(KERN_WARNING "jffs_scan_flash(): Did not find even a single " - "chunk of free space. This is BAD!\n"); - } - - /* Return happy */ - D3(printk("jffs_scan_flash(): Leaving...\n")); - flash_safe_release(fmc->mtd); - - /* This is to trap the "free size accounting screwed error. */ - free_chunk_size1 = jffs_free_size1(fmc); - free_chunk_size2 = jffs_free_size2(fmc); - - if (free_chunk_size1 + free_chunk_size2 != fmc->free_size) { - - printk(KERN_WARNING "jffs_scan_falsh():Free size accounting screwed\n"); - printk(KERN_WARNING "jfffs_scan_flash():free_chunk_size1 == 0x%x, " - "free_chunk_size2 == 0x%x, fmc->free_size == 0x%x\n", - free_chunk_size1, free_chunk_size2, fmc->free_size); - - return -1; /* Do NOT mount f/s so that we can inspect what happened. - Mounting this screwed up f/s will screw us up anyway. - */ - } - - return 0; /* as far as we are concerned, we are happy! */ -} /* jffs_scan_flash() */ - - -/* Insert any kind of node into the file system. Take care of data - insertions and deletions. Also remove redundant information. The - memory allocated for the `name' is regarded as "given away" in the - caller's perspective. */ -int -jffs_insert_node(struct jffs_control *c, struct jffs_file *f, - const struct jffs_raw_inode *raw_inode, - const char *name, struct jffs_node *node) -{ - int update_name = 0; - int insert_into_tree = 0; - - D2(printk("jffs_insert_node(): ino = %u, version = %u, " - "name = \"%s\", deleted = %d\n", - raw_inode->ino, raw_inode->version, - ((name && *name) ? name : ""), raw_inode->deleted)); - - /* If there doesn't exist an associated jffs_file, then - create, initialize and insert one into the file system. */ - if (!f && !(f = jffs_find_file(c, raw_inode->ino))) { - if (!(f = jffs_create_file(c, raw_inode))) { - return -ENOMEM; - } - jffs_insert_file_into_hash(f); - insert_into_tree = 1; - } - node->ino = raw_inode->ino; - node->version = raw_inode->version; - node->data_size = raw_inode->dsize; - node->fm_offset = sizeof(struct jffs_raw_inode) + raw_inode->nsize - + JFFS_GET_PAD_BYTES(raw_inode->nsize); - node->name_size = raw_inode->nsize; - - /* Now insert the node at the correct position into the file's - version list. */ - if (!f->version_head) { - /* This is the first node. */ - f->version_head = node; - f->version_tail = node; - node->version_prev = NULL; - node->version_next = NULL; - f->highest_version = node->version; - update_name = 1; - f->mode = raw_inode->mode; - f->uid = raw_inode->uid; - f->gid = raw_inode->gid; - f->atime = raw_inode->atime; - f->mtime = raw_inode->mtime; - f->ctime = raw_inode->ctime; - } - else if ((f->highest_version < node->version) - || (node->version == 0)) { - /* Insert at the end of the list. I.e. this node is the - newest one so far. */ - node->version_prev = f->version_tail; - node->version_next = NULL; - f->version_tail->version_next = node; - f->version_tail = node; - f->highest_version = node->version; - update_name = 1; - f->pino = raw_inode->pino; - f->mode = raw_inode->mode; - f->uid = raw_inode->uid; - f->gid = raw_inode->gid; - f->atime = raw_inode->atime; - f->mtime = raw_inode->mtime; - f->ctime = raw_inode->ctime; - } - else if (f->version_head->version > node->version) { - /* Insert at the bottom of the list. */ - node->version_prev = NULL; - node->version_next = f->version_head; - f->version_head->version_prev = node; - f->version_head = node; - if (!f->name) { - update_name = 1; - } - } - else { - struct jffs_node *n; - int newer_name = 0; - /* Search for the insertion position starting from - the tail (newest node). */ - for (n = f->version_tail; n; n = n->version_prev) { - if (n->version < node->version) { - node->version_prev = n; - node->version_next = n->version_next; - node->version_next->version_prev = node; - n->version_next = node; - if (!newer_name) { - update_name = 1; - } - break; - } - if (n->name_size) { - newer_name = 1; - } - } - } - - /* Deletion is irreversible. If any 'deleted' node is ever - written, the file is deleted */ - if (raw_inode->deleted) - f->deleted = raw_inode->deleted; - - /* Perhaps update the name. */ - if (raw_inode->nsize && update_name && name && *name && (name != f->name)) { - if (f->name) { - kfree(f->name); - DJM(no_name--); - } - if (!(f->name = kmalloc(raw_inode->nsize + 1, - GFP_KERNEL))) { - return -ENOMEM; - } - DJM(no_name++); - memcpy(f->name, name, raw_inode->nsize); - f->name[raw_inode->nsize] = '\0'; - f->nsize = raw_inode->nsize; - D3(printk("jffs_insert_node(): Updated the name of " - "the file to \"%s\".\n", name)); - } - - if (!c->building_fs) { - D3(printk("jffs_insert_node(): ---------------------------" - "------------------------------------------- 1\n")); - if (insert_into_tree) { - jffs_insert_file_into_tree(f); - } - /* Once upon a time, we would call jffs_possibly_delete_file() - here. That causes an oops if someone's still got the file - open, so now we only do it in jffs_delete_inode() - -- dwmw2 - */ - if (node->data_size || node->removed_size) { - jffs_update_file(f, node); - } - jffs_remove_redundant_nodes(f); - - jffs_garbage_collect_trigger(c); - - D3(printk("jffs_insert_node(): ---------------------------" - "------------------------------------------- 2\n")); - } - - return 0; -} /* jffs_insert_node() */ - - -/* Unlink a jffs_node from the version list it is in. */ -static inline void -jffs_unlink_node_from_version_list(struct jffs_file *f, - struct jffs_node *node) -{ - if (node->version_prev) { - node->version_prev->version_next = node->version_next; - } else { - f->version_head = node->version_next; - } - if (node->version_next) { - node->version_next->version_prev = node->version_prev; - } else { - f->version_tail = node->version_prev; - } -} - - -/* Unlink a jffs_node from the range list it is in. */ -static inline void -jffs_unlink_node_from_range_list(struct jffs_file *f, struct jffs_node *node) -{ - if (node->range_prev) { - node->range_prev->range_next = node->range_next; - } - else { - f->range_head = node->range_next; - } - if (node->range_next) { - node->range_next->range_prev = node->range_prev; - } - else { - f->range_tail = node->range_prev; - } -} - - -/* Function used by jffs_remove_redundant_nodes() below. This function - classifies what kind of information a node adds to a file. */ -static inline __u8 -jffs_classify_node(struct jffs_node *node) -{ - __u8 mod_type = JFFS_MODIFY_INODE; - - if (node->name_size) { - mod_type |= JFFS_MODIFY_NAME; - } - if (node->data_size || node->removed_size) { - mod_type |= JFFS_MODIFY_DATA; - } - return mod_type; -} - - -/* Remove redundant nodes from a file. Mark the on-flash memory - as dirty. */ -static int -jffs_remove_redundant_nodes(struct jffs_file *f) -{ - struct jffs_node *newest_node; - struct jffs_node *cur; - struct jffs_node *prev; - __u8 newest_type; - __u8 mod_type; - __u8 node_with_name_later = 0; - - if (!(newest_node = f->version_tail)) { - return 0; - } - - /* What does the `newest_node' modify? */ - newest_type = jffs_classify_node(newest_node); - node_with_name_later = newest_type & JFFS_MODIFY_NAME; - - D3(printk("jffs_remove_redundant_nodes(): ino: %u, name: \"%s\", " - "newest_type: %u\n", f->ino, (f->name ? f->name : ""), - newest_type)); - - /* Traverse the file's nodes and determine which of them that are - superfluous. Yeah, this might look very complex at first - glance but it is actually very simple. */ - for (cur = newest_node->version_prev; cur; cur = prev) { - prev = cur->version_prev; - mod_type = jffs_classify_node(cur); - if ((mod_type <= JFFS_MODIFY_INODE) - || ((newest_type & JFFS_MODIFY_NAME) - && (mod_type - <= (JFFS_MODIFY_INODE + JFFS_MODIFY_NAME))) - || (cur->data_size == 0 && cur->removed_size - && !cur->version_prev && node_with_name_later)) { - /* Yes, this node is redundant. Remove it. */ - D2(printk("jffs_remove_redundant_nodes(): " - "Removing node: ino: %u, version: %u, " - "mod_type: %u\n", cur->ino, cur->version, - mod_type)); - jffs_unlink_node_from_version_list(f, cur); - jffs_fmfree(f->c->fmc, cur->fm, cur); - jffs_free_node(cur); - DJM(no_jffs_node--); - } - else { - node_with_name_later |= (mod_type & JFFS_MODIFY_NAME); - } - } - - return 0; -} - - -/* Insert a file into the hash table. */ -static int -jffs_insert_file_into_hash(struct jffs_file *f) -{ - int i = f->ino % f->c->hash_len; - - D3(printk("jffs_insert_file_into_hash(): f->ino: %u\n", f->ino)); - - list_add(&f->hash, &f->c->hash[i]); - return 0; -} - - -/* Insert a file into the file system tree. */ -int -jffs_insert_file_into_tree(struct jffs_file *f) -{ - struct jffs_file *parent; - - D3(printk("jffs_insert_file_into_tree(): name: \"%s\"\n", - (f->name ? f->name : ""))); - - if (!(parent = jffs_find_file(f->c, f->pino))) { - if (f->pino == 0) { - f->c->root = f; - f->parent = NULL; - f->sibling_prev = NULL; - f->sibling_next = NULL; - return 0; - } - else { - D1(printk("jffs_insert_file_into_tree(): Found " - "inode with no parent and pino == %u\n", - f->pino)); - return -1; - } - } - f->parent = parent; - f->sibling_next = parent->children; - if (f->sibling_next) { - f->sibling_next->sibling_prev = f; - } - f->sibling_prev = NULL; - parent->children = f; - return 0; -} - - -/* Remove a file from the hash table. */ -static int -jffs_unlink_file_from_hash(struct jffs_file *f) -{ - D3(printk("jffs_unlink_file_from_hash(): f: 0x%p, " - "ino %u\n", f, f->ino)); - - list_del(&f->hash); - return 0; -} - - -/* Just remove the file from the parent's children. Don't free - any memory. */ -int -jffs_unlink_file_from_tree(struct jffs_file *f) -{ - D3(printk("jffs_unlink_file_from_tree(): ino: %d, pino: %d, name: " - "\"%s\"\n", f->ino, f->pino, (f->name ? f->name : ""))); - - if (f->sibling_prev) { - f->sibling_prev->sibling_next = f->sibling_next; - } - else if (f->parent) { - D3(printk("f->parent=%p\n", f->parent)); - f->parent->children = f->sibling_next; - } - if (f->sibling_next) { - f->sibling_next->sibling_prev = f->sibling_prev; - } - return 0; -} - - -/* Find a file with its inode number. */ -struct jffs_file * -jffs_find_file(struct jffs_control *c, __u32 ino) -{ - struct jffs_file *f; - int i = ino % c->hash_len; - - D3(printk("jffs_find_file(): ino: %u\n", ino)); - - list_for_each_entry(f, &c->hash[i], hash) { - if (ino != f->ino) - continue; - D3(printk("jffs_find_file(): Found file with ino " - "%u. (name: \"%s\")\n", - ino, (f->name ? f->name : "")); - ); - return f; - } - D3(printk("jffs_find_file(): Didn't find file " - "with ino %u.\n", ino); - ); - return NULL; -} - - -/* Find a file in a directory. We are comparing the names. */ -struct jffs_file * -jffs_find_child(struct jffs_file *dir, const char *name, int len) -{ - struct jffs_file *f; - - D3(printk("jffs_find_child()\n")); - - for (f = dir->children; f; f = f->sibling_next) { - if (!f->deleted && f->name - && !strncmp(f->name, name, len) - && f->name[len] == '\0') { - break; - } - } - - D3(if (f) { - printk("jffs_find_child(): Found \"%s\".\n", f->name); - } - else { - char *copy = kmalloc(len + 1, GFP_KERNEL); - if (copy) { - memcpy(copy, name, len); - copy[len] = '\0'; - } - printk("jffs_find_child(): Didn't find the file \"%s\".\n", - (copy ? copy : "")); - kfree(copy); - }); - - return f; -} - - -/* Write a raw inode that takes up a certain amount of space in the flash - memory. At the end of the flash device, there is often space that is - impossible to use. At these times we want to mark this space as not - used. In the cases when the amount of space is greater or equal than - a struct jffs_raw_inode, we write a "dummy node" that takes up this - space. The space after the raw inode, if it exists, is left as it is. - Since this space after the raw inode contains JFFS_EMPTY_BITMASK bytes, - we can compute the checksum of it; we don't have to manipulate it any - further. - - If the space left on the device is less than the size of a struct - jffs_raw_inode, this space is filled with JFFS_DIRTY_BITMASK bytes. - No raw inode is written this time. */ -static int -jffs_write_dummy_node(struct jffs_control *c, struct jffs_fm *dirty_fm) -{ - struct jffs_fmcontrol *fmc = c->fmc; - int err; - - D1(printk("jffs_write_dummy_node(): dirty_fm->offset = 0x%08x, " - "dirty_fm->size = %u\n", - dirty_fm->offset, dirty_fm->size)); - - if (dirty_fm->size >= sizeof(struct jffs_raw_inode)) { - struct jffs_raw_inode raw_inode; - memset(&raw_inode, 0, sizeof(struct jffs_raw_inode)); - raw_inode.magic = JFFS_MAGIC_BITMASK; - raw_inode.dsize = dirty_fm->size - - sizeof(struct jffs_raw_inode); - raw_inode.dchksum = raw_inode.dsize * 0xff; - raw_inode.chksum - = jffs_checksum(&raw_inode, sizeof(struct jffs_raw_inode)); - - if ((err = flash_safe_write(fmc->mtd, - dirty_fm->offset, - (u_char *)&raw_inode, - sizeof(struct jffs_raw_inode))) - < 0) { - printk(KERN_ERR "JFFS: jffs_write_dummy_node: " - "flash_safe_write failed!\n"); - return err; - } - } - else { - flash_safe_acquire(fmc->mtd); - flash_memset(fmc->mtd, dirty_fm->offset, 0, dirty_fm->size); - flash_safe_release(fmc->mtd); - } - - D3(printk("jffs_write_dummy_node(): Leaving...\n")); - return 0; -} - - -/* Write a raw inode, possibly its name and possibly some data. */ -int -jffs_write_node(struct jffs_control *c, struct jffs_node *node, - struct jffs_raw_inode *raw_inode, - const char *name, const unsigned char *data, - int recoverable, - struct jffs_file *f) -{ - struct jffs_fmcontrol *fmc = c->fmc; - struct jffs_fm *fm; - struct kvec node_iovec[4]; - unsigned long iovec_cnt; - - __u32 pos; - int err; - __u32 slack = 0; - - __u32 total_name_size = raw_inode->nsize - + JFFS_GET_PAD_BYTES(raw_inode->nsize); - __u32 total_data_size = raw_inode->dsize - + JFFS_GET_PAD_BYTES(raw_inode->dsize); - __u32 total_size = sizeof(struct jffs_raw_inode) - + total_name_size + total_data_size; - - /* If this node isn't something that will eventually let - GC free even more space, then don't allow it unless - there's at least max_chunk_size space still available - */ - if (!recoverable) - slack = fmc->max_chunk_size; - - - /* Fire the retrorockets and shoot the fruiton torpedoes, sir! */ - - ASSERT(if (!node) { - printk("jffs_write_node(): node == NULL\n"); - return -EINVAL; - }); - ASSERT(if (raw_inode && raw_inode->nsize && !name) { - printk("*** jffs_write_node(): nsize = %u but name == NULL\n", - raw_inode->nsize); - return -EINVAL; - }); - - D1(printk("jffs_write_node(): filename = \"%s\", ino = %u, " - "total_size = %u\n", - (name ? name : ""), raw_inode->ino, - total_size)); - - jffs_fm_write_lock(fmc); - -retry: - fm = NULL; - err = 0; - while (!fm) { - - /* Deadlocks suck. */ - while(fmc->free_size < fmc->min_free_size + total_size + slack) { - jffs_fm_write_unlock(fmc); - if (!JFFS_ENOUGH_SPACE(c, total_size + slack)) - return -ENOSPC; - jffs_fm_write_lock(fmc); - } - - /* First try to allocate some flash memory. */ - err = jffs_fmalloc(fmc, total_size, node, &fm); - - if (err == -ENOSPC) { - /* Just out of space. GC and try again */ - if (fmc->dirty_size < fmc->sector_size) { - D(printk("jffs_write_node(): jffs_fmalloc(0x%p, %u) " - "failed, no dirty space to GC\n", fmc, - total_size)); - return err; - } - - D1(printk(KERN_INFO "jffs_write_node(): Calling jffs_garbage_collect_now()\n")); - jffs_fm_write_unlock(fmc); - if ((err = jffs_garbage_collect_now(c))) { - D(printk("jffs_write_node(): jffs_garbage_collect_now() failed\n")); - return err; - } - jffs_fm_write_lock(fmc); - continue; - } - - if (err < 0) { - jffs_fm_write_unlock(fmc); - - D(printk("jffs_write_node(): jffs_fmalloc(0x%p, %u) " - "failed!\n", fmc, total_size)); - return err; - } - - if (!fm->nodes) { - /* The jffs_fm struct that we got is not good enough. - Make that space dirty and try again */ - if ((err = jffs_write_dummy_node(c, fm)) < 0) { - kfree(fm); - DJM(no_jffs_fm--); - jffs_fm_write_unlock(fmc); - D(printk("jffs_write_node(): " - "jffs_write_dummy_node(): Failed!\n")); - return err; - } - fm = NULL; - } - } /* while(!fm) */ - node->fm = fm; - - ASSERT(if (fm->nodes == 0) { - printk(KERN_ERR "jffs_write_node(): fm->nodes == 0\n"); - }); - - pos = node->fm->offset; - - /* Increment the version number here. We can't let the caller - set it beforehand, because we might have had to do GC on a node - of this file - and we'd end up reusing version numbers. - */ - if (f) { - raw_inode->version = f->highest_version + 1; - D1(printk (KERN_NOTICE "jffs_write_node(): setting version of %s to %d\n", f->name, raw_inode->version)); - - /* if the file was deleted, set the deleted bit in the raw inode */ - if (f->deleted) - raw_inode->deleted = 1; - } - - /* Compute the checksum for the data and name chunks. */ - raw_inode->dchksum = jffs_checksum(data, raw_inode->dsize); - raw_inode->nchksum = jffs_checksum(name, raw_inode->nsize); - - /* The checksum is calculated without the chksum and accurate - fields so set them to zero first. */ - raw_inode->accurate = 0; - raw_inode->chksum = 0; - raw_inode->chksum = jffs_checksum(raw_inode, - sizeof(struct jffs_raw_inode)); - raw_inode->accurate = 0xff; - - D3(printk("jffs_write_node(): About to write this raw inode to the " - "flash at pos 0x%lx:\n", (long)pos)); - D3(jffs_print_raw_inode(raw_inode)); - - /* The actual raw JFFS node */ - node_iovec[0].iov_base = (void *) raw_inode; - node_iovec[0].iov_len = (size_t) sizeof(struct jffs_raw_inode); - iovec_cnt = 1; - - /* Get name and size if there is one */ - if (raw_inode->nsize) { - node_iovec[iovec_cnt].iov_base = (void *) name; - node_iovec[iovec_cnt].iov_len = (size_t) raw_inode->nsize; - iovec_cnt++; - - if (JFFS_GET_PAD_BYTES(raw_inode->nsize)) { - static unsigned char allff[3]={255,255,255}; - /* Add some extra padding if necessary */ - node_iovec[iovec_cnt].iov_base = allff; - node_iovec[iovec_cnt].iov_len = - JFFS_GET_PAD_BYTES(raw_inode->nsize); - iovec_cnt++; - } - } - - /* Get data and size if there is any */ - if (raw_inode->dsize) { - node_iovec[iovec_cnt].iov_base = (void *) data; - node_iovec[iovec_cnt].iov_len = (size_t) raw_inode->dsize; - iovec_cnt++; - /* No need to pad this because we're not actually putting - anything after it. - */ - } - - if ((err = flash_safe_writev(fmc->mtd, node_iovec, iovec_cnt, - pos)) < 0) { - jffs_fmfree_partly(fmc, fm, 0); - jffs_fm_write_unlock(fmc); - printk(KERN_ERR "JFFS: jffs_write_node: Failed to write, " - "requested %i, wrote %i\n", total_size, err); - goto retry; - } - if (raw_inode->deleted) - f->deleted = 1; - - jffs_fm_write_unlock(fmc); - D3(printk("jffs_write_node(): Leaving...\n")); - return raw_inode->dsize; -} /* jffs_write_node() */ - - -/* Read data from the node and write it to the buffer. 'node_offset' - is how much we have read from this particular node before and which - shouldn't be read again. 'max_size' is how much space there is in - the buffer. */ -static int -jffs_get_node_data(struct jffs_file *f, struct jffs_node *node, - unsigned char *buf,__u32 node_offset, __u32 max_size) -{ - struct jffs_fmcontrol *fmc = f->c->fmc; - __u32 pos = node->fm->offset + node->fm_offset + node_offset; - __u32 avail = node->data_size - node_offset; - __u32 r; - - D2(printk(" jffs_get_node_data(): file: \"%s\", ino: %u, " - "version: %u, node_offset: %u\n", - f->name, node->ino, node->version, node_offset)); - - r = min(avail, max_size); - D3(printk(KERN_NOTICE "jffs_get_node_data\n")); - flash_safe_read(fmc->mtd, pos, buf, r); - - D3(printk(" jffs_get_node_data(): Read %u byte%s.\n", - r, (r == 1 ? "" : "s"))); - - return r; -} - - -/* Read data from the file's nodes. Write the data to the buffer - 'buf'. 'read_offset' tells how much data we should skip. */ -int -jffs_read_data(struct jffs_file *f, unsigned char *buf, __u32 read_offset, - __u32 size) -{ - struct jffs_node *node; - __u32 read_data = 0; /* Total amount of read data. */ - __u32 node_offset = 0; - __u32 pos = 0; /* Number of bytes traversed. */ - - D2(printk("jffs_read_data(): file = \"%s\", read_offset = %d, " - "size = %u\n", - (f->name ? f->name : ""), read_offset, size)); - - if (read_offset >= f->size) { - D(printk(" f->size: %d\n", f->size)); - return 0; - } - - /* First find the node to read data from. */ - node = f->range_head; - while (pos <= read_offset) { - node_offset = read_offset - pos; - if (node_offset >= node->data_size) { - pos += node->data_size; - node = node->range_next; - } - else { - break; - } - } - - /* "Cats are living proof that not everything in nature - has to be useful." - - Garrison Keilor ('97) */ - - /* Fill the buffer. */ - while (node && (read_data < size)) { - int r; - if (!node->fm) { - /* This node does not refer to real data. */ - r = min(size - read_data, - node->data_size - node_offset); - memset(&buf[read_data], 0, r); - } - else if ((r = jffs_get_node_data(f, node, &buf[read_data], - node_offset, - size - read_data)) < 0) { - return r; - } - read_data += r; - node_offset = 0; - node = node->range_next; - } - D3(printk(" jffs_read_data(): Read %u bytes.\n", read_data)); - return read_data; -} - - -/* Used for traversing all nodes in the hash table. */ -int -jffs_foreach_file(struct jffs_control *c, int (*func)(struct jffs_file *)) -{ - int pos; - int r; - int result = 0; - - for (pos = 0; pos < c->hash_len; pos++) { - struct jffs_file *f, *next; - - /* We must do _safe, because 'func' might remove the - current file 'f' from the list. */ - list_for_each_entry_safe(f, next, &c->hash[pos], hash) { - r = func(f); - if (r < 0) - return r; - result += r; - } - } - - return result; -} - - -/* Free all nodes associated with a file. */ -static int -jffs_free_node_list(struct jffs_file *f) -{ - struct jffs_node *node; - struct jffs_node *p; - - D3(printk("jffs_free_node_list(): f #%u, \"%s\"\n", - f->ino, (f->name ? f->name : ""))); - node = f->version_head; - while (node) { - p = node; - node = node->version_next; - jffs_free_node(p); - DJM(no_jffs_node--); - } - return 0; -} - - -/* Free a file and its name. */ -static int -jffs_free_file(struct jffs_file *f) -{ - D3(printk("jffs_free_file: f #%u, \"%s\"\n", - f->ino, (f->name ? f->name : ""))); - - if (f->name) { - kfree(f->name); - DJM(no_name--); - } - kfree(f); - no_jffs_file--; - return 0; -} - -static long -jffs_get_file_count(void) -{ - return no_jffs_file; -} - -/* See if a file is deleted. If so, mark that file's nodes as obsolete. */ -int -jffs_possibly_delete_file(struct jffs_file *f) -{ - struct jffs_node *n; - - D3(printk("jffs_possibly_delete_file(): ino: %u\n", - f->ino)); - - ASSERT(if (!f) { - printk(KERN_ERR "jffs_possibly_delete_file(): f == NULL\n"); - return -1; - }); - - if (f->deleted) { - /* First try to remove all older versions. Commence with - the oldest node. */ - for (n = f->version_head; n; n = n->version_next) { - if (!n->fm) { - continue; - } - if (jffs_fmfree(f->c->fmc, n->fm, n) < 0) { - break; - } - } - /* Unlink the file from the filesystem. */ - if (!f->c->building_fs) { - jffs_unlink_file_from_tree(f); - } - jffs_unlink_file_from_hash(f); - jffs_free_node_list(f); - jffs_free_file(f); - } - return 0; -} - - -/* Used in conjunction with jffs_foreach_file() to count the number - of files in the file system. */ -int -jffs_file_count(struct jffs_file *f) -{ - return 1; -} - - -/* Build up a file's range list from scratch by going through the - version list. */ -static int -jffs_build_file(struct jffs_file *f) -{ - struct jffs_node *n; - - D3(printk("jffs_build_file(): ino: %u, name: \"%s\"\n", - f->ino, (f->name ? f->name : ""))); - - for (n = f->version_head; n; n = n->version_next) { - jffs_update_file(f, n); - } - return 0; -} - - -/* Remove an amount of data from a file. If this amount of data is - zero, that could mean that a node should be split in two parts. - We remove or change the appropriate nodes in the lists. - - Starting offset of area to be removed is node->data_offset, - and the length of the area is in node->removed_size. */ -static int -jffs_delete_data(struct jffs_file *f, struct jffs_node *node) -{ - struct jffs_node *n; - __u32 offset = node->data_offset; - __u32 remove_size = node->removed_size; - - D3(printk("jffs_delete_data(): offset = %u, remove_size = %u\n", - offset, remove_size)); - - if (remove_size == 0 - && f->range_tail - && f->range_tail->data_offset + f->range_tail->data_size - == offset) { - /* A simple append; nothing to remove or no node to split. */ - return 0; - } - - /* Find the node where we should begin the removal. */ - for (n = f->range_head; n; n = n->range_next) { - if (n->data_offset + n->data_size > offset) { - break; - } - } - if (!n) { - /* If there's no data in the file there's no data to - remove either. */ - return 0; - } - - if (n->data_offset > offset) { - /* XXX: Not implemented yet. */ - printk(KERN_WARNING "JFFS: An unexpected situation " - "occurred in jffs_delete_data.\n"); - } - else if (n->data_offset < offset) { - /* See if the node has to be split into two parts. */ - if (n->data_offset + n->data_size > offset + remove_size) { - /* Do the split. */ - struct jffs_node *new_node; - D3(printk("jffs_delete_data(): Split node with " - "version number %u.\n", n->version)); - - if (!(new_node = jffs_alloc_node())) { - D(printk("jffs_delete_data(): -ENOMEM\n")); - return -ENOMEM; - } - DJM(no_jffs_node++); - - new_node->ino = n->ino; - new_node->version = n->version; - new_node->data_offset = offset; - new_node->data_size = n->data_size - (remove_size + (offset - n->data_offset)); - new_node->fm_offset = n->fm_offset + (remove_size + (offset - n->data_offset)); - new_node->name_size = n->name_size; - new_node->fm = n->fm; - new_node->version_prev = n; - new_node->version_next = n->version_next; - if (new_node->version_next) { - new_node->version_next->version_prev - = new_node; - } - else { - f->version_tail = new_node; - } - n->version_next = new_node; - new_node->range_prev = n; - new_node->range_next = n->range_next; - if (new_node->range_next) { - new_node->range_next->range_prev = new_node; - } - else { - f->range_tail = new_node; - } - /* A very interesting can of worms. */ - n->range_next = new_node; - n->data_size = offset - n->data_offset; - if (new_node->fm) - jffs_add_node(new_node); - else { - D1(printk(KERN_WARNING "jffs_delete_data(): Splitting an empty node (file hold).\n!")); - D1(printk(KERN_WARNING "FIXME: Did dwmw2 do the right thing here?\n")); - } - n = new_node->range_next; - remove_size = 0; - } - else { - /* No. No need to split the node. Just remove - the end of the node. */ - int r = min(n->data_offset + n->data_size - - offset, remove_size); - n->data_size -= r; - remove_size -= r; - n = n->range_next; - } - } - - /* Remove as many nodes as necessary. */ - while (n && remove_size) { - if (n->data_size <= remove_size) { - struct jffs_node *p = n; - remove_size -= n->data_size; - n = n->range_next; - D3(printk("jffs_delete_data(): Removing node: " - "ino: %u, version: %u%s\n", - p->ino, p->version, - (p->fm ? "" : " (virtual)"))); - if (p->fm) { - jffs_fmfree(f->c->fmc, p->fm, p); - } - jffs_unlink_node_from_range_list(f, p); - jffs_unlink_node_from_version_list(f, p); - jffs_free_node(p); - DJM(no_jffs_node--); - } - else { - n->data_size -= remove_size; - n->fm_offset += remove_size; - n->data_offset -= (node->removed_size - remove_size); - n = n->range_next; - break; - } - } - - /* Adjust the following nodes' information about offsets etc. */ - while (n && node->removed_size) { - n->data_offset -= node->removed_size; - n = n->range_next; - } - - if (node->removed_size > (f->size - node->data_offset)) { - /* It's possible that the removed_size is in fact - * greater than the amount of data we actually thought - * were present in the first place - some of the nodes - * which this node originally obsoleted may already have - * been deleted from the flash by subsequent garbage - * collection. - * - * If this is the case, don't let f->size go negative. - * Bad things would happen :) - */ - f->size = node->data_offset; - } else { - f->size -= node->removed_size; - } - D3(printk("jffs_delete_data(): f->size = %d\n", f->size)); - return 0; -} /* jffs_delete_data() */ - - -/* Insert some data into a file. Prior to the call to this function, - jffs_delete_data should be called. */ -static int -jffs_insert_data(struct jffs_file *f, struct jffs_node *node) -{ - D3(printk("jffs_insert_data(): node->data_offset = %u, " - "node->data_size = %u, f->size = %u\n", - node->data_offset, node->data_size, f->size)); - - /* Find the position where we should insert data. */ - retry: - if (node->data_offset == f->size) { - /* A simple append. This is the most common operation. */ - node->range_next = NULL; - node->range_prev = f->range_tail; - if (node->range_prev) { - node->range_prev->range_next = node; - } - f->range_tail = node; - f->size += node->data_size; - if (!f->range_head) { - f->range_head = node; - } - } - else if (node->data_offset < f->size) { - /* Trying to insert data into the middle of the file. This - means no problem because jffs_delete_data() has already - prepared the range list for us. */ - struct jffs_node *n; - - /* Find the correct place for the insertion and then insert - the node. */ - for (n = f->range_head; n; n = n->range_next) { - D2(printk("Cool stuff's happening!\n")); - - if (n->data_offset == node->data_offset) { - node->range_prev = n->range_prev; - if (node->range_prev) { - node->range_prev->range_next = node; - } - else { - f->range_head = node; - } - node->range_next = n; - n->range_prev = node; - break; - } - ASSERT(else if (n->data_offset + n->data_size > - node->data_offset) { - printk(KERN_ERR "jffs_insert_data(): " - "Couldn't find a place to insert " - "the data!\n"); - return -1; - }); - } - - /* Adjust later nodes' offsets etc. */ - n = node->range_next; - while (n) { - n->data_offset += node->data_size; - n = n->range_next; - } - f->size += node->data_size; - } - else if (node->data_offset > f->size) { - /* Okay. This is tricky. This means that we want to insert - data at a place that is beyond the limits of the file as - it is constructed right now. This is actually a common - event that for instance could occur during the mounting - of the file system if a large file have been truncated, - rewritten and then only partially garbage collected. */ - - struct jffs_node *n; - - /* We need a place holder for the data that is missing in - front of this insertion. This "virtual node" will not - be associated with any space on the flash device. */ - struct jffs_node *virtual_node; - if (!(virtual_node = jffs_alloc_node())) { - return -ENOMEM; - } - - D(printk("jffs_insert_data: Inserting a virtual node.\n")); - D(printk(" node->data_offset = %u\n", node->data_offset)); - D(printk(" f->size = %u\n", f->size)); - - virtual_node->ino = node->ino; - virtual_node->version = node->version; - virtual_node->removed_size = 0; - virtual_node->fm_offset = 0; - virtual_node->name_size = 0; - virtual_node->fm = NULL; /* This is a virtual data holder. */ - virtual_node->version_prev = NULL; - virtual_node->version_next = NULL; - virtual_node->range_next = NULL; - - /* Are there any data at all in the file yet? */ - if (f->range_head) { - virtual_node->data_offset - = f->range_tail->data_offset - + f->range_tail->data_size; - virtual_node->data_size - = node->data_offset - virtual_node->data_offset; - virtual_node->range_prev = f->range_tail; - f->range_tail->range_next = virtual_node; - } - else { - virtual_node->data_offset = 0; - virtual_node->data_size = node->data_offset; - virtual_node->range_prev = NULL; - f->range_head = virtual_node; - } - - f->range_tail = virtual_node; - f->size += virtual_node->data_size; - - /* Insert this virtual node in the version list as well. */ - for (n = f->version_head; n ; n = n->version_next) { - if (n->version == virtual_node->version) { - virtual_node->version_prev = n->version_prev; - n->version_prev = virtual_node; - if (virtual_node->version_prev) { - virtual_node->version_prev - ->version_next = virtual_node; - } - else { - f->version_head = virtual_node; - } - virtual_node->version_next = n; - break; - } - } - - D(jffs_print_node(virtual_node)); - - /* Make a new try to insert the node. */ - goto retry; - } - - D3(printk("jffs_insert_data(): f->size = %d\n", f->size)); - return 0; -} - - -/* A new node (with data) has been added to the file and now the range - list has to be modified. */ -static int -jffs_update_file(struct jffs_file *f, struct jffs_node *node) -{ - int err; - - D3(printk("jffs_update_file(): ino: %u, version: %u\n", - f->ino, node->version)); - - if (node->data_size == 0) { - if (node->removed_size == 0) { - /* data_offset == X */ - /* data_size == 0 */ - /* remove_size == 0 */ - } - else { - /* data_offset == X */ - /* data_size == 0 */ - /* remove_size != 0 */ - if ((err = jffs_delete_data(f, node)) < 0) { - return err; - } - } - } - else { - /* data_offset == X */ - /* data_size != 0 */ - /* remove_size == Y */ - if ((err = jffs_delete_data(f, node)) < 0) { - return err; - } - if ((err = jffs_insert_data(f, node)) < 0) { - return err; - } - } - return 0; -} - -/* Print the contents of a file. */ -#if 0 -int -jffs_print_file(struct jffs_file *f) -{ - D(int i); - D(printk("jffs_file: 0x%p\n", f)); - D(printk("{\n")); - D(printk(" 0x%08x, /* ino */\n", f->ino)); - D(printk(" 0x%08x, /* pino */\n", f->pino)); - D(printk(" 0x%08x, /* mode */\n", f->mode)); - D(printk(" 0x%04x, /* uid */\n", f->uid)); - D(printk(" 0x%04x, /* gid */\n", f->gid)); - D(printk(" 0x%08x, /* atime */\n", f->atime)); - D(printk(" 0x%08x, /* mtime */\n", f->mtime)); - D(printk(" 0x%08x, /* ctime */\n", f->ctime)); - D(printk(" 0x%02x, /* nsize */\n", f->nsize)); - D(printk(" 0x%02x, /* nlink */\n", f->nlink)); - D(printk(" 0x%02x, /* deleted */\n", f->deleted)); - D(printk(" \"%s\", ", (f->name ? f->name : ""))); - D(for (i = strlen(f->name ? f->name : ""); i < 8; ++i) { - printk(" "); - }); - D(printk("/* name */\n")); - D(printk(" 0x%08x, /* size */\n", f->size)); - D(printk(" 0x%08x, /* highest_version */\n", - f->highest_version)); - D(printk(" 0x%p, /* c */\n", f->c)); - D(printk(" 0x%p, /* parent */\n", f->parent)); - D(printk(" 0x%p, /* children */\n", f->children)); - D(printk(" 0x%p, /* sibling_prev */\n", f->sibling_prev)); - D(printk(" 0x%p, /* sibling_next */\n", f->sibling_next)); - D(printk(" 0x%p, /* hash_prev */\n", f->hash.prev)); - D(printk(" 0x%p, /* hash_next */\n", f->hash.next)); - D(printk(" 0x%p, /* range_head */\n", f->range_head)); - D(printk(" 0x%p, /* range_tail */\n", f->range_tail)); - D(printk(" 0x%p, /* version_head */\n", f->version_head)); - D(printk(" 0x%p, /* version_tail */\n", f->version_tail)); - D(printk("}\n")); - return 0; -} -#endif /* 0 */ - -void -jffs_print_hash_table(struct jffs_control *c) -{ - int i; - - printk("JFFS: Dumping the file system's hash table...\n"); - for (i = 0; i < c->hash_len; i++) { - struct jffs_file *f; - list_for_each_entry(f, &c->hash[i], hash) { - printk("*** c->hash[%u]: \"%s\" " - "(ino: %u, pino: %u)\n", - i, (f->name ? f->name : ""), - f->ino, f->pino); - } - } -} - - -void -jffs_print_tree(struct jffs_file *first_file, int indent) -{ - struct jffs_file *f; - char *space; - int dir; - - if (!first_file) { - return; - } - - if (!(space = kmalloc(indent + 1, GFP_KERNEL))) { - printk("jffs_print_tree(): Out of memory!\n"); - return; - } - - memset(space, ' ', indent); - space[indent] = '\0'; - - for (f = first_file; f; f = f->sibling_next) { - dir = S_ISDIR(f->mode); - printk("%s%s%s (ino: %u, highest_version: %u, size: %u)\n", - space, (f->name ? f->name : ""), (dir ? "/" : ""), - f->ino, f->highest_version, f->size); - if (dir) { - jffs_print_tree(f->children, indent + 2); - } - } - - kfree(space); -} - - -#if defined(JFFS_MEMORY_DEBUG) && JFFS_MEMORY_DEBUG -void -jffs_print_memory_allocation_statistics(void) -{ - static long printout; - printk("________ Memory printout #%ld ________\n", ++printout); - printk("no_jffs_file = %ld\n", no_jffs_file); - printk("no_jffs_node = %ld\n", no_jffs_node); - printk("no_jffs_control = %ld\n", no_jffs_control); - printk("no_jffs_raw_inode = %ld\n", no_jffs_raw_inode); - printk("no_jffs_node_ref = %ld\n", no_jffs_node_ref); - printk("no_jffs_fm = %ld\n", no_jffs_fm); - printk("no_jffs_fmcontrol = %ld\n", no_jffs_fmcontrol); - printk("no_hash = %ld\n", no_hash); - printk("no_name = %ld\n", no_name); - printk("\n"); -} -#endif - - -/* Rewrite `size' bytes, and begin at `node'. */ -static int -jffs_rewrite_data(struct jffs_file *f, struct jffs_node *node, __u32 size) -{ - struct jffs_control *c = f->c; - struct jffs_fmcontrol *fmc = c->fmc; - struct jffs_raw_inode raw_inode; - struct jffs_node *new_node; - struct jffs_fm *fm; - __u32 pos; - __u32 pos_dchksum; - __u32 total_name_size; - __u32 total_data_size; - __u32 total_size; - int err; - - D1(printk("***jffs_rewrite_data(): node: %u, name: \"%s\", size: %u\n", - f->ino, (f->name ? f->name : "(null)"), size)); - - /* Create and initialize the new node. */ - if (!(new_node = jffs_alloc_node())) { - D(printk("jffs_rewrite_data(): " - "Failed to allocate node.\n")); - return -ENOMEM; - } - DJM(no_jffs_node++); - new_node->data_offset = node->data_offset; - new_node->removed_size = size; - total_name_size = JFFS_PAD(f->nsize); - total_data_size = JFFS_PAD(size); - total_size = sizeof(struct jffs_raw_inode) - + total_name_size + total_data_size; - new_node->fm_offset = sizeof(struct jffs_raw_inode) - + total_name_size; - -retry: - jffs_fm_write_lock(fmc); - err = 0; - - if ((err = jffs_fmalloc(fmc, total_size, new_node, &fm)) < 0) { - DJM(no_jffs_node--); - jffs_fm_write_unlock(fmc); - D(printk("jffs_rewrite_data(): Failed to allocate fm.\n")); - jffs_free_node(new_node); - return err; - } - else if (!fm->nodes) { - /* The jffs_fm struct that we got is not big enough. */ - /* This should never happen, because we deal with this case - in jffs_garbage_collect_next().*/ - printk(KERN_WARNING "jffs_rewrite_data(): Allocated node is too small (%d bytes of %d)\n", fm->size, total_size); - if ((err = jffs_write_dummy_node(c, fm)) < 0) { - D(printk("jffs_rewrite_data(): " - "jffs_write_dummy_node() Failed!\n")); - } else { - err = -ENOSPC; - } - DJM(no_jffs_fm--); - jffs_fm_write_unlock(fmc); - kfree(fm); - - return err; - } - new_node->fm = fm; - - /* Initialize the raw inode. */ - raw_inode.magic = JFFS_MAGIC_BITMASK; - raw_inode.ino = f->ino; - raw_inode.pino = f->pino; - raw_inode.version = f->highest_version + 1; - raw_inode.mode = f->mode; - raw_inode.uid = f->uid; - raw_inode.gid = f->gid; - raw_inode.atime = f->atime; - raw_inode.mtime = f->mtime; - raw_inode.ctime = f->ctime; - raw_inode.offset = node->data_offset; - raw_inode.dsize = size; - raw_inode.rsize = size; - raw_inode.nsize = f->nsize; - raw_inode.nlink = f->nlink; - raw_inode.spare = 0; - raw_inode.rename = 0; - raw_inode.deleted = f->deleted; - raw_inode.accurate = 0xff; - raw_inode.dchksum = 0; - raw_inode.nchksum = 0; - - pos = new_node->fm->offset; - pos_dchksum = pos +JFFS_RAW_INODE_DCHKSUM_OFFSET; - - D3(printk("jffs_rewrite_data(): Writing this raw inode " - "to pos 0x%ul.\n", pos)); - D3(jffs_print_raw_inode(&raw_inode)); - - if ((err = flash_safe_write(fmc->mtd, pos, - (u_char *) &raw_inode, - sizeof(struct jffs_raw_inode) - - sizeof(__u32) - - sizeof(__u16) - sizeof(__u16))) < 0) { - jffs_fmfree_partly(fmc, fm, - total_name_size + total_data_size); - jffs_fm_write_unlock(fmc); - printk(KERN_ERR "JFFS: jffs_rewrite_data: Write error during " - "rewrite. (raw inode)\n"); - printk(KERN_ERR "JFFS: jffs_rewrite_data: Now retrying " - "rewrite. (raw inode)\n"); - goto retry; - } - pos += sizeof(struct jffs_raw_inode); - - /* Write the name to the flash memory. */ - if (f->nsize) { - D3(printk("jffs_rewrite_data(): Writing name \"%s\" to " - "pos 0x%ul.\n", f->name, (unsigned int) pos)); - if ((err = flash_safe_write(fmc->mtd, pos, - (u_char *)f->name, - f->nsize)) < 0) { - jffs_fmfree_partly(fmc, fm, total_data_size); - jffs_fm_write_unlock(fmc); - printk(KERN_ERR "JFFS: jffs_rewrite_data: Write " - "error during rewrite. (name)\n"); - printk(KERN_ERR "JFFS: jffs_rewrite_data: Now retrying " - "rewrite. (name)\n"); - goto retry; - } - pos += total_name_size; - raw_inode.nchksum = jffs_checksum(f->name, f->nsize); - } - - /* Write the data. */ - if (size) { - int r; - unsigned char *page; - __u32 offset = node->data_offset; - - if (!(page = (unsigned char *)__get_free_page(GFP_KERNEL))) { - jffs_fmfree_partly(fmc, fm, 0); - return -1; - } - - while (size) { - __u32 s = min(size, (__u32)PAGE_SIZE); - if ((r = jffs_read_data(f, (char *)page, - offset, s)) < s) { - free_page((unsigned long)page); - jffs_fmfree_partly(fmc, fm, 0); - jffs_fm_write_unlock(fmc); - printk(KERN_ERR "JFFS: jffs_rewrite_data: " - "jffs_read_data() " - "failed! (r = %d)\n", r); - return -1; - } - if ((err = flash_safe_write(fmc->mtd, - pos, page, r)) < 0) { - free_page((unsigned long)page); - jffs_fmfree_partly(fmc, fm, 0); - jffs_fm_write_unlock(fmc); - printk(KERN_ERR "JFFS: jffs_rewrite_data: " - "Write error during rewrite. " - "(data)\n"); - goto retry; - } - pos += r; - size -= r; - offset += r; - raw_inode.dchksum += jffs_checksum(page, r); - } - - free_page((unsigned long)page); - } - - raw_inode.accurate = 0; - raw_inode.chksum = jffs_checksum(&raw_inode, - sizeof(struct jffs_raw_inode) - - sizeof(__u16)); - - /* Add the checksum. */ - if ((err - = flash_safe_write(fmc->mtd, pos_dchksum, - &((u_char *) - &raw_inode)[JFFS_RAW_INODE_DCHKSUM_OFFSET], - sizeof(__u32) + sizeof(__u16) - + sizeof(__u16))) < 0) { - jffs_fmfree_partly(fmc, fm, 0); - jffs_fm_write_unlock(fmc); - printk(KERN_ERR "JFFS: jffs_rewrite_data: Write error during " - "rewrite. (checksum)\n"); - goto retry; - } - - /* Now make the file system aware of the newly written node. */ - jffs_insert_node(c, f, &raw_inode, f->name, new_node); - jffs_fm_write_unlock(fmc); - - D3(printk("jffs_rewrite_data(): Leaving...\n")); - return 0; -} /* jffs_rewrite_data() */ - - -/* jffs_garbage_collect_next implements one step in the garbage collect - process and is often called multiple times at each occasion of a - garbage collect. */ - -static int -jffs_garbage_collect_next(struct jffs_control *c) -{ - struct jffs_fmcontrol *fmc = c->fmc; - struct jffs_node *node; - struct jffs_file *f; - int err = 0; - __u32 size; - __u32 data_size; - __u32 total_name_size; - __u32 extra_available; - __u32 space_needed; - __u32 free_chunk_size1 = jffs_free_size1(fmc); - D2(__u32 free_chunk_size2 = jffs_free_size2(fmc)); - - /* Get the oldest node in the flash. */ - node = jffs_get_oldest_node(fmc); - ASSERT(if (!node) { - printk(KERN_ERR "JFFS: jffs_garbage_collect_next: " - "No oldest node found!\n"); - err = -1; - goto jffs_garbage_collect_next_end; - - - }); - - /* Find its corresponding file too. */ - f = jffs_find_file(c, node->ino); - - if (!f) { - printk (KERN_ERR "JFFS: jffs_garbage_collect_next: " - "No file to garbage collect! " - "(ino = 0x%08x)\n", node->ino); - /* FIXME: Free the offending node and recover. */ - err = -1; - goto jffs_garbage_collect_next_end; - } - - /* We always write out the name. Theoretically, we don't need - to, but for now it's easier - because otherwise we'd have - to keep track of how many times the current name exists on - the flash and make sure it never reaches zero. - - The current approach means that would be possible to cause - the GC to end up eating its tail by writing lots of nodes - with no name for it to garbage-collect. Hence the change in - inode.c to write names with _every_ node. - - It sucks, but it _should_ work. - */ - total_name_size = JFFS_PAD(f->nsize); - - D1(printk("jffs_garbage_collect_next(): \"%s\", " - "ino: %u, version: %u, location 0x%x, dsize %u\n", - (f->name ? f->name : ""), node->ino, node->version, - node->fm->offset, node->data_size)); - - /* Compute how many data it's possible to rewrite at the moment. */ - data_size = f->size - node->data_offset; - - /* And from that, the total size of the chunk we want to write */ - size = sizeof(struct jffs_raw_inode) + total_name_size - + data_size + JFFS_GET_PAD_BYTES(data_size); - - /* If that's more than max_chunk_size, reduce it accordingly */ - if (size > fmc->max_chunk_size) { - size = fmc->max_chunk_size; - data_size = size - sizeof(struct jffs_raw_inode) - - total_name_size; - } - - /* If we're asking to take up more space than free_chunk_size1 - but we _could_ fit in it, shrink accordingly. - */ - if (size > free_chunk_size1) { - - if (free_chunk_size1 < - (sizeof(struct jffs_raw_inode) + total_name_size + BLOCK_SIZE)){ - /* The space left is too small to be of any - use really. */ - struct jffs_fm *dirty_fm - = jffs_fmalloced(fmc, - fmc->tail->offset + fmc->tail->size, - free_chunk_size1, NULL); - if (!dirty_fm) { - printk(KERN_ERR "JFFS: " - "jffs_garbage_collect_next: " - "Failed to allocate `dirty' " - "flash memory!\n"); - err = -1; - goto jffs_garbage_collect_next_end; - } - D1(printk("Dirtying end of flash - too small\n")); - jffs_write_dummy_node(c, dirty_fm); - err = 0; - goto jffs_garbage_collect_next_end; - } - D1(printk("Reducing size of new node from %d to %d to avoid " - " exceeding free_chunk_size1\n", - size, free_chunk_size1)); - - size = free_chunk_size1; - data_size = size - sizeof(struct jffs_raw_inode) - - total_name_size; - } - - - /* Calculate the amount of space needed to hold the nodes - which are remaining in the tail */ - space_needed = fmc->min_free_size - (node->fm->offset % fmc->sector_size); - - /* From that, calculate how much 'extra' space we can use to - increase the size of the node we're writing from the size - of the node we're obsoleting - */ - if (space_needed > fmc->free_size) { - /* If we've gone below min_free_size for some reason, - don't fuck up. This is why we have - min_free_size > sector_size. Whinge about it though, - just so I can convince myself my maths is right. - */ - D1(printk(KERN_WARNING "jffs_garbage_collect_next(): " - "space_needed %d exceeded free_size %d\n", - space_needed, fmc->free_size)); - extra_available = 0; - } else { - extra_available = fmc->free_size - space_needed; - } - - /* Check that we don't use up any more 'extra' space than - what's available */ - if (size > JFFS_PAD(node->data_size) + total_name_size + - sizeof(struct jffs_raw_inode) + extra_available) { - D1(printk("Reducing size of new node from %d to %ld to avoid " - "catching our tail\n", size, - (long) (JFFS_PAD(node->data_size) + JFFS_PAD(node->name_size) + - sizeof(struct jffs_raw_inode) + extra_available))); - D1(printk("space_needed = %d, extra_available = %d\n", - space_needed, extra_available)); - - size = JFFS_PAD(node->data_size) + total_name_size + - sizeof(struct jffs_raw_inode) + extra_available; - data_size = size - sizeof(struct jffs_raw_inode) - - total_name_size; - }; - - D2(printk(" total_name_size: %u\n", total_name_size)); - D2(printk(" data_size: %u\n", data_size)); - D2(printk(" size: %u\n", size)); - D2(printk(" f->nsize: %u\n", f->nsize)); - D2(printk(" f->size: %u\n", f->size)); - D2(printk(" node->data_offset: %u\n", node->data_offset)); - D2(printk(" free_chunk_size1: %u\n", free_chunk_size1)); - D2(printk(" free_chunk_size2: %u\n", free_chunk_size2)); - D2(printk(" node->fm->offset: 0x%08x\n", node->fm->offset)); - - if ((err = jffs_rewrite_data(f, node, data_size))) { - printk(KERN_WARNING "jffs_rewrite_data() failed: %d\n", err); - return err; - } - -jffs_garbage_collect_next_end: - D3(printk("jffs_garbage_collect_next: Leaving...\n")); - return err; -} /* jffs_garbage_collect_next */ - - -/* If an obsolete node is partly going to be erased due to garbage - collection, the part that isn't going to be erased must be filled - with zeroes so that the scan of the flash will work smoothly next - time. (The data in the file could for instance be a JFFS image - which could cause enormous confusion during a scan of the flash - device if we didn't do this.) - There are two phases in this procedure: First, the clearing of - the name and data parts of the node. Second, possibly also clearing - a part of the raw inode as well. If the box is power cycled during - the first phase, only the checksum of this node-to-be-cleared-at- - the-end will be wrong. If the box is power cycled during, or after, - the clearing of the raw inode, the information like the length of - the name and data parts are zeroed. The next time the box is - powered up, the scanning algorithm manages this faulty data too - because: - - - The checksum is invalid and thus the raw inode must be discarded - in any case. - - If the lengths of the data part or the name part are zeroed, the - scanning just continues after the raw inode. But after the inode - the scanning procedure just finds zeroes which is the same as - dirt. - - So, in the end, this could never fail. :-) Even if it does fail, - the scanning algorithm should manage that too. */ - -static int -jffs_clear_end_of_node(struct jffs_control *c, __u32 erase_size) -{ - struct jffs_fm *fm; - struct jffs_fmcontrol *fmc = c->fmc; - __u32 zero_offset; - __u32 zero_size; - __u32 zero_offset_data; - __u32 zero_size_data; - __u32 cutting_raw_inode = 0; - - if (!(fm = jffs_cut_node(fmc, erase_size))) { - D3(printk("jffs_clear_end_of_node(): fm == NULL\n")); - return 0; - } - - /* Where and how much shall we clear? */ - zero_offset = fmc->head->offset + erase_size; - zero_size = fm->offset + fm->size - zero_offset; - - /* Do we have to clear the raw_inode explicitly? */ - if (fm->size - zero_size < sizeof(struct jffs_raw_inode)) { - cutting_raw_inode = sizeof(struct jffs_raw_inode) - - (fm->size - zero_size); - } - - /* First, clear the name and data fields. */ - zero_offset_data = zero_offset + cutting_raw_inode; - zero_size_data = zero_size - cutting_raw_inode; - flash_safe_acquire(fmc->mtd); - flash_memset(fmc->mtd, zero_offset_data, 0, zero_size_data); - flash_safe_release(fmc->mtd); - - /* Should we clear a part of the raw inode? */ - if (cutting_raw_inode) { - /* I guess it is ok to clear the raw inode in this order. */ - flash_safe_acquire(fmc->mtd); - flash_memset(fmc->mtd, zero_offset, 0, - cutting_raw_inode); - flash_safe_release(fmc->mtd); - } - - return 0; -} /* jffs_clear_end_of_node() */ - -/* Try to erase as much as possible of the dirt in the flash memory. */ -static long -jffs_try_to_erase(struct jffs_control *c) -{ - struct jffs_fmcontrol *fmc = c->fmc; - long erase_size; - int err; - __u32 offset; - - D3(printk("jffs_try_to_erase()\n")); - - erase_size = jffs_erasable_size(fmc); - - D2(printk("jffs_try_to_erase(): erase_size = %ld\n", erase_size)); - - if (erase_size == 0) { - return 0; - } - else if (erase_size < 0) { - printk(KERN_ERR "JFFS: jffs_try_to_erase: " - "jffs_erasable_size returned %ld.\n", erase_size); - return erase_size; - } - - if ((err = jffs_clear_end_of_node(c, erase_size)) < 0) { - printk(KERN_ERR "JFFS: jffs_try_to_erase: " - "Clearing of node failed.\n"); - return err; - } - - offset = fmc->head->offset; - - /* Now, let's try to do the erase. */ - if ((err = flash_erase_region(fmc->mtd, - offset, erase_size)) < 0) { - printk(KERN_ERR "JFFS: Erase of flash failed. " - "offset = %u, erase_size = %ld\n", - offset, erase_size); - /* XXX: Here we should allocate this area as dirty - with jffs_fmalloced or something similar. Now - we just report the error. */ - return err; - } - -#if 0 - /* Check if the erased sectors really got erased. */ - { - __u32 pos; - __u32 end; - - pos = (__u32)flash_get_direct_pointer(to_kdev_t(c->sb->s_dev), offset); - end = pos + erase_size; - - D2(printk("JFFS: Checking erased sector(s)...\n")); - - flash_safe_acquire(fmc->mtd); - - for (; pos < end; pos += 4) { - if (*(__u32 *)pos != JFFS_EMPTY_BITMASK) { - printk("JFFS: Erase failed! pos = 0x%lx\n", - (long)pos); - jffs_hexdump(fmc->mtd, pos, - jffs_min(256, end - pos)); - err = -1; - break; - } - } - - flash_safe_release(fmc->mtd); - - if (!err) { - D2(printk("JFFS: Erase succeeded.\n")); - } - else { - /* XXX: Here we should allocate the memory - with jffs_fmalloced() in order to prevent - JFFS from using this area accidentally. */ - return err; - } - } -#endif - - /* Update the flash memory data structures. */ - jffs_sync_erase(fmc, erase_size); - - return erase_size; -} - - -/* There are different criteria that should trigger a garbage collect: - - 1. There is too much dirt in the memory. - 2. The free space is becoming small. - 3. There are many versions of a node. - - The garbage collect should always be done in a manner that guarantees - that future garbage collects cannot be locked. E.g. Rewritten chunks - should not be too large (span more than one sector in the flash memory - for exemple). Of course there is a limit on how intelligent this garbage - collection can be. */ - - -static int -jffs_garbage_collect_now(struct jffs_control *c) -{ - struct jffs_fmcontrol *fmc = c->fmc; - long erased = 0; - int result = 0; - D1(int i = 1); - D2(printk("***jffs_garbage_collect_now(): fmc->dirty_size = %u, fmc->free_size = 0x%x\n, fcs1=0x%x, fcs2=0x%x", - fmc->dirty_size, fmc->free_size, jffs_free_size1(fmc), jffs_free_size2(fmc))); - D2(jffs_print_fmcontrol(fmc)); - - // down(&fmc->gclock); - - /* If it is possible to garbage collect, do so. */ - - while (erased == 0) { - D1(printk("***jffs_garbage_collect_now(): round #%u, " - "fmc->dirty_size = %u\n", i++, fmc->dirty_size)); - D2(jffs_print_fmcontrol(fmc)); - - if ((erased = jffs_try_to_erase(c)) < 0) { - printk(KERN_WARNING "JFFS: Error in " - "garbage collector.\n"); - result = erased; - goto gc_end; - } - if (erased) - break; - - if (fmc->free_size == 0) { - /* Argh */ - printk(KERN_ERR "jffs_garbage_collect_now(): free_size == 0. This is BAD.\n"); - result = -ENOSPC; - break; - } - - if (fmc->dirty_size < fmc->sector_size) { - /* Actually, we _may_ have been able to free some, - * if there are many overlapping nodes which aren't - * actually marked dirty because they still have - * some valid data in each. - */ - result = -ENOSPC; - break; - } - - /* Let's dare to make a garbage collect. */ - if ((result = jffs_garbage_collect_next(c)) < 0) { - printk(KERN_ERR "JFFS: Something " - "has gone seriously wrong " - "with a garbage collect.\n"); - goto gc_end; - } - - D1(printk(" jffs_garbage_collect_now(): erased: %ld\n", erased)); - DJM(jffs_print_memory_allocation_statistics()); - } - -gc_end: - // up(&fmc->gclock); - - D3(printk(" jffs_garbage_collect_now(): Leaving...\n")); - D1(if (erased) { - printk("jffs_g_c_now(): erased = %ld\n", erased); - jffs_print_fmcontrol(fmc); - }); - - if (!erased && !result) - return -ENOSPC; - - return result; -} /* jffs_garbage_collect_now() */ - - -/* Determine if it is reasonable to start garbage collection. - We start a gc pass if either: - - The number of free bytes < MIN_FREE_BYTES && at least one - block is dirty, OR - - The number of dirty bytes > MAX_DIRTY_BYTES -*/ -static inline int thread_should_wake (struct jffs_control *c) -{ - D1(printk (KERN_NOTICE "thread_should_wake(): free=%d, dirty=%d, blocksize=%d.\n", - c->fmc->free_size, c->fmc->dirty_size, c->fmc->sector_size)); - - /* If there's not enough dirty space to free a block, there's no point. */ - if (c->fmc->dirty_size < c->fmc->sector_size) { - D2(printk(KERN_NOTICE "thread_should_wake(): Not waking. Insufficient dirty space\n")); - return 0; - } -#if 1 - /* If there is too much RAM used by the various structures, GC */ - if (jffs_get_node_inuse() > (c->fmc->used_size/c->fmc->max_chunk_size * 5 + jffs_get_file_count() * 2 + 50)) { - /* FIXME: Provide proof that this test can be satisfied. We - don't want a filesystem doing endless GC just because this - condition cannot ever be false. - */ - D2(printk(KERN_NOTICE "thread_should_wake(): Waking due to number of nodes\n")); - return 1; - } -#endif - /* If there are fewer free bytes than the threshold, GC */ - if (c->fmc->free_size < c->gc_minfree_threshold) { - D2(printk(KERN_NOTICE "thread_should_wake(): Waking due to insufficent free space\n")); - return 1; - } - /* If there are more dirty bytes than the threshold, GC */ - if (c->fmc->dirty_size > c->gc_maxdirty_threshold) { - D2(printk(KERN_NOTICE "thread_should_wake(): Waking due to excessive dirty space\n")); - return 1; - } - /* FIXME: What about the "There are many versions of a node" condition? */ - - return 0; -} - - -void jffs_garbage_collect_trigger(struct jffs_control *c) -{ - /* NOTE: We rely on the fact that we have the BKL here. - * Otherwise, the gc_task could go away between the check - * and the wake_up_process() - */ - if (c->gc_task && thread_should_wake(c)) - send_sig(SIGHUP, c->gc_task, 1); -} - - -/* Kernel threads take (void *) as arguments. Thus we pass - the jffs_control data as a (void *) and then cast it. */ -int -jffs_garbage_collect_thread(void *ptr) -{ - struct jffs_control *c = (struct jffs_control *) ptr; - struct jffs_fmcontrol *fmc = c->fmc; - long erased; - int result = 0; - D1(int i = 1); - - daemonize("jffs_gcd"); - - c->gc_task = current; - - lock_kernel(); - init_completion(&c->gc_thread_comp); /* barrier */ - spin_lock_irq(¤t->sighand->siglock); - siginitsetinv (¤t->blocked, sigmask(SIGHUP) | sigmask(SIGKILL) | sigmask(SIGSTOP) | sigmask(SIGCONT)); - recalc_sigpending(); - spin_unlock_irq(¤t->sighand->siglock); - - D1(printk (KERN_NOTICE "jffs_garbage_collect_thread(): Starting infinite loop.\n")); - - for (;;) { - - /* See if we need to start gc. If we don't, go to sleep. - - Current implementation is a BAD THING(tm). If we try - to unmount the FS, the unmount operation will sleep waiting - for this thread to exit. We need to arrange to send it a - sig before the umount process sleeps. - */ - - if (!thread_should_wake(c)) - set_current_state (TASK_INTERRUPTIBLE); - - schedule(); /* Yes, we do this even if we want to go - on immediately - we're a low priority - background task. */ - - /* Put_super will send a SIGKILL and then wait on the sem. - */ - while (signal_pending(current)) { - siginfo_t info; - unsigned long signr = 0; - - if (try_to_freeze()) - continue; - - spin_lock_irq(¤t->sighand->siglock); - signr = dequeue_signal(current, ¤t->blocked, &info); - spin_unlock_irq(¤t->sighand->siglock); - - switch(signr) { - case SIGSTOP: - D1(printk("jffs_garbage_collect_thread(): SIGSTOP received.\n")); - set_current_state(TASK_STOPPED); - schedule(); - break; - - case SIGKILL: - D1(printk("jffs_garbage_collect_thread(): SIGKILL received.\n")); - c->gc_task = NULL; - complete_and_exit(&c->gc_thread_comp, 0); - } - } - - - D1(printk (KERN_NOTICE "jffs_garbage_collect_thread(): collecting.\n")); - - D3(printk (KERN_NOTICE "g_c_thread(): down biglock\n")); - mutex_lock(&fmc->biglock); - - D1(printk("***jffs_garbage_collect_thread(): round #%u, " - "fmc->dirty_size = %u\n", i++, fmc->dirty_size)); - D2(jffs_print_fmcontrol(fmc)); - - if ((erased = jffs_try_to_erase(c)) < 0) { - printk(KERN_WARNING "JFFS: Error in " - "garbage collector: %ld.\n", erased); - } - - if (erased) - goto gc_end; - - if (fmc->free_size == 0) { - /* Argh. Might as well commit suicide. */ - printk(KERN_ERR "jffs_garbage_collect_thread(): free_size == 0. This is BAD.\n"); - send_sig(SIGQUIT, c->gc_task, 1); - // panic() - goto gc_end; - } - - /* Let's dare to make a garbage collect. */ - if ((result = jffs_garbage_collect_next(c)) < 0) { - printk(KERN_ERR "JFFS: Something " - "has gone seriously wrong " - "with a garbage collect: %d\n", result); - } - - gc_end: - D3(printk (KERN_NOTICE "g_c_thread(): up biglock\n")); - mutex_unlock(&fmc->biglock); - } /* for (;;) */ -} /* jffs_garbage_collect_thread() */ diff --git a/fs/jffs/intrep.h b/fs/jffs/intrep.h deleted file mode 100644 index 5c7abe0e2695..000000000000 --- a/fs/jffs/intrep.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - * JFFS -- Journaling Flash File System, Linux implementation. - * - * Copyright (C) 1999, 2000 Axis Communications AB. - * - * Created by Finn Hakansson . - * - * This 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. - * - * $Id: intrep.h,v 1.14 2001/09/23 23:28:37 dwmw2 Exp $ - * - */ - -#ifndef __LINUX_JFFS_INTREP_H__ -#define __LINUX_JFFS_INTREP_H__ -#include "jffs_fm.h" -struct jffs_node *jffs_alloc_node(void); -void jffs_free_node(struct jffs_node *n); -int jffs_get_node_inuse(void); - -void jffs_cleanup_control(struct jffs_control *c); -int jffs_build_fs(struct super_block *sb); - -int jffs_insert_node(struct jffs_control *c, struct jffs_file *f, - const struct jffs_raw_inode *raw_inode, - const char *name, struct jffs_node *node); -struct jffs_file *jffs_find_file(struct jffs_control *c, __u32 ino); -struct jffs_file *jffs_find_child(struct jffs_file *dir, const char *name, int len); - -void jffs_free_node(struct jffs_node *node); - -int jffs_foreach_file(struct jffs_control *c, int (*func)(struct jffs_file *)); -int jffs_possibly_delete_file(struct jffs_file *f); -int jffs_insert_file_into_tree(struct jffs_file *f); -int jffs_unlink_file_from_tree(struct jffs_file *f); -int jffs_file_count(struct jffs_file *f); - -int jffs_write_node(struct jffs_control *c, struct jffs_node *node, - struct jffs_raw_inode *raw_inode, - const char *name, const unsigned char *buf, - int recoverable, struct jffs_file *f); -int jffs_read_data(struct jffs_file *f, unsigned char *buf, __u32 read_offset, __u32 size); - -/* Garbage collection stuff. */ -int jffs_garbage_collect_thread(void *c); -void jffs_garbage_collect_trigger(struct jffs_control *c); - -/* For debugging purposes. */ -#if 0 -int jffs_print_file(struct jffs_file *f); -#endif /* 0 */ -void jffs_print_hash_table(struct jffs_control *c); -void jffs_print_tree(struct jffs_file *first_file, int indent); - -#endif /* __LINUX_JFFS_INTREP_H__ */ diff --git a/fs/jffs/jffs_fm.c b/fs/jffs/jffs_fm.c deleted file mode 100644 index 5a95fbdd6fdb..000000000000 --- a/fs/jffs/jffs_fm.c +++ /dev/null @@ -1,798 +0,0 @@ -/* - * JFFS -- Journaling Flash File System, Linux implementation. - * - * Copyright (C) 1999, 2000 Axis Communications AB. - * - * Created by Finn Hakansson . - * - * This 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. - * - * $Id: jffs_fm.c,v 1.27 2001/09/20 12:29:47 dwmw2 Exp $ - * - * Ported to Linux 2.3.x and MTD: - * Copyright (C) 2000 Alexander Larsson (alex@cendio.se), Cendio Systems AB - * - */ -#include -#include -#include -#include -#include "jffs_fm.h" -#include "intrep.h" - -#if defined(JFFS_MARK_OBSOLETE) && JFFS_MARK_OBSOLETE -static int jffs_mark_obsolete(struct jffs_fmcontrol *fmc, __u32 fm_offset); -#endif - -static struct jffs_fm *jffs_alloc_fm(void); -static void jffs_free_fm(struct jffs_fm *n); - -extern struct kmem_cache *fm_cache; -extern struct kmem_cache *node_cache; - -#if CONFIG_JFFS_FS_VERBOSE > 0 -void -jffs_print_fmcontrol(struct jffs_fmcontrol *fmc) -{ - D(printk("struct jffs_fmcontrol: 0x%p\n", fmc)); - D(printk("{\n")); - D(printk(" %u, /* flash_size */\n", fmc->flash_size)); - D(printk(" %u, /* used_size */\n", fmc->used_size)); - D(printk(" %u, /* dirty_size */\n", fmc->dirty_size)); - D(printk(" %u, /* free_size */\n", fmc->free_size)); - D(printk(" %u, /* sector_size */\n", fmc->sector_size)); - D(printk(" %u, /* min_free_size */\n", fmc->min_free_size)); - D(printk(" %u, /* max_chunk_size */\n", fmc->max_chunk_size)); - D(printk(" 0x%p, /* mtd */\n", fmc->mtd)); - D(printk(" 0x%p, /* head */ " - "(head->offset = 0x%08x)\n", - fmc->head, (fmc->head ? fmc->head->offset : 0))); - D(printk(" 0x%p, /* tail */ " - "(tail->offset + tail->size = 0x%08x)\n", - fmc->tail, - (fmc->tail ? fmc->tail->offset + fmc->tail->size : 0))); - D(printk(" 0x%p, /* head_extra */\n", fmc->head_extra)); - D(printk(" 0x%p, /* tail_extra */\n", fmc->tail_extra)); - D(printk("}\n")); -} -#endif /* CONFIG_JFFS_FS_VERBOSE > 0 */ - -#if CONFIG_JFFS_FS_VERBOSE > 2 -static void -jffs_print_fm(struct jffs_fm *fm) -{ - D(printk("struct jffs_fm: 0x%p\n", fm)); - D(printk("{\n")); - D(printk(" 0x%08x, /* offset */\n", fm->offset)); - D(printk(" %u, /* size */\n", fm->size)); - D(printk(" 0x%p, /* prev */\n", fm->prev)); - D(printk(" 0x%p, /* next */\n", fm->next)); - D(printk(" 0x%p, /* nodes */\n", fm->nodes)); - D(printk("}\n")); -} -#endif /* CONFIG_JFFS_FS_VERBOSE > 2 */ - -#if 0 -void -jffs_print_node_ref(struct jffs_node_ref *ref) -{ - D(printk("struct jffs_node_ref: 0x%p\n", ref)); - D(printk("{\n")); - D(printk(" 0x%p, /* node */\n", ref->node)); - D(printk(" 0x%p, /* next */\n", ref->next)); - D(printk("}\n")); -} -#endif /* 0 */ - -/* This function creates a new shiny flash memory control structure. */ -struct jffs_fmcontrol * -jffs_build_begin(struct jffs_control *c, int unit) -{ - struct jffs_fmcontrol *fmc; - struct mtd_info *mtd; - - D3(printk("jffs_build_begin()\n")); - fmc = kmalloc(sizeof(*fmc), GFP_KERNEL); - if (!fmc) { - D(printk("jffs_build_begin(): Allocation of " - "struct jffs_fmcontrol failed!\n")); - return (struct jffs_fmcontrol *)0; - } - DJM(no_jffs_fmcontrol++); - - mtd = get_mtd_device(NULL, unit); - - if (IS_ERR(mtd)) { - kfree(fmc); - DJM(no_jffs_fmcontrol--); - return NULL; - } - - /* Retrieve the size of the flash memory. */ - fmc->flash_size = mtd->size; - D3(printk(" fmc->flash_size = %d bytes\n", fmc->flash_size)); - - fmc->used_size = 0; - fmc->dirty_size = 0; - fmc->free_size = mtd->size; - fmc->sector_size = mtd->erasesize; - fmc->max_chunk_size = fmc->sector_size >> 1; - /* min_free_size: - 1 sector, obviously. - + 1 x max_chunk_size, for when a nodes overlaps the end of a sector - + 1 x max_chunk_size again, which ought to be enough to handle - the case where a rename causes a name to grow, and GC has - to write out larger nodes than the ones it's obsoleting. - We should fix it so it doesn't have to write the name - _every_ time. Later. - + another 2 sectors because people keep getting GC stuck and - we don't know why. This scares me - I want formal proof - of correctness of whatever number we put here. dwmw2. - */ - fmc->min_free_size = fmc->sector_size << 2; - fmc->mtd = mtd; - fmc->c = c; - fmc->head = NULL; - fmc->tail = NULL; - fmc->head_extra = NULL; - fmc->tail_extra = NULL; - mutex_init(&fmc->biglock); - return fmc; -} - - -/* When the flash memory scan has completed, this function should be called - before use of the control structure. */ -void -jffs_build_end(struct jffs_fmcontrol *fmc) -{ - D3(printk("jffs_build_end()\n")); - - if (!fmc->head) { - fmc->head = fmc->head_extra; - fmc->tail = fmc->tail_extra; - } - else if (fmc->head_extra) { - fmc->tail_extra->next = fmc->head; - fmc->head->prev = fmc->tail_extra; - fmc->head = fmc->head_extra; - } - fmc->head_extra = NULL; /* These two instructions should be omitted. */ - fmc->tail_extra = NULL; - D3(jffs_print_fmcontrol(fmc)); -} - - -/* Call this function when the file system is unmounted. This function - frees all memory used by this module. */ -void -jffs_cleanup_fmcontrol(struct jffs_fmcontrol *fmc) -{ - if (fmc) { - struct jffs_fm *next = fmc->head; - while (next) { - struct jffs_fm *cur = next; - next = next->next; - jffs_free_fm(cur); - } - put_mtd_device(fmc->mtd); - kfree(fmc); - DJM(no_jffs_fmcontrol--); - } -} - - -/* This function returns the size of the first chunk of free space on the - flash memory. This function will return something nonzero if the flash - memory contains any free space. */ -__u32 -jffs_free_size1(struct jffs_fmcontrol *fmc) -{ - __u32 head; - __u32 tail; - __u32 end = fmc->flash_size; - - if (!fmc->head) { - /* There is nothing on the flash. */ - return fmc->flash_size; - } - - /* Compute the beginning and ending of the contents of the flash. */ - head = fmc->head->offset; - tail = fmc->tail->offset + fmc->tail->size; - if (tail == end) { - tail = 0; - } - ASSERT(else if (tail > end) { - printk(KERN_WARNING "jffs_free_size1(): tail > end\n"); - tail = 0; - }); - - if (head <= tail) { - return end - tail; - } - else { - return head - tail; - } -} - -/* This function will return something nonzero in case there are two free - areas on the flash. Like this: - - +----------------+------------------+----------------+ - | FREE 1 | USED / DIRTY | FREE 2 | - +----------------+------------------+----------------+ - fmc->head -----^ - fmc->tail ------------------------^ - - The value returned, will be the size of the first empty area on the - flash, in this case marked "FREE 1". */ -__u32 -jffs_free_size2(struct jffs_fmcontrol *fmc) -{ - if (fmc->head) { - __u32 head = fmc->head->offset; - __u32 tail = fmc->tail->offset + fmc->tail->size; - if (tail == fmc->flash_size) { - tail = 0; - } - - if (tail >= head) { - return head; - } - } - return 0; -} - - -/* Allocate a chunk of flash memory. If there is enough space on the - device, a reference to the associated node is stored in the jffs_fm - struct. */ -int -jffs_fmalloc(struct jffs_fmcontrol *fmc, __u32 size, struct jffs_node *node, - struct jffs_fm **result) -{ - struct jffs_fm *fm; - __u32 free_chunk_size1; - __u32 free_chunk_size2; - - D2(printk("jffs_fmalloc(): fmc = 0x%p, size = %d, " - "node = 0x%p\n", fmc, size, node)); - - *result = NULL; - - if (!(fm = jffs_alloc_fm())) { - D(printk("jffs_fmalloc(): kmalloc() failed! (fm)\n")); - return -ENOMEM; - } - - free_chunk_size1 = jffs_free_size1(fmc); - free_chunk_size2 = jffs_free_size2(fmc); - if (free_chunk_size1 + free_chunk_size2 != fmc->free_size) { - printk(KERN_WARNING "Free size accounting screwed\n"); - printk(KERN_WARNING "free_chunk_size1 == 0x%x, free_chunk_size2 == 0x%x, fmc->free_size == 0x%x\n", free_chunk_size1, free_chunk_size2, fmc->free_size); - } - - D3(printk("jffs_fmalloc(): free_chunk_size1 = %u, " - "free_chunk_size2 = %u\n", - free_chunk_size1, free_chunk_size2)); - - if (size <= free_chunk_size1) { - if (!(fm->nodes = (struct jffs_node_ref *) - kmalloc(sizeof(struct jffs_node_ref), - GFP_KERNEL))) { - D(printk("jffs_fmalloc(): kmalloc() failed! " - "(node_ref)\n")); - jffs_free_fm(fm); - return -ENOMEM; - } - DJM(no_jffs_node_ref++); - fm->nodes->node = node; - fm->nodes->next = NULL; - if (fmc->tail) { - fm->offset = fmc->tail->offset + fmc->tail->size; - if (fm->offset == fmc->flash_size) { - fm->offset = 0; - } - ASSERT(else if (fm->offset > fmc->flash_size) { - printk(KERN_WARNING "jffs_fmalloc(): " - "offset > flash_end\n"); - fm->offset = 0; - }); - } - else { - /* There don't have to be files in the file - system yet. */ - fm->offset = 0; - } - fm->size = size; - fmc->free_size -= size; - fmc->used_size += size; - } - else if (size > free_chunk_size2) { - printk(KERN_WARNING "JFFS: Tried to allocate a too " - "large flash memory chunk. (size = %u)\n", size); - jffs_free_fm(fm); - return -ENOSPC; - } - else { - fm->offset = fmc->tail->offset + fmc->tail->size; - fm->size = free_chunk_size1; - fm->nodes = NULL; - fmc->free_size -= fm->size; - fmc->dirty_size += fm->size; /* Changed by simonk. This seemingly fixes a - bug that caused infinite garbage collection. - It previously set fmc->dirty_size to size (which is the - size of the requested chunk). - */ - } - - fm->next = NULL; - if (!fmc->head) { - fm->prev = NULL; - fmc->head = fm; - fmc->tail = fm; - } - else { - fm->prev = fmc->tail; - fmc->tail->next = fm; - fmc->tail = fm; - } - - D3(jffs_print_fmcontrol(fmc)); - D3(jffs_print_fm(fm)); - *result = fm; - return 0; -} - - -/* The on-flash space is not needed anymore by the passed node. Remove - the reference to the node from the node list. If the data chunk in - the flash memory isn't used by any more nodes anymore (fm->nodes == 0), - then mark that chunk as dirty. */ -int -jffs_fmfree(struct jffs_fmcontrol *fmc, struct jffs_fm *fm, struct jffs_node *node) -{ - struct jffs_node_ref *ref; - struct jffs_node_ref *prev; - ASSERT(int del = 0); - - D2(printk("jffs_fmfree(): node->ino = %u, node->version = %u\n", - node->ino, node->version)); - - ASSERT(if (!fmc || !fm || !fm->nodes) { - printk(KERN_ERR "jffs_fmfree(): fmc: 0x%p, fm: 0x%p, " - "fm->nodes: 0x%p\n", - fmc, fm, (fm ? fm->nodes : NULL)); - return -1; - }); - - /* Find the reference to the node that is going to be removed - and remove it. */ - for (ref = fm->nodes, prev = NULL; ref; ref = ref->next) { - if (ref->node == node) { - if (prev) { - prev->next = ref->next; - } - else { - fm->nodes = ref->next; - } - kfree(ref); - DJM(no_jffs_node_ref--); - ASSERT(del = 1); - break; - } - prev = ref; - } - - /* If the data chunk in the flash memory isn't used anymore - just mark it as obsolete. */ - if (!fm->nodes) { - /* No node uses this chunk so let's remove it. */ - fmc->used_size -= fm->size; - fmc->dirty_size += fm->size; -#if defined(JFFS_MARK_OBSOLETE) && JFFS_MARK_OBSOLETE - if (jffs_mark_obsolete(fmc, fm->offset) < 0) { - D1(printk("jffs_fmfree(): Failed to mark an on-flash " - "node obsolete!\n")); - return -1; - } -#endif - } - - ASSERT(if (!del) { - printk(KERN_WARNING "***jffs_fmfree(): " - "Didn't delete any node reference!\n"); - }); - - return 0; -} - - -/* This allocation function is used during the initialization of - the file system. */ -struct jffs_fm * -jffs_fmalloced(struct jffs_fmcontrol *fmc, __u32 offset, __u32 size, - struct jffs_node *node) -{ - struct jffs_fm *fm; - - D3(printk("jffs_fmalloced()\n")); - - if (!(fm = jffs_alloc_fm())) { - D(printk("jffs_fmalloced(0x%p, %u, %u, 0x%p): failed!\n", - fmc, offset, size, node)); - return NULL; - } - fm->offset = offset; - fm->size = size; - fm->prev = NULL; - fm->next = NULL; - fm->nodes = NULL; - if (node) { - /* `node' exists and it should be associated with the - jffs_fm structure `fm'. */ - if (!(fm->nodes = (struct jffs_node_ref *) - kmalloc(sizeof(struct jffs_node_ref), - GFP_KERNEL))) { - D(printk("jffs_fmalloced(): !fm->nodes\n")); - jffs_free_fm(fm); - return NULL; - } - DJM(no_jffs_node_ref++); - fm->nodes->node = node; - fm->nodes->next = NULL; - fmc->used_size += size; - fmc->free_size -= size; - } - else { - /* If there is no node, then this is just a chunk of dirt. */ - fmc->dirty_size += size; - fmc->free_size -= size; - } - - if (fmc->head_extra) { - fm->prev = fmc->tail_extra; - fmc->tail_extra->next = fm; - fmc->tail_extra = fm; - } - else if (!fmc->head) { - fmc->head = fm; - fmc->tail = fm; - } - else if (fmc->tail->offset + fmc->tail->size < offset) { - fmc->head_extra = fm; - fmc->tail_extra = fm; - } - else { - fm->prev = fmc->tail; - fmc->tail->next = fm; - fmc->tail = fm; - } - D3(jffs_print_fmcontrol(fmc)); - D3(jffs_print_fm(fm)); - return fm; -} - - -/* Add a new node to an already existing jffs_fm struct. */ -int -jffs_add_node(struct jffs_node *node) -{ - struct jffs_node_ref *ref; - - D3(printk("jffs_add_node(): ino = %u\n", node->ino)); - - ref = kmalloc(sizeof(*ref), GFP_KERNEL); - if (!ref) - return -ENOMEM; - - DJM(no_jffs_node_ref++); - ref->node = node; - ref->next = node->fm->nodes; - node->fm->nodes = ref; - return 0; -} - - -/* Free a part of some allocated space. */ -void -jffs_fmfree_partly(struct jffs_fmcontrol *fmc, struct jffs_fm *fm, __u32 size) -{ - D1(printk("***jffs_fmfree_partly(): fm = 0x%p, fm->nodes = 0x%p, " - "fm->nodes->node->ino = %u, size = %u\n", - fm, (fm ? fm->nodes : 0), - (!fm ? 0 : (!fm->nodes ? 0 : fm->nodes->node->ino)), size)); - - if (fm->nodes) { - kfree(fm->nodes); - DJM(no_jffs_node_ref--); - fm->nodes = NULL; - } - fmc->used_size -= fm->size; - if (fm == fmc->tail) { - fm->size -= size; - fmc->free_size += size; - } - fmc->dirty_size += fm->size; -} - - -/* Find the jffs_fm struct that contains the end of the data chunk that - begins at the logical beginning of the flash memory and spans `size' - bytes. If we want to erase a sector of the flash memory, we use this - function to find where the sector limit cuts a chunk of data. */ -struct jffs_fm * -jffs_cut_node(struct jffs_fmcontrol *fmc, __u32 size) -{ - struct jffs_fm *fm; - __u32 pos = 0; - - if (size == 0) { - return NULL; - } - - ASSERT(if (!fmc) { - printk(KERN_ERR "jffs_cut_node(): fmc == NULL\n"); - return NULL; - }); - - fm = fmc->head; - - while (fm) { - pos += fm->size; - if (pos < size) { - fm = fm->next; - } - else if (pos > size) { - break; - } - else { - fm = NULL; - break; - } - } - - return fm; -} - - -/* Move the head of the fmc structures and delete the obsolete parts. */ -void -jffs_sync_erase(struct jffs_fmcontrol *fmc, int erased_size) -{ - struct jffs_fm *fm; - struct jffs_fm *del; - - ASSERT(if (!fmc) { - printk(KERN_ERR "jffs_sync_erase(): fmc == NULL\n"); - return; - }); - - fmc->dirty_size -= erased_size; - fmc->free_size += erased_size; - - for (fm = fmc->head; fm && (erased_size > 0);) { - if (erased_size >= fm->size) { - erased_size -= fm->size; - del = fm; - fm = fm->next; - fm->prev = NULL; - fmc->head = fm; - jffs_free_fm(del); - } - else { - fm->size -= erased_size; - fm->offset += erased_size; - break; - } - } -} - - -/* Return the oldest used node in the flash memory. */ -struct jffs_node * -jffs_get_oldest_node(struct jffs_fmcontrol *fmc) -{ - struct jffs_fm *fm; - struct jffs_node_ref *nref; - struct jffs_node *node = NULL; - - ASSERT(if (!fmc) { - printk(KERN_ERR "jffs_get_oldest_node(): fmc == NULL\n"); - return NULL; - }); - - for (fm = fmc->head; fm && !fm->nodes; fm = fm->next); - - if (!fm) { - return NULL; - } - - /* The oldest node is the last one in the reference list. This list - shouldn't be too long; just one or perhaps two elements. */ - for (nref = fm->nodes; nref; nref = nref->next) { - node = nref->node; - } - - D2(printk("jffs_get_oldest_node(): ino = %u, version = %u\n", - (node ? node->ino : 0), (node ? node->version : 0))); - - return node; -} - - -#if defined(JFFS_MARK_OBSOLETE) && JFFS_MARK_OBSOLETE - -/* Mark an on-flash node as obsolete. - - Note that this is just an optimization that isn't necessary for the - filesystem to work. */ - -static int -jffs_mark_obsolete(struct jffs_fmcontrol *fmc, __u32 fm_offset) -{ - /* The `accurate_pos' holds the position of the accurate byte - in the jffs_raw_inode structure that we are going to mark - as obsolete. */ - __u32 accurate_pos = fm_offset + JFFS_RAW_INODE_ACCURATE_OFFSET; - unsigned char zero = 0x00; - size_t len; - - D3(printk("jffs_mark_obsolete(): accurate_pos = %u\n", accurate_pos)); - ASSERT(if (!fmc) { - printk(KERN_ERR "jffs_mark_obsolete(): fmc == NULL\n"); - return -1; - }); - - /* Write 0x00 to the raw inode's accurate member. Don't care - about the return value. */ - MTD_WRITE(fmc->mtd, accurate_pos, 1, &len, &zero); - return 0; -} - -#endif /* JFFS_MARK_OBSOLETE */ - -/* check if it's possible to erase the wanted range, and if not, return - * the range that IS erasable, or a negative error code. - */ -static long -jffs_flash_erasable_size(struct mtd_info *mtd, __u32 offset, __u32 size) -{ - u_long ssize; - - /* assume that sector size for a partition is constant even - * if it spans more than one chip (you usually put the same - * type of chips in a system) - */ - - ssize = mtd->erasesize; - - if (offset % ssize) { - printk(KERN_WARNING "jffs_flash_erasable_size() given non-aligned offset %x (erasesize %lx)\n", offset, ssize); - /* The offset is not sector size aligned. */ - return -1; - } - else if (offset > mtd->size) { - printk(KERN_WARNING "jffs_flash_erasable_size given offset off the end of device (%x > %x)\n", offset, mtd->size); - return -2; - } - else if (offset + size > mtd->size) { - printk(KERN_WARNING "jffs_flash_erasable_size() given length which runs off the end of device (ofs %x + len %x = %x, > %x)\n", offset,size, offset+size, mtd->size); - return -3; - } - - return (size / ssize) * ssize; -} - - -/* How much dirty flash memory is possible to erase at the moment? */ -long -jffs_erasable_size(struct jffs_fmcontrol *fmc) -{ - struct jffs_fm *fm; - __u32 size = 0; - long ret; - - ASSERT(if (!fmc) { - printk(KERN_ERR "jffs_erasable_size(): fmc = NULL\n"); - return -1; - }); - - if (!fmc->head) { - /* The flash memory is totally empty. No nodes. No dirt. - Just return. */ - return 0; - } - - /* Calculate how much space that is dirty. */ - for (fm = fmc->head; fm && !fm->nodes; fm = fm->next) { - if (size && fm->offset == 0) { - /* We have reached the beginning of the flash. */ - break; - } - size += fm->size; - } - - /* Someone's signature contained this: - There's a fine line between fishing and just standing on - the shore like an idiot... */ - ret = jffs_flash_erasable_size(fmc->mtd, fmc->head->offset, size); - - ASSERT(if (ret < 0) { - printk("jffs_erasable_size: flash_erasable_size() " - "returned something less than zero (%ld).\n", ret); - printk("jffs_erasable_size: offset = 0x%08x\n", - fmc->head->offset); - }); - - /* If there is dirt on the flash (which is the reason to why - this function was called in the first place) but no space is - possible to erase right now, the initial part of the list of - jffs_fm structs, that hold place for dirty space, could perhaps - be shortened. The list's initial "dirty" elements are merged - into just one large dirty jffs_fm struct. This operation must - only be performed if nothing is possible to erase. Otherwise, - jffs_clear_end_of_node() won't work as expected. */ - if (ret == 0) { - struct jffs_fm *head = fmc->head; - struct jffs_fm *del; - /* While there are two dirty nodes beside each other.*/ - while (head->nodes == 0 - && head->next - && head->next->nodes == 0) { - del = head->next; - head->size += del->size; - head->next = del->next; - if (del->next) { - del->next->prev = head; - } - jffs_free_fm(del); - } - } - - return (ret >= 0 ? ret : 0); -} - -static struct jffs_fm *jffs_alloc_fm(void) -{ - struct jffs_fm *fm; - - fm = kmem_cache_alloc(fm_cache,GFP_KERNEL); - DJM(if (fm) no_jffs_fm++;); - - return fm; -} - -static void jffs_free_fm(struct jffs_fm *n) -{ - kmem_cache_free(fm_cache,n); - DJM(no_jffs_fm--); -} - - - -struct jffs_node *jffs_alloc_node(void) -{ - struct jffs_node *n; - - n = (struct jffs_node *)kmem_cache_alloc(node_cache,GFP_KERNEL); - if(n != NULL) - no_jffs_node++; - return n; -} - -void jffs_free_node(struct jffs_node *n) -{ - kmem_cache_free(node_cache,n); - no_jffs_node--; -} - - -int jffs_get_node_inuse(void) -{ - return no_jffs_node; -} diff --git a/fs/jffs/jffs_fm.h b/fs/jffs/jffs_fm.h deleted file mode 100644 index 9ee6ad29eff5..000000000000 --- a/fs/jffs/jffs_fm.h +++ /dev/null @@ -1,149 +0,0 @@ -/* - * JFFS -- Journaling Flash File System, Linux implementation. - * - * Copyright (C) 1999, 2000 Axis Communications AB. - * - * Created by Finn Hakansson . - * - * This 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. - * - * $Id: jffs_fm.h,v 1.13 2001/01/11 12:03:25 dwmw2 Exp $ - * - * Ported to Linux 2.3.x and MTD: - * Copyright (C) 2000 Alexander Larsson (alex@cendio.se), Cendio Systems AB - * - */ - -#ifndef __LINUX_JFFS_FM_H__ -#define __LINUX_JFFS_FM_H__ - -#include -#include -#include -#include - -/* The alignment between two nodes in the flash memory. */ -#define JFFS_ALIGN_SIZE 4 - -/* Mark the on-flash space as obsolete when appropriate. */ -#define JFFS_MARK_OBSOLETE 0 - -#ifndef CONFIG_JFFS_FS_VERBOSE -#define CONFIG_JFFS_FS_VERBOSE 1 -#endif - -#if CONFIG_JFFS_FS_VERBOSE > 0 -#define D(x) x -#define D1(x) D(x) -#else -#define D(x) -#define D1(x) -#endif - -#if CONFIG_JFFS_FS_VERBOSE > 1 -#define D2(x) D(x) -#else -#define D2(x) -#endif - -#if CONFIG_JFFS_FS_VERBOSE > 2 -#define D3(x) D(x) -#else -#define D3(x) -#endif - -#define ASSERT(x) x - -/* How many padding bytes should be inserted between two chunks of data - on the flash? */ -#define JFFS_GET_PAD_BYTES(size) ( (JFFS_ALIGN_SIZE-1) & -(__u32)(size) ) -#define JFFS_PAD(size) ( (size + (JFFS_ALIGN_SIZE-1)) & ~(JFFS_ALIGN_SIZE-1) ) - - - -struct jffs_node_ref -{ - struct jffs_node *node; - struct jffs_node_ref *next; -}; - - -/* The struct jffs_fm represents a chunk of data in the flash memory. */ -struct jffs_fm -{ - __u32 offset; - __u32 size; - struct jffs_fm *prev; - struct jffs_fm *next; - struct jffs_node_ref *nodes; /* USED if != 0. */ -}; - -struct jffs_fmcontrol -{ - __u32 flash_size; - __u32 used_size; - __u32 dirty_size; - __u32 free_size; - __u32 sector_size; - __u32 min_free_size; /* The minimum free space needed to be able - to perform garbage collections. */ - __u32 max_chunk_size; /* The maximum size of a chunk of data. */ - struct mtd_info *mtd; - struct jffs_control *c; - struct jffs_fm *head; - struct jffs_fm *tail; - struct jffs_fm *head_extra; - struct jffs_fm *tail_extra; - struct mutex biglock; -}; - -/* Notice the two members head_extra and tail_extra in the jffs_control - structure above. Those are only used during the scanning of the flash - memory; while the file system is being built. If the data in the flash - memory is organized like - - +----------------+------------------+----------------+ - | USED / DIRTY | FREE | USED / DIRTY | - +----------------+------------------+----------------+ - - then the scan is split in two parts. The first scanned part of the - flash memory is organized through the members head and tail. The - second scanned part is organized with head_extra and tail_extra. When - the scan is completed, the two lists are merged together. The jffs_fm - struct that head_extra references is the logical beginning of the - flash memory so it will be referenced by the head member. */ - - - -struct jffs_fmcontrol *jffs_build_begin(struct jffs_control *c, int unit); -void jffs_build_end(struct jffs_fmcontrol *fmc); -void jffs_cleanup_fmcontrol(struct jffs_fmcontrol *fmc); - -int jffs_fmalloc(struct jffs_fmcontrol *fmc, __u32 size, - struct jffs_node *node, struct jffs_fm **result); -int jffs_fmfree(struct jffs_fmcontrol *fmc, struct jffs_fm *fm, - struct jffs_node *node); - -__u32 jffs_free_size1(struct jffs_fmcontrol *fmc); -__u32 jffs_free_size2(struct jffs_fmcontrol *fmc); -void jffs_sync_erase(struct jffs_fmcontrol *fmc, int erased_size); -struct jffs_fm *jffs_cut_node(struct jffs_fmcontrol *fmc, __u32 size); -struct jffs_node *jffs_get_oldest_node(struct jffs_fmcontrol *fmc); -long jffs_erasable_size(struct jffs_fmcontrol *fmc); -struct jffs_fm *jffs_fmalloced(struct jffs_fmcontrol *fmc, __u32 offset, - __u32 size, struct jffs_node *node); -int jffs_add_node(struct jffs_node *node); -void jffs_fmfree_partly(struct jffs_fmcontrol *fmc, struct jffs_fm *fm, - __u32 size); - -#if CONFIG_JFFS_FS_VERBOSE > 0 -void jffs_print_fmcontrol(struct jffs_fmcontrol *fmc); -#endif -#if 0 -void jffs_print_node_ref(struct jffs_node_ref *ref); -#endif /* 0 */ - -#endif /* __LINUX_JFFS_FM_H__ */ diff --git a/fs/jffs/jffs_proc.c b/fs/jffs/jffs_proc.c deleted file mode 100644 index 9bdd99a557c2..000000000000 --- a/fs/jffs/jffs_proc.c +++ /dev/null @@ -1,261 +0,0 @@ -/* - * JFFS -- Journaling Flash File System, Linux implementation. - * - * Copyright (C) 2000 Axis Communications AB. - * - * Created by Simon Kagstrom . - * - * $Id: jffs_proc.c,v 1.5 2001/06/02 14:34:55 dwmw2 Exp $ - * - * This 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. - * - * Overview: - * This file defines JFFS partition entries in the proc file system. - * - * TODO: - * Create some more proc files for different kinds of info, i.e. statistics - * about written and read bytes, number of calls to different routines, - * reports about failures. - */ - -#include -#include -#include -#include -#include -#include -#include -#include "jffs_fm.h" -#include "jffs_proc.h" - -/* - * Structure for a JFFS partition in the system - */ -struct jffs_partition_dir { - struct jffs_control *c; - struct proc_dir_entry *part_root; - struct proc_dir_entry *part_info; - struct proc_dir_entry *part_layout; - struct jffs_partition_dir *next; -}; - -/* - * Structure for top-level entry in '/proc/fs' directory - */ -struct proc_dir_entry *jffs_proc_root; - -/* - * Linked list of 'jffs_partition_dirs' to help us track - * the mounted JFFS partitions in the system - */ -static struct jffs_partition_dir *jffs_part_dirs; - -/* - * Read functions for entries - */ -static int jffs_proc_info_read(char *page, char **start, off_t off, - int count, int *eof, void *data); -static int jffs_proc_layout_read (char *page, char **start, off_t off, - int count, int *eof, void *data); - - -/* - * Register a JFFS partition directory (called upon mount) - */ -int jffs_register_jffs_proc_dir(int mtd, struct jffs_control *c) -{ - struct jffs_partition_dir *part_dir; - struct proc_dir_entry *part_info = NULL; - struct proc_dir_entry *part_layout = NULL; - struct proc_dir_entry *part_root = NULL; - char name[10]; - - sprintf(name, "%d", mtd); - /* Allocate structure for local JFFS partition table */ - part_dir = (struct jffs_partition_dir *) - kmalloc(sizeof (struct jffs_partition_dir), GFP_KERNEL); - if (!part_dir) - goto out; - - /* Create entry for this partition */ - part_root = proc_mkdir(name, jffs_proc_root); - if (!part_root) - goto out1; - - /* Create entry for 'info' file */ - part_info = create_proc_entry ("info", 0, part_root); - if (!part_info) - goto out2; - part_info->read_proc = jffs_proc_info_read; - part_info->data = (void *) c; - - /* Create entry for 'layout' file */ - part_layout = create_proc_entry ("layout", 0, part_root); - if (!part_layout) - goto out3; - part_layout->read_proc = jffs_proc_layout_read; - part_layout->data = (void *) c; - - /* Fill in structure for table and insert in the list */ - part_dir->c = c; - part_dir->part_root = part_root; - part_dir->part_info = part_info; - part_dir->part_layout = part_layout; - part_dir->next = jffs_part_dirs; - jffs_part_dirs = part_dir; - - /* Return happy */ - return 0; - -out3: - remove_proc_entry("info", part_root); -out2: - remove_proc_entry(name, jffs_proc_root); -out1: - kfree(part_dir); -out: - return -ENOMEM; -} - - -/* - * Unregister a JFFS partition directory (called at umount) - */ -int jffs_unregister_jffs_proc_dir(struct jffs_control *c) -{ - struct jffs_partition_dir *part_dir = jffs_part_dirs; - struct jffs_partition_dir *prev_part_dir = NULL; - - while (part_dir) { - if (part_dir->c == c) { - /* Remove entries for partition */ - remove_proc_entry (part_dir->part_info->name, - part_dir->part_root); - remove_proc_entry (part_dir->part_layout->name, - part_dir->part_root); - remove_proc_entry (part_dir->part_root->name, - jffs_proc_root); - - /* Remove entry from list */ - if (prev_part_dir) - prev_part_dir->next = part_dir->next; - else - jffs_part_dirs = part_dir->next; - - /* - * Check to see if this is the last one - * and remove the entry from '/proc/fs' - * if it is. - */ - if (jffs_part_dirs == part_dir->next) - remove_proc_entry ("jffs", proc_root_fs); - - /* Free memory for entry */ - kfree(part_dir); - - /* Return happy */ - return 0; - } - - /* Move to next entry */ - prev_part_dir = part_dir; - part_dir = part_dir->next; - } - - /* Return unhappy */ - return -1; -} - - -/* - * Read a JFFS partition's `info' file - */ -static int jffs_proc_info_read (char *page, char **start, off_t off, - int count, int *eof, void *data) -{ - struct jffs_control *c = (struct jffs_control *) data; - int len = 0; - - /* Get information on the parition */ - len += sprintf (page, - "partition size: %08lX (%u)\n" - "sector size: %08lX (%u)\n" - "used size: %08lX (%u)\n" - "dirty size: %08lX (%u)\n" - "free size: %08lX (%u)\n\n", - (unsigned long) c->fmc->flash_size, c->fmc->flash_size, - (unsigned long) c->fmc->sector_size, c->fmc->sector_size, - (unsigned long) c->fmc->used_size, c->fmc->used_size, - (unsigned long) c->fmc->dirty_size, c->fmc->dirty_size, - (unsigned long) (c->fmc->flash_size - - (c->fmc->used_size + c->fmc->dirty_size)), - c->fmc->flash_size - (c->fmc->used_size + c->fmc->dirty_size)); - - /* We're done */ - *eof = 1; - - /* Return length */ - return len; -} - - -/* - * Read a JFFS partition's `layout' file - */ -static int jffs_proc_layout_read (char *page, char **start, off_t off, - int count, int *eof, void *data) -{ - struct jffs_control *c = (struct jffs_control *) data; - struct jffs_fm *fm = NULL; - struct jffs_fm *last_fm = NULL; - int len = 0; - - /* Get the first item in the list */ - fm = c->fmc->head; - - /* Print free space */ - if (fm && fm->offset) { - len += sprintf (page, "00000000 %08lX free\n", - (unsigned long) fm->offset); - } - - /* Loop through all of the flash control structures */ - while (fm && (len < (off + count))) { - if (fm->nodes) { - len += sprintf (page + len, - "%08lX %08lX ino=%08lX, ver=%08lX\n", - (unsigned long) fm->offset, - (unsigned long) fm->size, - (unsigned long) fm->nodes->node->ino, - (unsigned long) fm->nodes->node->version); - } - else { - len += sprintf (page + len, - "%08lX %08lX dirty\n", - (unsigned long) fm->offset, - (unsigned long) fm->size); - } - last_fm = fm; - fm = fm->next; - } - - /* Print free space */ - if ((len < (off + count)) && last_fm - && (last_fm->offset < c->fmc->flash_size)) { - len += sprintf (page + len, - "%08lX %08lX free\n", - (unsigned long) last_fm->offset + - last_fm->size, - (unsigned long) (c->fmc->flash_size - - (last_fm->offset + last_fm->size))); - } - - /* We're done */ - *eof = 1; - - /* Return length */ - return len; -} diff --git a/fs/jffs/jffs_proc.h b/fs/jffs/jffs_proc.h deleted file mode 100644 index 39a1c5d162b0..000000000000 --- a/fs/jffs/jffs_proc.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * JFFS -- Journaling Flash File System, Linux implementation. - * - * Copyright (C) 2000 Axis Communications AB. - * - * Created by Simon Kagstrom . - * - * This 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. - * - * $Id: jffs_proc.h,v 1.2 2000/11/15 22:04:12 sjhill Exp $ - */ - -/* jffs_proc.h defines a structure for inclusion in the proc-file system. */ -#ifndef __LINUX_JFFS_PROC_H__ -#define __LINUX_JFFS_PROC_H__ - -#include - -/* The proc_dir_entry for jffs (defined in jffs_proc.c). */ -extern struct proc_dir_entry *jffs_proc_root; - -int jffs_register_jffs_proc_dir(int mtd, struct jffs_control *c); -int jffs_unregister_jffs_proc_dir(struct jffs_control *c); - -#endif /* __LINUX_JFFS_PROC_H__ */ diff --git a/include/linux/jffs.h b/include/linux/jffs.h deleted file mode 100644 index 92213215277f..000000000000 --- a/include/linux/jffs.h +++ /dev/null @@ -1,224 +0,0 @@ -/* - * JFFS -- Journalling Flash File System, Linux implementation. - * - * Copyright (C) 1999, 2000 Axis Communications AB. - * - * Created by Finn Hakansson . - * - * This 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. - * - * $Id: jffs.h,v 1.20 2001/09/18 21:33:37 dwmw2 Exp $ - * - * Ported to Linux 2.3.x and MTD: - * Copyright (C) 2000 Alexander Larsson (alex@cendio.se), Cendio Systems AB - * - */ - -#ifndef __LINUX_JFFS_H__ -#define __LINUX_JFFS_H__ - -#include -#include - -#define JFFS_VERSION_STRING "1.0" - -/* This is a magic number that is used as an identification number for - this file system. It is written to the super_block structure. */ -#define JFFS_MAGIC_SB_BITMASK 0x07c0 /* 1984 */ - -/* This is a magic number that every on-flash raw inode begins with. */ -#define JFFS_MAGIC_BITMASK 0x34383931 /* "1984" */ - -/* These two bitmasks are the valid ones for the flash memories we have - for the moment. */ -#define JFFS_EMPTY_BITMASK 0xffffffff -#define JFFS_DIRTY_BITMASK 0x00000000 - -/* This is the inode number of the root node. */ -#define JFFS_MIN_INO 1 - -/* How many slots in the file hash table should we have? */ -#define JFFS_HASH_SIZE 40 - -/* Don't use more than 254 bytes as the maximum allowed length of a file's - name due to errors that could occur during the scanning of the flash - memory. In fact, a name length of 255 or 0xff, could be the result of - an uncompleted write. For instance, if a raw inode is written to the - flash memory and there is a power lossage just before the length of - the name is written, the length 255 would be interpreted as an illegal - value. */ -#define JFFS_MAX_NAME_LEN 254 - -/* Commands for ioctl(). */ -#define JFFS_IOCTL_MAGIC 't' -#define JFFS_PRINT_HASH _IO(JFFS_IOCTL_MAGIC, 90) -#define JFFS_PRINT_TREE _IO(JFFS_IOCTL_MAGIC, 91) -#define JFFS_GET_STATUS _IO(JFFS_IOCTL_MAGIC, 92) - -/* XXX: This is something that we should try to get rid of in the future. */ -#define JFFS_MODIFY_INODE 0x01 -#define JFFS_MODIFY_NAME 0x02 -#define JFFS_MODIFY_DATA 0x04 -#define JFFS_MODIFY_EXIST 0x08 - -struct jffs_control; - -/* The JFFS raw inode structure: Used for storage on physical media. */ -/* Perhaps the uid, gid, atime, mtime and ctime members should have - more space due to future changes in the Linux kernel. Anyhow, since - a user of this filesystem probably have to fix a large number of - other things, we have decided to not be forward compatible. */ -struct jffs_raw_inode -{ - __u32 magic; /* A constant magic number. */ - __u32 ino; /* Inode number. */ - __u32 pino; /* Parent's inode number. */ - __u32 version; /* Version number. */ - __u32 mode; /* The file's type or mode. */ - __u16 uid; /* The file's owner. */ - __u16 gid; /* The file's group. */ - __u32 atime; /* Last access time. */ - __u32 mtime; /* Last modification time. */ - __u32 ctime; /* Creation time. */ - __u32 offset; /* Where to begin to write. */ - __u32 dsize; /* Size of the node's data. */ - __u32 rsize; /* How much are going to be replaced? */ - __u8 nsize; /* Name length. */ - __u8 nlink; /* Number of links. */ - __u8 spare : 6; /* For future use. */ - __u8 rename : 1; /* Rename to a name of an already existing file? */ - __u8 deleted : 1; /* Has this file been deleted? */ - __u8 accurate; /* The inode is obsolete if accurate == 0. */ - __u32 dchksum; /* Checksum for the data. */ - __u16 nchksum; /* Checksum for the name. */ - __u16 chksum; /* Checksum for the raw inode. */ -}; - -/* Define the offset of the accurate byte in struct jffs_raw_inode. */ -#define JFFS_RAW_INODE_ACCURATE_OFFSET (sizeof(struct jffs_raw_inode) \ - - 2 * sizeof(__u32) - sizeof(__u8)) - -/* Define the offset of the chksum member in struct jffs_raw_inode. */ -#define JFFS_RAW_INODE_CHKSUM_OFFSET (sizeof(struct jffs_raw_inode) \ - - sizeof(__u16)) - -/* Define the offset of the dchksum member in struct jffs_raw_inode. */ -#define JFFS_RAW_INODE_DCHKSUM_OFFSET (sizeof(struct jffs_raw_inode) \ - - sizeof(__u16) - sizeof(__u16) \ - - sizeof(__u32)) - - -/* The RAM representation of the node. The names of pointers to - jffs_nodes are very often just called `n' in the source code. */ -struct jffs_node -{ - __u32 ino; /* Inode number. */ - __u32 version; /* Version number. */ - __u32 data_offset; /* Logic location of the data to insert. */ - __u32 data_size; /* The amount of data this node inserts. */ - __u32 removed_size; /* The amount of data that this node removes. */ - __u32 fm_offset; /* Physical location of the data in the actual - flash memory data chunk. */ - __u8 name_size; /* Size of the name. */ - struct jffs_fm *fm; /* Physical memory information. */ - struct jffs_node *version_prev; - struct jffs_node *version_next; - struct jffs_node *range_prev; - struct jffs_node *range_next; -}; - - -/* The RAM representation of a file (plain files, directories, - links, etc.). Pointers to jffs_files are normally named `f' - in the JFFS source code. */ -struct jffs_file -{ - __u32 ino; /* Inode number. */ - __u32 pino; /* Parent's inode number. */ - __u32 mode; /* file_type, mode */ - __u16 uid; /* owner */ - __u16 gid; /* group */ - __u32 atime; /* Last access time. */ - __u32 mtime; /* Last modification time. */ - __u32 ctime; /* Creation time. */ - __u8 nsize; /* Name length. */ - __u8 nlink; /* Number of links. */ - __u8 deleted; /* Has this file been deleted? */ - char *name; /* The name of this file; NULL-terminated. */ - __u32 size; /* The total size of the file's data. */ - __u32 highest_version; /* The highest version number of this file. */ - struct jffs_control *c; - struct jffs_file *parent; /* Reference to the parent directory. */ - struct jffs_file *children; /* Always NULL for plain files. */ - struct jffs_file *sibling_prev; /* Siblings in the same directory. */ - struct jffs_file *sibling_next; - struct list_head hash; /* hash list. */ - struct jffs_node *range_head; /* The final data. */ - struct jffs_node *range_tail; /* The first data. */ - struct jffs_node *version_head; /* The youngest node. */ - struct jffs_node *version_tail; /* The oldest node. */ -}; - - -/* This is just a definition of a simple list used for keeping track of - files deleted due to a rename. This list is only used during the - mounting of the file system and only if there have been rename operations - earlier. */ -struct jffs_delete_list -{ - __u32 ino; - struct jffs_delete_list *next; -}; - - -/* A struct for the overall file system control. Pointers to - jffs_control structs are named `c' in the source code. */ -struct jffs_control -{ - struct super_block *sb; /* Reference to the VFS super block. */ - struct jffs_file *root; /* The root directory file. */ - struct list_head *hash; /* Hash table for finding files by ino. */ - struct jffs_fmcontrol *fmc; /* Flash memory control structure. */ - __u32 hash_len; /* The size of the hash table. */ - __u32 next_ino; /* Next inode number to use for new files. */ - __u16 building_fs; /* Is the file system being built right now? */ - struct jffs_delete_list *delete_list; /* Track deleted files. */ - pid_t thread_pid; /* GC thread's PID */ - struct task_struct *gc_task; /* GC task struct */ - struct completion gc_thread_comp; /* GC thread exit mutex */ - __u32 gc_minfree_threshold; /* GC trigger thresholds */ - __u32 gc_maxdirty_threshold; -}; - - -/* Used to inform about flash status. */ -struct jffs_flash_status -{ - __u32 size; - __u32 used; - __u32 dirty; - __u32 begin; - __u32 end; -}; - -/* This stuff could be used for finding memory leaks. */ -#define JFFS_MEMORY_DEBUG 0 - -extern long no_jffs_node; -#if defined(JFFS_MEMORY_DEBUG) && JFFS_MEMORY_DEBUG -extern long no_jffs_control; -extern long no_jffs_raw_inode; -extern long no_jffs_node_ref; -extern long no_jffs_fm; -extern long no_jffs_fmcontrol; -extern long no_hash; -extern long no_name; -#define DJM(x) x -#else -#define DJM(x) -#endif - -#endif /* __LINUX_JFFS_H__ */ -- cgit v1.2.3 From e03abc0c963a31cb07dfbc07c7d85d75e0d13cf4 Mon Sep 17 00:00:00 2001 From: Eric Van Hensbergen Date: Sun, 11 Feb 2007 13:21:39 -0600 Subject: 9p: implement optional loose read cache While cacheing is generally frowned upon in the 9p world, it has its place -- particularly in situations where the remote file system is exclusive and/or read-only. The vacfs views of venti content addressable store are a real-world instance of such a situation. To facilitate higher performance for these workloads (and eventually use the fscache patches), we have enabled a "loose" cache mode which does not attempt to maintain any form of consistency on the page-cache or dcache. This results in over two orders of magnitude performance improvement for cacheable block reads in the Bonnie benchmark. The more aggressive use of the dcache also seems to improve metadata operational performance. Signed-off-by: Eric Van Hensbergen --- Documentation/filesystems/00-INDEX | 4 ++-- Documentation/filesystems/9p.txt | 4 ++++ fs/9p/fid.c | 3 ++- fs/9p/v9fs.c | 9 ++++++++- fs/9p/v9fs.h | 9 ++++++++- fs/9p/v9fs_vfs.h | 2 ++ fs/9p/vfs_addr.c | 2 ++ fs/9p/vfs_dentry.c | 26 ++++++++++++++++++++++++++ fs/9p/vfs_file.c | 18 ++++++++++++++++++ fs/9p/vfs_inode.c | 20 ++++++++++++++++---- 10 files changed, 88 insertions(+), 9 deletions(-) (limited to 'Documentation') diff --git a/Documentation/filesystems/00-INDEX b/Documentation/filesystems/00-INDEX index 4dc28cc93503..571785887a4f 100644 --- a/Documentation/filesystems/00-INDEX +++ b/Documentation/filesystems/00-INDEX @@ -4,6 +4,8 @@ Exporting - explanation of how to make filesystems exportable. Locking - info on locking rules as they pertain to Linux VFS. +9p.txt + - 9p (v9fs) is an implementation of the Plan 9 remote fs protocol. adfs.txt - info and mount options for the Acorn Advanced Disc Filing System. afs.txt @@ -82,8 +84,6 @@ udf.txt - info and mount options for the UDF filesystem. ufs.txt - info on the ufs filesystem. -v9fs.txt - - v9fs is a Unix implementation of the Plan 9 9p remote fs protocol. vfat.txt - info on using the VFAT filesystem used in Windows NT and Windows 95 vfs.txt diff --git a/Documentation/filesystems/9p.txt b/Documentation/filesystems/9p.txt index 4d075a4558f9..bbd8b28c13de 100644 --- a/Documentation/filesystems/9p.txt +++ b/Documentation/filesystems/9p.txt @@ -40,6 +40,10 @@ OPTIONS aname=name aname specifies the file tree to access when the server is offering several exported file systems. + cache=mode specifies a cacheing policy. By default, no caches are used. + loose = no attempts are made at consistency, + intended for exclusive, read-only mounts + debug=n specifies debug level. The debug level is a bitmask. 0x01 = display verbose error messages 0x02 = developer debug (DEBUG_CURRENT) diff --git a/fs/9p/fid.c b/fs/9p/fid.c index a9b6301a04fc..90419715c7e9 100644 --- a/fs/9p/fid.c +++ b/fs/9p/fid.c @@ -136,7 +136,8 @@ struct v9fs_fid *v9fs_fid_lookup(struct dentry *dentry) } /** - * v9fs_fid_clone - lookup the fid for a dentry, clone a private copy and release it + * v9fs_fid_clone - lookup the fid for a dentry, clone a private copy and + * release it * @dentry: dentry to look for fid in * * find a fid in the dentry and then clone to a new private fid diff --git a/fs/9p/v9fs.c b/fs/9p/v9fs.c index d9b561ba5e58..6ad6f192b6e4 100644 --- a/fs/9p/v9fs.c +++ b/fs/9p/v9fs.c @@ -53,6 +53,8 @@ enum { Opt_uname, Opt_remotename, /* Options that take no arguments */ Opt_legacy, Opt_nodevmap, Opt_unix, Opt_tcp, Opt_fd, + /* Cache options */ + Opt_cache_loose, /* Error token */ Opt_err }; @@ -76,6 +78,8 @@ static match_table_t tokens = { {Opt_fd, "fd"}, {Opt_legacy, "noextend"}, {Opt_nodevmap, "nodevmap"}, + {Opt_cache_loose, "cache=loose"}, + {Opt_cache_loose, "loose"}, {Opt_err, NULL} }; @@ -106,6 +110,7 @@ static void v9fs_parse_options(char *options, struct v9fs_session_info *v9ses) v9ses->debug = 0; v9ses->rfdno = ~0; v9ses->wfdno = ~0; + v9ses->cache = 0; if (!options) return; @@ -121,7 +126,6 @@ static void v9fs_parse_options(char *options, struct v9fs_session_info *v9ses) "integer field, but no integer?\n"); continue; } - } switch (token) { case Opt_port: @@ -169,6 +173,9 @@ static void v9fs_parse_options(char *options, struct v9fs_session_info *v9ses) case Opt_nodevmap: v9ses->nodev = 1; break; + case Opt_cache_loose: + v9ses->cache = CACHE_LOOSE; + break; default: continue; } diff --git a/fs/9p/v9fs.h b/fs/9p/v9fs.h index c134d104cb28..820bf5ca35d8 100644 --- a/fs/9p/v9fs.h +++ b/fs/9p/v9fs.h @@ -47,7 +47,7 @@ struct v9fs_session_info { unsigned int afid; /* authentication fid */ unsigned int rfdno; /* read file descriptor number */ unsigned int wfdno; /* write file descriptor number */ - + unsigned int cache; /* cache mode */ char *name; /* user name to mount as */ char *remotename; /* name of remote hierarchy being mounted */ @@ -73,6 +73,13 @@ enum { PROTO_FD, }; +/* possible values of ->cache */ +/* eventually support loose, tight, time, session, default always none */ +enum { + CACHE_NONE, /* default */ + CACHE_LOOSE, /* no consistency */ +}; + extern struct dentry *v9fs_debugfs_root; int v9fs_session_init(struct v9fs_session_info *, const char *, char *); diff --git a/fs/9p/v9fs_vfs.h b/fs/9p/v9fs_vfs.h index 450b0c1b385e..8ada4c5c5d70 100644 --- a/fs/9p/v9fs_vfs.h +++ b/fs/9p/v9fs_vfs.h @@ -40,8 +40,10 @@ extern struct file_system_type v9fs_fs_type; extern const struct address_space_operations v9fs_addr_operations; extern const struct file_operations v9fs_file_operations; +extern const struct file_operations v9fs_cached_file_operations; extern const struct file_operations v9fs_dir_operations; extern struct dentry_operations v9fs_dentry_operations; +extern struct dentry_operations v9fs_cached_dentry_operations; struct inode *v9fs_get_inode(struct super_block *sb, int mode); ino_t v9fs_qid2ino(struct v9fs_qid *qid); diff --git a/fs/9p/vfs_addr.c b/fs/9p/vfs_addr.c index cc24abf232d5..bed48fa96521 100644 --- a/fs/9p/vfs_addr.c +++ b/fs/9p/vfs_addr.c @@ -63,6 +63,8 @@ static int v9fs_vfs_readpage(struct file *filp, struct page *page) int total = 0; int result = 0; + dprintk(DEBUG_VFS, "\n"); + buffer = kmap(page); do { if (count < rsize) diff --git a/fs/9p/vfs_dentry.c b/fs/9p/vfs_dentry.c index 062daa6000ab..ddffd8aa902d 100644 --- a/fs/9p/vfs_dentry.c +++ b/fs/9p/vfs_dentry.c @@ -53,9 +53,30 @@ static int v9fs_dentry_delete(struct dentry *dentry) { dprintk(DEBUG_VFS, " dentry: %s (%p)\n", dentry->d_iname, dentry); + return 1; } +/** + * v9fs_cached_dentry_delete - called when dentry refcount equals 0 + * @dentry: dentry in question + * + * Only return 1 if our inode is invalid. Only non-synthetic files + * (ones without mtime == 0) should be calling this function. + * + */ + +static int v9fs_cached_dentry_delete(struct dentry *dentry) +{ + struct inode *inode = dentry->d_inode; + dprintk(DEBUG_VFS, " dentry: %s (%p)\n", dentry->d_iname, dentry); + + if(!inode) + return 1; + + return 0; +} + /** * v9fs_dentry_release - called when dentry is going to be freed * @dentry: dentry that is being release @@ -87,6 +108,11 @@ void v9fs_dentry_release(struct dentry *dentry) } } +struct dentry_operations v9fs_cached_dentry_operations = { + .d_delete = v9fs_cached_dentry_delete, + .d_release = v9fs_dentry_release, +}; + struct dentry_operations v9fs_dentry_operations = { .d_delete = v9fs_dentry_delete, .d_release = v9fs_dentry_release, diff --git a/fs/9p/vfs_file.c b/fs/9p/vfs_file.c index 6c78343cf690..653dfa5b2531 100644 --- a/fs/9p/vfs_file.c +++ b/fs/9p/vfs_file.c @@ -79,6 +79,13 @@ int v9fs_file_open(struct inode *inode, struct file *file) vfid->filp = file; kfree(fcall); + if((vfid->qid.version) && (v9ses->cache)) { + dprintk(DEBUG_VFS, "cached"); + /* enable cached file options */ + if(file->f_op == &v9fs_file_operations) + file->f_op = &v9fs_cached_file_operations; + } + return 0; Clunk_Fid: @@ -238,6 +245,17 @@ v9fs_file_write(struct file *filp, const char __user * data, return total; } +const struct file_operations v9fs_cached_file_operations = { + .llseek = generic_file_llseek, + .read = do_sync_read, + .aio_read = generic_file_aio_read, + .write = v9fs_file_write, + .open = v9fs_file_open, + .release = v9fs_dir_release, + .lock = v9fs_file_lock, + .mmap = generic_file_mmap, +}; + const struct file_operations v9fs_file_operations = { .llseek = generic_file_llseek, .read = v9fs_file_read, diff --git a/fs/9p/vfs_inode.c b/fs/9p/vfs_inode.c index 5cf22134826b..124a085d1f2e 100644 --- a/fs/9p/vfs_inode.c +++ b/fs/9p/vfs_inode.c @@ -504,7 +504,10 @@ v9fs_vfs_create(struct inode *dir, struct dentry *dentry, int mode, goto error; } - dentry->d_op = &v9fs_dentry_operations; + if(v9ses->cache) + dentry->d_op = &v9fs_cached_dentry_operations; + else + dentry->d_op = &v9fs_dentry_operations; d_instantiate(dentry, inode); if (nd && nd->flags & LOOKUP_OPEN) { @@ -589,7 +592,10 @@ static int v9fs_vfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) goto error; } - dentry->d_op = &v9fs_dentry_operations; + if(v9ses->cache) + dentry->d_op = &v9fs_cached_dentry_operations; + else + dentry->d_op = &v9fs_dentry_operations; d_instantiate(dentry, inode); return 0; @@ -626,7 +632,6 @@ static struct dentry *v9fs_vfs_lookup(struct inode *dir, struct dentry *dentry, sb = dir->i_sb; v9ses = v9fs_inode2v9ses(dir); - dentry->d_op = &v9fs_dentry_operations; dirfid = v9fs_fid_lookup(dentry->d_parent); if(IS_ERR(dirfid)) @@ -697,6 +702,10 @@ static struct dentry *v9fs_vfs_lookup(struct inode *dir, struct dentry *dentry, fid->qid = fcall->params.rstat.stat.qid; v9fs_stat2inode(&fcall->params.rstat.stat, inode, inode->i_sb); + if((fid->qid.version)&&(v9ses->cache)) + dentry->d_op = &v9fs_cached_dentry_operations; + else + dentry->d_op = &v9fs_dentry_operations; d_add(dentry, inode); kfree(fcall); @@ -1184,7 +1193,10 @@ static int v9fs_vfs_mkspecial(struct inode *dir, struct dentry *dentry, goto free_vfid; } - dentry->d_op = &v9fs_dentry_operations; + if(v9ses->cache) + dentry->d_op = &v9fs_cached_dentry_operations; + else + dentry->d_op = &v9fs_dentry_operations; d_instantiate(dentry, inode); return 0; -- cgit v1.2.3 From 304301347bed8315d6d13fd0e63032dfae6ef403 Mon Sep 17 00:00:00 2001 From: Simon Horman Date: Tue, 20 Feb 2007 13:58:07 -0800 Subject: [PATCH] PPC64 Kdump documentation update Patch from Mohan Kumar M to add the ppc64 portions of the kdump documentation. http://thread.gmane.org/gmane.linux.kernel/481689/focus=3375 Cc: Mohan Kumar M Cc: Vivek Goyal Signed-off-by: Simon Horman Cc: Paul Mackerras Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/kdump/kdump.txt | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) (limited to 'Documentation') diff --git a/Documentation/kdump/kdump.txt b/Documentation/kdump/kdump.txt index 79775a4130b5..2fedc081b4c8 100644 --- a/Documentation/kdump/kdump.txt +++ b/Documentation/kdump/kdump.txt @@ -30,6 +30,10 @@ On x86 machines, the first 640 KB of physical memory is needed to boot, regardless of where the kernel loads. Therefore, kexec backs up this region just before rebooting into the dump-capture kernel. +Similarly on PPC64 machines first 32KB of physical memory is needed for +booting regardless of where the kernel is loaded and to support 64K page +size kexec backs up the first 64KB memory. + All of the necessary information about the system kernel's core image is encoded in the ELF format, and stored in a reserved area of memory before a crash. The physical address of the start of the ELF header is @@ -224,7 +228,7 @@ Dump-capture kernel config options (Arch Dependent, x86_64) Dump-capture kernel config options (Arch Dependent, ppc64) ---------------------------------------------------------- -- Make and install the kernel and its modules. DO NOT add this kernel +* Make and install the kernel and its modules. DO NOT add this kernel to the boot loader configuration files. Dump-capture kernel config options (Arch Dependent, ia64) @@ -251,8 +255,8 @@ Dump-capture kernel config options (Arch Dependent, ia64) Boot into System Kernel ======================= -1) Make and install the kernel and its modules. Update the boot loader - (such as grub, yaboot, or lilo) configuration files as necessary. +1) Update the boot loader (such as grub, yaboot, or lilo) configuration + files as necessary. 2) Boot the system kernel with the boot parameter "crashkernel=Y@X", where Y specifies how much memory to reserve for the dump-capture kernel @@ -356,10 +360,11 @@ If die() is called, and it happens to be a thread with pid 0 or 1, or die() is called inside interrupt context or die() is called and panic_on_oops is set, the system will boot into the dump-capture kernel. -On powererpc systems when a soft-reset is generated, die() is called by all cpus and the system will boot into the dump-capture kernel. +On powererpc systems when a soft-reset is generated, die() is called by all cpus +and the system will boot into the dump-capture kernel. For testing purposes, you can trigger a crash by using "ALT-SysRq-c", -"echo c > /proc/sysrq-trigger or write a module to force the panic. +"echo c > /proc/sysrq-trigger" or write a module to force the panic. Write Out the Dump File ======================= @@ -410,12 +415,9 @@ format. Crash is available on Dave Anderson's site at the following URL: To Do ===== -1) Provide a kernel pages filtering mechanism, so core file size is not - extreme on systems with huge memory banks. - -2) Relocatable kernel can help in maintaining multiple kernels for - crash_dump, and the same kernel as the system kernel can be used to - capture the dump. +1) Provide relocatable kernels for all architectures to help in maintaining + multiple kernels for crash_dump, and the same kernel as the system kernel + can be used to capture the dump. Contact -- cgit v1.2.3 From 955eff5acc8b8cd1c7d4eec0229c35eaabe013db Mon Sep 17 00:00:00 2001 From: Nick Piggin Date: Tue, 20 Feb 2007 13:58:08 -0800 Subject: [PATCH] fs: fix libfs data leak simple_prepare_write leaks uninitialised kernel data. This happens because the it leaves an uninitialised "hole" over the part of the page that the write is expected to go to. This is fine, but it then marks the page uptodate, which means a concurrent read can come in and copy the uninitialised memory into userspace before it written to. Fix it by simply marking it uptodate in simple_commit_write instead, after the hole has been filled in. This could theoretically break an fs that uses simple_prepare_write and not simple_commit_write, and that relies on the incorrect simple_prepare_write behaviour. Luckily, none of those exists in the tree. Signed-off-by: Nick Piggin Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/filesystems/vfs.txt | 5 +++++ fs/libfs.c | 5 +++-- 2 files changed, 8 insertions(+), 2 deletions(-) (limited to 'Documentation') diff --git a/Documentation/filesystems/vfs.txt b/Documentation/filesystems/vfs.txt index 7737bfd03cf8..ea271f2d3954 100644 --- a/Documentation/filesystems/vfs.txt +++ b/Documentation/filesystems/vfs.txt @@ -617,6 +617,11 @@ struct address_space_operations { In this case the prepare_write will be retried one the lock is regained. + Note: the page _must not_ be marked uptodate in this function + (or anywhere else) unless it actually is uptodate right now. As + soon as a page is marked uptodate, it is possible for a concurrent + read(2) to copy it to userspace. + commit_write: If prepare_write succeeds, new data will be copied into the page and then commit_write will be called. It will typically update the size of the file (if appropriate) and diff --git a/fs/libfs.c b/fs/libfs.c index 7d487047dbb8..cf79196535ec 100644 --- a/fs/libfs.c +++ b/fs/libfs.c @@ -335,17 +335,18 @@ int simple_prepare_write(struct file *file, struct page *page, flush_dcache_page(page); kunmap_atomic(kaddr, KM_USER0); } - SetPageUptodate(page); } return 0; } int simple_commit_write(struct file *file, struct page *page, - unsigned offset, unsigned to) + unsigned from, unsigned to) { struct inode *inode = page->mapping->host; loff_t pos = ((loff_t)page->index << PAGE_CACHE_SHIFT) + to; + if (!PageUptodate(page)) + SetPageUptodate(page); /* * No need to use i_size_read() here, the i_size * cannot change under us because we hold the i_mutex. -- cgit v1.2.3 From a631694a36a3b52b786b3ae6abe54bd8d1b6eb74 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Tue, 20 Feb 2007 13:58:12 -0800 Subject: [PATCH] update Doc/oops-tracing.txt for TAINT_USER Add TAINT_USER description to Tainted flags in oops-tracing.txt. Signed-off-by: Randy Dunlap Signed-off-by: "Theodore Ts'o" Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/oops-tracing.txt | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'Documentation') diff --git a/Documentation/oops-tracing.txt b/Documentation/oops-tracing.txt index 2503404ae5c2..ea55ea8bc8ef 100644 --- a/Documentation/oops-tracing.txt +++ b/Documentation/oops-tracing.txt @@ -234,6 +234,12 @@ characters, each representing a particular tainted value. 6: 'B' if a page-release function has found a bad page reference or some unexpected page flags. + 7: 'U' if a user specifically requested that the Tainted flag be set, + ' ' otherwise. + + 7: 'U' if a user or user application specifically requested that the + Tainted flag be set, ' ' otherwise. + The primary reason for the 'Tainted: ' string is to tell kernel debuggers if this is a clean kernel or if anything unusual has occurred. Tainting is permanent: even if an offending module is -- cgit v1.2.3 From e12ceaf4962d804320d639faed1da61e2cb85723 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 18 Dec 2006 13:06:30 -0300 Subject: V4L/DVB (4981): Update cx2341x documentation. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/cx2341x/fw-decoder-api.txt | 14 ++++++++++++++ Documentation/video4linux/cx2341x/fw-memory.txt | 2 +- 2 files changed, 15 insertions(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/video4linux/cx2341x/fw-decoder-api.txt b/Documentation/video4linux/cx2341x/fw-decoder-api.txt index 78bf5f21e513..15c388b8bd19 100644 --- a/Documentation/video4linux/cx2341x/fw-decoder-api.txt +++ b/Documentation/video4linux/cx2341x/fw-decoder-api.txt @@ -32,6 +32,10 @@ Description playback stops at specified PTS. Param[0] Display 0=last frame, 1=black + Note: this takes effect immediately, so if you want to wait for a PTS, + then use '0', otherwise the screen goes to black at once. + You can call this later (even if there is no playback) with a 1 value + to set the screen to black. Param[1] PTS low Param[2] @@ -60,8 +64,12 @@ Param[0] 31 Speed: '0' slow '1' fast + Note: n seems to be limited to 2. Anything higher does not result in + faster playback. Instead the host should start dropping frames. Param[1] Direction: 0=forward, 1=reverse + Note: to make reverse playback work you have to write full GOPs in + reverse order. Param[2] Picture mask: 1=I frames @@ -69,6 +77,8 @@ Param[2] 7=I, P, B frames Param[3] B frames per GOP (for reverse play only) + Note: apparently this does not work. For reverse play I can only make it + work by selecting I or I and P frames in the Picture mask. Param[4] Mute audio: 0=disable, 1=enable Param[5] @@ -212,6 +222,7 @@ Description Select audio mode Param[0] Dual mono mode action + 0=Stereo, 1=Left, 2=Right, 3=Mono, 4=Swap, -1=Unchanged Param[1] Stereo mode action: 0=Stereo, 1=Left, 2=Right, 3=Mono, 4=Swap, -1=Unchanged @@ -225,6 +236,9 @@ Description Counterpart to API 0xD5 Param[0] Event: 0=Audio mode change between stereo and dual channel + Event: 3=Decoder started + Event: 4=Unknown: goes off 10-15 times per second while decoding. + Event: 5=Some sync event: goes off once per frame. Param[1] Notification 0=disabled, 1=enabled Param[2] diff --git a/Documentation/video4linux/cx2341x/fw-memory.txt b/Documentation/video4linux/cx2341x/fw-memory.txt index ef0aad3f88fc..0cf24918c9fc 100644 --- a/Documentation/video4linux/cx2341x/fw-memory.txt +++ b/Documentation/video4linux/cx2341x/fw-memory.txt @@ -125,7 +125,7 @@ Bit 27 Encoder DMA complete 26 25 Decoder copy protect detection event -24 Decoder audio mode change detection event +24 Decoder audio mode change detection event (through event notification) 23 22 Decoder data request 21 Decoder I-Frame? done -- cgit v1.2.3 From 20a919f7960df1c0bf1cb4f637149ed4b6bc9ec3 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 18 Dec 2006 22:43:36 -0300 Subject: V4L/DVB (4985): Update cx2341x documentation. Removed a few unimplemented commands. Added a note for a few fields that are not implemented in the firmware, and clarified several issues around reverse playback. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- .../video4linux/cx2341x/fw-decoder-api.txt | 48 +++------------------- .../video4linux/cx2341x/fw-encoder-api.txt | 2 +- 2 files changed, 7 insertions(+), 43 deletions(-) (limited to 'Documentation') diff --git a/Documentation/video4linux/cx2341x/fw-decoder-api.txt b/Documentation/video4linux/cx2341x/fw-decoder-api.txt index 15c388b8bd19..1345d267c5fc 100644 --- a/Documentation/video4linux/cx2341x/fw-decoder-api.txt +++ b/Documentation/video4linux/cx2341x/fw-decoder-api.txt @@ -21,7 +21,7 @@ Param[0] 0 based frame number in GOP to begin playback from. Param[1] Specifies the number of muted audio frames to play before normal - audio resumes. + audio resumes. (This is not implemented in the firmware, leave at 0) ------------------------------------------------------------------------------- @@ -64,7 +64,7 @@ Param[0] 31 Speed: '0' slow '1' fast - Note: n seems to be limited to 2. Anything higher does not result in + Note: n is limited to 2. Anything higher does not result in faster playback. Instead the host should start dropping frames. Param[1] Direction: 0=forward, 1=reverse @@ -77,15 +77,16 @@ Param[2] 7=I, P, B frames Param[3] B frames per GOP (for reverse play only) - Note: apparently this does not work. For reverse play I can only make it - work by selecting I or I and P frames in the Picture mask. + Note: for reverse playback the Picture Mask should be set to I or I, P. + Adding B frames to the mask will result in corrupt video. This field + has to be set to the correct value in order to keep the timing correct. Param[4] Mute audio: 0=disable, 1=enable Param[5] Display 0=frame, 1=field Param[6] Specifies the number of muted audio frames to play before normal audio - resumes. + resumes. (Not implemented in the firmware, leave at 0) ------------------------------------------------------------------------------- @@ -287,43 +288,6 @@ Param[3] ------------------------------------------------------------------------------- -Name CX2341X_DEC_SET_AUDIO_OUTPUT -Enum 27/0x1B -Description - Select audio output format -Param[0] - Bitmask: - 0:1 Data size: - '00' 16 bit - '01' 20 bit - '10' 24 bit - 2:7 Unused - 8:9 Mode: - '00' 2 channels - '01' 4 channels - '10' 6 channels - '11' 6 channels with one line data mode - (for left justified MSB first mode, 20 bit only) - 10:11 Unused - 12:13 Channel format: - '00' right justified MSB first mode - '01' left justified MSB first mode - '10' I2S mode - 14:15 Unused - 16:21 Right justify bit count - 22:31 Unused - -------------------------------------------------------------------------------- - -Name CX2341X_DEC_SET_AV_DELAY -Enum 28/0x1C -Description - Set audio/video delay in 90Khz ticks -Param[0] - 0=A/V in sync, negative=audio lags, positive=video lags - -------------------------------------------------------------------------------- - Name CX2341X_DEC_SET_PREBUFFERING Enum 30/0x1E Description diff --git a/Documentation/video4linux/cx2341x/fw-encoder-api.txt b/Documentation/video4linux/cx2341x/fw-encoder-api.txt index 15df0df57ddd..9a5710646778 100644 --- a/Documentation/video4linux/cx2341x/fw-encoder-api.txt +++ b/Documentation/video4linux/cx2341x/fw-encoder-api.txt @@ -216,7 +216,7 @@ Param[1] Name CX2341X_ENC_SET_3_2_PULLDOWN Enum 177/0xB1 Description - 3:2 pulldown properties + 3:2 pulldown properties (This command is not implemented in the firmware) Param[0] 0=enabled 1=disabled -- cgit v1.2.3 From 75558ab92dc95c3b5a99df7c77e95a6e8484e05c Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 18 Dec 2006 22:52:21 -0300 Subject: V4L/DVB (4986): Removed unimplemented cx2341x API commands The commands CX2341X_DEC_SET_AUDIO_OUTPUT, CX2341X_DEC_SET_AV_DELAY and CX2341X_ENC_SET_3_2_PULLDOWN are not implemented in the Conexant firmware. So these commands are removed. This also means that the V4L2_CID_MPEG_VIDEO_PULLDOWN control in cx2341x.c and pvrusb2-hdw.c is removed. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/cx2341x/fw-encoder-api.txt | 10 ---------- drivers/media/video/cx2341x.c | 17 ++--------------- drivers/media/video/pvrusb2/pvrusb2-hdw.c | 3 --- include/media/cx2341x.h | 4 ---- 4 files changed, 2 insertions(+), 32 deletions(-) (limited to 'Documentation') diff --git a/Documentation/video4linux/cx2341x/fw-encoder-api.txt b/Documentation/video4linux/cx2341x/fw-encoder-api.txt index 9a5710646778..2412718c3d0b 100644 --- a/Documentation/video4linux/cx2341x/fw-encoder-api.txt +++ b/Documentation/video4linux/cx2341x/fw-encoder-api.txt @@ -213,16 +213,6 @@ Param[1] ------------------------------------------------------------------------------- -Name CX2341X_ENC_SET_3_2_PULLDOWN -Enum 177/0xB1 -Description - 3:2 pulldown properties (This command is not implemented in the firmware) -Param[0] - 0=enabled - 1=disabled - -------------------------------------------------------------------------------- - Name CX2341X_ENC_SET_VBI_LINE Enum 183/0xB7 Description diff --git a/drivers/media/video/cx2341x.c b/drivers/media/video/cx2341x.c index 2f5ca71e0261..d60cd5ecf821 100644 --- a/drivers/media/video/cx2341x.c +++ b/drivers/media/video/cx2341x.c @@ -56,7 +56,6 @@ const u32 cx2341x_mpeg_ctrls[] = { V4L2_CID_MPEG_VIDEO_B_FRAMES, V4L2_CID_MPEG_VIDEO_GOP_SIZE, V4L2_CID_MPEG_VIDEO_GOP_CLOSURE, - V4L2_CID_MPEG_VIDEO_PULLDOWN, V4L2_CID_MPEG_VIDEO_BITRATE_MODE, V4L2_CID_MPEG_VIDEO_BITRATE, V4L2_CID_MPEG_VIDEO_BITRATE_PEAK, @@ -118,9 +117,6 @@ static int cx2341x_get_ctrl(struct cx2341x_mpeg_params *params, case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE: ctrl->value = params->video_gop_closure; break; - case V4L2_CID_MPEG_VIDEO_PULLDOWN: - ctrl->value = params->video_pulldown; - break; case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: ctrl->value = params->video_bitrate_mode; break; @@ -231,9 +227,6 @@ static int cx2341x_set_ctrl(struct cx2341x_mpeg_params *params, case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE: params->video_gop_closure = ctrl->value; break; - case V4L2_CID_MPEG_VIDEO_PULLDOWN: - params->video_pulldown = ctrl->value; - break; case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: /* MPEG-1 only allows CBR */ if (params->video_encoding == V4L2_MPEG_VIDEO_ENCODING_MPEG_1 && @@ -679,7 +672,6 @@ void cx2341x_fill_defaults(struct cx2341x_mpeg_params *p) .video_b_frames = 2, .video_gop_size = 12, .video_gop_closure = 1, - .video_pulldown = 0, .video_bitrate_mode = V4L2_MPEG_VIDEO_BITRATE_MODE_VBR, .video_bitrate = 6000000, .video_bitrate_peak = 8000000, @@ -783,10 +775,6 @@ int cx2341x_update(void *priv, cx2341x_mbox_func func, err = cx2341x_api(priv, func, CX2341X_ENC_SET_GOP_CLOSURE, 1, new->video_gop_closure); if (err) return err; } - if (old == NULL || old->video_pulldown != new->video_pulldown) { - err = cx2341x_api(priv, func, CX2341X_ENC_SET_3_2_PULLDOWN, 1, new->video_pulldown); - if (err) return err; - } if (old == NULL || old->audio_properties != new->audio_properties) { err = cx2341x_api(priv, func, CX2341X_ENC_SET_AUDIO_PROPERTIES, 1, new->audio_properties); if (err) return err; @@ -888,11 +876,10 @@ void cx2341x_log_status(struct cx2341x_mpeg_params *p, const char *prefix) printk(", Peak %d", p->video_bitrate_peak); } printk("\n"); - printk(KERN_INFO "%s: Video: GOP Size %d, %d B-Frames, %sGOP Closure, %s3:2 Pulldown\n", + printk(KERN_INFO "%s: Video: GOP Size %d, %d B-Frames, %sGOP Closure\n", prefix, p->video_gop_size, p->video_b_frames, - p->video_gop_closure ? "" : "No ", - p->video_pulldown ? "" : "No "); + p->video_gop_closure ? "" : "No "); if (p->video_temporal_decimation) { printk(KERN_INFO "%s: Video: Temporal Decimation %d\n", prefix, p->video_temporal_decimation); diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/drivers/media/video/pvrusb2/pvrusb2-hdw.c index d2004965187b..c37f8e18b70b 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -159,9 +159,6 @@ static const struct pvr2_mpeg_ids mpeg_ids[] = { },{ .strid = "video_gop_closure", .id = V4L2_CID_MPEG_VIDEO_GOP_CLOSURE, - },{ - .strid = "video_pulldown", - .id = V4L2_CID_MPEG_VIDEO_PULLDOWN, },{ .strid = "video_bitrate_mode", .id = V4L2_CID_MPEG_VIDEO_BITRATE_MODE, diff --git a/include/media/cx2341x.h b/include/media/cx2341x.h index ecad55bf0162..d758a52cf556 100644 --- a/include/media/cx2341x.h +++ b/include/media/cx2341x.h @@ -57,7 +57,6 @@ struct cx2341x_mpeg_params { u16 video_b_frames; u16 video_gop_size; u16 video_gop_closure; - u16 video_pulldown; enum v4l2_mpeg_video_bitrate_mode video_bitrate_mode; u32 video_bitrate; u32 video_bitrate_peak; @@ -121,8 +120,6 @@ void cx2341x_log_status(struct cx2341x_mpeg_params *p, const char *prefix); #define CX2341X_DEC_SET_DISPLAY_BUFFERS 0x18 #define CX2341X_DEC_EXTRACT_VBI 0x19 #define CX2341X_DEC_SET_DECODER_SOURCE 0x1a -#define CX2341X_DEC_SET_AUDIO_OUTPUT 0x1b -#define CX2341X_DEC_SET_AV_DELAY 0x1c #define CX2341X_DEC_SET_PREBUFFERING 0x1e /* MPEG encoder API */ @@ -141,7 +138,6 @@ void cx2341x_log_status(struct cx2341x_mpeg_params *p, const char *prefix); #define CX2341X_ENC_SET_DNR_FILTER_PROPS 0x9d #define CX2341X_ENC_SET_CORING_LEVELS 0x9f #define CX2341X_ENC_SET_SPATIAL_FILTER_TYPE 0xa1 -#define CX2341X_ENC_SET_3_2_PULLDOWN 0xb1 #define CX2341X_ENC_SET_VBI_LINE 0xb7 #define CX2341X_ENC_SET_STREAM_TYPE 0xb9 #define CX2341X_ENC_SET_OUTPUT_PORT 0xbb -- cgit v1.2.3 From d84e2bdca6e168557639b29c9244cbcf2500fe21 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 20 Dec 2006 06:50:18 -0300 Subject: V4L/DVB (4987): Improve cx2341x documentation Document the program index table format, removed unused interrupt documentation and improve the documentation regarding the audio mode (stereo/joint/dual/mono). Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- .../video4linux/cx2341x/fw-decoder-api.txt | 2 +- .../video4linux/cx2341x/fw-encoder-api.txt | 24 ++++++++++++++++++---- Documentation/video4linux/cx2341x/fw-memory.txt | 4 ---- 3 files changed, 21 insertions(+), 9 deletions(-) (limited to 'Documentation') diff --git a/Documentation/video4linux/cx2341x/fw-decoder-api.txt b/Documentation/video4linux/cx2341x/fw-decoder-api.txt index 1345d267c5fc..8c317b7a4fc9 100644 --- a/Documentation/video4linux/cx2341x/fw-decoder-api.txt +++ b/Documentation/video4linux/cx2341x/fw-decoder-api.txt @@ -236,7 +236,7 @@ Description Setup firmware to notify the host about a particular event. Counterpart to API 0xD5 Param[0] - Event: 0=Audio mode change between stereo and dual channel + Event: 0=Audio mode change between mono, (joint) stereo and dual channel. Event: 3=Decoder started Event: 4=Unknown: goes off 10-15 times per second while decoding. Event: 5=Some sync event: goes off once per frame. diff --git a/Documentation/video4linux/cx2341x/fw-encoder-api.txt b/Documentation/video4linux/cx2341x/fw-encoder-api.txt index 2412718c3d0b..fe02bdb84598 100644 --- a/Documentation/video4linux/cx2341x/fw-encoder-api.txt +++ b/Documentation/video4linux/cx2341x/fw-encoder-api.txt @@ -322,9 +322,7 @@ Param[0] '01'=JointStereo '10'=Dual '11'=Mono - Note: testing seems to indicate that Mono and possibly - JointStereo are not working (default to stereo). - Dual does work, though. + Note: the cx23415 cannot decode Joint Stereo properly. 10:11 Mode Extension used in joint_stereo mode. In Layer I and II they indicate which subbands are in @@ -403,16 +401,34 @@ Name CX2341X_ENC_SET_PGM_INDEX_INFO Enum 199/0xC7 Description Sets the Program Index Information. + The information is stored as follows: + + struct info { + u32 length; // Length of this frame + u32 offset_low; // Offset in the file of the + u32 offset_high; // start of this frame + u32 mask1; // Bits 0-1 are the type mask: + // 1=I, 2=P, 4=B + u32 pts; // The PTS of the frame + u32 mask2; // Bit 0 is bit 32 of the pts. + }; + u32 table_ptr; + struct info index[400]; + + The table_ptr is the encoder memory address in the table were + *new* entries will be written. Note that this is a ringbuffer, + so the table_ptr will wraparound. Param[0] Picture Mask: 0=No index capture 1=I frames 3=I,P frames 7=I,P,B frames + (Seems to be ignored, it always indexes I, P and B frames) Param[1] Elements requested (up to 400) Result[0] - Offset in SDF memory of the table. + Offset in the encoder memory of the start of the table. Result[1] Number of allocated elements up to a maximum of Param[1] diff --git a/Documentation/video4linux/cx2341x/fw-memory.txt b/Documentation/video4linux/cx2341x/fw-memory.txt index 0cf24918c9fc..d445e457fc1b 100644 --- a/Documentation/video4linux/cx2341x/fw-memory.txt +++ b/Documentation/video4linux/cx2341x/fw-memory.txt @@ -123,12 +123,8 @@ Bit 29 Encoder VBI capture 28 Encoder Video Input Module reset event 27 Encoder DMA complete -26 -25 Decoder copy protect detection event 24 Decoder audio mode change detection event (through event notification) -23 22 Decoder data request -21 Decoder I-Frame? done 20 Decoder DMA complete 19 Decoder VBI re-insertion 18 Decoder DMA err (linked-list bad) -- cgit v1.2.3 From ce700b4e2f4b41f79815ee3a6c4f9b9390b2af36 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 20 Dec 2006 09:53:09 -0300 Subject: V4L/DVB (4993): Updated cardlist to reflect the newly added saa7134 board Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/CARDLIST.saa7134 | 1 + 1 file changed, 1 insertion(+) (limited to 'Documentation') diff --git a/Documentation/video4linux/CARDLIST.saa7134 b/Documentation/video4linux/CARDLIST.saa7134 index f6201cc37ec5..1a53513c6250 100644 --- a/Documentation/video4linux/CARDLIST.saa7134 +++ b/Documentation/video4linux/CARDLIST.saa7134 @@ -104,3 +104,4 @@ 103 -> Compro Videomate DVB-T200A 104 -> Hauppauge WinTV-HVR1110 DVB-T/Hybrid [0070:6701] 105 -> Terratec Cinergy HT PCMCIA [153b:1172] +106 -> Encore ENLTV [1131:2342] -- cgit v1.2.3 From c36c459a5530da8869a4de832188cdcb75b60359 Mon Sep 17 00:00:00 2001 From: Juan Pablo Sormani Date: Wed, 27 Dec 2006 12:46:36 -0300 Subject: V4L/DVB (5015): Add support for more Encore TV cards Signed-off-by: Juan Pablo Sormani Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/CARDLIST.saa7134 | 3 +- drivers/media/common/ir-keymaps.c | 78 ++++++++++++++++++++++++++ drivers/media/video/saa7134/saa7134-cards.c | 86 ++++++++++++++++++++++++++++- drivers/media/video/saa7134/saa7134-input.c | 7 +++ drivers/media/video/saa7134/saa7134.h | 1 + include/media/ir-common.h | 1 + 6 files changed, 172 insertions(+), 4 deletions(-) (limited to 'Documentation') diff --git a/Documentation/video4linux/CARDLIST.saa7134 b/Documentation/video4linux/CARDLIST.saa7134 index 1a53513c6250..bcba180194e8 100644 --- a/Documentation/video4linux/CARDLIST.saa7134 +++ b/Documentation/video4linux/CARDLIST.saa7134 @@ -104,4 +104,5 @@ 103 -> Compro Videomate DVB-T200A 104 -> Hauppauge WinTV-HVR1110 DVB-T/Hybrid [0070:6701] 105 -> Terratec Cinergy HT PCMCIA [153b:1172] -106 -> Encore ENLTV [1131:2342] +106 -> Encore ENLTV [1131:2342,1131:2341,3016:2344] +107 -> Encore ENLTV-FM [1131:230f] diff --git a/drivers/media/common/ir-keymaps.c b/drivers/media/common/ir-keymaps.c index 4a54a559d81c..8b290204ff6c 100644 --- a/drivers/media/common/ir-keymaps.c +++ b/drivers/media/common/ir-keymaps.c @@ -1660,3 +1660,81 @@ IR_KEYTAB_TYPE ir_codes_asus_pc39[IR_KEYTAB_SIZE] = { }; EXPORT_SYMBOL_GPL(ir_codes_asus_pc39); + + +/* Encore ENLTV-FM - black plastic, white front cover with white glowing buttons + Juan Pablo Sormani */ +IR_KEYTAB_TYPE ir_codes_encore_enltv[IR_KEYTAB_SIZE] = { + + /* Power button does nothing, neither in Windows app, + although it sends data (used for BIOS wakeup?) */ + [ 0x0d ] = KEY_MUTE, + + [ 0x1e ] = KEY_TV, + [ 0x00 ] = KEY_VIDEO, + [ 0x01 ] = KEY_AUDIO, /* music */ + [ 0x02 ] = KEY_MHP, /* picture */ + + [ 0x1f ] = KEY_1, + [ 0x03 ] = KEY_2, + [ 0x04 ] = KEY_3, + [ 0x05 ] = KEY_4, + [ 0x1c ] = KEY_5, + [ 0x06 ] = KEY_6, + [ 0x07 ] = KEY_7, + [ 0x08 ] = KEY_8, + [ 0x1d ] = KEY_9, + [ 0x0a ] = KEY_0, + + [ 0x09 ] = KEY_LIST, /* -/-- */ + [ 0x0b ] = KEY_LAST, /* recall */ + + [ 0x14 ] = KEY_HOME, /* win start menu */ + [ 0x15 ] = KEY_EXIT, /* exit */ + [ 0x16 ] = KEY_UP, + [ 0x12 ] = KEY_DOWN, + [ 0x0c ] = KEY_RIGHT, + [ 0x17 ] = KEY_LEFT, + + [ 0x18 ] = KEY_ENTER, /* OK */ + + [ 0x0e ] = KEY_ESC, + [ 0x13 ] = KEY_D, /* desktop */ + [ 0x11 ] = KEY_TAB, + [ 0x19 ] = KEY_SWITCHVIDEOMODE, /* switch */ + + [ 0x1a ] = KEY_MENU, + [ 0x1b ] = KEY_ZOOM, /* fullscreen */ + [ 0x44 ] = KEY_TIME, /* time shift */ + [ 0x40 ] = KEY_MODE, /* source */ + + [ 0x5a ] = KEY_RECORD, + [ 0x42 ] = KEY_PLAY, /* play/pause */ + [ 0x45 ] = KEY_STOP, + [ 0x43 ] = KEY_CAMERA, /* camera icon */ + + [ 0x48 ] = KEY_REWIND, + [ 0x4a ] = KEY_FASTFORWARD, + [ 0x49 ] = KEY_PREVIOUS, + [ 0x4b ] = KEY_NEXT, + + [ 0x4c ] = KEY_FAVORITES, /* tv wall */ + [ 0x4d ] = KEY_SOUND, /* DVD sound */ + [ 0x4e ] = KEY_LANGUAGE, /* DVD lang */ + [ 0x4f ] = KEY_TEXT, /* DVD text */ + + [ 0x50 ] = KEY_SLEEP, /* shutdown */ + [ 0x51 ] = KEY_MODE, /* stereo > main */ + [ 0x52 ] = KEY_SELECT, /* stereo > sap */ + [ 0x53 ] = KEY_PROG1, /* teletext */ + + + [ 0x59 ] = KEY_RED, /* AP1 */ + [ 0x41 ] = KEY_GREEN, /* AP2 */ + [ 0x47 ] = KEY_YELLOW, /* AP3 */ + [ 0x57 ] = KEY_BLUE, /* AP4 */ + + +}; + +EXPORT_SYMBOL_GPL(ir_codes_encore_enltv); diff --git a/drivers/media/video/saa7134/saa7134-cards.c b/drivers/media/video/saa7134/saa7134-cards.c index 9d26ab48edd4..0403ae9e5b8a 100644 --- a/drivers/media/video/saa7134/saa7134-cards.c +++ b/drivers/media/video/saa7134/saa7134-cards.c @@ -3189,8 +3189,10 @@ struct saa7134_board saa7134_boards[] = { }}, }, [SAA7134_BOARD_ENCORE_ENLTV] = { - /* Steven Walter */ + /* Steven Walter + Juan Pablo Sormani */ .name = "Encore ENLTV", + .audio_clock = 0x00200000, .tuner_type = TUNER_TNF_5335MF, .radio_type = UNSET, .tuner_addr = ADDR_UNSET, @@ -3198,13 +3200,71 @@ struct saa7134_board saa7134_boards[] = { .inputs = {{ .name = name_tv, .vmux = 1, + .amux = 3, + .tv = 1, + },{ + .name = name_tv_mono, + .vmux = 7, + .amux = 4, + .tv = 1, + },{ + .name = name_comp1, + .vmux = 3, + .amux = 2, + },{ + .name = name_svideo, + .vmux = 0, + .amux = 2, + }}, + .radio = { + .name = name_radio, .amux = LINE2, +/* .gpio = 0x00300001,*/ + .gpio = 0x20000, + + }, + .mute = { + .name = name_mute, + .amux = 0, + }, + }, + [SAA7134_BOARD_ENCORE_ENLTV_FM] = { + /* Juan Pablo Sormani */ + .name = "Encore ENLTV-FM", + .audio_clock = 0x00200000, + .tuner_type = TUNER_PHILIPS_ATSC, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = 3, .tv = 1, + },{ + .name = name_tv_mono, + .vmux = 7, + .amux = 4, + .tv = 1, + },{ + .name = name_comp1, + .vmux = 3, + .amux = 2, },{ .name = name_svideo, - .vmux = 6, - .amux = LINE1, + .vmux = 0, + .amux = 2, }}, + .radio = { + .name = name_radio, + .amux = LINE2, + .gpio = 0x20000, + + }, + .mute = { + .name = name_mute, + .amux = 0, + }, }, }; @@ -3850,6 +3910,24 @@ struct pci_device_id saa7134_pci_tbl[] = { .subvendor = PCI_VENDOR_ID_PHILIPS, .subdevice = 0x2342, .driver_data = SAA7134_BOARD_ENCORE_ENLTV, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7130, + .subvendor = 0x1131, + .subdevice = 0x2341, + .driver_data = SAA7134_BOARD_ENCORE_ENLTV, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7130, + .subvendor = 0x3016, + .subdevice = 0x2344, + .driver_data = SAA7134_BOARD_ENCORE_ENLTV, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7130, + .subvendor = 0x1131, + .subdevice = 0x230f, + .driver_data = SAA7134_BOARD_ENCORE_ENLTV_FM, },{ /* --- boards without eeprom + subsystem ID --- */ .vendor = PCI_VENDOR_ID_PHILIPS, @@ -3959,6 +4037,8 @@ int saa7134_board_init1(struct saa7134_dev *dev) case SAA7134_BOARD_FLYDVBTDUO: case SAA7134_BOARD_PROTEUS_2309: case SAA7134_BOARD_AVERMEDIA_A16AR: + case SAA7134_BOARD_ENCORE_ENLTV: + case SAA7134_BOARD_ENCORE_ENLTV_FM: dev->has_remote = SAA7134_REMOTE_GPIO; break; case SAA7134_BOARD_FLYDVBS_LR300: diff --git a/drivers/media/video/saa7134/saa7134-input.c b/drivers/media/video/saa7134/saa7134-input.c index 08848ebc682f..1d0dd7e12521 100644 --- a/drivers/media/video/saa7134/saa7134-input.c +++ b/drivers/media/video/saa7134/saa7134-input.c @@ -325,6 +325,13 @@ int saa7134_input_init1(struct saa7134_dev *dev) mask_keydown = 0x0040000; rc5_gpio = 1; break; + case SAA7134_BOARD_ENCORE_ENLTV: + case SAA7134_BOARD_ENCORE_ENLTV_FM: + ir_codes = ir_codes_encore_enltv; + mask_keycode = 0x00007f; + mask_keyup = 0x040000; + polling = 50; // ms + break; } if (NULL == ir_codes) { printk("%s: Oops: IR config error [card=%d]\n", diff --git a/drivers/media/video/saa7134/saa7134.h b/drivers/media/video/saa7134/saa7134.h index 6e65fa6f2b3e..ec08412b1c66 100644 --- a/drivers/media/video/saa7134/saa7134.h +++ b/drivers/media/video/saa7134/saa7134.h @@ -233,6 +233,7 @@ struct saa7134_format { #define SAA7134_BOARD_HAUPPAUGE_HVR1110 104 #define SAA7134_BOARD_CINERGY_HT_PCMCIA 105 #define SAA7134_BOARD_ENCORE_ENLTV 106 +#define SAA7134_BOARD_ENCORE_ENLTV_FM 107 #define SAA7134_MAXBOARDS 8 #define SAA7134_INPUT_MAX 8 diff --git a/include/media/ir-common.h b/include/media/ir-common.h index c60a30997973..04201f77b85b 100644 --- a/include/media/ir-common.h +++ b/include/media/ir-common.h @@ -141,6 +141,7 @@ extern IR_KEYTAB_TYPE ir_codes_norwood[IR_KEYTAB_SIZE]; extern IR_KEYTAB_TYPE ir_codes_proteus_2309[IR_KEYTAB_SIZE]; extern IR_KEYTAB_TYPE ir_codes_budget_ci_old[IR_KEYTAB_SIZE]; extern IR_KEYTAB_TYPE ir_codes_asus_pc39[IR_KEYTAB_SIZE]; +extern IR_KEYTAB_TYPE ir_codes_encore_enltv[IR_KEYTAB_SIZE]; #endif -- cgit v1.2.3 From 19790db00bb7ff4d6621b82933afb3423586644e Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Sun, 7 Jan 2007 22:12:22 -0300 Subject: V4L/DVB (5061): Bt8xx: add support for Ultraview DVB-T Lite Ultraview DVB-T Lite is a clone of DViCO FusionHDTV DVB-T Lite Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/CARDLIST.bttv | 2 +- drivers/media/dvb/bt8xx/bt878.c | 1 + drivers/media/video/bt8xx/bttv-cards.c | 1 + sound/pci/bt87x.c | 1 + 4 files changed, 4 insertions(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/video4linux/CARDLIST.bttv b/Documentation/video4linux/CARDLIST.bttv index 4efa4645885f..fc2fe9bc6713 100644 --- a/Documentation/video4linux/CARDLIST.bttv +++ b/Documentation/video4linux/CARDLIST.bttv @@ -126,7 +126,7 @@ 125 -> MATRIX Vision Sigma-SQ 126 -> MATRIX Vision Sigma-SLC 127 -> APAC Viewcomp 878(AMAX) -128 -> DViCO FusionHDTV DVB-T Lite [18ac:db10] +128 -> DViCO FusionHDTV DVB-T Lite [18ac:db10,18ac:db11] 129 -> V-Gear MyVCD 130 -> Super TV Tuner 131 -> Tibet Systems 'Progress DVR' CS16 diff --git a/drivers/media/dvb/bt8xx/bt878.c b/drivers/media/dvb/bt8xx/bt878.c index 329a51c18562..83b090ef2445 100644 --- a/drivers/media/dvb/bt8xx/bt878.c +++ b/drivers/media/dvb/bt8xx/bt878.c @@ -390,6 +390,7 @@ static struct cards card_list[] __devinitdata = { { 0xfc00270f, BTTV_BOARD_TWINHAN_DST, "ChainTech digitop DST-1000 DVB-S" }, { 0x07711461, BTTV_BOARD_AVDVBT_771, "AVermedia AverTV DVB-T 771" }, { 0xdb1018ac, BTTV_BOARD_DVICO_DVBT_LITE, "DViCO FusionHDTV DVB-T Lite" }, + { 0xdb1118ac, BTTV_BOARD_DVICO_DVBT_LITE, "Ultraview DVB-T Lite" }, { 0xd50018ac, BTTV_BOARD_DVICO_FUSIONHDTV_5_LITE, "DViCO FusionHDTV 5 Lite" }, { 0x20007063, BTTV_BOARD_PC_HDTV, "pcHDTV HD-2000 TV" }, { 0x00261822, BTTV_BOARD_TWINHAN_DST, "DNTV Live! Mini" }, diff --git a/drivers/media/video/bt8xx/bttv-cards.c b/drivers/media/video/bt8xx/bttv-cards.c index 3b653c924730..6addc42df045 100644 --- a/drivers/media/video/bt8xx/bttv-cards.c +++ b/drivers/media/video/bt8xx/bttv-cards.c @@ -307,6 +307,7 @@ static struct CARD { { 0x07711461, BTTV_BOARD_AVDVBT_771, "AVermedia AverTV DVB-T 771" }, { 0x07611461, BTTV_BOARD_AVDVBT_761, "AverMedia AverTV DVB-T 761" }, { 0xdb1018ac, BTTV_BOARD_DVICO_DVBT_LITE, "DViCO FusionHDTV DVB-T Lite" }, + { 0xdb1118ac, BTTV_BOARD_DVICO_DVBT_LITE, "Ultraview DVB-T Lite" }, { 0xd50018ac, BTTV_BOARD_DVICO_FUSIONHDTV_5_LITE, "DViCO FusionHDTV 5 Lite" }, { 0x00261822, BTTV_BOARD_TWINHAN_DST, "DNTV Live! Mini "}, diff --git a/sound/pci/bt87x.c b/sound/pci/bt87x.c index c3f3da211234..e9b029e1cd6d 100644 --- a/sound/pci/bt87x.c +++ b/sound/pci/bt87x.c @@ -804,6 +804,7 @@ static struct { {0x1822, 0x0001}, /* Twinhan VisionPlus DVB-T */ {0x18ac, 0xd500}, /* DVICO FusionHDTV 5 Lite */ {0x18ac, 0xdb10}, /* DVICO FusionHDTV DVB-T Lite */ + {0x18ac, 0xdb11}, /* Ultraview DVB-T Lite */ {0x270f, 0xfc00}, /* Chaintech Digitop DST-1000 DVB-S */ {0x7063, 0x2000}, /* pcHDTV HD-2000 TV */ }; -- cgit v1.2.3 From f327ebbd004fb2f08291ca4c6637f5f27319683c Mon Sep 17 00:00:00 2001 From: Luca Risolia Date: Mon, 8 Jan 2007 10:43:56 -0300 Subject: V4L/DVB (5062): SN9C102 driver updates - Add support for SN9C105 and SN9C120 - Add some more USB device identifiers - Add support for OV7660 - Implement audio ioctl's and VIDIOC_ENUM_FRAMESIZES - Add preliminary support for 0x0c45/0x6007 - Documentation updates - Generic improvements Signed-off-by: Luca Risolia Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/sn9c102.txt | 246 +++++--- drivers/media/video/sn9c102/Kconfig | 4 +- drivers/media/video/sn9c102/Makefile | 2 +- drivers/media/video/sn9c102/sn9c102.h | 57 +- drivers/media/video/sn9c102/sn9c102_config.h | 86 +++ drivers/media/video/sn9c102/sn9c102_core.c | 719 ++++++++++++++++++----- drivers/media/video/sn9c102/sn9c102_devtable.h | 142 +++++ drivers/media/video/sn9c102/sn9c102_hv7131d.c | 7 +- drivers/media/video/sn9c102/sn9c102_mi0343.c | 7 +- drivers/media/video/sn9c102/sn9c102_ov7630.c | 336 ++++++----- drivers/media/video/sn9c102/sn9c102_ov7660.c | 592 +++++++++++++++++++ drivers/media/video/sn9c102/sn9c102_pas106b.c | 7 +- drivers/media/video/sn9c102/sn9c102_pas202bca.c | 238 -------- drivers/media/video/sn9c102/sn9c102_pas202bcb.c | 97 ++- drivers/media/video/sn9c102/sn9c102_sensor.h | 168 ++---- drivers/media/video/sn9c102/sn9c102_tas5110c1b.c | 8 +- drivers/media/video/sn9c102/sn9c102_tas5130d1b.c | 8 +- 17 files changed, 1894 insertions(+), 830 deletions(-) create mode 100644 drivers/media/video/sn9c102/sn9c102_config.h create mode 100644 drivers/media/video/sn9c102/sn9c102_devtable.h create mode 100644 drivers/media/video/sn9c102/sn9c102_ov7660.c delete mode 100644 drivers/media/video/sn9c102/sn9c102_pas202bca.c (limited to 'Documentation') diff --git a/Documentation/video4linux/sn9c102.txt b/Documentation/video4linux/sn9c102.txt index 8cda472db36d..2913da3d0878 100644 --- a/Documentation/video4linux/sn9c102.txt +++ b/Documentation/video4linux/sn9c102.txt @@ -1,5 +1,5 @@ - SN9C10x PC Camera Controllers + SN9C1xx PC Camera Controllers Driver for Linux ============================= @@ -53,20 +53,14 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 4. Overview and features ======================== -This driver attempts to support the video interface of the devices mounting the -SONiX SN9C101, SN9C102 and SN9C103 PC Camera Controllers. - -It's worth to note that SONiX has never collaborated with the author during the -development of this project, despite several requests for enough detailed -specifications of the register tables, compression engine and video data format -of the above chips. Nevertheless, these informations are no longer necessary, -because all the aspects related to these chips are known and have been -described in detail in this documentation. +This driver attempts to support the video interface of the devices assembling +the SONiX SN9C101, SN9C102, SN9C103, SN9C105 and SN9C120 PC Camera Controllers +("SN9C1xx" from now on). The driver relies on the Video4Linux2 and USB core modules. It has been designed to run properly on SMP systems as well. -The latest version of the SN9C10x driver can be found at the following URL: +The latest version of the SN9C1xx driver can be found at the following URL: http://www.linux-projects.org/ Some of the features of the driver are: @@ -85,11 +79,11 @@ Some of the features of the driver are: high compression quality (see also "Notes for V4L2 application developers" and "Video frame formats" paragraphs); - full support for the capabilities of many of the possible image sensors that - can be connected to the SN9C10x bridges, including, for instance, red, green, + can be connected to the SN9C1xx bridges, including, for instance, red, green, blue and global gain adjustments and exposure (see "Supported devices" paragraph for details); - use of default color settings for sunlight conditions; -- dynamic I/O interface for both SN9C10x and image sensor control and +- dynamic I/O interface for both SN9C1xx and image sensor control and monitoring (see "Optional device control through 'sysfs'" paragraph); - dynamic driver control thanks to various module parameters (see "Module parameters" paragraph); @@ -130,8 +124,8 @@ necessary: CONFIG_USB_UHCI_HCD=m CONFIG_USB_OHCI_HCD=m -The SN9C103 controller also provides a built-in microphone interface. It is -supported by the USB Audio driver thanks to the ALSA API: +The SN9C103, SN9c105 and SN9C120 controllers also provide a built-in microphone +interface. It is supported by the USB Audio driver thanks to the ALSA API: # Sound # @@ -155,18 +149,27 @@ And finally: 6. Module loading ================= To use the driver, it is necessary to load the "sn9c102" module into memory -after every other module required: "videodev", "usbcore" and, depending on -the USB host controller you have, "ehci-hcd", "uhci-hcd" or "ohci-hcd". +after every other module required: "videodev", "v4l2_common", "compat_ioctl32", +"usbcore" and, depending on the USB host controller you have, "ehci-hcd", +"uhci-hcd" or "ohci-hcd". Loading can be done as shown below: [root@localhost home]# modprobe sn9c102 -At this point the devices should be recognized. You can invoke "dmesg" to -analyze kernel messages and verify that the loading process has gone well: +Note that the module is called "sn9c102" for historic reasons, althought it +does not just support the SN9C102. + +At this point all the devices supported by the driver and connected to the USB +ports should be recognized. You can invoke "dmesg" to analyze kernel messages +and verify that the loading process has gone well: [user@localhost home]$ dmesg +or, to isolate all the kernel messages generated by the driver: + + [user@localhost home]$ dmesg | grep sn9c102 + 7. Module parameters ==================== @@ -198,10 +201,11 @@ Default: 0 ------------------------------------------------------------------------------- Name: frame_timeout Type: uint array (min = 0, max = 64) -Syntax: -Description: Timeout for a video frame in seconds. This parameter is - specific for each detected camera. This parameter can be - changed at runtime thanks to the /sys filesystem interface. +Syntax: <0|n[,...]> +Description: Timeout for a video frame in seconds before returning an I/O + error; 0 for infinity. This parameter is specific for each + detected camera and can be changed at runtime thanks to the + /sys filesystem interface. Default: 2 ------------------------------------------------------------------------------- Name: debug @@ -223,20 +227,21 @@ Default: 2 8. Optional device control through "sysfs" [1] ========================================== If the kernel has been compiled with the CONFIG_VIDEO_ADV_DEBUG option enabled, -it is possible to read and write both the SN9C10x and the image sensor +it is possible to read and write both the SN9C1xx and the image sensor registers by using the "sysfs" filesystem interface. Every time a supported device is recognized, a write-only file named "green" is created in the /sys/class/video4linux/videoX directory. You can set the green channel's gain by writing the desired value to it. The value may range from 0 -to 15 for SN9C101 or SN9C102 bridges, from 0 to 127 for SN9C103 bridges. -Similarly, only for SN9C103 controllers, blue and red gain control files are -available in the same directory, for which accepted values may range from 0 to -127. +to 15 for the SN9C101 or SN9C102 bridges, from 0 to 127 for the SN9C103, +SN9C105 and SN9C120 bridges. +Similarly, only for the SN9C103, SN9C105 and SN9120 controllers, blue and red +gain control files are available in the same directory, for which accepted +values may range from 0 to 127. There are other four entries in the directory above for each registered camera: "reg", "val", "i2c_reg" and "i2c_val". The first two files control the -SN9C10x bridge, while the other two control the sensor chip. "reg" and +SN9C1xx bridge, while the other two control the sensor chip. "reg" and "i2c_reg" hold the values of the current register index where the following reading/writing operations are addressed at through "val" and "i2c_val". Their use is not intended for end-users. Note that "i2c_reg" and "i2c_val" will not @@ -259,61 +264,84 @@ Now let's set the green gain's register of the SN9C101 or SN9C102 chips to 2: [root@localhost #] echo 0x11 > reg [root@localhost #] echo 2 > val -Note that the SN9C10x always returns 0 when some of its registers are read. +Note that the SN9C1xx always returns 0 when some of its registers are read. To avoid race conditions, all the I/O accesses to the above files are serialized. - The sysfs interface also provides the "frame_header" entry, which exports the frame header of the most recent requested and captured video frame. The header -is always 18-bytes long and is appended to every video frame by the SN9C10x +is always 18-bytes long and is appended to every video frame by the SN9C1xx controllers. As an example, this additional information can be used by the user application for implementing auto-exposure features via software. -The following table describes the frame header: - -Byte # Value Description ------- ----- ----------- -0x00 0xFF Frame synchronisation pattern. -0x01 0xFF Frame synchronisation pattern. -0x02 0x00 Frame synchronisation pattern. -0x03 0xC4 Frame synchronisation pattern. -0x04 0xC4 Frame synchronisation pattern. -0x05 0x96 Frame synchronisation pattern. -0x06 0xXX Unknown meaning. The exact value depends on the chip; - possible values are 0x00, 0x01 and 0x20. -0x07 0xXX Variable value, whose bits are ff00uzzc, where ff is a - frame counter, u is unknown, zz is a size indicator - (00 = VGA, 01 = SIF, 10 = QSIF) and c stands for - "compression enabled" (1 = yes, 0 = no). -0x08 0xXX Brightness sum inside Auto-Exposure area (low-byte). -0x09 0xXX Brightness sum inside Auto-Exposure area (high-byte). - For a pure white image, this number will be equal to 500 - times the area of the specified AE area. For images - that are not pure white, the value scales down according - to relative whiteness. -0x0A 0xXX Brightness sum outside Auto-Exposure area (low-byte). -0x0B 0xXX Brightness sum outside Auto-Exposure area (high-byte). - For a pure white image, this number will be equal to 125 - times the area outside of the specified AE area. For - images that are not pure white, the value scales down - according to relative whiteness. - according to relative whiteness. - -The following bytes are used by the SN9C103 bridge only: - -0x0C 0xXX Unknown meaning -0x0D 0xXX Unknown meaning -0x0E 0xXX Unknown meaning -0x0F 0xXX Unknown meaning -0x10 0xXX Unknown meaning -0x11 0xXX Unknown meaning +The following table describes the frame header exported by the SN9C101 and +SN9C102: + +Byte # Value or bits Description +------ ------------- ----------- +0x00 0xFF Frame synchronisation pattern +0x01 0xFF Frame synchronisation pattern +0x02 0x00 Frame synchronisation pattern +0x03 0xC4 Frame synchronisation pattern +0x04 0xC4 Frame synchronisation pattern +0x05 0x96 Frame synchronisation pattern +0x06 [3:0] Read channel gain control = (1+R_GAIN/8) + [7:4] Blue channel gain control = (1+B_GAIN/8) +0x07 [ 0 ] Compression mode. 0=No compression, 1=Compression enabled + [2:1] Maximum scale factor for compression + [ 3 ] 1 = USB fifo(2K bytes) is full + [ 4 ] 1 = Digital gain is finish + [ 5 ] 1 = Exposure is finish + [7:6] Frame index +0x08 [7:0] Y sum inside Auto-Exposure area (low-byte) +0x09 [7:0] Y sum inside Auto-Exposure area (high-byte) + where Y sum = (R/4 + 5G/16 + B/8) / 32 +0x0A [7:0] Y sum outside Auto-Exposure area (low-byte) +0x0B [7:0] Y sum outside Auto-Exposure area (high-byte) + where Y sum = (R/4 + 5G/16 + B/8) / 128 +0x0C 0xXX Not used +0x0D 0xXX Not used +0x0E 0xXX Not used +0x0F 0xXX Not used +0x10 0xXX Not used +0x11 0xXX Not used + +The following table describes the frame header exported by the SN9C103: + +Byte # Value or bits Description +------ ------------- ----------- +0x00 0xFF Frame synchronisation pattern +0x01 0xFF Frame synchronisation pattern +0x02 0x00 Frame synchronisation pattern +0x03 0xC4 Frame synchronisation pattern +0x04 0xC4 Frame synchronisation pattern +0x05 0x96 Frame synchronisation pattern +0x06 [6:0] Read channel gain control = (1/2+R_GAIN/64) +0x07 [6:0] Blue channel gain control = (1/2+B_GAIN/64) + [7:4] +0x08 [ 0 ] Compression mode. 0=No compression, 1=Compression enabled + [2:1] Maximum scale factor for compression + [ 3 ] 1 = USB fifo(2K bytes) is full + [ 4 ] 1 = Digital gain is finish + [ 5 ] 1 = Exposure is finish + [7:6] Frame index +0x09 [7:0] Y sum inside Auto-Exposure area (low-byte) +0x0A [7:0] Y sum inside Auto-Exposure area (high-byte) + where Y sum = (R/4 + 5G/16 + B/8) / 32 +0x0B [7:0] Y sum outside Auto-Exposure area (low-byte) +0x0C [7:0] Y sum outside Auto-Exposure area (high-byte) + where Y sum = (R/4 + 5G/16 + B/8) / 128 +0x0D [1:0] Audio frame number + [ 2 ] 1 = Audio is recording +0x0E [7:0] Audio summation (low-byte) +0x0F [7:0] Audio summation (high-byte) +0x10 [7:0] Audio sample count +0x11 [7:0] Audio peak data in audio frame The AE area (sx, sy, ex, ey) in the active window can be set by programming the -registers 0x1c, 0x1d, 0x1e and 0x1f of the SN9C10x controllers, where one unit +registers 0x1c, 0x1d, 0x1e and 0x1f of the SN9C1xx controllers, where one unit corresponds to 32 pixels. -[1] Part of the meaning of the frame header has been documented by Bertrik - Sikken. +[1] The frame headers exported by the SN9C105 and SN9C120 are not described. 9. Supported devices @@ -323,15 +351,19 @@ here. They have never collaborated with the author, so no advertising. From the point of view of a driver, what unambiguously identify a device are its vendor and product USB identifiers. Below is a list of known identifiers of -devices mounting the SN9C10x PC camera controllers: +devices assembling the SN9C1xx PC camera controllers: Vendor ID Product ID --------- ---------- +0x0471 0x0327 +0x0471 0x0328 0x0c45 0x6001 0x0c45 0x6005 0x0c45 0x6007 0x0c45 0x6009 0x0c45 0x600d +0x0c45 0x6011 +0x0c45 0x6019 0x0c45 0x6024 0x0c45 0x6025 0x0c45 0x6028 @@ -342,6 +374,7 @@ Vendor ID Product ID 0x0c45 0x602d 0x0c45 0x602e 0x0c45 0x6030 +0x0c45 0x603f 0x0c45 0x6080 0x0c45 0x6082 0x0c45 0x6083 @@ -368,24 +401,40 @@ Vendor ID Product ID 0x0c45 0x60bb 0x0c45 0x60bc 0x0c45 0x60be +0x0c45 0x60c0 +0x0c45 0x60c8 +0x0c45 0x60cc +0x0c45 0x60ea +0x0c45 0x60ec +0x0c45 0x60fa +0x0c45 0x60fb +0x0c45 0x60fc +0x0c45 0x60fe +0x0c45 0x6130 +0x0c45 0x613a +0x0c45 0x613b +0x0c45 0x613c +0x0c45 0x613e The list above does not imply that all those devices work with this driver: up -until now only the ones that mount the following image sensors are supported; -kernel messages will always tell you whether this is the case: +until now only the ones that assemble the following image sensors are +supported; kernel messages will always tell you whether this is the case (see +"Module loading" paragraph): Model Manufacturer ----- ------------ HV7131D Hynix Semiconductor, Inc. MI-0343 Micron Technology, Inc. OV7630 OmniVision Technologies, Inc. +OV7660 OmniVision Technologies, Inc. PAS106B PixArt Imaging, Inc. PAS202BCA PixArt Imaging, Inc. PAS202BCB PixArt Imaging, Inc. TAS5110C1B Taiwan Advanced Sensor Corporation TAS5130D1B Taiwan Advanced Sensor Corporation -All the available control settings of each image sensor are supported through -the V4L2 interface. +Some of the available control settings of each image sensor are supported +through the V4L2 interface. Donations of new models for further testing and support would be much appreciated. Non-available hardware will not be supported by the author of this @@ -429,12 +478,15 @@ supplied by this driver). 11. Video frame formats [1] ======================= -The SN9C10x PC Camera Controllers can send images in two possible video -formats over the USB: either native "Sequential RGB Bayer" or Huffman -compressed. The latter is used to achieve high frame rates. The current video -format may be selected or queried from the user application by calling the -VIDIOC_S_FMT or VIDIOC_G_FMT ioctl's, as described in the V4L2 API -specifications. +The SN9C1xx PC Camera Controllers can send images in two possible video +formats over the USB: either native "Sequential RGB Bayer" or compressed. +The compression is used to achieve high frame rates. With regard to the +SN9C101, SN9C102 and SN9C103, the compression is based on the Huffman encoding +algorithm described below, while the SN9C105 and SN9C120 the compression is +based on the JPEG standard. +The current video format may be selected or queried from the user application +by calling the VIDIOC_S_FMT or VIDIOC_G_FMT ioctl's, as described in the V4L2 +API specifications. The name "Sequential Bayer" indicates the organization of the red, green and blue pixels in one video frame. Each pixel is associated with a 8-bit long @@ -447,14 +499,14 @@ G[m] R[m+1] G[m+2] R[m+2] ... G[2m-2] R[2m-1] ... G[n(m-2)] R[n(m-1)] The above matrix also represents the sequential or progressive read-out mode of -the (n, m) Bayer color filter array used in many CCD/CMOS image sensors. +the (n, m) Bayer color filter array used in many CCD or CMOS image sensors. -One compressed video frame consists of a bitstream that encodes for every R, G, -or B pixel the difference between the value of the pixel itself and some -reference pixel value. Pixels are organised in the Bayer pattern and the Bayer -sub-pixels are tracked individually and alternatingly. For example, in the -first line values for the B and G1 pixels are alternatingly encoded, while in -the second line values for the G2 and R pixels are alternatingly encoded. +The Huffman compressed video frame consists of a bitstream that encodes for +every R, G, or B pixel the difference between the value of the pixel itself and +some reference pixel value. Pixels are organised in the Bayer pattern and the +Bayer sub-pixels are tracked individually and alternatingly. For example, in +the first line values for the B and G1 pixels are alternatingly encoded, while +in the second line values for the G2 and R pixels are alternatingly encoded. The pixel reference value is calculated as follows: - the 4 top left pixels are encoded in raw uncompressed 8-bit format; @@ -470,8 +522,9 @@ The pixel reference value is calculated as follows: decoding. The algorithm purely describes the conversion from compressed Bayer code used -in the SN9C10x chips to uncompressed Bayer. Additional steps are required to -convert this to a color image (i.e. a color interpolation algorithm). +in the SN9C101, SN9C102 and SN9C103 chips to uncompressed Bayer. Additional +steps are required to convert this to a color image (i.e. a color interpolation +algorithm). The following Huffman codes have been found: 0: +0 (relative to reference pixel value) @@ -506,13 +559,18 @@ order): - Philippe Coval for having helped testing the PAS202BCA image sensor; - Joao Rodrigo Fuzaro, Joao Limirio, Claudio Filho and Caio Begotti for the donation of a webcam; +- Dennis Heitmann for the donation of a webcam; - Jon Hollstrom for the donation of a webcam; +- Nick McGill for the donation of a webcam; - Carlos Eduardo Medaglia Dyonisio, who added the support for the PAS202BCB image sensor; - Stefano Mozzi, who donated 45 EU; - Andrew Pearce for the donation of a webcam; +- John Pullan for the donation of a webcam; - Bertrik Sikken, who reverse-engineered and documented the Huffman compression - algorithm used in the SN9C10x controllers and implemented the first decoder; + algorithm used in the SN9C101, SN9C102 and SN9C103 controllers and + implemented the first decoder; - Mizuno Takafumi for the donation of a webcam; - an "anonymous" donator (who didn't want his name to be revealed) for the donation of a webcam. +- an anonymous donator for the donation of four webcams. diff --git a/drivers/media/video/sn9c102/Kconfig b/drivers/media/video/sn9c102/Kconfig index cf552e6b8ecf..1a7ccb666ab0 100644 --- a/drivers/media/video/sn9c102/Kconfig +++ b/drivers/media/video/sn9c102/Kconfig @@ -1,9 +1,9 @@ config USB_SN9C102 - tristate "USB SN9C10x PC Camera Controller support" + tristate "USB SN9C1xx PC Camera Controller support" depends on USB && VIDEO_V4L1 ---help--- Say Y here if you want support for cameras based on SONiX SN9C101, - SN9C102 or SN9C103 PC Camera Controllers. + SN9C102, SN9C103, SN9C105 and SN9C120 PC Camera Controllers. See for more info. diff --git a/drivers/media/video/sn9c102/Makefile b/drivers/media/video/sn9c102/Makefile index 536ad3098da4..30e3dfe537fe 100644 --- a/drivers/media/video/sn9c102/Makefile +++ b/drivers/media/video/sn9c102/Makefile @@ -1,5 +1,5 @@ sn9c102-objs := sn9c102_core.o sn9c102_hv7131d.o sn9c102_mi0343.o \ - sn9c102_ov7630.o sn9c102_pas106b.o sn9c102_pas202bca.o \ + sn9c102_ov7630.o sn9c102_ov7660.o sn9c102_pas106b.o \ sn9c102_pas202bcb.o sn9c102_tas5110c1b.o \ sn9c102_tas5130d1b.o diff --git a/drivers/media/video/sn9c102/sn9c102.h b/drivers/media/video/sn9c102/sn9c102.h index 2c6ff396dafc..5428f34e7c5b 100644 --- a/drivers/media/video/sn9c102/sn9c102.h +++ b/drivers/media/video/sn9c102/sn9c102.h @@ -1,5 +1,5 @@ /*************************************************************************** - * V4L2 driver for SN9C10x PC Camera Controllers * + * V4L2 driver for SN9C1xx PC Camera Controllers * * * * Copyright (C) 2004-2006 by Luca Risolia * * * @@ -37,33 +37,10 @@ #include #include +#include "sn9c102_config.h" #include "sn9c102_sensor.h" +#include "sn9c102_devtable.h" -/*****************************************************************************/ - -#define SN9C102_DEBUG -#define SN9C102_DEBUG_LEVEL 2 -#define SN9C102_MAX_DEVICES 64 -#define SN9C102_PRESERVE_IMGSCALE 0 -#define SN9C102_FORCE_MUNMAP 0 -#define SN9C102_MAX_FRAMES 32 -#define SN9C102_URBS 2 -#define SN9C102_ISO_PACKETS 7 -#define SN9C102_ALTERNATE_SETTING 8 -#define SN9C102_URB_TIMEOUT msecs_to_jiffies(2 * SN9C102_ISO_PACKETS) -#define SN9C102_CTRL_TIMEOUT 300 -#define SN9C102_FRAME_TIMEOUT 2 - -/*****************************************************************************/ - -enum sn9c102_bridge { - BRIDGE_SN9C101 = 0x01, - BRIDGE_SN9C102 = 0x02, - BRIDGE_SN9C103 = 0x04, -}; - -SN9C102_ID_TABLE -SN9C102_SENSOR_TABLE enum sn9c102_frame_state { F_UNUSED, @@ -99,13 +76,11 @@ enum sn9c102_stream_state { STREAM_ON, }; -typedef char sn9c103_sof_header_t[18]; -typedef char sn9c102_sof_header_t[12]; -typedef char sn9c102_eof_header_t[4]; +typedef char sn9c102_sof_header_t[62]; struct sn9c102_sysfs_attr { u8 reg, i2c_reg; - sn9c103_sof_header_t frame_header; + sn9c102_sof_header_t frame_header; }; struct sn9c102_module_param { @@ -137,8 +112,8 @@ struct sn9c102_device { struct v4l2_jpegcompression compression; struct sn9c102_sysfs_attr sysfs; - sn9c103_sof_header_t sof_header; - u16 reg[63]; + sn9c102_sof_header_t sof_header; + u16 reg[384]; struct sn9c102_module_param module_param; @@ -155,10 +130,7 @@ struct sn9c102_device { struct sn9c102_device* sn9c102_match_id(struct sn9c102_device* cam, const struct usb_device_id *id) { - if (usb_match_id(usb_ifnum_to_if(cam->usbdev, 0), id)) - return cam; - - return NULL; + return usb_match_id(usb_ifnum_to_if(cam->usbdev, 0), id) ? cam : NULL; } @@ -169,6 +141,19 @@ sn9c102_attach_sensor(struct sn9c102_device* cam, memcpy(&cam->sensor, sensor, sizeof(struct sn9c102_sensor)); } + +enum sn9c102_bridge +sn9c102_get_bridge(struct sn9c102_device* cam) +{ + return cam->bridge; +} + + +struct sn9c102_sensor* sn9c102_get_sensor(struct sn9c102_device* cam) +{ + return &cam->sensor; +} + /*****************************************************************************/ #undef DBG diff --git a/drivers/media/video/sn9c102/sn9c102_config.h b/drivers/media/video/sn9c102/sn9c102_config.h new file mode 100644 index 000000000000..0f4e0378b071 --- /dev/null +++ b/drivers/media/video/sn9c102/sn9c102_config.h @@ -0,0 +1,86 @@ +/*************************************************************************** + * Global parameters for the V4L2 driver for SN9C1xx PC Camera Controllers * + * * + * Copyright (C) 2007 by Luca Risolia * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * + ***************************************************************************/ + +#ifndef _SN9C102_CONFIG_H_ +#define _SN9C102_CONFIG_H_ + +#include +#include + +#define SN9C102_DEBUG +#define SN9C102_DEBUG_LEVEL 2 +#define SN9C102_MAX_DEVICES 64 +#define SN9C102_PRESERVE_IMGSCALE 0 +#define SN9C102_FORCE_MUNMAP 0 +#define SN9C102_MAX_FRAMES 32 +#define SN9C102_URBS 2 +#define SN9C102_ISO_PACKETS 7 +#define SN9C102_ALTERNATE_SETTING 8 +#define SN9C102_URB_TIMEOUT msecs_to_jiffies(2 * SN9C102_ISO_PACKETS) +#define SN9C102_CTRL_TIMEOUT 300 +#define SN9C102_FRAME_TIMEOUT 0 + +/*****************************************************************************/ + +static const u8 SN9C102_Y_QTABLE0[64] = { + 8, 5, 5, 8, 12, 20, 25, 30, + 6, 6, 7, 9, 13, 29, 30, 27, + 7, 6, 8, 12, 20, 28, 34, 28, + 7, 8, 11, 14, 25, 43, 40, 31, + 9, 11, 18, 28, 34, 54, 51, 38, + 12, 17, 27, 32, 40, 52, 56, 46, + 24, 32, 39, 43, 51, 60, 60, 50, + 36, 46, 47, 49, 56, 50, 51, 49 +}; + +static const u8 SN9C102_UV_QTABLE0[64] = { + 8, 9, 12, 23, 49, 49, 49, 49, + 9, 10, 13, 33, 49, 49, 49, 49, + 12, 13, 28, 49, 49, 49, 49, 49, + 23, 33, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49 +}; + +static const u8 SN9C102_Y_QTABLE1[64] = { + 16, 11, 10, 16, 24, 40, 51, 61, + 12, 12, 14, 19, 26, 58, 60, 55, + 14, 13, 16, 24, 40, 57, 69, 56, + 14, 17, 22, 29, 51, 87, 80, 62, + 18, 22, 37, 56, 68, 109, 103, 77, + 24, 35, 55, 64, 81, 104, 113, 92, + 49, 64, 78, 87, 103, 121, 120, 101, + 72, 92, 95, 98, 112, 100, 103, 99 +}; + +static const u8 SN9C102_UV_QTABLE1[64] = { + 17, 18, 24, 47, 99, 99, 99, 99, + 18, 21, 26, 66, 99, 99, 99, 99, + 24, 26, 56, 99, 99, 99, 99, 99, + 47, 66, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99 +}; + +#endif /* _SN9C102_CONFIG_H_ */ diff --git a/drivers/media/video/sn9c102/sn9c102_core.c b/drivers/media/video/sn9c102/sn9c102_core.c index 04d4c8f28b89..d0e2b40a7725 100644 --- a/drivers/media/video/sn9c102/sn9c102_core.c +++ b/drivers/media/video/sn9c102/sn9c102_core.c @@ -1,7 +1,7 @@ /*************************************************************************** - * V4L2 driver for SN9C10x PC Camera Controllers * + * V4L2 driver for SN9C1xx PC Camera Controllers * * * - * Copyright (C) 2004-2006 by Luca Risolia * + * Copyright (C) 2004-2007 by Luca Risolia * * * * 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,12 +43,12 @@ /*****************************************************************************/ -#define SN9C102_MODULE_NAME "V4L2 driver for SN9C10x PC Camera Controllers" +#define SN9C102_MODULE_NAME "V4L2 driver for SN9C1xx PC Camera Controllers" #define SN9C102_MODULE_AUTHOR "(C) 2004-2006 Luca Risolia" #define SN9C102_AUTHOR_EMAIL "" #define SN9C102_MODULE_LICENSE "GPL" -#define SN9C102_MODULE_VERSION "1:1.27" -#define SN9C102_MODULE_VERSION_CODE KERNEL_VERSION(1, 0, 27) +#define SN9C102_MODULE_VERSION "1:1.34" +#define SN9C102_MODULE_VERSION_CODE KERNEL_VERSION(1, 1, 34) /*****************************************************************************/ @@ -91,7 +91,8 @@ static unsigned int frame_timeout[] = {[0 ... SN9C102_MAX_DEVICES-1] = SN9C102_FRAME_TIMEOUT}; module_param_array(frame_timeout, uint, NULL, 0644); MODULE_PARM_DESC(frame_timeout, - "\n Timeout for a video frame in seconds." + "\n<0|n[,...]> Timeout for a video frame in seconds before" + "\nreturning an I/O error; 0 for infinity." "\nThis parameter is specific for each detected camera." "\nDefault value is "__MODULE_STRING(SN9C102_FRAME_TIMEOUT)"." "\n"); @@ -113,32 +114,13 @@ MODULE_PARM_DESC(debug, /*****************************************************************************/ -static sn9c102_sof_header_t sn9c102_sof_header[] = { - {0xff, 0xff, 0x00, 0xc4, 0xc4, 0x96, 0x00}, - {0xff, 0xff, 0x00, 0xc4, 0xc4, 0x96, 0x01}, -}; - -static sn9c103_sof_header_t sn9c103_sof_header[] = { - {0xff, 0xff, 0x00, 0xc4, 0xc4, 0x96, 0x20}, -}; - -static sn9c102_eof_header_t sn9c102_eof_header[] = { - {0x00, 0x00, 0x00, 0x00}, - {0x40, 0x00, 0x00, 0x00}, - {0x80, 0x00, 0x00, 0x00}, - {0xc0, 0x00, 0x00, 0x00}, -}; - -/*****************************************************************************/ - static u32 sn9c102_request_buffers(struct sn9c102_device* cam, u32 count, enum sn9c102_io_method io) { struct v4l2_pix_format* p = &(cam->sensor.pix_format); struct v4l2_rect* r = &(cam->sensor.cropcap.bounds); - const size_t imagesize = cam->module_param.force_munmap || - io == IO_READ ? + size_t imagesize = cam->module_param.force_munmap || io == IO_READ ? (p->width * p->height * p->priv) / 8 : (r->width * r->height * p->priv) / 8; void* buff = NULL; @@ -147,9 +129,13 @@ sn9c102_request_buffers(struct sn9c102_device* cam, u32 count, if (count > SN9C102_MAX_FRAMES) count = SN9C102_MAX_FRAMES; + if (cam->bridge == BRIDGE_SN9C105 || cam->bridge == BRIDGE_SN9C120) + imagesize += 589 + 2; /* length of JPEG header + EOI marker */ + cam->nbuffers = count; while (cam->nbuffers > 0) { - if ((buff = vmalloc_32(cam->nbuffers * PAGE_ALIGN(imagesize)))) + if ((buff = vmalloc_32_user(cam->nbuffers * + PAGE_ALIGN(imagesize)))) break; cam->nbuffers--; } @@ -322,9 +308,21 @@ static int sn9c102_i2c_detect_read_error(struct sn9c102_device* cam, struct sn9c102_sensor* sensor) { - int r; + int r , err = 0; + r = sn9c102_read_reg(cam, 0x08); - return (r < 0 || (r >= 0 && !(r & 0x08))) ? -EIO : 0; + if (r < 0) + err += r; + + if (cam->bridge == BRIDGE_SN9C101 || cam->bridge == BRIDGE_SN9C102) { + if (!(r & 0x08)) + err += -1; + } else { + if (r & 0x08) + err += -1; + } + + return err ? -EIO : 0; } @@ -415,7 +413,7 @@ sn9c102_i2c_try_raw_write(struct sn9c102_device* cam, data[4] = data3; data[5] = data4; data[6] = data5; - data[7] = 0x14; + data[7] = 0x17; res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x08, 0x41, 0x08, 0, data, 8, SN9C102_CTRL_TIMEOUT); if (res < 0) @@ -467,31 +465,35 @@ int sn9c102_i2c_write(struct sn9c102_device* cam, u8 address, u8 value) /*****************************************************************************/ -static void* -sn9c102_find_sof_header(struct sn9c102_device* cam, void* mem, size_t len) +static size_t sn9c102_sof_length(struct sn9c102_device* cam) { - size_t soflen = 0, i; - u8 j, n = 0; - switch (cam->bridge) { case BRIDGE_SN9C101: case BRIDGE_SN9C102: - soflen = sizeof(sn9c102_sof_header_t); - n = sizeof(sn9c102_sof_header) / soflen; - break; + return 12; case BRIDGE_SN9C103: - soflen = sizeof(sn9c103_sof_header_t); - n = sizeof(sn9c103_sof_header) / soflen; + return 18; + case BRIDGE_SN9C105: + case BRIDGE_SN9C120: + return 62; } + return 0; +} + + +static void* +sn9c102_find_sof_header(struct sn9c102_device* cam, void* mem, size_t len) +{ + char sof_header[6] = {0xff, 0xff, 0x00, 0xc4, 0xc4, 0x96}; + size_t soflen = 0, i; + + soflen = sn9c102_sof_length(cam); + for (i = 0; (len >= soflen) && (i <= len - soflen); i++) - for (j = 0; j < n; j++) - /* The invariable part of the header is 6 bytes long */ - if ((cam->bridge != BRIDGE_SN9C103 && - !memcmp(mem + i, sn9c102_sof_header[j], 6)) || - (cam->bridge == BRIDGE_SN9C103 && - !memcmp(mem + i, sn9c103_sof_header[j], 6))) { - memcpy(cam->sof_header, mem + i, soflen); + if (!memcmp(mem + i, sof_header, sizeof(sof_header))) { + memcpy(cam->sof_header, mem + i, + sizeof(sn9c102_sof_header_t)); /* Skip the header */ return mem + i + soflen; } @@ -503,21 +505,123 @@ sn9c102_find_sof_header(struct sn9c102_device* cam, void* mem, size_t len) static void* sn9c102_find_eof_header(struct sn9c102_device* cam, void* mem, size_t len) { - size_t eoflen = sizeof(sn9c102_eof_header_t), i; - unsigned j, n = sizeof(sn9c102_eof_header) / eoflen; + char eof_header[4][4] = { + {0x00, 0x00, 0x00, 0x00}, + {0x40, 0x00, 0x00, 0x00}, + {0x80, 0x00, 0x00, 0x00}, + {0xc0, 0x00, 0x00, 0x00}, + }; + size_t i, j; - if (cam->sensor.pix_format.pixelformat == V4L2_PIX_FMT_SN9C10X) + if (cam->sensor.pix_format.pixelformat == V4L2_PIX_FMT_SN9C10X || + cam->sensor.pix_format.pixelformat == V4L2_PIX_FMT_JPEG) return NULL; /* EOF header does not exist in compressed data */ - for (i = 0; (len >= eoflen) && (i <= len - eoflen); i++) - for (j = 0; j < n; j++) - if (!memcmp(mem + i, sn9c102_eof_header[j], eoflen)) + for (i = 0; (len >= 4) && (i <= len - 4); i++) + for (j = 0; j < ARRAY_SIZE(eof_header); j++) + if (!memcmp(mem + i, eof_header[j], 4)) return mem + i; return NULL; } +static void +sn9c102_write_jpegheader(struct sn9c102_device* cam, struct sn9c102_frame_t* f) +{ + static u8 jpeg_header[589] = { + 0xff, 0xd8, 0xff, 0xdb, 0x00, 0x84, 0x00, 0x06, 0x04, 0x05, + 0x06, 0x05, 0x04, 0x06, 0x06, 0x05, 0x06, 0x07, 0x07, 0x06, + 0x08, 0x0a, 0x10, 0x0a, 0x0a, 0x09, 0x09, 0x0a, 0x14, 0x0e, + 0x0f, 0x0c, 0x10, 0x17, 0x14, 0x18, 0x18, 0x17, 0x14, 0x16, + 0x16, 0x1a, 0x1d, 0x25, 0x1f, 0x1a, 0x1b, 0x23, 0x1c, 0x16, + 0x16, 0x20, 0x2c, 0x20, 0x23, 0x26, 0x27, 0x29, 0x2a, 0x29, + 0x19, 0x1f, 0x2d, 0x30, 0x2d, 0x28, 0x30, 0x25, 0x28, 0x29, + 0x28, 0x01, 0x07, 0x07, 0x07, 0x0a, 0x08, 0x0a, 0x13, 0x0a, + 0x0a, 0x13, 0x28, 0x1a, 0x16, 0x1a, 0x28, 0x28, 0x28, 0x28, + 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, + 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, + 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, + 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, + 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0xff, 0xc4, 0x01, 0xa2, + 0x00, 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, + 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x01, + 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, + 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x10, 0x00, + 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03, 0x05, 0x05, 0x04, + 0x04, 0x00, 0x00, 0x01, 0x7d, 0x01, 0x02, 0x03, 0x00, 0x04, + 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, + 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08, 0x23, + 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0, 0x24, 0x33, 0x62, + 0x72, 0x82, 0x09, 0x0a, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x25, + 0x26, 0x27, 0x28, 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, + 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, + 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, + 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, + 0x77, 0x78, 0x79, 0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, + 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, + 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, + 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, + 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, + 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2, 0xe3, + 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf1, 0xf2, 0xf3, + 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0x11, 0x00, 0x02, + 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, 0x07, 0x05, 0x04, 0x04, + 0x00, 0x01, 0x02, 0x77, 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, + 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, + 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, 0xa1, 0xb1, + 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0, 0x15, 0x62, 0x72, 0xd1, + 0x0a, 0x16, 0x24, 0x34, 0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, + 0x1a, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38, + 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, + 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, + 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, + 0x77, 0x78, 0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, + 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, + 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, + 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, + 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe2, 0xe3, + 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf2, 0xf3, 0xf4, + 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xff, 0xc0, 0x00, 0x11, + 0x08, 0x01, 0xe0, 0x02, 0x80, 0x03, 0x01, 0x21, 0x00, 0x02, + 0x11, 0x01, 0x03, 0x11, 0x01, 0xff, 0xda, 0x00, 0x0c, 0x03, + 0x01, 0x00, 0x02, 0x11, 0x03, 0x11, 0x00, 0x3f, 0x00 + }; + u8 *pos = f->bufmem; + + memcpy(pos, jpeg_header, sizeof(jpeg_header)); + *(pos + 6) = 0x00; + *(pos + 7 + 64) = 0x01; + if (cam->compression.quality == 0) { + memcpy(pos + 7, SN9C102_Y_QTABLE0, 64); + memcpy(pos + 8 + 64, SN9C102_UV_QTABLE0, 64); + } else if (cam->compression.quality == 1) { + memcpy(pos + 7, SN9C102_Y_QTABLE1, 64); + memcpy(pos + 8 + 64, SN9C102_UV_QTABLE1, 64); + } + *(pos + 564) = cam->sensor.pix_format.width & 0xFF; + *(pos + 563) = (cam->sensor.pix_format.width >> 8) & 0xFF; + *(pos + 562) = cam->sensor.pix_format.height & 0xFF; + *(pos + 561) = (cam->sensor.pix_format.height >> 8) & 0xFF; + *(pos + 567) = 0x21; + + f->buf.bytesused += sizeof(jpeg_header); +} + + +static void +sn9c102_write_eoimarker(struct sn9c102_device* cam, struct sn9c102_frame_t* f) +{ + static const u8 eoi_marker[2] = {0xff, 0xd9}; + + memcpy(f->bufmem + f->buf.bytesused, eoi_marker, sizeof(eoi_marker)); + f->buf.bytesused += sizeof(eoi_marker); +} + + static void sn9c102_urb_complete(struct urb *urb) { struct sn9c102_device* cam = urb->context; @@ -535,7 +639,7 @@ static void sn9c102_urb_complete(struct urb *urb) cam->stream = STREAM_OFF; if ((*f)) (*f)->state = F_QUEUED; - DBG(3, "Stream interrupted"); + DBG(3, "Stream interrupted by application"); wake_up(&cam->wait_stream); } @@ -557,10 +661,9 @@ static void sn9c102_urb_complete(struct urb *urb) imagesize = (cam->sensor.pix_format.width * cam->sensor.pix_format.height * cam->sensor.pix_format.priv) / 8; - - soflen = (cam->bridge) == BRIDGE_SN9C103 ? - sizeof(sn9c103_sof_header_t) : - sizeof(sn9c102_sof_header_t); + if (cam->sensor.pix_format.pixelformat == V4L2_PIX_FMT_JPEG) + imagesize += 589; /* length of jpeg header */ + soflen = sn9c102_sof_length(cam); for (i = 0; i < urb->number_of_packets; i++) { unsigned int img, len, status; @@ -610,12 +713,21 @@ end_of_frame: (*f)->buf.bytesused += img; if ((*f)->buf.bytesused == imagesize || - (cam->sensor.pix_format.pixelformat == - V4L2_PIX_FMT_SN9C10X && eof)) { + ((cam->sensor.pix_format.pixelformat == + V4L2_PIX_FMT_SN9C10X || + cam->sensor.pix_format.pixelformat == + V4L2_PIX_FMT_JPEG) && eof)) { u32 b; + + if (cam->sensor.pix_format.pixelformat + == V4L2_PIX_FMT_JPEG) + sn9c102_write_eoimarker(cam, + (*f)); + b = (*f)->buf.bytesused; (*f)->state = F_DONE; (*f)->buf.sequence= ++cam->frame_count; + spin_lock(&cam->queue_lock); list_move_tail(&(*f)->frame, &cam->outqueue); @@ -627,8 +739,10 @@ end_of_frame: else (*f) = NULL; spin_unlock(&cam->queue_lock); + memcpy(cam->sysfs.frame_header, cam->sof_header, soflen); + DBG(3, "Video frame captured: %lu " "bytes", (unsigned long)(b)); @@ -661,6 +775,9 @@ start_of_frame: (*f)->buf.bytesused = 0; len -= (sof - pos); pos = sof; + if (cam->sensor.pix_format.pixelformat == + V4L2_PIX_FMT_JPEG) + sn9c102_write_jpegheader(cam, (*f)); DBG(3, "SOF detected: new video frame"); if (len) goto redo; @@ -671,7 +788,9 @@ start_of_frame: goto end_of_frame; /* (1) */ else { if (cam->sensor.pix_format.pixelformat == - V4L2_PIX_FMT_SN9C10X) { + V4L2_PIX_FMT_SN9C10X || + cam->sensor.pix_format.pixelformat == + V4L2_PIX_FMT_JPEG) { eof = sof - soflen; goto end_of_frame; } else { @@ -701,13 +820,11 @@ static int sn9c102_start_transfer(struct sn9c102_device* cam) { struct usb_device *udev = cam->usbdev; struct urb* urb; - const unsigned int sn9c102_wMaxPacketSize[] = {0, 128, 256, 384, 512, - 680, 800, 900, 1023}; - const unsigned int sn9c103_wMaxPacketSize[] = {0, 128, 256, 384, 512, - 680, 800, 900, 1003}; - const unsigned int psz = (cam->bridge == BRIDGE_SN9C103) ? - sn9c103_wMaxPacketSize[SN9C102_ALTERNATE_SETTING] : - sn9c102_wMaxPacketSize[SN9C102_ALTERNATE_SETTING]; + struct usb_host_interface* altsetting = usb_altnum_to_altsetting( + usb_ifnum_to_if(udev, 0), + SN9C102_ALTERNATE_SETTING); + const unsigned int psz = le16_to_cpu(altsetting-> + endpoint[0].desc.wMaxPacketSize); s8 i, j; int err = 0; @@ -775,7 +892,7 @@ static int sn9c102_start_transfer(struct sn9c102_device* cam) return 0; free_urbs: - for (i = 0; i < SN9C102_URBS; i++) + for (i = 0; (i < SN9C102_URBS) && cam->urb[i]; i++) usb_free_urb(cam->urb[i]); free_buffers: @@ -834,29 +951,29 @@ static int sn9c102_stream_interrupt(struct sn9c102_device* cam) /*****************************************************************************/ #ifdef CONFIG_VIDEO_ADV_DEBUG -static u8 sn9c102_strtou8(const char* buff, size_t len, ssize_t* count) +static u16 sn9c102_strtou16(const char* buff, size_t len, ssize_t* count) { - char str[5]; + char str[7]; char* endp; unsigned long val; - if (len < 4) { + if (len < 6) { strncpy(str, buff, len); str[len+1] = '\0'; } else { strncpy(str, buff, 4); - str[4] = '\0'; + str[6] = '\0'; } val = simple_strtoul(str, &endp, 0); *count = 0; - if (val <= 0xff) + if (val <= 0xffff) *count = (ssize_t)(endp - str); if ((*count) && (len == *count+1) && (buff[*count] == '\n')) *count += 1; - return (u8)val; + return (u16)val; } /* @@ -873,7 +990,8 @@ static ssize_t sn9c102_show_reg(struct class_device* cd, char* buf) if (mutex_lock_interruptible(&sn9c102_sysfs_lock)) return -ERESTARTSYS; - cam = video_get_drvdata(to_video_device(cd)); + cam = video_get_drvdata(container_of(cd, struct video_device, + class_dev)); if (!cam) { mutex_unlock(&sn9c102_sysfs_lock); return -ENODEV; @@ -891,27 +1009,28 @@ static ssize_t sn9c102_store_reg(struct class_device* cd, const char* buf, size_t len) { struct sn9c102_device* cam; - u8 index; + u16 index; ssize_t count; if (mutex_lock_interruptible(&sn9c102_sysfs_lock)) return -ERESTARTSYS; - cam = video_get_drvdata(to_video_device(cd)); + cam = video_get_drvdata(container_of(cd, struct video_device, + class_dev)); if (!cam) { mutex_unlock(&sn9c102_sysfs_lock); return -ENODEV; } - index = sn9c102_strtou8(buf, len, &count); - if (index > 0x1f || !count) { + index = sn9c102_strtou16(buf, len, &count); + if (index >= ARRAY_SIZE(cam->reg) || !count) { mutex_unlock(&sn9c102_sysfs_lock); return -EINVAL; } cam->sysfs.reg = index; - DBG(2, "Moved SN9C10X register index to 0x%02X", cam->sysfs.reg); + DBG(2, "Moved SN9C1XX register index to 0x%02X", cam->sysfs.reg); DBG(3, "Written bytes: %zd", count); mutex_unlock(&sn9c102_sysfs_lock); @@ -929,7 +1048,8 @@ static ssize_t sn9c102_show_val(struct class_device* cd, char* buf) if (mutex_lock_interruptible(&sn9c102_sysfs_lock)) return -ERESTARTSYS; - cam = video_get_drvdata(to_video_device(cd)); + cam = video_get_drvdata(container_of(cd, struct video_device, + class_dev)); if (!cam) { mutex_unlock(&sn9c102_sysfs_lock); return -ENODEV; @@ -954,20 +1074,21 @@ static ssize_t sn9c102_store_val(struct class_device* cd, const char* buf, size_t len) { struct sn9c102_device* cam; - u8 value; + u16 value; ssize_t count; int err; if (mutex_lock_interruptible(&sn9c102_sysfs_lock)) return -ERESTARTSYS; - cam = video_get_drvdata(to_video_device(cd)); + cam = video_get_drvdata(container_of(cd, struct video_device, + class_dev)); if (!cam) { mutex_unlock(&sn9c102_sysfs_lock); return -ENODEV; } - value = sn9c102_strtou8(buf, len, &count); + value = sn9c102_strtou16(buf, len, &count); if (!count) { mutex_unlock(&sn9c102_sysfs_lock); return -EINVAL; @@ -979,7 +1100,7 @@ sn9c102_store_val(struct class_device* cd, const char* buf, size_t len) return -EIO; } - DBG(2, "Written SN9C10X reg. 0x%02X, val. 0x%02X", + DBG(2, "Written SN9C1XX reg. 0x%02X, val. 0x%02X", cam->sysfs.reg, value); DBG(3, "Written bytes: %zd", count); @@ -997,7 +1118,8 @@ static ssize_t sn9c102_show_i2c_reg(struct class_device* cd, char* buf) if (mutex_lock_interruptible(&sn9c102_sysfs_lock)) return -ERESTARTSYS; - cam = video_get_drvdata(to_video_device(cd)); + cam = video_get_drvdata(container_of(cd, struct video_device, + class_dev)); if (!cam) { mutex_unlock(&sn9c102_sysfs_lock); return -ENODEV; @@ -1017,19 +1139,20 @@ static ssize_t sn9c102_store_i2c_reg(struct class_device* cd, const char* buf, size_t len) { struct sn9c102_device* cam; - u8 index; + u16 index; ssize_t count; if (mutex_lock_interruptible(&sn9c102_sysfs_lock)) return -ERESTARTSYS; - cam = video_get_drvdata(to_video_device(cd)); + cam = video_get_drvdata(container_of(cd, struct video_device, + class_dev)); if (!cam) { mutex_unlock(&sn9c102_sysfs_lock); return -ENODEV; } - index = sn9c102_strtou8(buf, len, &count); + index = sn9c102_strtou16(buf, len, &count); if (!count) { mutex_unlock(&sn9c102_sysfs_lock); return -EINVAL; @@ -1055,7 +1178,8 @@ static ssize_t sn9c102_show_i2c_val(struct class_device* cd, char* buf) if (mutex_lock_interruptible(&sn9c102_sysfs_lock)) return -ERESTARTSYS; - cam = video_get_drvdata(to_video_device(cd)); + cam = video_get_drvdata(container_of(cd, struct video_device, + class_dev)); if (!cam) { mutex_unlock(&sn9c102_sysfs_lock); return -ENODEV; @@ -1085,14 +1209,15 @@ static ssize_t sn9c102_store_i2c_val(struct class_device* cd, const char* buf, size_t len) { struct sn9c102_device* cam; - u8 value; + u16 value; ssize_t count; int err; if (mutex_lock_interruptible(&sn9c102_sysfs_lock)) return -ERESTARTSYS; - cam = video_get_drvdata(to_video_device(cd)); + cam = video_get_drvdata(container_of(cd, struct video_device, + class_dev)); if (!cam) { mutex_unlock(&sn9c102_sysfs_lock); return -ENODEV; @@ -1103,7 +1228,7 @@ sn9c102_store_i2c_val(struct class_device* cd, const char* buf, size_t len) return -ENOSYS; } - value = sn9c102_strtou8(buf, len, &count); + value = sn9c102_strtou16(buf, len, &count); if (!count) { mutex_unlock(&sn9c102_sysfs_lock); return -EINVAL; @@ -1131,13 +1256,14 @@ sn9c102_store_green(struct class_device* cd, const char* buf, size_t len) struct sn9c102_device* cam; enum sn9c102_bridge bridge; ssize_t res = 0; - u8 value; + u16 value; ssize_t count; if (mutex_lock_interruptible(&sn9c102_sysfs_lock)) return -ERESTARTSYS; - cam = video_get_drvdata(to_video_device(cd)); + cam = video_get_drvdata(container_of(cd, struct video_device, + class_dev)); if (!cam) { mutex_unlock(&sn9c102_sysfs_lock); return -ENODEV; @@ -1147,7 +1273,7 @@ sn9c102_store_green(struct class_device* cd, const char* buf, size_t len) mutex_unlock(&sn9c102_sysfs_lock); - value = sn9c102_strtou8(buf, len, &count); + value = sn9c102_strtou16(buf, len, &count); if (!count) return -EINVAL; @@ -1160,9 +1286,11 @@ sn9c102_store_green(struct class_device* cd, const char* buf, size_t len) res = sn9c102_store_val(cd, buf, len); break; case BRIDGE_SN9C103: + case BRIDGE_SN9C105: + case BRIDGE_SN9C120: if (value > 0x7f) return -EINVAL; - if ((res = sn9c102_store_reg(cd, "0x04", 4)) >= 0) + if ((res = sn9c102_store_reg(cd, "0x07", 4)) >= 0) res = sn9c102_store_val(cd, buf, len); break; } @@ -1175,10 +1303,10 @@ static ssize_t sn9c102_store_blue(struct class_device* cd, const char* buf, size_t len) { ssize_t res = 0; - u8 value; + u16 value; ssize_t count; - value = sn9c102_strtou8(buf, len, &count); + value = sn9c102_strtou16(buf, len, &count); if (!count || value > 0x7f) return -EINVAL; @@ -1193,10 +1321,10 @@ static ssize_t sn9c102_store_red(struct class_device* cd, const char* buf, size_t len) { ssize_t res = 0; - u8 value; + u16 value; ssize_t count; - value = sn9c102_strtou8(buf, len, &count); + value = sn9c102_strtou16(buf, len, &count); if (!count || value > 0x7f) return -EINVAL; @@ -1212,7 +1340,8 @@ static ssize_t sn9c102_show_frame_header(struct class_device* cd, char* buf) struct sn9c102_device* cam; ssize_t count; - cam = video_get_drvdata(to_video_device(cd)); + cam = video_get_drvdata(container_of(cd, struct video_device, + class_dev)); if (!cam) return -ENODEV; @@ -1243,30 +1372,36 @@ static CLASS_DEVICE_ATTR(frame_header, S_IRUGO, static int sn9c102_create_sysfs(struct sn9c102_device* cam) { struct video_device *v4ldev = cam->v4ldev; - int rc; + int err = 0; - rc = video_device_create_file(v4ldev, &class_device_attr_reg); - if (rc) goto err; - rc = video_device_create_file(v4ldev, &class_device_attr_val); - if (rc) goto err_reg; - rc = video_device_create_file(v4ldev, &class_device_attr_frame_header); - if (rc) goto err_val; + if ((err = video_device_create_file(v4ldev, &class_device_attr_reg))) + goto err_out; + if ((err = video_device_create_file(v4ldev, &class_device_attr_val))) + goto err_reg; + if ((err = video_device_create_file(v4ldev, + &class_device_attr_frame_header))) + goto err_val; if (cam->sensor.sysfs_ops) { - rc = video_device_create_file(v4ldev, &class_device_attr_i2c_reg); - if (rc) goto err_frhead; - rc = video_device_create_file(v4ldev, &class_device_attr_i2c_val); - if (rc) goto err_i2c_reg; + if ((err = video_device_create_file(v4ldev, + &class_device_attr_i2c_reg))) + goto err_frame_header; + if ((err = video_device_create_file(v4ldev, + &class_device_attr_i2c_val))) + goto err_i2c_reg; } if (cam->bridge == BRIDGE_SN9C101 || cam->bridge == BRIDGE_SN9C102) { - rc = video_device_create_file(v4ldev, &class_device_attr_green); - if (rc) goto err_i2c_val; - } else if (cam->bridge == BRIDGE_SN9C103) { - rc = video_device_create_file(v4ldev, &class_device_attr_blue); - if (rc) goto err_i2c_val; - rc = video_device_create_file(v4ldev, &class_device_attr_red); - if (rc) goto err_blue; + if ((err = video_device_create_file(v4ldev, + &class_device_attr_green))) + goto err_i2c_val; + } else { + if ((err = video_device_create_file(v4ldev, + &class_device_attr_blue))) + goto err_i2c_val; + if ((err = video_device_create_file(v4ldev, + &class_device_attr_red))) + goto err_blue; } return 0; @@ -1279,14 +1414,14 @@ err_i2c_val: err_i2c_reg: if (cam->sensor.sysfs_ops) video_device_remove_file(v4ldev, &class_device_attr_i2c_reg); -err_frhead: +err_frame_header: video_device_remove_file(v4ldev, &class_device_attr_frame_header); err_val: video_device_remove_file(v4ldev, &class_device_attr_val); err_reg: video_device_remove_file(v4ldev, &class_device_attr_reg); -err: - return rc; +err_out: + return err; } #endif /* CONFIG_VIDEO_ADV_DEBUG */ @@ -1297,10 +1432,36 @@ sn9c102_set_pix_format(struct sn9c102_device* cam, struct v4l2_pix_format* pix) { int err = 0; - if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X) - err += sn9c102_write_reg(cam, cam->reg[0x18] | 0x80, 0x18); - else - err += sn9c102_write_reg(cam, cam->reg[0x18] & 0x7f, 0x18); + if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X || + pix->pixelformat == V4L2_PIX_FMT_JPEG) { + switch (cam->bridge) { + case BRIDGE_SN9C101: + case BRIDGE_SN9C102: + case BRIDGE_SN9C103: + err += sn9c102_write_reg(cam, cam->reg[0x18] | 0x80, + 0x18); + break; + case BRIDGE_SN9C105: + case BRIDGE_SN9C120: + err += sn9c102_write_reg(cam, cam->reg[0x18] & 0x7f, + 0x18); + break; + } + } else { + switch (cam->bridge) { + case BRIDGE_SN9C101: + case BRIDGE_SN9C102: + case BRIDGE_SN9C103: + err += sn9c102_write_reg(cam, cam->reg[0x18] & 0x7f, + 0x18); + break; + case BRIDGE_SN9C105: + case BRIDGE_SN9C120: + err += sn9c102_write_reg(cam, cam->reg[0x18] | 0x80, + 0x18); + break; + } + } return err ? -EIO : 0; } @@ -1310,12 +1471,46 @@ static int sn9c102_set_compression(struct sn9c102_device* cam, struct v4l2_jpegcompression* compression) { - int err = 0; + int i, err = 0; + switch (cam->bridge) { + case BRIDGE_SN9C101: + case BRIDGE_SN9C102: + case BRIDGE_SN9C103: if (compression->quality == 0) - err += sn9c102_write_reg(cam, cam->reg[0x17] | 0x01, 0x17); + err += sn9c102_write_reg(cam, cam->reg[0x17] | 0x01, + 0x17); else if (compression->quality == 1) - err += sn9c102_write_reg(cam, cam->reg[0x17] & 0xfe, 0x17); + err += sn9c102_write_reg(cam, cam->reg[0x17] & 0xfe, + 0x17); + break; + case BRIDGE_SN9C105: + case BRIDGE_SN9C120: + if (compression->quality == 0) { + for (i = 0; i <= 63; i++) { + err += sn9c102_write_reg(cam, + SN9C102_Y_QTABLE0[i], + 0x100 + i); + err += sn9c102_write_reg(cam, + SN9C102_UV_QTABLE0[i], + 0x140 + i); + } + err += sn9c102_write_reg(cam, cam->reg[0x18] & 0xbf, + 0x18); + } else if (compression->quality == 1) { + for (i = 0; i <= 63; i++) { + err += sn9c102_write_reg(cam, + SN9C102_Y_QTABLE1[i], + 0x100 + i); + err += sn9c102_write_reg(cam, + SN9C102_UV_QTABLE1[i], + 0x140 + i); + } + err += sn9c102_write_reg(cam, cam->reg[0x18] | 0x40, + 0x18); + } + break; + } return err ? -EIO : 0; } @@ -1399,7 +1594,16 @@ static int sn9c102_init(struct sn9c102_device* cam) } if (!(cam->state & DEV_INITIALIZED)) - cam->compression.quality = cam->reg[0x17] & 0x01 ? 0 : 1; + if (cam->bridge == BRIDGE_SN9C101 || + cam->bridge == BRIDGE_SN9C102 || + cam->bridge == BRIDGE_SN9C103) { + cam->compression.quality = cam->reg[0x17] & 0x01 ? + 0 : 1; + } else { + cam->compression.quality = cam->reg[0x18] & 0x40 ? + 0 : 1; + err += sn9c102_set_compression(cam, &cam->compression); + } else err += sn9c102_set_compression(cam, &cam->compression); err += sn9c102_set_pix_format(cam, &s->pix_format); @@ -1408,7 +1612,8 @@ static int sn9c102_init(struct sn9c102_device* cam) if (err) return err; - if (s->pix_format.pixelformat == V4L2_PIX_FMT_SN9C10X) + if (s->pix_format.pixelformat == V4L2_PIX_FMT_SN9C10X || + s->pix_format.pixelformat == V4L2_PIX_FMT_JPEG) DBG(3, "Compressed video format is active, quality %d", cam->compression.quality); else @@ -1490,6 +1695,7 @@ static int sn9c102_open(struct inode* inode, struct file* filp) if (cam->users) { DBG(2, "Device /dev/video%d is busy...", cam->v4ldev->minor); + DBG(3, "Simultaneous opens are not supported"); if ((filp->f_flags & O_NONBLOCK) || (filp->f_flags & O_NDELAY)) { err = -EWOULDBLOCK; @@ -1628,6 +1834,17 @@ sn9c102_read(struct file* filp, char __user * buf, size_t count, loff_t* f_pos) mutex_unlock(&cam->fileop_mutex); return -EAGAIN; } + if (!cam->module_param.frame_timeout) { + err = wait_event_interruptible + ( cam->wait_frame, + (!list_empty(&cam->outqueue)) || + (cam->state & DEV_DISCONNECTED) || + (cam->state & DEV_MISCONFIGURED) ); + if (err) { + mutex_unlock(&cam->fileop_mutex); + return err; + } + } else { timeout = wait_event_interruptible_timeout ( cam->wait_frame, (!list_empty(&cam->outqueue)) || @@ -1638,12 +1855,18 @@ sn9c102_read(struct file* filp, char __user * buf, size_t count, loff_t* f_pos) if (timeout < 0) { mutex_unlock(&cam->fileop_mutex); return timeout; + } else if (timeout == 0 && + !(cam->state & DEV_DISCONNECTED)) { + DBG(1, "Video frame timeout elapsed"); + mutex_unlock(&cam->fileop_mutex); + return -EIO; + } } if (cam->state & DEV_DISCONNECTED) { mutex_unlock(&cam->fileop_mutex); return -ENODEV; } - if (!timeout || (cam->state & DEV_MISCONFIGURED)) { + if (cam->state & DEV_MISCONFIGURED) { mutex_unlock(&cam->fileop_mutex); return -EIO; } @@ -1940,6 +2163,9 @@ exit: if (copy_to_user(arg, &ctrl, sizeof(ctrl))) return -EFAULT; + PDBGG("VIDIOC_G_CTRL: id %lu, value %lu", + (unsigned long)ctrl.id, (unsigned long)ctrl.value); + return err; } @@ -2126,6 +2352,45 @@ sn9c102_vidioc_s_crop(struct sn9c102_device* cam, void __user * arg) } +static int +sn9c102_vidioc_enum_framesizes(struct sn9c102_device* cam, void __user * arg) +{ + struct v4l2_frmsizeenum frmsize; + + if (copy_from_user(&frmsize, arg, sizeof(frmsize))) + return -EFAULT; + + if (frmsize.index != 0) + return -EINVAL; + + switch (cam->bridge) { + case BRIDGE_SN9C101: + case BRIDGE_SN9C102: + case BRIDGE_SN9C103: + if (frmsize.pixel_format != V4L2_PIX_FMT_SN9C10X && + frmsize.pixel_format != V4L2_PIX_FMT_SBGGR8) + return -EINVAL; + case BRIDGE_SN9C105: + case BRIDGE_SN9C120: + if (frmsize.pixel_format != V4L2_PIX_FMT_JPEG && + frmsize.pixel_format != V4L2_PIX_FMT_SBGGR8) + return -EINVAL; + } + + frmsize.type = V4L2_FRMSIZE_TYPE_STEPWISE; + frmsize.stepwise.min_width = frmsize.stepwise.step_width = 16; + frmsize.stepwise.min_height = frmsize.stepwise.step_height = 16; + frmsize.stepwise.max_width = cam->sensor.cropcap.bounds.width; + frmsize.stepwise.max_height = cam->sensor.cropcap.bounds.height; + memset(&frmsize.reserved, 0, sizeof(frmsize.reserved)); + + if (copy_to_user(arg, &frmsize, sizeof(frmsize))) + return -EFAULT; + + return 0; +} + + static int sn9c102_vidioc_enum_fmt(struct sn9c102_device* cam, void __user * arg) { @@ -2134,12 +2399,26 @@ sn9c102_vidioc_enum_fmt(struct sn9c102_device* cam, void __user * arg) if (copy_from_user(&fmtd, arg, sizeof(fmtd))) return -EFAULT; + if (fmtd.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + if (fmtd.index == 0) { strcpy(fmtd.description, "bayer rgb"); fmtd.pixelformat = V4L2_PIX_FMT_SBGGR8; } else if (fmtd.index == 1) { + switch (cam->bridge) { + case BRIDGE_SN9C101: + case BRIDGE_SN9C102: + case BRIDGE_SN9C103: strcpy(fmtd.description, "compressed"); fmtd.pixelformat = V4L2_PIX_FMT_SN9C10X; + break; + case BRIDGE_SN9C105: + case BRIDGE_SN9C120: + strcpy(fmtd.description, "JPEG"); + fmtd.pixelformat = V4L2_PIX_FMT_JPEG; + break; + } fmtd.flags = V4L2_FMT_FLAG_COMPRESSED; } else return -EINVAL; @@ -2166,7 +2445,8 @@ sn9c102_vidioc_g_fmt(struct sn9c102_device* cam, void __user * arg) if (format.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; - pfmt->bytesperline = (pfmt->pixelformat==V4L2_PIX_FMT_SN9C10X) + pfmt->bytesperline = (pfmt->pixelformat==V4L2_PIX_FMT_SN9C10X || + pfmt->pixelformat==V4L2_PIX_FMT_JPEG) ? 0 : (pfmt->width * pfmt->priv) / 8; pfmt->sizeimage = pfmt->height * ((pfmt->width*pfmt->priv)/8); pfmt->field = V4L2_FIELD_NONE; @@ -2237,12 +2517,25 @@ sn9c102_vidioc_try_s_fmt(struct sn9c102_device* cam, unsigned int cmd, pix->width = rect.width / scale; pix->height = rect.height / scale; + switch (cam->bridge) { + case BRIDGE_SN9C101: + case BRIDGE_SN9C102: + case BRIDGE_SN9C103: if (pix->pixelformat != V4L2_PIX_FMT_SN9C10X && pix->pixelformat != V4L2_PIX_FMT_SBGGR8) pix->pixelformat = pfmt->pixelformat; + break; + case BRIDGE_SN9C105: + case BRIDGE_SN9C120: + if (pix->pixelformat != V4L2_PIX_FMT_JPEG && + pix->pixelformat != V4L2_PIX_FMT_SBGGR8) + pix->pixelformat = pfmt->pixelformat; + break; + } pix->priv = pfmt->priv; /* bpp */ pix->colorspace = pfmt->colorspace; - pix->bytesperline = (pix->pixelformat == V4L2_PIX_FMT_SN9C10X) + pix->bytesperline = (pix->pixelformat == V4L2_PIX_FMT_SN9C10X || + pix->pixelformat == V4L2_PIX_FMT_JPEG) ? 0 : (pix->width * pix->priv) / 8; pix->sizeimage = pix->height * ((pix->width * pix->priv) / 8); pix->field = V4L2_FIELD_NONE; @@ -2315,8 +2608,7 @@ sn9c102_vidioc_try_s_fmt(struct sn9c102_device* cam, unsigned int cmd, static int sn9c102_vidioc_g_jpegcomp(struct sn9c102_device* cam, void __user * arg) { - if (copy_to_user(arg, &cam->compression, - sizeof(cam->compression))) + if (copy_to_user(arg, &cam->compression, sizeof(cam->compression))) return -EFAULT; return 0; @@ -2471,6 +2763,7 @@ sn9c102_vidioc_dqbuf(struct sn9c102_device* cam, struct file* filp, struct sn9c102_frame_t *f; unsigned long lock_flags; long timeout; + int err = 0; if (copy_from_user(&b, arg, sizeof(b))) return -EFAULT; @@ -2483,6 +2776,15 @@ sn9c102_vidioc_dqbuf(struct sn9c102_device* cam, struct file* filp, return -EINVAL; if (filp->f_flags & O_NONBLOCK) return -EAGAIN; + if (!cam->module_param.frame_timeout) { + err = wait_event_interruptible + ( cam->wait_frame, + (!list_empty(&cam->outqueue)) || + (cam->state & DEV_DISCONNECTED) || + (cam->state & DEV_MISCONFIGURED) ); + if (err) + return err; + } else { timeout = wait_event_interruptible_timeout ( cam->wait_frame, (!list_empty(&cam->outqueue)) || @@ -2492,9 +2794,15 @@ sn9c102_vidioc_dqbuf(struct sn9c102_device* cam, struct file* filp, 1000 * msecs_to_jiffies(1) ); if (timeout < 0) return timeout; + else if (timeout == 0 && + !(cam->state & DEV_DISCONNECTED)) { + DBG(1, "Video frame timeout elapsed"); + return -EIO; + } + } if (cam->state & DEV_DISCONNECTED) return -ENODEV; - if (!timeout || (cam->state & DEV_MISCONFIGURED)) + if (cam->state & DEV_MISCONFIGURED) return -EIO; } @@ -2612,6 +2920,70 @@ sn9c102_vidioc_s_parm(struct sn9c102_device* cam, void __user * arg) } +static int +sn9c102_vidioc_enumaudio(struct sn9c102_device* cam, void __user * arg) +{ + struct v4l2_audio audio; + + if (cam->bridge == BRIDGE_SN9C101 || cam->bridge == BRIDGE_SN9C102) + return -EINVAL; + + if (copy_from_user(&audio, arg, sizeof(audio))) + return -EFAULT; + + if (audio.index != 0) + return -EINVAL; + + strcpy(audio.name, "Microphone"); + audio.capability = 0; + audio.mode = 0; + + if (copy_to_user(arg, &audio, sizeof(audio))) + return -EFAULT; + + return 0; +} + + +static int +sn9c102_vidioc_g_audio(struct sn9c102_device* cam, void __user * arg) +{ + struct v4l2_audio audio; + + if (cam->bridge == BRIDGE_SN9C101 || cam->bridge == BRIDGE_SN9C102) + return -EINVAL; + + if (copy_from_user(&audio, arg, sizeof(audio))) + return -EFAULT; + + memset(&audio, 0, sizeof(audio)); + strcpy(audio.name, "Microphone"); + + if (copy_to_user(arg, &audio, sizeof(audio))) + return -EFAULT; + + return 0; +} + + +static int +sn9c102_vidioc_s_audio(struct sn9c102_device* cam, void __user * arg) +{ + struct v4l2_audio audio; + + if (cam->bridge == BRIDGE_SN9C101 || cam->bridge == BRIDGE_SN9C102) + return -EINVAL; + + if (copy_from_user(&audio, arg, sizeof(audio))) + return -EFAULT; + + if (audio.index != 0) + return -EINVAL; + + return 0; +} + + static int sn9c102_ioctl_v4l2(struct inode* inode, struct file* filp, unsigned int cmd, void __user * arg) { @@ -2649,6 +3021,9 @@ static int sn9c102_ioctl_v4l2(struct inode* inode, struct file* filp, case VIDIOC_S_CROP: return sn9c102_vidioc_s_crop(cam, arg); + case VIDIOC_ENUM_FRAMESIZES: + return sn9c102_vidioc_enum_framesizes(cam, arg); + case VIDIOC_ENUM_FMT: return sn9c102_vidioc_enum_fmt(cam, arg); @@ -2689,11 +3064,21 @@ static int sn9c102_ioctl_v4l2(struct inode* inode, struct file* filp, case VIDIOC_S_PARM: return sn9c102_vidioc_s_parm(cam, arg); + case VIDIOC_ENUMAUDIO: + return sn9c102_vidioc_enumaudio(cam, arg); + + case VIDIOC_G_AUDIO: + return sn9c102_vidioc_g_audio(cam, arg); + + case VIDIOC_S_AUDIO: + return sn9c102_vidioc_s_audio(cam, arg); + case VIDIOC_G_STD: case VIDIOC_S_STD: case VIDIOC_QUERYSTD: case VIDIOC_ENUMSTD: case VIDIOC_QUERYMENU: + case VIDIOC_ENUM_FRAMEINTERVALS: return -EINVAL; default: @@ -2741,6 +3126,7 @@ static const struct file_operations sn9c102_fops = { .open = sn9c102_open, .release = sn9c102_release, .ioctl = sn9c102_ioctl, + .compat_ioctl = v4l_compat_ioctl32, .read = sn9c102_read, .poll = sn9c102_poll, .mmap = sn9c102_mmap, @@ -2765,7 +3151,7 @@ sn9c102_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) cam->usbdev = udev; if (!(cam->control_buffer = kzalloc(8, GFP_KERNEL))) { - DBG(1, "kmalloc() failed"); + DBG(1, "kzalloc() failed"); err = -ENOMEM; goto fail; } @@ -2779,24 +3165,31 @@ sn9c102_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) mutex_init(&cam->dev_mutex); r = sn9c102_read_reg(cam, 0x00); - if (r < 0 || r != 0x10) { - DBG(1, "Sorry, this is not a SN9C10x based camera " - "(vid/pid 0x%04X/0x%04X)", id->idVendor, id->idProduct); + if (r < 0 || (r != 0x10 && r != 0x11 && r != 0x12)) { + DBG(1, "Sorry, this is not a SN9C1xx based camera " + "(vid/pid 0x%04X:0x%04X)", id->idVendor, id->idProduct); err = -ENODEV; goto fail; } - cam->bridge = (id->idProduct & 0xffc0) == 0x6080 ? - BRIDGE_SN9C103 : BRIDGE_SN9C102; + cam->bridge = id->driver_info; switch (cam->bridge) { case BRIDGE_SN9C101: case BRIDGE_SN9C102: DBG(2, "SN9C10[12] PC Camera Controller detected " - "(vid/pid 0x%04X/0x%04X)", id->idVendor, id->idProduct); + "(vid/pid 0x%04X:0x%04X)", id->idVendor, id->idProduct); break; case BRIDGE_SN9C103: DBG(2, "SN9C103 PC Camera Controller detected " - "(vid/pid 0x%04X/0x%04X)", id->idVendor, id->idProduct); + "(vid/pid 0x%04X:0x%04X)", id->idVendor, id->idProduct); + break; + case BRIDGE_SN9C105: + DBG(2, "SN9C105 PC Camera Controller detected " + "(vid/pid 0x%04X:0x%04X)", id->idVendor, id->idProduct); + break; + case BRIDGE_SN9C120: + DBG(2, "SN9C120 PC Camera Controller detected " + "(vid/pid 0x%04X:0x%04X)", id->idVendor, id->idProduct); break; } @@ -2816,12 +3209,18 @@ sn9c102_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) goto fail; } + if (!(cam->bridge & cam->sensor.supported_bridge)) { + DBG(1, "Bridge not supported"); + err = -ENODEV; + goto fail; + } + if (sn9c102_init(cam)) { DBG(1, "Initialization failed. I will retry on open()."); cam->state |= DEV_MISCONFIGURED; } - strcpy(cam->v4ldev->name, "SN9C10x PC Camera"); + strcpy(cam->v4ldev->name, "SN9C1xx PC Camera"); cam->v4ldev->owner = THIS_MODULE; cam->v4ldev->type = VID_TYPE_CAPTURE | VID_TYPE_SCALES; cam->v4ldev->hardware = 0; @@ -2838,7 +3237,10 @@ sn9c102_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) DBG(1, "V4L2 device registration failed"); if (err == -ENFILE && video_nr[dev_nr] == -1) DBG(1, "Free /dev/videoX node not found"); - goto fail2; + video_nr[dev_nr] = -1; + dev_nr = (dev_nr < SN9C102_MAX_DEVICES-1) ? dev_nr+1 : 0; + mutex_unlock(&cam->dev_mutex); + goto fail; } DBG(2, "V4L2 device registered as /dev/video%d", cam->v4ldev->minor); @@ -2850,9 +3252,14 @@ sn9c102_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) #ifdef CONFIG_VIDEO_ADV_DEBUG err = sn9c102_create_sysfs(cam); - if (err) - goto fail3; - DBG(2, "Optional device control through 'sysfs' interface ready"); + if (!err) + DBG(2, "Optional device control through 'sysfs' " + "interface ready"); + else + DBG(2, "Failed to create optional 'sysfs' interface for " + "device controlling. Error #%d", err); +#else + DBG(2, "Optional device control through 'sysfs' interface disabled"); #endif usb_set_intfdata(intf, cam); @@ -2861,14 +3268,6 @@ sn9c102_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) return 0; -#ifdef CONFIG_VIDEO_ADV_DEBUG -fail3: - video_unregister_device(cam->v4ldev); -#endif -fail2: - video_nr[dev_nr] = -1; - dev_nr = (dev_nr < SN9C102_MAX_DEVICES-1) ? dev_nr+1 : 0; - mutex_unlock(&cam->dev_mutex); fail: if (cam) { kfree(cam->control_buffer); diff --git a/drivers/media/video/sn9c102/sn9c102_devtable.h b/drivers/media/video/sn9c102/sn9c102_devtable.h new file mode 100644 index 000000000000..3a682eca6c65 --- /dev/null +++ b/drivers/media/video/sn9c102/sn9c102_devtable.h @@ -0,0 +1,142 @@ +/*************************************************************************** + * Table of device identifiers of the SN9C1xx PC Camera Controllers * + * * + * Copyright (C) 2007 by Luca Risolia * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * + ***************************************************************************/ + +#ifndef _SN9C102_DEVTABLE_H_ +#define _SN9C102_DEVTABLE_H_ + +#include + +struct sn9c102_device; + +/* + Each SN9C1xx camera has proper PID/VID identifiers. + SN9C103, SN9C105, SN9C120 support multiple interfaces, but we only have to + handle the video class interface. +*/ +#define SN9C102_USB_DEVICE(vend, prod, bridge) \ + .match_flags = USB_DEVICE_ID_MATCH_DEVICE | \ + USB_DEVICE_ID_MATCH_INT_CLASS, \ + .idVendor = (vend), \ + .idProduct = (prod), \ + .bInterfaceClass = 0xff, \ + .driver_info = (bridge) + +static const struct usb_device_id sn9c102_id_table[] = { + /* SN9C101 and SN9C102 */ + { SN9C102_USB_DEVICE(0x0c45, 0x6001, BRIDGE_SN9C102), }, + { SN9C102_USB_DEVICE(0x0c45, 0x6005, BRIDGE_SN9C102), }, + { SN9C102_USB_DEVICE(0x0c45, 0x6007, BRIDGE_SN9C102), }, + { SN9C102_USB_DEVICE(0x0c45, 0x6009, BRIDGE_SN9C102), }, + { SN9C102_USB_DEVICE(0x0c45, 0x6011, BRIDGE_SN9C102), }, + { SN9C102_USB_DEVICE(0x0c45, 0x600d, BRIDGE_SN9C102), }, + { SN9C102_USB_DEVICE(0x0c45, 0x6019, BRIDGE_SN9C102), }, + { SN9C102_USB_DEVICE(0x0c45, 0x6024, BRIDGE_SN9C102), }, + { SN9C102_USB_DEVICE(0x0c45, 0x6025, BRIDGE_SN9C102), }, + { SN9C102_USB_DEVICE(0x0c45, 0x6028, BRIDGE_SN9C102), }, + { SN9C102_USB_DEVICE(0x0c45, 0x6029, BRIDGE_SN9C102), }, + { SN9C102_USB_DEVICE(0x0c45, 0x602a, BRIDGE_SN9C102), }, + { SN9C102_USB_DEVICE(0x0c45, 0x602b, BRIDGE_SN9C102), }, + { SN9C102_USB_DEVICE(0x0c45, 0x602c, BRIDGE_SN9C102), }, + { SN9C102_USB_DEVICE(0x0c45, 0x602d, BRIDGE_SN9C102), }, + { SN9C102_USB_DEVICE(0x0c45, 0x602e, BRIDGE_SN9C102), }, + { SN9C102_USB_DEVICE(0x0c45, 0x6030, BRIDGE_SN9C102), }, + { SN9C102_USB_DEVICE(0x0c45, 0x603f, BRIDGE_SN9C102), }, + /* SN9C103 */ + { SN9C102_USB_DEVICE(0x0c45, 0x6080, BRIDGE_SN9C103), }, + { SN9C102_USB_DEVICE(0x0c45, 0x6082, BRIDGE_SN9C103), }, + { SN9C102_USB_DEVICE(0x0c45, 0x6083, BRIDGE_SN9C103), }, + { SN9C102_USB_DEVICE(0x0c45, 0x6088, BRIDGE_SN9C103), }, + { SN9C102_USB_DEVICE(0x0c45, 0x608a, BRIDGE_SN9C103), }, + { SN9C102_USB_DEVICE(0x0c45, 0x608b, BRIDGE_SN9C103), }, + { SN9C102_USB_DEVICE(0x0c45, 0x608c, BRIDGE_SN9C103), }, + { SN9C102_USB_DEVICE(0x0c45, 0x608e, BRIDGE_SN9C103), }, + { SN9C102_USB_DEVICE(0x0c45, 0x608f, BRIDGE_SN9C103), }, + { SN9C102_USB_DEVICE(0x0c45, 0x60a0, BRIDGE_SN9C103), }, + { SN9C102_USB_DEVICE(0x0c45, 0x60a2, BRIDGE_SN9C103), }, + { SN9C102_USB_DEVICE(0x0c45, 0x60a3, BRIDGE_SN9C103), }, + { SN9C102_USB_DEVICE(0x0c45, 0x60a8, BRIDGE_SN9C103), }, + { SN9C102_USB_DEVICE(0x0c45, 0x60aa, BRIDGE_SN9C103), }, + { SN9C102_USB_DEVICE(0x0c45, 0x60ab, BRIDGE_SN9C103), }, + { SN9C102_USB_DEVICE(0x0c45, 0x60ac, BRIDGE_SN9C103), }, + { SN9C102_USB_DEVICE(0x0c45, 0x60ae, BRIDGE_SN9C103), }, + { SN9C102_USB_DEVICE(0x0c45, 0x60af, BRIDGE_SN9C103), }, + { SN9C102_USB_DEVICE(0x0c45, 0x60b0, BRIDGE_SN9C103), }, + { SN9C102_USB_DEVICE(0x0c45, 0x60b2, BRIDGE_SN9C103), }, + { SN9C102_USB_DEVICE(0x0c45, 0x60b3, BRIDGE_SN9C103), }, + { SN9C102_USB_DEVICE(0x0c45, 0x60b8, BRIDGE_SN9C103), }, + { SN9C102_USB_DEVICE(0x0c45, 0x60ba, BRIDGE_SN9C103), }, + { SN9C102_USB_DEVICE(0x0c45, 0x60bb, BRIDGE_SN9C103), }, + { SN9C102_USB_DEVICE(0x0c45, 0x60bc, BRIDGE_SN9C103), }, + { SN9C102_USB_DEVICE(0x0c45, 0x60be, BRIDGE_SN9C103), }, + /* SN9C105 */ + { SN9C102_USB_DEVICE(0x0471, 0x0327, BRIDGE_SN9C105), }, + { SN9C102_USB_DEVICE(0x0471, 0x0328, BRIDGE_SN9C105), }, + { SN9C102_USB_DEVICE(0x0c45, 0x60c0, BRIDGE_SN9C105), }, + { SN9C102_USB_DEVICE(0x0c45, 0x60c8, BRIDGE_SN9C105), }, + { SN9C102_USB_DEVICE(0x0c45, 0x60cc, BRIDGE_SN9C105), }, + { SN9C102_USB_DEVICE(0x0c45, 0x60ea, BRIDGE_SN9C105), }, + { SN9C102_USB_DEVICE(0x0c45, 0x60ec, BRIDGE_SN9C105), }, + { SN9C102_USB_DEVICE(0x0c45, 0x60fa, BRIDGE_SN9C105), }, + { SN9C102_USB_DEVICE(0x0c45, 0x60fb, BRIDGE_SN9C105), }, + { SN9C102_USB_DEVICE(0x0c45, 0x60fc, BRIDGE_SN9C105), }, + { SN9C102_USB_DEVICE(0x0c45, 0x60fe, BRIDGE_SN9C105), }, + /* SN9C120 */ + { SN9C102_USB_DEVICE(0x0c45, 0x6130, BRIDGE_SN9C120), }, + { SN9C102_USB_DEVICE(0x0c45, 0x613a, BRIDGE_SN9C120), }, + { SN9C102_USB_DEVICE(0x0c45, 0x613b, BRIDGE_SN9C120), }, + { SN9C102_USB_DEVICE(0x0c45, 0x613c, BRIDGE_SN9C120), }, + { SN9C102_USB_DEVICE(0x0c45, 0x613e, BRIDGE_SN9C120), }, + { } +}; + +/* + Probing functions: on success, you must attach the sensor to the camera + by calling sn9c102_attach_sensor(). + To enable the I2C communication, you might need to perform a really basic + initialization of the SN9C1XX chip. + Functions must return 0 on success, the appropriate error otherwise. +*/ +extern int sn9c102_probe_hv7131d(struct sn9c102_device* cam); +extern int sn9c102_probe_mi0343(struct sn9c102_device* cam); +extern int sn9c102_probe_ov7630(struct sn9c102_device* cam); +extern int sn9c102_probe_ov7660(struct sn9c102_device* cam); +extern int sn9c102_probe_pas106b(struct sn9c102_device* cam); +extern int sn9c102_probe_pas202bcb(struct sn9c102_device* cam); +extern int sn9c102_probe_tas5110c1b(struct sn9c102_device* cam); +extern int sn9c102_probe_tas5130d1b(struct sn9c102_device* cam); + +/* + Add the above entries to this table. Be sure to add the entry in the right + place, since, on failure, the next probing routine is called according to + the order of the list below, from top to bottom. +*/ +static int (*sn9c102_sensor_table[])(struct sn9c102_device*) = { + &sn9c102_probe_mi0343, /* strong detection based on SENSOR ids */ + &sn9c102_probe_pas106b, /* strong detection based on SENSOR ids */ + &sn9c102_probe_pas202bcb, /* strong detection based on SENSOR ids */ + &sn9c102_probe_hv7131d, /* strong detection based on SENSOR ids */ + &sn9c102_probe_ov7630, /* strong detection based on SENSOR ids */ + &sn9c102_probe_ov7660, /* strong detection based on SENSOR ids */ + &sn9c102_probe_tas5110c1b, /* detection based on USB pid/vid */ + &sn9c102_probe_tas5130d1b, /* detection based on USB pid/vid */ + NULL, +}; + +#endif /* _SN9C102_DEVTABLE_H_ */ diff --git a/drivers/media/video/sn9c102/sn9c102_hv7131d.c b/drivers/media/video/sn9c102/sn9c102_hv7131d.c index c4117bf64b69..7ae368f60d89 100644 --- a/drivers/media/video/sn9c102/sn9c102_hv7131d.c +++ b/drivers/media/video/sn9c102/sn9c102_hv7131d.c @@ -1,8 +1,8 @@ /*************************************************************************** - * Plug-in for HV7131D image sensor connected to the SN9C10x PC Camera * + * Plug-in for HV7131D image sensor connected to the SN9C1xx PC Camera * * Controllers * * * - * Copyright (C) 2004-2006 by Luca Risolia * + * Copyright (C) 2004-2007 by Luca Risolia * * * * 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 * @@ -124,7 +124,7 @@ static int hv7131d_set_ctrl(struct sn9c102_device* cam, static int hv7131d_set_crop(struct sn9c102_device* cam, const struct v4l2_rect* rect) { - struct sn9c102_sensor* s = &hv7131d; + struct sn9c102_sensor* s = sn9c102_get_sensor(cam); int err = 0; u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 2, v_start = (u8)(rect->top - s->cropcap.bounds.top) + 2; @@ -153,6 +153,7 @@ static int hv7131d_set_pix_format(struct sn9c102_device* cam, static struct sn9c102_sensor hv7131d = { .name = "HV7131D", .maintainer = "Luca Risolia ", + .supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102 | BRIDGE_SN9C103, .sysfs_ops = SN9C102_I2C_READ | SN9C102_I2C_WRITE, .frequency = SN9C102_I2C_100KHZ, .interface = SN9C102_I2C_2WIRES, diff --git a/drivers/media/video/sn9c102/sn9c102_mi0343.c b/drivers/media/video/sn9c102/sn9c102_mi0343.c index 4169ea4a2e20..a33d1bc10f90 100644 --- a/drivers/media/video/sn9c102/sn9c102_mi0343.c +++ b/drivers/media/video/sn9c102/sn9c102_mi0343.c @@ -1,8 +1,8 @@ /*************************************************************************** - * Plug-in for MI-0343 image sensor connected to the SN9C10x PC Camera * + * Plug-in for MI-0343 image sensor connected to the SN9C1xx PC Camera * * Controllers * * * - * Copyright (C) 2004-2006 by Luca Risolia * + * Copyright (C) 2004-2007 by Luca Risolia * * * * 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 * @@ -201,7 +201,7 @@ static int mi0343_set_ctrl(struct sn9c102_device* cam, static int mi0343_set_crop(struct sn9c102_device* cam, const struct v4l2_rect* rect) { - struct sn9c102_sensor* s = &mi0343; + struct sn9c102_sensor* s = sn9c102_get_sensor(cam); int err = 0; u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 0, v_start = (u8)(rect->top - s->cropcap.bounds.top) + 2; @@ -237,6 +237,7 @@ static int mi0343_set_pix_format(struct sn9c102_device* cam, static struct sn9c102_sensor mi0343 = { .name = "MI-0343", .maintainer = "Luca Risolia ", + .supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102 | BRIDGE_SN9C103, .frequency = SN9C102_I2C_100KHZ, .interface = SN9C102_I2C_2WIRES, .i2c_slave_id = 0x5d, diff --git a/drivers/media/video/sn9c102/sn9c102_ov7630.c b/drivers/media/video/sn9c102/sn9c102_ov7630.c index 3da042021787..7df09ff38e63 100644 --- a/drivers/media/video/sn9c102/sn9c102_ov7630.c +++ b/drivers/media/video/sn9c102/sn9c102_ov7630.c @@ -1,8 +1,8 @@ /*************************************************************************** - * Plug-in for OV7630 image sensor connected to the SN9C10x PC Camera * + * Plug-in for OV7630 image sensor connected to the SN9C1xx PC Camera * * Controllers * * * - * Copyright (C) 2005-2006 by Luca Risolia * + * Copyright (C) 2006-2007 by Luca Risolia * * * * 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 * @@ -29,13 +29,17 @@ static int ov7630_init(struct sn9c102_device* cam) { int err = 0; + switch (sn9c102_get_bridge(cam)) { + case BRIDGE_SN9C101: + case BRIDGE_SN9C102: err += sn9c102_write_reg(cam, 0x00, 0x14); err += sn9c102_write_reg(cam, 0x60, 0x17); err += sn9c102_write_reg(cam, 0x0f, 0x18); err += sn9c102_write_reg(cam, 0x50, 0x19); - err += sn9c102_i2c_write(cam, 0x12, 0x80); - err += sn9c102_i2c_write(cam, 0x11, 0x01); + err += sn9c102_i2c_write(cam, 0x12, 0x8d); + err += sn9c102_i2c_write(cam, 0x12, 0x0d); + err += sn9c102_i2c_write(cam, 0x11, 0x00); err += sn9c102_i2c_write(cam, 0x15, 0x34); err += sn9c102_i2c_write(cam, 0x16, 0x03); err += sn9c102_i2c_write(cam, 0x17, 0x1c); @@ -43,14 +47,72 @@ static int ov7630_init(struct sn9c102_device* cam) err += sn9c102_i2c_write(cam, 0x19, 0x06); err += sn9c102_i2c_write(cam, 0x1a, 0xf6); err += sn9c102_i2c_write(cam, 0x1b, 0x04); - err += sn9c102_i2c_write(cam, 0x20, 0xf6); + err += sn9c102_i2c_write(cam, 0x20, 0x44); + err += sn9c102_i2c_write(cam, 0x23, 0xee); + err += sn9c102_i2c_write(cam, 0x26, 0xa0); + err += sn9c102_i2c_write(cam, 0x27, 0x9a); + err += sn9c102_i2c_write(cam, 0x28, 0x20); + err += sn9c102_i2c_write(cam, 0x29, 0x30); + err += sn9c102_i2c_write(cam, 0x2f, 0x3d); + err += sn9c102_i2c_write(cam, 0x30, 0x24); + err += sn9c102_i2c_write(cam, 0x32, 0x86); + err += sn9c102_i2c_write(cam, 0x60, 0xa9); + err += sn9c102_i2c_write(cam, 0x61, 0x42); + err += sn9c102_i2c_write(cam, 0x65, 0x00); + err += sn9c102_i2c_write(cam, 0x69, 0x38); + err += sn9c102_i2c_write(cam, 0x6f, 0x88); + err += sn9c102_i2c_write(cam, 0x70, 0x0b); + err += sn9c102_i2c_write(cam, 0x71, 0x00); + err += sn9c102_i2c_write(cam, 0x74, 0x21); + err += sn9c102_i2c_write(cam, 0x7d, 0xf7); + break; + case BRIDGE_SN9C103: + err += sn9c102_write_reg(cam, 0x00, 0x02); + err += sn9c102_write_reg(cam, 0x00, 0x03); + err += sn9c102_write_reg(cam, 0x1a, 0x04); + err += sn9c102_write_reg(cam, 0x20, 0x05); + err += sn9c102_write_reg(cam, 0x20, 0x06); + err += sn9c102_write_reg(cam, 0x20, 0x07); + err += sn9c102_write_reg(cam, 0x03, 0x10); + err += sn9c102_write_reg(cam, 0x0a, 0x14); + err += sn9c102_write_reg(cam, 0x60, 0x17); + err += sn9c102_write_reg(cam, 0x0f, 0x18); + err += sn9c102_write_reg(cam, 0x50, 0x19); + err += sn9c102_write_reg(cam, 0x1d, 0x1a); + err += sn9c102_write_reg(cam, 0x10, 0x1b); + err += sn9c102_write_reg(cam, 0x02, 0x1c); + err += sn9c102_write_reg(cam, 0x03, 0x1d); + err += sn9c102_write_reg(cam, 0x0f, 0x1e); + err += sn9c102_write_reg(cam, 0x0c, 0x1f); + err += sn9c102_write_reg(cam, 0x00, 0x20); + err += sn9c102_write_reg(cam, 0x10, 0x21); + err += sn9c102_write_reg(cam, 0x20, 0x22); + err += sn9c102_write_reg(cam, 0x30, 0x23); + err += sn9c102_write_reg(cam, 0x40, 0x24); + err += sn9c102_write_reg(cam, 0x50, 0x25); + err += sn9c102_write_reg(cam, 0x60, 0x26); + err += sn9c102_write_reg(cam, 0x70, 0x27); + err += sn9c102_write_reg(cam, 0x80, 0x28); + err += sn9c102_write_reg(cam, 0x90, 0x29); + err += sn9c102_write_reg(cam, 0xa0, 0x2a); + err += sn9c102_write_reg(cam, 0xb0, 0x2b); + err += sn9c102_write_reg(cam, 0xc0, 0x2c); + err += sn9c102_write_reg(cam, 0xd0, 0x2d); + err += sn9c102_write_reg(cam, 0xe0, 0x2e); + err += sn9c102_write_reg(cam, 0xf0, 0x2f); + err += sn9c102_write_reg(cam, 0xff, 0x30); + + err += sn9c102_i2c_write(cam, 0x12, 0x8d); + err += sn9c102_i2c_write(cam, 0x12, 0x0d); + err += sn9c102_i2c_write(cam, 0x15, 0x34); + err += sn9c102_i2c_write(cam, 0x11, 0x01); + err += sn9c102_i2c_write(cam, 0x1b, 0x04); + err += sn9c102_i2c_write(cam, 0x20, 0x44); err += sn9c102_i2c_write(cam, 0x23, 0xee); err += sn9c102_i2c_write(cam, 0x26, 0xa0); err += sn9c102_i2c_write(cam, 0x27, 0x9a); - err += sn9c102_i2c_write(cam, 0x28, 0xa0); + err += sn9c102_i2c_write(cam, 0x28, 0x20); err += sn9c102_i2c_write(cam, 0x29, 0x30); - err += sn9c102_i2c_write(cam, 0x2a, 0xa0); - err += sn9c102_i2c_write(cam, 0x2b, 0x1f); err += sn9c102_i2c_write(cam, 0x2f, 0x3d); err += sn9c102_i2c_write(cam, 0x30, 0x24); err += sn9c102_i2c_write(cam, 0x32, 0x86); @@ -63,45 +125,97 @@ static int ov7630_init(struct sn9c102_device* cam) err += sn9c102_i2c_write(cam, 0x71, 0x00); err += sn9c102_i2c_write(cam, 0x74, 0x21); err += sn9c102_i2c_write(cam, 0x7d, 0xf7); + break; + default: + break; + } return err; } -static int ov7630_set_ctrl(struct sn9c102_device* cam, - const struct v4l2_control* ctrl) +static int ov7630_get_ctrl(struct sn9c102_device* cam, + struct v4l2_control* ctrl) { int err = 0; switch (ctrl->id) { case V4L2_CID_EXPOSURE: - err += sn9c102_i2c_write(cam, 0x10, ctrl->value >> 2); - err += sn9c102_i2c_write(cam, 0x76, ctrl->value & 0x03); + if ((ctrl->value = sn9c102_i2c_read(cam, 0x10)) < 0) + return -EIO; break; case V4L2_CID_RED_BALANCE: - err += sn9c102_i2c_write(cam, 0x02, ctrl->value); + ctrl->value = sn9c102_pread_reg(cam, 0x07); break; case V4L2_CID_BLUE_BALANCE: - err += sn9c102_i2c_write(cam, 0x01, ctrl->value); + ctrl->value = sn9c102_pread_reg(cam, 0x06); + break; + case SN9C102_V4L2_CID_GREEN_BALANCE: + ctrl->value = sn9c102_pread_reg(cam, 0x05); break; case V4L2_CID_GAIN: - err += sn9c102_i2c_write(cam, 0x00, ctrl->value); + if ((ctrl->value = sn9c102_i2c_read(cam, 0x00)) < 0) + return -EIO; + ctrl->value &= 0x3f; + break; + case V4L2_CID_DO_WHITE_BALANCE: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x0c)) < 0) + return -EIO; + ctrl->value &= 0x3f; + break; + case V4L2_CID_WHITENESS: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x0d)) < 0) + return -EIO; + ctrl->value &= 0x3f; + break; + case V4L2_CID_AUTOGAIN: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x13)) < 0) + return -EIO; + ctrl->value &= 0x01; + break; + case V4L2_CID_VFLIP: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x75)) < 0) + return -EIO; + ctrl->value = (ctrl->value & 0x80) ? 1 : 0; + break; + case SN9C102_V4L2_CID_GAMMA: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x14)) < 0) + return -EIO; + ctrl->value = (ctrl->value & 0x02) ? 1 : 0; + break; + case SN9C102_V4L2_CID_BAND_FILTER: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x2d)) < 0) + return -EIO; + ctrl->value = (ctrl->value & 0x02) ? 1 : 0; + break; + default: + return -EINVAL; + } + + return err ? -EIO : 0; +} + + +static int ov7630_set_ctrl(struct sn9c102_device* cam, + const struct v4l2_control* ctrl) +{ + int err = 0; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + err += sn9c102_i2c_write(cam, 0x10, ctrl->value); break; - case V4L2_CID_CONTRAST: - err += ctrl->value ? sn9c102_i2c_write(cam, 0x05, - (ctrl->value-1) | 0x20) - : sn9c102_i2c_write(cam, 0x05, 0x00); + case V4L2_CID_RED_BALANCE: + err += sn9c102_write_reg(cam, ctrl->value, 0x07); break; - case V4L2_CID_BRIGHTNESS: - err += sn9c102_i2c_write(cam, 0x06, ctrl->value); + case V4L2_CID_BLUE_BALANCE: + err += sn9c102_write_reg(cam, ctrl->value, 0x06); break; - case V4L2_CID_SATURATION: - err += sn9c102_i2c_write(cam, 0x03, ctrl->value << 4); + case SN9C102_V4L2_CID_GREEN_BALANCE: + err += sn9c102_write_reg(cam, ctrl->value, 0x05); break; - case V4L2_CID_HUE: - err += ctrl->value ? sn9c102_i2c_write(cam, 0x04, - (ctrl->value-1) | 0x20) - : sn9c102_i2c_write(cam, 0x04, 0x00); + case V4L2_CID_GAIN: + err += sn9c102_i2c_write(cam, 0x00, ctrl->value); break; case V4L2_CID_DO_WHITE_BALANCE: err += sn9c102_i2c_write(cam, 0x0c, ctrl->value); @@ -109,23 +223,15 @@ static int ov7630_set_ctrl(struct sn9c102_device* cam, case V4L2_CID_WHITENESS: err += sn9c102_i2c_write(cam, 0x0d, ctrl->value); break; - case V4L2_CID_AUTO_WHITE_BALANCE: - err += sn9c102_i2c_write(cam, 0x12, (ctrl->value << 2) | 0x78); - break; case V4L2_CID_AUTOGAIN: - err += sn9c102_i2c_write(cam, 0x13, ctrl->value); + err += sn9c102_i2c_write(cam, 0x13, ctrl->value | + (ctrl->value << 1)); break; case V4L2_CID_VFLIP: err += sn9c102_i2c_write(cam, 0x75, 0x0e | (ctrl->value << 7)); break; - case V4L2_CID_BLACK_LEVEL: - err += sn9c102_i2c_write(cam, 0x25, ctrl->value); - break; - case SN9C102_V4L2_CID_BRIGHT_LEVEL: - err += sn9c102_i2c_write(cam, 0x24, ctrl->value); - break; case SN9C102_V4L2_CID_GAMMA: - err += sn9c102_i2c_write(cam, 0x14, (ctrl->value << 2) | 0x80); + err += sn9c102_i2c_write(cam, 0x14, ctrl->value << 2); break; case SN9C102_V4L2_CID_BAND_FILTER: err += sn9c102_i2c_write(cam, 0x2d, ctrl->value << 2); @@ -141,10 +247,12 @@ static int ov7630_set_ctrl(struct sn9c102_device* cam, static int ov7630_set_crop(struct sn9c102_device* cam, const struct v4l2_rect* rect) { - struct sn9c102_sensor* s = &ov7630; + struct sn9c102_sensor* s = sn9c102_get_sensor(cam); int err = 0; - u8 v_start = (u8)(rect->top - s->cropcap.bounds.top) + 1; + u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 1, + v_start = (u8)(rect->top - s->cropcap.bounds.top) + 1; + err += sn9c102_write_reg(cam, h_start, 0x12); err += sn9c102_write_reg(cam, v_start, 0x13); return err; @@ -168,7 +276,8 @@ static int ov7630_set_pix_format(struct sn9c102_device* cam, static struct sn9c102_sensor ov7630 = { .name = "OV7630", .maintainer = "Luca Risolia ", - .sysfs_ops = SN9C102_I2C_WRITE, + .supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102 | BRIDGE_SN9C103, + .sysfs_ops = SN9C102_I2C_READ | SN9C102_I2C_WRITE, .frequency = SN9C102_I2C_100KHZ, .interface = SN9C102_I2C_2WIRES, .i2c_slave_id = 0x21, @@ -184,74 +293,24 @@ static struct sn9c102_sensor ov7630 = { .default_value = 0x14, .flags = 0, }, - { - .id = V4L2_CID_HUE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "hue", - .minimum = 0x00, - .maximum = 0x1f+1, - .step = 0x01, - .default_value = 0x00, - .flags = 0, - }, - { - .id = V4L2_CID_SATURATION, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "saturation", - .minimum = 0x00, - .maximum = 0x0f, - .step = 0x01, - .default_value = 0x08, - .flags = 0, - }, - { - .id = V4L2_CID_CONTRAST, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "contrast", - .minimum = 0x00, - .maximum = 0x1f+1, - .step = 0x01, - .default_value = 0x00, - .flags = 0, - }, { .id = V4L2_CID_EXPOSURE, .type = V4L2_CTRL_TYPE_INTEGER, .name = "exposure", - .minimum = 0x000, - .maximum = 0x3ff, - .step = 0x001, - .default_value = 0x83<<2, - .flags = 0, - }, - { - .id = V4L2_CID_RED_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "red balance", - .minimum = 0x00, - .maximum = 0xff, - .step = 0x01, - .default_value = 0x3a, - .flags = 0, - }, - { - .id = V4L2_CID_BLUE_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "blue balance", .minimum = 0x00, .maximum = 0xff, .step = 0x01, - .default_value = 0x77, + .default_value = 0x60, .flags = 0, }, { - .id = V4L2_CID_BRIGHTNESS, + .id = V4L2_CID_WHITENESS, .type = V4L2_CTRL_TYPE_INTEGER, - .name = "brightness", + .name = "white balance background: red", .minimum = 0x00, - .maximum = 0xff, + .maximum = 0x3f, .step = 0x01, - .default_value = 0xa0, + .default_value = 0x20, .flags = 0, }, { @@ -265,31 +324,31 @@ static struct sn9c102_sensor ov7630 = { .flags = 0, }, { - .id = V4L2_CID_WHITENESS, + .id = V4L2_CID_RED_BALANCE, .type = V4L2_CTRL_TYPE_INTEGER, - .name = "white balance background: red", + .name = "red balance", .minimum = 0x00, - .maximum = 0x3f, + .maximum = 0x7f, .step = 0x01, .default_value = 0x20, .flags = 0, }, { - .id = V4L2_CID_AUTO_WHITE_BALANCE, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "auto white balance", + .id = V4L2_CID_BLUE_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "blue balance", .minimum = 0x00, - .maximum = 0x01, + .maximum = 0x7f, .step = 0x01, - .default_value = 0x01, + .default_value = 0x20, .flags = 0, }, { .id = V4L2_CID_AUTOGAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "gain & exposure mode", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "auto adjust", .minimum = 0x00, - .maximum = 0x03, + .maximum = 0x01, .step = 0x01, .default_value = 0x00, .flags = 0, @@ -305,23 +364,13 @@ static struct sn9c102_sensor ov7630 = { .flags = 0, }, { - .id = V4L2_CID_BLACK_LEVEL, + .id = SN9C102_V4L2_CID_GREEN_BALANCE, .type = V4L2_CTRL_TYPE_INTEGER, - .name = "black pixel ratio", - .minimum = 0x01, - .maximum = 0x9a, - .step = 0x01, - .default_value = 0x8a, - .flags = 0, - }, - { - .id = SN9C102_V4L2_CID_BRIGHT_LEVEL, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "bright pixel ratio", - .minimum = 0x01, - .maximum = 0x9a, + .name = "green balance", + .minimum = 0x00, + .maximum = 0x7f, .step = 0x01, - .default_value = 0x10, + .default_value = 0x20, .flags = 0, }, { @@ -345,6 +394,7 @@ static struct sn9c102_sensor ov7630 = { .flags = 0, }, }, + .get_ctrl = &ov7630_get_ctrl, .set_ctrl = &ov7630_set_ctrl, .cropcap = { .bounds = { @@ -364,7 +414,7 @@ static struct sn9c102_sensor ov7630 = { .pix_format = { .width = 640, .height = 480, - .pixelformat = V4L2_PIX_FMT_SBGGR8, + .pixelformat = V4L2_PIX_FMT_SN9C10X, .priv = 8, }, .set_pix_format = &ov7630_set_pix_format @@ -373,28 +423,36 @@ static struct sn9c102_sensor ov7630 = { int sn9c102_probe_ov7630(struct sn9c102_device* cam) { - const struct usb_device_id ov7630_id_table[] = { - { USB_DEVICE(0x0c45, 0x602c), }, - { USB_DEVICE(0x0c45, 0x602d), }, - { USB_DEVICE(0x0c45, 0x608f), }, - { USB_DEVICE(0x0c45, 0x60b0), }, - { } - }; - int err = 0; - - if (!sn9c102_match_id(cam, ov7630_id_table)) - return -ENODEV; + int pid, ver, err = 0; + switch (sn9c102_get_bridge(cam)) { + case BRIDGE_SN9C101: + case BRIDGE_SN9C102: err += sn9c102_write_reg(cam, 0x01, 0x01); err += sn9c102_write_reg(cam, 0x00, 0x01); err += sn9c102_write_reg(cam, 0x28, 0x17); - if (err) - return -EIO; + break; + case BRIDGE_SN9C103: /* do _not_ change anything! */ + err += sn9c102_write_reg(cam, 0x09, 0x01); + err += sn9c102_write_reg(cam, 0x42, 0x01); + err += sn9c102_write_reg(cam, 0x28, 0x17); + err += sn9c102_write_reg(cam, 0x44, 0x02); + pid = sn9c102_i2c_try_read(cam, &ov7630, 0x0a); + if (err || pid < 0) { /* try a different initialization */ + err = sn9c102_write_reg(cam, 0x01, 0x01); + err += sn9c102_write_reg(cam, 0x00, 0x01); + } + break; + default: + break; + } - err += sn9c102_i2c_try_write(cam, &ov7630, 0x0b, 0); - if (err) + pid = sn9c102_i2c_try_read(cam, &ov7630, 0x0a); + ver = sn9c102_i2c_try_read(cam, &ov7630, 0x0b); + if (err || pid < 0 || ver < 0) + return -EIO; + if (pid != 0x76 || ver != 0x31) return -ENODEV; - sn9c102_attach_sensor(cam, &ov7630); return 0; diff --git a/drivers/media/video/sn9c102/sn9c102_ov7660.c b/drivers/media/video/sn9c102/sn9c102_ov7660.c new file mode 100644 index 000000000000..d670c24d4435 --- /dev/null +++ b/drivers/media/video/sn9c102/sn9c102_ov7660.c @@ -0,0 +1,592 @@ +/*************************************************************************** + * Plug-in for OV7660 image sensor connected to the SN9C1xx PC Camera * + * Controllers * + * * + * Copyright (C) 2007 by Luca Risolia * + * * + * 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 "sn9c102_sensor.h" + + +static struct sn9c102_sensor ov7660; + + +static int ov7660_init(struct sn9c102_device* cam) +{ + int err = 0; + + err += sn9c102_write_reg(cam, 0x40, 0x02); + err += sn9c102_write_reg(cam, 0x00, 0x03); + err += sn9c102_write_reg(cam, 0x1a, 0x04); + err += sn9c102_write_reg(cam, 0x03, 0x10); + err += sn9c102_write_reg(cam, 0x08, 0x14); + err += sn9c102_write_reg(cam, 0x20, 0x17); + err += sn9c102_write_reg(cam, 0x8b, 0x18); + err += sn9c102_write_reg(cam, 0x00, 0x19); + err += sn9c102_write_reg(cam, 0x1d, 0x1a); + err += sn9c102_write_reg(cam, 0x10, 0x1b); + err += sn9c102_write_reg(cam, 0x02, 0x1c); + err += sn9c102_write_reg(cam, 0x03, 0x1d); + err += sn9c102_write_reg(cam, 0x0f, 0x1e); + err += sn9c102_write_reg(cam, 0x0c, 0x1f); + err += sn9c102_write_reg(cam, 0x00, 0x20); + err += sn9c102_write_reg(cam, 0x29, 0x21); + err += sn9c102_write_reg(cam, 0x40, 0x22); + err += sn9c102_write_reg(cam, 0x54, 0x23); + err += sn9c102_write_reg(cam, 0x66, 0x24); + err += sn9c102_write_reg(cam, 0x76, 0x25); + err += sn9c102_write_reg(cam, 0x85, 0x26); + err += sn9c102_write_reg(cam, 0x94, 0x27); + err += sn9c102_write_reg(cam, 0xa1, 0x28); + err += sn9c102_write_reg(cam, 0xae, 0x29); + err += sn9c102_write_reg(cam, 0xbb, 0x2a); + err += sn9c102_write_reg(cam, 0xc7, 0x2b); + err += sn9c102_write_reg(cam, 0xd3, 0x2c); + err += sn9c102_write_reg(cam, 0xde, 0x2d); + err += sn9c102_write_reg(cam, 0xea, 0x2e); + err += sn9c102_write_reg(cam, 0xf4, 0x2f); + err += sn9c102_write_reg(cam, 0xff, 0x30); + err += sn9c102_write_reg(cam, 0x00, 0x3F); + err += sn9c102_write_reg(cam, 0xC7, 0x40); + err += sn9c102_write_reg(cam, 0x01, 0x41); + err += sn9c102_write_reg(cam, 0x44, 0x42); + err += sn9c102_write_reg(cam, 0x00, 0x43); + err += sn9c102_write_reg(cam, 0x44, 0x44); + err += sn9c102_write_reg(cam, 0x00, 0x45); + err += sn9c102_write_reg(cam, 0x44, 0x46); + err += sn9c102_write_reg(cam, 0x00, 0x47); + err += sn9c102_write_reg(cam, 0xC7, 0x48); + err += sn9c102_write_reg(cam, 0x01, 0x49); + err += sn9c102_write_reg(cam, 0xC7, 0x4A); + err += sn9c102_write_reg(cam, 0x01, 0x4B); + err += sn9c102_write_reg(cam, 0xC7, 0x4C); + err += sn9c102_write_reg(cam, 0x01, 0x4D); + err += sn9c102_write_reg(cam, 0x44, 0x4E); + err += sn9c102_write_reg(cam, 0x00, 0x4F); + err += sn9c102_write_reg(cam, 0x44, 0x50); + err += sn9c102_write_reg(cam, 0x00, 0x51); + err += sn9c102_write_reg(cam, 0x44, 0x52); + err += sn9c102_write_reg(cam, 0x00, 0x53); + err += sn9c102_write_reg(cam, 0xC7, 0x54); + err += sn9c102_write_reg(cam, 0x01, 0x55); + err += sn9c102_write_reg(cam, 0xC7, 0x56); + err += sn9c102_write_reg(cam, 0x01, 0x57); + err += sn9c102_write_reg(cam, 0xC7, 0x58); + err += sn9c102_write_reg(cam, 0x01, 0x59); + err += sn9c102_write_reg(cam, 0x44, 0x5A); + err += sn9c102_write_reg(cam, 0x00, 0x5B); + err += sn9c102_write_reg(cam, 0x44, 0x5C); + err += sn9c102_write_reg(cam, 0x00, 0x5D); + err += sn9c102_write_reg(cam, 0x44, 0x5E); + err += sn9c102_write_reg(cam, 0x00, 0x5F); + err += sn9c102_write_reg(cam, 0xC7, 0x60); + err += sn9c102_write_reg(cam, 0x01, 0x61); + err += sn9c102_write_reg(cam, 0xC7, 0x62); + err += sn9c102_write_reg(cam, 0x01, 0x63); + err += sn9c102_write_reg(cam, 0xC7, 0x64); + err += sn9c102_write_reg(cam, 0x01, 0x65); + err += sn9c102_write_reg(cam, 0x44, 0x66); + err += sn9c102_write_reg(cam, 0x00, 0x67); + err += sn9c102_write_reg(cam, 0x44, 0x68); + err += sn9c102_write_reg(cam, 0x00, 0x69); + err += sn9c102_write_reg(cam, 0x44, 0x6A); + err += sn9c102_write_reg(cam, 0x00, 0x6B); + err += sn9c102_write_reg(cam, 0xC7, 0x6C); + err += sn9c102_write_reg(cam, 0x01, 0x6D); + err += sn9c102_write_reg(cam, 0xC7, 0x6E); + err += sn9c102_write_reg(cam, 0x01, 0x6F); + err += sn9c102_write_reg(cam, 0xC7, 0x70); + err += sn9c102_write_reg(cam, 0x01, 0x71); + err += sn9c102_write_reg(cam, 0x44, 0x72); + err += sn9c102_write_reg(cam, 0x00, 0x73); + err += sn9c102_write_reg(cam, 0x44, 0x74); + err += sn9c102_write_reg(cam, 0x00, 0x75); + err += sn9c102_write_reg(cam, 0x44, 0x76); + err += sn9c102_write_reg(cam, 0x00, 0x77); + err += sn9c102_write_reg(cam, 0xC7, 0x78); + err += sn9c102_write_reg(cam, 0x01, 0x79); + err += sn9c102_write_reg(cam, 0xC7, 0x7A); + err += sn9c102_write_reg(cam, 0x01, 0x7B); + err += sn9c102_write_reg(cam, 0xC7, 0x7C); + err += sn9c102_write_reg(cam, 0x01, 0x7D); + err += sn9c102_write_reg(cam, 0x44, 0x7E); + err += sn9c102_write_reg(cam, 0x00, 0x7F); + err += sn9c102_write_reg(cam, 0x14, 0x84); + err += sn9c102_write_reg(cam, 0x00, 0x85); + err += sn9c102_write_reg(cam, 0x27, 0x86); + err += sn9c102_write_reg(cam, 0x00, 0x87); + err += sn9c102_write_reg(cam, 0x07, 0x88); + err += sn9c102_write_reg(cam, 0x00, 0x89); + err += sn9c102_write_reg(cam, 0xEC, 0x8A); + err += sn9c102_write_reg(cam, 0x0f, 0x8B); + err += sn9c102_write_reg(cam, 0xD8, 0x8C); + err += sn9c102_write_reg(cam, 0x0f, 0x8D); + err += sn9c102_write_reg(cam, 0x3D, 0x8E); + err += sn9c102_write_reg(cam, 0x00, 0x8F); + err += sn9c102_write_reg(cam, 0x3D, 0x90); + err += sn9c102_write_reg(cam, 0x00, 0x91); + err += sn9c102_write_reg(cam, 0xCD, 0x92); + err += sn9c102_write_reg(cam, 0x0f, 0x93); + err += sn9c102_write_reg(cam, 0xf7, 0x94); + err += sn9c102_write_reg(cam, 0x0f, 0x95); + err += sn9c102_write_reg(cam, 0x0C, 0x96); + err += sn9c102_write_reg(cam, 0x00, 0x97); + err += sn9c102_write_reg(cam, 0x00, 0x98); + err += sn9c102_write_reg(cam, 0x66, 0x99); + err += sn9c102_write_reg(cam, 0x05, 0x9A); + err += sn9c102_write_reg(cam, 0x00, 0x9B); + err += sn9c102_write_reg(cam, 0x04, 0x9C); + err += sn9c102_write_reg(cam, 0x00, 0x9D); + err += sn9c102_write_reg(cam, 0x08, 0x9E); + err += sn9c102_write_reg(cam, 0x00, 0x9F); + err += sn9c102_write_reg(cam, 0x2D, 0xC0); + err += sn9c102_write_reg(cam, 0x2D, 0xC1); + err += sn9c102_write_reg(cam, 0x3A, 0xC2); + err += sn9c102_write_reg(cam, 0x05, 0xC3); + err += sn9c102_write_reg(cam, 0x04, 0xC4); + err += sn9c102_write_reg(cam, 0x3F, 0xC5); + err += sn9c102_write_reg(cam, 0x00, 0xC6); + err += sn9c102_write_reg(cam, 0x00, 0xC7); + err += sn9c102_write_reg(cam, 0x50, 0xC8); + err += sn9c102_write_reg(cam, 0x3C, 0xC9); + err += sn9c102_write_reg(cam, 0x28, 0xCA); + err += sn9c102_write_reg(cam, 0xD8, 0xCB); + err += sn9c102_write_reg(cam, 0x14, 0xCC); + err += sn9c102_write_reg(cam, 0xEC, 0xCD); + err += sn9c102_write_reg(cam, 0x32, 0xCE); + err += sn9c102_write_reg(cam, 0xDD, 0xCF); + err += sn9c102_write_reg(cam, 0x32, 0xD0); + err += sn9c102_write_reg(cam, 0xDD, 0xD1); + err += sn9c102_write_reg(cam, 0x6A, 0xD2); + err += sn9c102_write_reg(cam, 0x50, 0xD3); + err += sn9c102_write_reg(cam, 0x00, 0xD4); + err += sn9c102_write_reg(cam, 0x00, 0xD5); + err += sn9c102_write_reg(cam, 0x00, 0xD6); + + err += sn9c102_i2c_write(cam, 0x12, 0x80); + err += sn9c102_i2c_write(cam, 0x11, 0x09); + err += sn9c102_i2c_write(cam, 0x00, 0x0A); + err += sn9c102_i2c_write(cam, 0x01, 0x78); + err += sn9c102_i2c_write(cam, 0x02, 0x90); + err += sn9c102_i2c_write(cam, 0x03, 0x00); + err += sn9c102_i2c_write(cam, 0x04, 0x00); + err += sn9c102_i2c_write(cam, 0x05, 0x08); + err += sn9c102_i2c_write(cam, 0x06, 0x0B); + err += sn9c102_i2c_write(cam, 0x07, 0x00); + err += sn9c102_i2c_write(cam, 0x08, 0x1C); + err += sn9c102_i2c_write(cam, 0x09, 0x01); + err += sn9c102_i2c_write(cam, 0x0A, 0x76); + err += sn9c102_i2c_write(cam, 0x0B, 0x60); + err += sn9c102_i2c_write(cam, 0x0C, 0x00); + err += sn9c102_i2c_write(cam, 0x0D, 0x08); + err += sn9c102_i2c_write(cam, 0x0E, 0x04); + err += sn9c102_i2c_write(cam, 0x0F, 0x6F); + err += sn9c102_i2c_write(cam, 0x10, 0x20); + err += sn9c102_i2c_write(cam, 0x11, 0x03); + err += sn9c102_i2c_write(cam, 0x12, 0x05); + err += sn9c102_i2c_write(cam, 0x13, 0xF8); + err += sn9c102_i2c_write(cam, 0x14, 0x2C); + err += sn9c102_i2c_write(cam, 0x15, 0x00); + err += sn9c102_i2c_write(cam, 0x16, 0x02); + err += sn9c102_i2c_write(cam, 0x17, 0x10); + err += sn9c102_i2c_write(cam, 0x18, 0x60); + err += sn9c102_i2c_write(cam, 0x19, 0x02); + err += sn9c102_i2c_write(cam, 0x1A, 0x7B); + err += sn9c102_i2c_write(cam, 0x1B, 0x02); + err += sn9c102_i2c_write(cam, 0x1C, 0x7F); + err += sn9c102_i2c_write(cam, 0x1D, 0xA2); + err += sn9c102_i2c_write(cam, 0x1E, 0x01); + err += sn9c102_i2c_write(cam, 0x1F, 0x0E); + err += sn9c102_i2c_write(cam, 0x20, 0x05); + err += sn9c102_i2c_write(cam, 0x21, 0x05); + err += sn9c102_i2c_write(cam, 0x22, 0x05); + err += sn9c102_i2c_write(cam, 0x23, 0x05); + err += sn9c102_i2c_write(cam, 0x24, 0x68); + err += sn9c102_i2c_write(cam, 0x25, 0x58); + err += sn9c102_i2c_write(cam, 0x26, 0xD4); + err += sn9c102_i2c_write(cam, 0x27, 0x80); + err += sn9c102_i2c_write(cam, 0x28, 0x80); + err += sn9c102_i2c_write(cam, 0x29, 0x30); + err += sn9c102_i2c_write(cam, 0x2A, 0x00); + err += sn9c102_i2c_write(cam, 0x2B, 0x00); + err += sn9c102_i2c_write(cam, 0x2C, 0x80); + err += sn9c102_i2c_write(cam, 0x2D, 0x00); + err += sn9c102_i2c_write(cam, 0x2E, 0x00); + err += sn9c102_i2c_write(cam, 0x2F, 0x0E); + err += sn9c102_i2c_write(cam, 0x30, 0x08); + err += sn9c102_i2c_write(cam, 0x31, 0x30); + err += sn9c102_i2c_write(cam, 0x32, 0xB4); + err += sn9c102_i2c_write(cam, 0x33, 0x00); + err += sn9c102_i2c_write(cam, 0x34, 0x07); + err += sn9c102_i2c_write(cam, 0x35, 0x84); + err += sn9c102_i2c_write(cam, 0x36, 0x00); + err += sn9c102_i2c_write(cam, 0x37, 0x0C); + err += sn9c102_i2c_write(cam, 0x38, 0x02); + err += sn9c102_i2c_write(cam, 0x39, 0x43); + err += sn9c102_i2c_write(cam, 0x3A, 0x00); + err += sn9c102_i2c_write(cam, 0x3B, 0x02); + err += sn9c102_i2c_write(cam, 0x3C, 0x6C); + err += sn9c102_i2c_write(cam, 0x3D, 0x99); + err += sn9c102_i2c_write(cam, 0x3E, 0x0E); + err += sn9c102_i2c_write(cam, 0x3F, 0x41); + err += sn9c102_i2c_write(cam, 0x40, 0xC1); + err += sn9c102_i2c_write(cam, 0x41, 0x22); + err += sn9c102_i2c_write(cam, 0x42, 0x08); + err += sn9c102_i2c_write(cam, 0x43, 0xF0); + err += sn9c102_i2c_write(cam, 0x44, 0x10); + err += sn9c102_i2c_write(cam, 0x45, 0x78); + err += sn9c102_i2c_write(cam, 0x46, 0xA8); + err += sn9c102_i2c_write(cam, 0x47, 0x60); + err += sn9c102_i2c_write(cam, 0x48, 0x80); + err += sn9c102_i2c_write(cam, 0x49, 0x00); + err += sn9c102_i2c_write(cam, 0x4A, 0x00); + err += sn9c102_i2c_write(cam, 0x4B, 0x00); + err += sn9c102_i2c_write(cam, 0x4C, 0x00); + err += sn9c102_i2c_write(cam, 0x4D, 0x00); + err += sn9c102_i2c_write(cam, 0x4E, 0x00); + err += sn9c102_i2c_write(cam, 0x4F, 0x46); + err += sn9c102_i2c_write(cam, 0x50, 0x36); + err += sn9c102_i2c_write(cam, 0x51, 0x0F); + err += sn9c102_i2c_write(cam, 0x52, 0x17); + err += sn9c102_i2c_write(cam, 0x53, 0x7F); + err += sn9c102_i2c_write(cam, 0x54, 0x96); + err += sn9c102_i2c_write(cam, 0x55, 0x40); + err += sn9c102_i2c_write(cam, 0x56, 0x40); + err += sn9c102_i2c_write(cam, 0x57, 0x40); + err += sn9c102_i2c_write(cam, 0x58, 0x0F); + err += sn9c102_i2c_write(cam, 0x59, 0xBA); + err += sn9c102_i2c_write(cam, 0x5A, 0x9A); + err += sn9c102_i2c_write(cam, 0x5B, 0x22); + err += sn9c102_i2c_write(cam, 0x5C, 0xB9); + err += sn9c102_i2c_write(cam, 0x5D, 0x9B); + err += sn9c102_i2c_write(cam, 0x5E, 0x10); + err += sn9c102_i2c_write(cam, 0x5F, 0xF0); + err += sn9c102_i2c_write(cam, 0x60, 0x05); + err += sn9c102_i2c_write(cam, 0x61, 0x60); + err += sn9c102_i2c_write(cam, 0x62, 0x00); + err += sn9c102_i2c_write(cam, 0x63, 0x00); + err += sn9c102_i2c_write(cam, 0x64, 0x50); + err += sn9c102_i2c_write(cam, 0x65, 0x30); + err += sn9c102_i2c_write(cam, 0x66, 0x00); + err += sn9c102_i2c_write(cam, 0x67, 0x80); + err += sn9c102_i2c_write(cam, 0x68, 0x7A); + err += sn9c102_i2c_write(cam, 0x69, 0x90); + err += sn9c102_i2c_write(cam, 0x6A, 0x80); + err += sn9c102_i2c_write(cam, 0x6B, 0x0A); + err += sn9c102_i2c_write(cam, 0x6C, 0x30); + err += sn9c102_i2c_write(cam, 0x6D, 0x48); + err += sn9c102_i2c_write(cam, 0x6E, 0x80); + err += sn9c102_i2c_write(cam, 0x6F, 0x74); + err += sn9c102_i2c_write(cam, 0x70, 0x64); + err += sn9c102_i2c_write(cam, 0x71, 0x60); + err += sn9c102_i2c_write(cam, 0x72, 0x5C); + err += sn9c102_i2c_write(cam, 0x73, 0x58); + err += sn9c102_i2c_write(cam, 0x74, 0x54); + err += sn9c102_i2c_write(cam, 0x75, 0x4C); + err += sn9c102_i2c_write(cam, 0x76, 0x40); + err += sn9c102_i2c_write(cam, 0x77, 0x38); + err += sn9c102_i2c_write(cam, 0x78, 0x34); + err += sn9c102_i2c_write(cam, 0x79, 0x30); + err += sn9c102_i2c_write(cam, 0x7A, 0x2F); + err += sn9c102_i2c_write(cam, 0x7B, 0x2B); + err += sn9c102_i2c_write(cam, 0x7C, 0x03); + err += sn9c102_i2c_write(cam, 0x7D, 0x07); + err += sn9c102_i2c_write(cam, 0x7E, 0x17); + err += sn9c102_i2c_write(cam, 0x7F, 0x34); + err += sn9c102_i2c_write(cam, 0x80, 0x41); + err += sn9c102_i2c_write(cam, 0x81, 0x4D); + err += sn9c102_i2c_write(cam, 0x82, 0x58); + err += sn9c102_i2c_write(cam, 0x83, 0x63); + err += sn9c102_i2c_write(cam, 0x84, 0x6E); + err += sn9c102_i2c_write(cam, 0x85, 0x77); + err += sn9c102_i2c_write(cam, 0x86, 0x87); + err += sn9c102_i2c_write(cam, 0x87, 0x95); + err += sn9c102_i2c_write(cam, 0x88, 0xAF); + err += sn9c102_i2c_write(cam, 0x89, 0xC7); + err += sn9c102_i2c_write(cam, 0x8A, 0xDF); + err += sn9c102_i2c_write(cam, 0x8B, 0x99); + err += sn9c102_i2c_write(cam, 0x8C, 0x99); + err += sn9c102_i2c_write(cam, 0x8D, 0xCF); + err += sn9c102_i2c_write(cam, 0x8E, 0x20); + err += sn9c102_i2c_write(cam, 0x8F, 0x26); + err += sn9c102_i2c_write(cam, 0x90, 0x10); + err += sn9c102_i2c_write(cam, 0x91, 0x0C); + err += sn9c102_i2c_write(cam, 0x92, 0x25); + err += sn9c102_i2c_write(cam, 0x93, 0x00); + err += sn9c102_i2c_write(cam, 0x94, 0x50); + err += sn9c102_i2c_write(cam, 0x95, 0x50); + err += sn9c102_i2c_write(cam, 0x96, 0x00); + err += sn9c102_i2c_write(cam, 0x97, 0x01); + err += sn9c102_i2c_write(cam, 0x98, 0x10); + err += sn9c102_i2c_write(cam, 0x99, 0x40); + err += sn9c102_i2c_write(cam, 0x9A, 0x40); + err += sn9c102_i2c_write(cam, 0x9B, 0x20); + err += sn9c102_i2c_write(cam, 0x9C, 0x00); + err += sn9c102_i2c_write(cam, 0x9D, 0x99); + err += sn9c102_i2c_write(cam, 0x9E, 0x7F); + err += sn9c102_i2c_write(cam, 0x9F, 0x00); + err += sn9c102_i2c_write(cam, 0xA0, 0x00); + err += sn9c102_i2c_write(cam, 0xA1, 0x00); + + return err; +} + + +static int ov7660_get_ctrl(struct sn9c102_device* cam, + struct v4l2_control* ctrl) +{ + int err = 0; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x10)) < 0) + return -EIO; + break; + case V4L2_CID_DO_WHITE_BALANCE: + ctrl->value = sn9c102_pread_reg(cam, 0x02); + ctrl->value = (ctrl->value & 0x04) ? 1 : 0; + break; + case V4L2_CID_RED_BALANCE: + ctrl->value = sn9c102_pread_reg(cam, 0x05); + ctrl->value &= 0x7f; + break; + case V4L2_CID_BLUE_BALANCE: + ctrl->value = sn9c102_pread_reg(cam, 0x06); + ctrl->value &= 0x7f; + break; + case SN9C102_V4L2_CID_GREEN_BALANCE: + ctrl->value = sn9c102_pread_reg(cam, 0x07); + ctrl->value &= 0x7f; + break; + case V4L2_CID_GAIN: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x00)) < 0) + return -EIO; + ctrl->value &= 0x7f; + break; + case V4L2_CID_AUTOGAIN: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x13)) < 0) + return -EIO; + ctrl->value &= 0x01; + break; + default: + return -EINVAL; + } + + return err ? -EIO : 0; +} + + +static int ov7660_set_ctrl(struct sn9c102_device* cam, + const struct v4l2_control* ctrl) +{ + int err = 0; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + err += sn9c102_i2c_write(cam, 0x10, ctrl->value); + break; + case V4L2_CID_DO_WHITE_BALANCE: + err += sn9c102_write_reg(cam, 0x43 | (ctrl->value << 2), 0x02); + break; + case V4L2_CID_RED_BALANCE: + err += sn9c102_write_reg(cam, ctrl->value, 0x05); + break; + case V4L2_CID_BLUE_BALANCE: + err += sn9c102_write_reg(cam, ctrl->value, 0x06); + break; + case SN9C102_V4L2_CID_GREEN_BALANCE: + err += sn9c102_write_reg(cam, ctrl->value, 0x07); + break; + case V4L2_CID_GAIN: + err += sn9c102_i2c_write(cam, 0x00, ctrl->value); + break; + case V4L2_CID_AUTOGAIN: + err += sn9c102_i2c_write(cam, 0x13, 0xf0 | ctrl->value | + (ctrl->value << 1)); + break; + default: + return -EINVAL; + } + + return err ? -EIO : 0; +} + + +static int ov7660_set_crop(struct sn9c102_device* cam, + const struct v4l2_rect* rect) +{ + struct sn9c102_sensor* s = sn9c102_get_sensor(cam); + int err = 0; + u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 1, + v_start = (u8)(rect->top - s->cropcap.bounds.top) + 1; + + err += sn9c102_write_reg(cam, h_start, 0x12); + err += sn9c102_write_reg(cam, v_start, 0x13); + + return err; +} + + +static int ov7660_set_pix_format(struct sn9c102_device* cam, + const struct v4l2_pix_format* pix) +{ + int r0, err = 0; + + r0 = sn9c102_pread_reg(cam, 0x01); + + if (pix->pixelformat == V4L2_PIX_FMT_JPEG) { + err += sn9c102_write_reg(cam, r0 | 0x40, 0x01); + err += sn9c102_write_reg(cam, 0xa2, 0x17); + err += sn9c102_i2c_write(cam, 0x11, 0x00); + } else { + err += sn9c102_write_reg(cam, r0 | 0x40, 0x01); + err += sn9c102_write_reg(cam, 0xa2, 0x17); + err += sn9c102_i2c_write(cam, 0x11, 0x0d); + } + + return err; +} + + +static struct sn9c102_sensor ov7660 = { + .name = "OV7660", + .maintainer = "Luca Risolia ", + .supported_bridge = BRIDGE_SN9C105 | BRIDGE_SN9C120, + .sysfs_ops = SN9C102_I2C_READ | SN9C102_I2C_WRITE, + .frequency = SN9C102_I2C_100KHZ, + .interface = SN9C102_I2C_2WIRES, + .i2c_slave_id = 0x21, + .init = &ov7660_init, + .qctrl = { + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "global gain", + .minimum = 0x00, + .maximum = 0x7f, + .step = 0x01, + .default_value = 0x0a, + .flags = 0, + }, + { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "exposure", + .minimum = 0x00, + .maximum = 0xff, + .step = 0x01, + .default_value = 0x50, + .flags = 0, + }, + { + .id = V4L2_CID_DO_WHITE_BALANCE, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "night mode", + .minimum = 0x00, + .maximum = 0x01, + .step = 0x01, + .default_value = 0x00, + .flags = 0, + }, + { + .id = V4L2_CID_RED_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "red balance", + .minimum = 0x00, + .maximum = 0x7f, + .step = 0x01, + .default_value = 0x1f, + .flags = 0, + }, + { + .id = V4L2_CID_BLUE_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "blue balance", + .minimum = 0x00, + .maximum = 0x7f, + .step = 0x01, + .default_value = 0x1e, + .flags = 0, + }, + { + .id = V4L2_CID_AUTOGAIN, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "auto adjust", + .minimum = 0x00, + .maximum = 0x01, + .step = 0x01, + .default_value = 0x00, + .flags = 0, + }, + { + .id = SN9C102_V4L2_CID_GREEN_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "green balance", + .minimum = 0x00, + .maximum = 0x7f, + .step = 0x01, + .default_value = 0x20, + .flags = 0, + }, + }, + .get_ctrl = &ov7660_get_ctrl, + .set_ctrl = &ov7660_set_ctrl, + .cropcap = { + .bounds = { + .left = 0, + .top = 0, + .width = 640, + .height = 480, + }, + .defrect = { + .left = 0, + .top = 0, + .width = 640, + .height = 480, + }, + }, + .set_crop = &ov7660_set_crop, + .pix_format = { + .width = 640, + .height = 480, + .pixelformat = V4L2_PIX_FMT_JPEG, + .priv = 8, + }, + .set_pix_format = &ov7660_set_pix_format +}; + + +int sn9c102_probe_ov7660(struct sn9c102_device* cam) +{ + int pid, ver, err = 0; + + err += sn9c102_write_reg(cam, 0x01, 0xf1); + err += sn9c102_write_reg(cam, 0x00, 0xf1); + err += sn9c102_write_reg(cam, 0x01, 0x01); + err += sn9c102_write_reg(cam, 0x00, 0x01); + err += sn9c102_write_reg(cam, 0x28, 0x17); + + pid = sn9c102_i2c_try_read(cam, &ov7660, 0x0a); + ver = sn9c102_i2c_try_read(cam, &ov7660, 0x0b); + if (err || pid < 0 || ver < 0) + return -EIO; + if (pid != 0x76 || ver != 0x60) + return -ENODEV; + sn9c102_attach_sensor(cam, &ov7660); + + return 0; +} diff --git a/drivers/media/video/sn9c102/sn9c102_pas106b.c b/drivers/media/video/sn9c102/sn9c102_pas106b.c index 9915944235e8..8d79a5fae5de 100644 --- a/drivers/media/video/sn9c102/sn9c102_pas106b.c +++ b/drivers/media/video/sn9c102/sn9c102_pas106b.c @@ -1,8 +1,8 @@ /*************************************************************************** - * Plug-in for PAS106B image sensor connected to the SN9C10x PC Camera * + * Plug-in for PAS106B image sensor connected to the SN9C1xx PC Camera * * Controllers * * * - * Copyright (C) 2004-2006 by Luca Risolia * + * Copyright (C) 2004-2007 by Luca Risolia * * * * 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 * @@ -143,7 +143,7 @@ static int pas106b_set_ctrl(struct sn9c102_device* cam, static int pas106b_set_crop(struct sn9c102_device* cam, const struct v4l2_rect* rect) { - struct sn9c102_sensor* s = &pas106b; + struct sn9c102_sensor* s = sn9c102_get_sensor(cam); int err = 0; u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 4, v_start = (u8)(rect->top - s->cropcap.bounds.top) + 3; @@ -172,6 +172,7 @@ static int pas106b_set_pix_format(struct sn9c102_device* cam, static struct sn9c102_sensor pas106b = { .name = "PAS106B", .maintainer = "Luca Risolia ", + .supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102 | BRIDGE_SN9C103, .sysfs_ops = SN9C102_I2C_READ | SN9C102_I2C_WRITE, .frequency = SN9C102_I2C_400KHZ | SN9C102_I2C_100KHZ, .interface = SN9C102_I2C_2WIRES, diff --git a/drivers/media/video/sn9c102/sn9c102_pas202bca.c b/drivers/media/video/sn9c102/sn9c102_pas202bca.c deleted file mode 100644 index c8f1ae2152b8..000000000000 --- a/drivers/media/video/sn9c102/sn9c102_pas202bca.c +++ /dev/null @@ -1,238 +0,0 @@ -/*************************************************************************** - * Plug-in for PAS202BCA image sensor connected to the SN9C10x PC Camera * - * Controllers * - * * - * Copyright (C) 2006 by Luca Risolia * - * * - * 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 "sn9c102_sensor.h" - - -static struct sn9c102_sensor pas202bca; - - -static int pas202bca_init(struct sn9c102_device* cam) -{ - int err = 0; - - err += sn9c102_write_reg(cam, 0x00, 0x10); - err += sn9c102_write_reg(cam, 0x00, 0x11); - err += sn9c102_write_reg(cam, 0x00, 0x14); - err += sn9c102_write_reg(cam, 0x20, 0x17); - err += sn9c102_write_reg(cam, 0x30, 0x19); - err += sn9c102_write_reg(cam, 0x09, 0x18); - - err += sn9c102_i2c_write(cam, 0x02, 0x14); - err += sn9c102_i2c_write(cam, 0x03, 0x40); - err += sn9c102_i2c_write(cam, 0x0d, 0x2c); - err += sn9c102_i2c_write(cam, 0x0e, 0x01); - err += sn9c102_i2c_write(cam, 0x0f, 0xa9); - err += sn9c102_i2c_write(cam, 0x10, 0x08); - err += sn9c102_i2c_write(cam, 0x13, 0x63); - err += sn9c102_i2c_write(cam, 0x15, 0x70); - err += sn9c102_i2c_write(cam, 0x11, 0x01); - - msleep(400); - - return err; -} - - -static int pas202bca_set_pix_format(struct sn9c102_device* cam, - const struct v4l2_pix_format* pix) -{ - int err = 0; - - if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X) - err += sn9c102_write_reg(cam, 0x24, 0x17); - else - err += sn9c102_write_reg(cam, 0x20, 0x17); - - return err; -} - - -static int pas202bca_set_ctrl(struct sn9c102_device* cam, - const struct v4l2_control* ctrl) -{ - int err = 0; - - switch (ctrl->id) { - case V4L2_CID_EXPOSURE: - err += sn9c102_i2c_write(cam, 0x04, ctrl->value >> 6); - err += sn9c102_i2c_write(cam, 0x05, ctrl->value & 0x3f); - break; - case V4L2_CID_RED_BALANCE: - err += sn9c102_i2c_write(cam, 0x09, ctrl->value); - break; - case V4L2_CID_BLUE_BALANCE: - err += sn9c102_i2c_write(cam, 0x07, ctrl->value); - break; - case V4L2_CID_GAIN: - err += sn9c102_i2c_write(cam, 0x10, ctrl->value); - break; - case SN9C102_V4L2_CID_GREEN_BALANCE: - err += sn9c102_i2c_write(cam, 0x08, ctrl->value); - break; - case SN9C102_V4L2_CID_DAC_MAGNITUDE: - err += sn9c102_i2c_write(cam, 0x0c, ctrl->value); - break; - default: - return -EINVAL; - } - err += sn9c102_i2c_write(cam, 0x11, 0x01); - - return err ? -EIO : 0; -} - - -static int pas202bca_set_crop(struct sn9c102_device* cam, - const struct v4l2_rect* rect) -{ - struct sn9c102_sensor* s = &pas202bca; - int err = 0; - u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 3, - v_start = (u8)(rect->top - s->cropcap.bounds.top) + 3; - - err += sn9c102_write_reg(cam, h_start, 0x12); - err += sn9c102_write_reg(cam, v_start, 0x13); - - return err; -} - - -static struct sn9c102_sensor pas202bca = { - .name = "PAS202BCA", - .maintainer = "Luca Risolia ", - .sysfs_ops = SN9C102_I2C_READ | SN9C102_I2C_WRITE, - .frequency = SN9C102_I2C_400KHZ | SN9C102_I2C_100KHZ, - .interface = SN9C102_I2C_2WIRES, - .i2c_slave_id = 0x40, - .init = &pas202bca_init, - .qctrl = { - { - .id = V4L2_CID_EXPOSURE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "exposure", - .minimum = 0x01e5, - .maximum = 0x3fff, - .step = 0x0001, - .default_value = 0x01e5, - .flags = 0, - }, - { - .id = V4L2_CID_GAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "global gain", - .minimum = 0x00, - .maximum = 0x1f, - .step = 0x01, - .default_value = 0x0c, - .flags = 0, - }, - { - .id = V4L2_CID_RED_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "red balance", - .minimum = 0x00, - .maximum = 0x0f, - .step = 0x01, - .default_value = 0x01, - .flags = 0, - }, - { - .id = V4L2_CID_BLUE_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "blue balance", - .minimum = 0x00, - .maximum = 0x0f, - .step = 0x01, - .default_value = 0x05, - .flags = 0, - }, - { - .id = SN9C102_V4L2_CID_GREEN_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "green balance", - .minimum = 0x00, - .maximum = 0x0f, - .step = 0x01, - .default_value = 0x00, - .flags = 0, - }, - { - .id = SN9C102_V4L2_CID_DAC_MAGNITUDE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "DAC magnitude", - .minimum = 0x00, - .maximum = 0xff, - .step = 0x01, - .default_value = 0x04, - .flags = 0, - }, - }, - .set_ctrl = &pas202bca_set_ctrl, - .cropcap = { - .bounds = { - .left = 0, - .top = 0, - .width = 640, - .height = 480, - }, - .defrect = { - .left = 0, - .top = 0, - .width = 640, - .height = 480, - }, - }, - .set_crop = &pas202bca_set_crop, - .pix_format = { - .width = 640, - .height = 480, - .pixelformat = V4L2_PIX_FMT_SBGGR8, - .priv = 8, - }, - .set_pix_format = &pas202bca_set_pix_format -}; - - -int sn9c102_probe_pas202bca(struct sn9c102_device* cam) -{ - const struct usb_device_id pas202bca_id_table[] = { - { USB_DEVICE(0x0c45, 0x60af), }, - { } - }; - int err = 0; - - if (!sn9c102_match_id(cam,pas202bca_id_table)) - return -ENODEV; - - err += sn9c102_write_reg(cam, 0x01, 0x01); - err += sn9c102_write_reg(cam, 0x40, 0x01); - err += sn9c102_write_reg(cam, 0x28, 0x17); - if (err) - return -EIO; - - if (sn9c102_i2c_try_write(cam, &pas202bca, 0x10, 0)) /* try to write */ - return -ENODEV; - - sn9c102_attach_sensor(cam, &pas202bca); - - return 0; -} diff --git a/drivers/media/video/sn9c102/sn9c102_pas202bcb.c b/drivers/media/video/sn9c102/sn9c102_pas202bcb.c index e3c1178e339c..7894f01b56e8 100644 --- a/drivers/media/video/sn9c102/sn9c102_pas202bcb.c +++ b/drivers/media/video/sn9c102/sn9c102_pas202bcb.c @@ -1,13 +1,13 @@ /*************************************************************************** - * Plug-in for PAS202BCB image sensor connected to the SN9C10x PC Camera * + * Plug-in for PAS202BCB image sensor connected to the SN9C1xx PC Camera * * Controllers * * * * Copyright (C) 2004 by Carlos Eduardo Medaglia Dyonisio * * * * http://cadu.homelinux.com:8080/ * * * - * DAC Magnitude, exposure and green gain controls added by * - * Luca Risolia * + * Support for SN9C103, DAC Magnitude, exposure and green gain controls * + * added by Luca Risolia * * * * 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 * @@ -35,12 +35,54 @@ static int pas202bcb_init(struct sn9c102_device* cam) { int err = 0; + switch (sn9c102_get_bridge(cam)) { + case BRIDGE_SN9C101: + case BRIDGE_SN9C102: err += sn9c102_write_reg(cam, 0x00, 0x10); err += sn9c102_write_reg(cam, 0x00, 0x11); err += sn9c102_write_reg(cam, 0x00, 0x14); err += sn9c102_write_reg(cam, 0x20, 0x17); err += sn9c102_write_reg(cam, 0x30, 0x19); err += sn9c102_write_reg(cam, 0x09, 0x18); + break; + case BRIDGE_SN9C103: + err += sn9c102_write_reg(cam, 0x00, 0x02); + err += sn9c102_write_reg(cam, 0x00, 0x03); + err += sn9c102_write_reg(cam, 0x1a, 0x04); + err += sn9c102_write_reg(cam, 0x20, 0x05); + err += sn9c102_write_reg(cam, 0x20, 0x06); + err += sn9c102_write_reg(cam, 0x20, 0x07); + err += sn9c102_write_reg(cam, 0x00, 0x10); + err += sn9c102_write_reg(cam, 0x00, 0x11); + err += sn9c102_write_reg(cam, 0x00, 0x14); + err += sn9c102_write_reg(cam, 0x20, 0x17); + err += sn9c102_write_reg(cam, 0x30, 0x19); + err += sn9c102_write_reg(cam, 0x09, 0x18); + err += sn9c102_write_reg(cam, 0x02, 0x1c); + err += sn9c102_write_reg(cam, 0x03, 0x1d); + err += sn9c102_write_reg(cam, 0x0f, 0x1e); + err += sn9c102_write_reg(cam, 0x0c, 0x1f); + err += sn9c102_write_reg(cam, 0x00, 0x20); + err += sn9c102_write_reg(cam, 0x10, 0x21); + err += sn9c102_write_reg(cam, 0x20, 0x22); + err += sn9c102_write_reg(cam, 0x30, 0x23); + err += sn9c102_write_reg(cam, 0x40, 0x24); + err += sn9c102_write_reg(cam, 0x50, 0x25); + err += sn9c102_write_reg(cam, 0x60, 0x26); + err += sn9c102_write_reg(cam, 0x70, 0x27); + err += sn9c102_write_reg(cam, 0x80, 0x28); + err += sn9c102_write_reg(cam, 0x90, 0x29); + err += sn9c102_write_reg(cam, 0xa0, 0x2a); + err += sn9c102_write_reg(cam, 0xb0, 0x2b); + err += sn9c102_write_reg(cam, 0xc0, 0x2c); + err += sn9c102_write_reg(cam, 0xd0, 0x2d); + err += sn9c102_write_reg(cam, 0xe0, 0x2e); + err += sn9c102_write_reg(cam, 0xf0, 0x2f); + err += sn9c102_write_reg(cam, 0xff, 0x30); + break; + default: + break; + } err += sn9c102_i2c_write(cam, 0x02, 0x14); err += sn9c102_i2c_write(cam, 0x03, 0x40); @@ -107,7 +149,7 @@ static int pas202bcb_set_pix_format(struct sn9c102_device* cam, int err = 0; if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X) - err += sn9c102_write_reg(cam, 0x24, 0x17); + err += sn9c102_write_reg(cam, 0x28, 0x17); else err += sn9c102_write_reg(cam, 0x20, 0x17); @@ -152,11 +194,23 @@ static int pas202bcb_set_ctrl(struct sn9c102_device* cam, static int pas202bcb_set_crop(struct sn9c102_device* cam, const struct v4l2_rect* rect) { - struct sn9c102_sensor* s = &pas202bcb; + struct sn9c102_sensor* s = sn9c102_get_sensor(cam); int err = 0; - u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 4, + u8 h_start = 0, v_start = (u8)(rect->top - s->cropcap.bounds.top) + 3; + switch (sn9c102_get_bridge(cam)) { + case BRIDGE_SN9C101: + case BRIDGE_SN9C102: + h_start = (u8)(rect->left - s->cropcap.bounds.left) + 4; + break; + case BRIDGE_SN9C103: + h_start = (u8)(rect->left - s->cropcap.bounds.left) + 3; + break; + default: + break; + } + err += sn9c102_write_reg(cam, h_start, 0x12); err += sn9c102_write_reg(cam, v_start, 0x13); @@ -166,8 +220,8 @@ static int pas202bcb_set_crop(struct sn9c102_device* cam, static struct sn9c102_sensor pas202bcb = { .name = "PAS202BCB", - .maintainer = "Carlos Eduardo Medaglia Dyonisio " - "", + .maintainer = "Luca Risolia ", + .supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102 | BRIDGE_SN9C103, .sysfs_ops = SN9C102_I2C_READ | SN9C102_I2C_WRITE, .frequency = SN9C102_I2C_400KHZ | SN9C102_I2C_100KHZ, .interface = SN9C102_I2C_2WIRES, @@ -191,7 +245,7 @@ static struct sn9c102_sensor pas202bcb = { .minimum = 0x00, .maximum = 0x1f, .step = 0x01, - .default_value = 0x0c, + .default_value = 0x0b, .flags = 0, }, { @@ -201,7 +255,7 @@ static struct sn9c102_sensor pas202bcb = { .minimum = 0x00, .maximum = 0x0f, .step = 0x01, - .default_value = 0x01, + .default_value = 0x00, .flags = 0, }, { @@ -271,16 +325,27 @@ int sn9c102_probe_pas202bcb(struct sn9c102_device* cam) * Minimal initialization to enable the I2C communication * NOTE: do NOT change the values! */ - err += sn9c102_write_reg(cam, 0x01, 0x01); /* sensor power down */ - err += sn9c102_write_reg(cam, 0x40, 0x01); /* sensor power on */ - err += sn9c102_write_reg(cam, 0x28, 0x17); /* sensor clock at 24 MHz */ - if (err) - return -EIO; + switch (sn9c102_get_bridge(cam)) { + case BRIDGE_SN9C101: + case BRIDGE_SN9C102: + err += sn9c102_write_reg(cam, 0x01, 0x01); /* power down */ + err += sn9c102_write_reg(cam, 0x40, 0x01); /* power on */ + err += sn9c102_write_reg(cam, 0x28, 0x17); /* clock 24 MHz */ + break; + case BRIDGE_SN9C103: /* do _not_ change anything! */ + err += sn9c102_write_reg(cam, 0x09, 0x01); + err += sn9c102_write_reg(cam, 0x44, 0x01); + err += sn9c102_write_reg(cam, 0x44, 0x02); + err += sn9c102_write_reg(cam, 0x29, 0x17); + break; + default: + break; + } r0 = sn9c102_i2c_try_read(cam, &pas202bcb, 0x00); r1 = sn9c102_i2c_try_read(cam, &pas202bcb, 0x01); - if (r0 < 0 || r1 < 0) + if (err || r0 < 0 || r1 < 0) return -EIO; pid = (r0 << 4) | ((r1 & 0xf0) >> 4); diff --git a/drivers/media/video/sn9c102/sn9c102_sensor.h b/drivers/media/video/sn9c102/sn9c102_sensor.h index 2a874ee6f9f5..05f2942639c3 100644 --- a/drivers/media/video/sn9c102/sn9c102_sensor.h +++ b/drivers/media/video/sn9c102/sn9c102_sensor.h @@ -1,7 +1,7 @@ /*************************************************************************** - * API for image sensors connected to the SN9C10x PC Camera Controllers * + * API for image sensors connected to the SN9C1xx PC Camera Controllers * * * - * Copyright (C) 2004-2006 by Luca Risolia * + * Copyright (C) 2004-2007 by Luca Risolia * * * * 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 * @@ -36,14 +36,13 @@ struct sn9c102_sensor; /* OVERVIEW. This is a small interface that allows you to add support for any CCD/CMOS - image sensors connected to the SN9C10X bridges. The entire API is documented + image sensors connected to the SN9C1XX bridges. The entire API is documented below. In the most general case, to support a sensor there are three steps you have to follow: 1) define the main "sn9c102_sensor" structure by setting the basic fields; 2) write a probing function to be called by the core module when the USB camera is recognized, then add both the USB ids and the name of that - function to the two corresponding tables SENSOR_TABLE and ID_TABLE (see - below); + function to the two corresponding tables in sn9c102_devtable.h; 3) implement the methods that you want/need (and fill the rest of the main structure accordingly). "sn9c102_pas106b.c" is an example of all this stuff. Remember that you do @@ -54,42 +53,21 @@ struct sn9c102_sensor; /*****************************************************************************/ -/* - Probing functions: on success, you must attach the sensor to the camera - by calling sn9c102_attach_sensor() provided below. - To enable the I2C communication, you might need to perform a really basic - initialization of the SN9C10X chip by using the write function declared - ahead. - Functions must return 0 on success, the appropriate error otherwise. -*/ -extern int sn9c102_probe_hv7131d(struct sn9c102_device* cam); -extern int sn9c102_probe_mi0343(struct sn9c102_device* cam); -extern int sn9c102_probe_ov7630(struct sn9c102_device* cam); -extern int sn9c102_probe_pas106b(struct sn9c102_device* cam); -extern int sn9c102_probe_pas202bca(struct sn9c102_device* cam); -extern int sn9c102_probe_pas202bcb(struct sn9c102_device* cam); -extern int sn9c102_probe_tas5110c1b(struct sn9c102_device* cam); -extern int sn9c102_probe_tas5130d1b(struct sn9c102_device* cam); - -/* - Add the above entries to this table. Be sure to add the entry in the right - place, since, on failure, the next probing routine is called according to - the order of the list below, from top to bottom. -*/ -#define SN9C102_SENSOR_TABLE \ -static int (*sn9c102_sensor_table[])(struct sn9c102_device*) = { \ - &sn9c102_probe_mi0343, /* strong detection based on SENSOR ids */ \ - &sn9c102_probe_pas106b, /* strong detection based on SENSOR ids */ \ - &sn9c102_probe_pas202bcb, /* strong detection based on SENSOR ids */ \ - &sn9c102_probe_hv7131d, /* strong detection based on SENSOR ids */ \ - &sn9c102_probe_pas202bca, /* detection mostly based on USB pid/vid */ \ - &sn9c102_probe_ov7630, /* detection mostly based on USB pid/vid */ \ - &sn9c102_probe_tas5110c1b, /* detection based on USB pid/vid */ \ - &sn9c102_probe_tas5130d1b, /* detection based on USB pid/vid */ \ - NULL, \ +enum sn9c102_bridge { + BRIDGE_SN9C101 = 0x01, + BRIDGE_SN9C102 = 0x02, + BRIDGE_SN9C103 = 0x04, + BRIDGE_SN9C105 = 0x08, + BRIDGE_SN9C120 = 0x10, }; -/* Device identification */ +/* Return the bridge name */ +enum sn9c102_bridge sn9c102_get_bridge(struct sn9c102_device* cam); + +/* Return a pointer the sensor struct attached to the camera */ +struct sn9c102_sensor* sn9c102_get_sensor(struct sn9c102_device* cam); + +/* Identify a device */ extern struct sn9c102_device* sn9c102_match_id(struct sn9c102_device* cam, const struct usb_device_id *id); @@ -98,69 +76,9 @@ extern void sn9c102_attach_sensor(struct sn9c102_device* cam, struct sn9c102_sensor* sensor); -/* - Each SN9C10x camera has proper PID/VID identifiers. - SN9C103 supports multiple interfaces, but we only handle the video class - interface. -*/ -#define SN9C102_USB_DEVICE(vend, prod, intclass) \ - .match_flags = USB_DEVICE_ID_MATCH_DEVICE | \ - USB_DEVICE_ID_MATCH_INT_CLASS, \ - .idVendor = (vend), \ - .idProduct = (prod), \ - .bInterfaceClass = (intclass) - -#define SN9C102_ID_TABLE \ -static const struct usb_device_id sn9c102_id_table[] = { \ - { USB_DEVICE(0x0c45, 0x6001), }, /* TAS5110C1B */ \ - { USB_DEVICE(0x0c45, 0x6005), }, /* TAS5110C1B */ \ - { USB_DEVICE(0x0c45, 0x6007), }, \ - { USB_DEVICE(0x0c45, 0x6009), }, /* PAS106B */ \ - { USB_DEVICE(0x0c45, 0x600d), }, /* PAS106B */ \ - { USB_DEVICE(0x0c45, 0x6024), }, \ - { USB_DEVICE(0x0c45, 0x6025), }, /* TAS5130D1B and TAS5110C1B */ \ - { USB_DEVICE(0x0c45, 0x6028), }, /* PAS202BCB */ \ - { USB_DEVICE(0x0c45, 0x6029), }, /* PAS106B */ \ - { USB_DEVICE(0x0c45, 0x602a), }, /* HV7131D */ \ - { USB_DEVICE(0x0c45, 0x602b), }, /* MI-0343 */ \ - { USB_DEVICE(0x0c45, 0x602c), }, /* OV7630 */ \ - { USB_DEVICE(0x0c45, 0x602d), }, \ - { USB_DEVICE(0x0c45, 0x602e), }, /* OV7630 */ \ - { USB_DEVICE(0x0c45, 0x6030), }, /* MI03x */ \ - { SN9C102_USB_DEVICE(0x0c45, 0x6080, 0xff), }, \ - { SN9C102_USB_DEVICE(0x0c45, 0x6082, 0xff), }, /* MI0343 & MI0360 */ \ - { SN9C102_USB_DEVICE(0x0c45, 0x6083, 0xff), }, /* HV7131[D|E1] */ \ - { SN9C102_USB_DEVICE(0x0c45, 0x6088, 0xff), }, \ - { SN9C102_USB_DEVICE(0x0c45, 0x608a, 0xff), }, \ - { SN9C102_USB_DEVICE(0x0c45, 0x608b, 0xff), }, \ - { SN9C102_USB_DEVICE(0x0c45, 0x608c, 0xff), }, /* HV7131/R */ \ - { SN9C102_USB_DEVICE(0x0c45, 0x608e, 0xff), }, /* CIS-VF10 */ \ - { SN9C102_USB_DEVICE(0x0c45, 0x608f, 0xff), }, /* OV7630 */ \ - { SN9C102_USB_DEVICE(0x0c45, 0x60a0, 0xff), }, \ - { SN9C102_USB_DEVICE(0x0c45, 0x60a2, 0xff), }, \ - { SN9C102_USB_DEVICE(0x0c45, 0x60a3, 0xff), }, \ - { SN9C102_USB_DEVICE(0x0c45, 0x60a8, 0xff), }, /* PAS106B */ \ - { SN9C102_USB_DEVICE(0x0c45, 0x60aa, 0xff), }, /* TAS5130D1B */ \ - { SN9C102_USB_DEVICE(0x0c45, 0x60ab, 0xff), }, /* TAS5110C1B */ \ - { SN9C102_USB_DEVICE(0x0c45, 0x60ac, 0xff), }, \ - { SN9C102_USB_DEVICE(0x0c45, 0x60ae, 0xff), }, \ - { SN9C102_USB_DEVICE(0x0c45, 0x60af, 0xff), }, /* PAS202BCB */ \ - { SN9C102_USB_DEVICE(0x0c45, 0x60b0, 0xff), }, /* OV7630 (?) */ \ - { SN9C102_USB_DEVICE(0x0c45, 0x60b2, 0xff), }, \ - { SN9C102_USB_DEVICE(0x0c45, 0x60b3, 0xff), }, \ - { SN9C102_USB_DEVICE(0x0c45, 0x60b8, 0xff), }, \ - { SN9C102_USB_DEVICE(0x0c45, 0x60ba, 0xff), }, \ - { SN9C102_USB_DEVICE(0x0c45, 0x60bb, 0xff), }, \ - { SN9C102_USB_DEVICE(0x0c45, 0x60bc, 0xff), }, \ - { SN9C102_USB_DEVICE(0x0c45, 0x60be, 0xff), }, \ - { } \ -}; - -/*****************************************************************************/ - /* Read/write routines: they always return -1 on error, 0 or the read value - otherwise. NOTE that a real read operation is not supported by the SN9C10X + otherwise. NOTE that a real read operation is not supported by the SN9C1XX chip for some of its registers. To work around this problem, a pseudo-read call is provided instead: it returns the last successfully written value on the register (0 if it has never been written), the usual -1 on error. @@ -176,7 +94,7 @@ extern int sn9c102_i2c_try_read(struct sn9c102_device*,struct sn9c102_sensor*, These must be used if and only if the sensor doesn't implement the standard I2C protocol. There are a number of good reasons why you must use the single-byte versions of these functions: do not abuse. The first function - writes n bytes, from data0 to datan, to registers 0x09 - 0x09+n of SN9C10X + writes n bytes, from data0 to datan, to registers 0x09 - 0x09+n of SN9C1XX chip. The second one programs the registers 0x09 and 0x10 with data0 and data1, and places the n bytes read from the sensor register table in the buffer pointed by 'buffer'. Both the functions return -1 on error; the write @@ -200,16 +118,6 @@ extern int sn9c102_write_regs(struct sn9c102_device*, u8* buff, u16 index); extern int sn9c102_write_reg(struct sn9c102_device*, u8 value, u16 index); extern int sn9c102_pread_reg(struct sn9c102_device*, u16 index); -/* - NOTE: there are no exported debugging functions. To uniform the output you - must use the dev_info()/dev_warn()/dev_err() macros defined in device.h, - already included here, the argument being the struct device '&usbdev->dev' - of the sensor structure. Do NOT use these macros before the sensor is - attached or the kernel will crash! However, you should not need to notify - the user about common errors or other messages, since this is done by the - master module. -*/ - /*****************************************************************************/ enum sn9c102_i2c_sysfs_ops { @@ -227,17 +135,19 @@ enum sn9c102_i2c_interface { SN9C102_I2C_3WIRES, }; -#define SN9C102_MAX_CTRLS V4L2_CID_LASTP1-V4L2_CID_BASE+10 +#define SN9C102_MAX_CTRLS (V4L2_CID_LASTP1-V4L2_CID_BASE+10) struct sn9c102_sensor { char name[32], /* sensor name */ maintainer[64]; /* name of the mantainer */ + enum sn9c102_bridge supported_bridge; /* supported SN9C1xx bridges */ + /* Supported operations through the 'sysfs' interface */ enum sn9c102_i2c_sysfs_ops sysfs_ops; /* - These sensor capabilities must be provided if the SN9C10X controller + These sensor capabilities must be provided if the SN9C1XX controller needs to communicate through the sensor serial interface by using at least one of the i2c functions available. */ @@ -260,7 +170,7 @@ struct sn9c102_sensor { /* This function will be called after the sensor has been attached. It should be used to initialize the sensor only, but may also - configure part of the SN9C10X chip if necessary. You don't need to + configure part of the SN9C1XX chip if necessary. You don't need to setup picture settings like brightness, contrast, etc.. here, if the corrisponding controls are implemented (see below), since they are adjusted in the core driver by calling the set_ctrl() @@ -300,7 +210,7 @@ struct sn9c102_sensor { It is not always true that the largest achievable active window can cover the whole array of pixels. The V4L2 API defines another area called "source rectangle", which, in turn, is a subrectangle of - the active window. The SN9C10X chip is always programmed to read the + the active window. The SN9C1XX chip is always programmed to read the source rectangle. The bounds of both the active window and the source rectangle are specified in the cropcap substructures 'bounds' and 'defrect'. @@ -326,13 +236,13 @@ struct sn9c102_sensor { const struct v4l2_rect* rect); /* To be called on VIDIOC_C_SETCROP. The core module always calls a - default routine which configures the appropriate SN9C10X regs (also + default routine which configures the appropriate SN9C1XX regs (also scaling), but you may need to override/adjust specific stuff. 'rect' contains width and height values that are multiple of 16: in case you override the default function, you always have to program the chip to match those values; on error return the corresponding error code without rolling back. - NOTE: in case, you must program the SN9C10X chip to get rid of + NOTE: in case, you must program the SN9C1XX chip to get rid of blank pixels or blank lines at the _start_ of each line or frame after each HSYNC or VSYNC, so that the image starts with real RGB data (see regs 0x12, 0x13) (having set H_SIZE and, @@ -344,16 +254,16 @@ struct sn9c102_sensor { /* What you have to define here are: 1) initial 'width' and 'height' of the target rectangle 2) the initial 'pixelformat', which can be - either V4L2_PIX_FMT_SN9C10X (for compressed video) or - V4L2_PIX_FMT_SBGGR8 3) 'priv', which we'll be used to indicate the - number of bits per pixel for uncompressed video, 8 or 9 (despite the - current value of 'pixelformat'). + either V4L2_PIX_FMT_SN9C10X, V4L2_PIX_FMT_JPEG (for ompressed video) + or V4L2_PIX_FMT_SBGGR8 3) 'priv', which we'll be used to indicate + the number of bits per pixel for uncompressed video, 8 or 9 (despite + the current value of 'pixelformat'). NOTE 1: both 'width' and 'height' _must_ be either 1/1 or 1/2 or 1/4 of cropcap.defrect.width and cropcap.defrect.height. I suggest 1/1. NOTE 2: The initial compression quality is defined by the first bit of reg 0x17 during the initialization of the image sensor. - NOTE 3: as said above, you have to program the SN9C10X chip to get + NOTE 3: as said above, you have to program the SN9C1XX chip to get rid of any blank pixels, so that the output of the sensor matches the RGB bayer sequence (i.e. BGBGBG...GRGRGR). */ @@ -378,12 +288,12 @@ struct sn9c102_sensor { /*****************************************************************************/ /* Private ioctl's for control settings supported by some image sensors */ -#define SN9C102_V4L2_CID_DAC_MAGNITUDE V4L2_CID_PRIVATE_BASE -#define SN9C102_V4L2_CID_GREEN_BALANCE V4L2_CID_PRIVATE_BASE + 1 -#define SN9C102_V4L2_CID_RESET_LEVEL V4L2_CID_PRIVATE_BASE + 2 -#define SN9C102_V4L2_CID_PIXEL_BIAS_VOLTAGE V4L2_CID_PRIVATE_BASE + 3 -#define SN9C102_V4L2_CID_GAMMA V4L2_CID_PRIVATE_BASE + 4 -#define SN9C102_V4L2_CID_BAND_FILTER V4L2_CID_PRIVATE_BASE + 5 -#define SN9C102_V4L2_CID_BRIGHT_LEVEL V4L2_CID_PRIVATE_BASE + 6 +#define SN9C102_V4L2_CID_DAC_MAGNITUDE (V4L2_CID_PRIVATE_BASE + 0) +#define SN9C102_V4L2_CID_GREEN_BALANCE (V4L2_CID_PRIVATE_BASE + 1) +#define SN9C102_V4L2_CID_RESET_LEVEL (V4L2_CID_PRIVATE_BASE + 2) +#define SN9C102_V4L2_CID_PIXEL_BIAS_VOLTAGE (V4L2_CID_PRIVATE_BASE + 3) +#define SN9C102_V4L2_CID_GAMMA (V4L2_CID_PRIVATE_BASE + 4) +#define SN9C102_V4L2_CID_BAND_FILTER (V4L2_CID_PRIVATE_BASE + 5) +#define SN9C102_V4L2_CID_BRIGHT_LEVEL (V4L2_CID_PRIVATE_BASE + 6) #endif /* _SN9C102_SENSOR_H_ */ diff --git a/drivers/media/video/sn9c102/sn9c102_tas5110c1b.c b/drivers/media/video/sn9c102/sn9c102_tas5110c1b.c index 294eb02fbd88..90023ad63adc 100644 --- a/drivers/media/video/sn9c102/sn9c102_tas5110c1b.c +++ b/drivers/media/video/sn9c102/sn9c102_tas5110c1b.c @@ -1,8 +1,8 @@ /*************************************************************************** - * Plug-in for TAS5110C1B image sensor connected to the SN9C10x PC Camera * + * Plug-in for TAS5110C1B image sensor connected to the SN9C1xx PC Camera * * Controllers * * * - * Copyright (C) 2004-2006 by Luca Risolia * + * Copyright (C) 2004-2007 by Luca Risolia * * * * 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 * @@ -64,7 +64,7 @@ static int tas5110c1b_set_ctrl(struct sn9c102_device* cam, static int tas5110c1b_set_crop(struct sn9c102_device* cam, const struct v4l2_rect* rect) { - struct sn9c102_sensor* s = &tas5110c1b; + struct sn9c102_sensor* s = sn9c102_get_sensor(cam); int err = 0; u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 69, v_start = (u8)(rect->top - s->cropcap.bounds.top) + 9; @@ -98,6 +98,7 @@ static int tas5110c1b_set_pix_format(struct sn9c102_device* cam, static struct sn9c102_sensor tas5110c1b = { .name = "TAS5110C1B", .maintainer = "Luca Risolia ", + .supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102 | BRIDGE_SN9C103, .sysfs_ops = SN9C102_I2C_WRITE, .frequency = SN9C102_I2C_100KHZ, .interface = SN9C102_I2C_3WIRES, @@ -145,6 +146,7 @@ int sn9c102_probe_tas5110c1b(struct sn9c102_device* cam) const struct usb_device_id tas5110c1b_id_table[] = { { USB_DEVICE(0x0c45, 0x6001), }, { USB_DEVICE(0x0c45, 0x6005), }, + { USB_DEVICE(0x0c45, 0x6007), }, { USB_DEVICE(0x0c45, 0x60ab), }, { } }; diff --git a/drivers/media/video/sn9c102/sn9c102_tas5130d1b.c b/drivers/media/video/sn9c102/sn9c102_tas5130d1b.c index 9ecb09032b68..cb1b318bc1ff 100644 --- a/drivers/media/video/sn9c102/sn9c102_tas5130d1b.c +++ b/drivers/media/video/sn9c102/sn9c102_tas5130d1b.c @@ -1,8 +1,8 @@ /*************************************************************************** - * Plug-in for TAS5130D1B image sensor connected to the SN9C10x PC Camera * + * Plug-in for TAS5130D1B image sensor connected to the SN9C1xx PC Camera * * Controllers * * * - * Copyright (C) 2004-2006 by Luca Risolia * + * Copyright (C) 2004-2007 by Luca Risolia * * * * 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 * @@ -65,7 +65,7 @@ static int tas5130d1b_set_ctrl(struct sn9c102_device* cam, static int tas5130d1b_set_crop(struct sn9c102_device* cam, const struct v4l2_rect* rect) { - struct sn9c102_sensor* s = &tas5130d1b; + struct sn9c102_sensor* s = sn9c102_get_sensor(cam); u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 104, v_start = (u8)(rect->top - s->cropcap.bounds.top) + 12; int err = 0; @@ -99,6 +99,7 @@ static int tas5130d1b_set_pix_format(struct sn9c102_device* cam, static struct sn9c102_sensor tas5130d1b = { .name = "TAS5130D1B", .maintainer = "Luca Risolia ", + .supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102 | BRIDGE_SN9C103, .sysfs_ops = SN9C102_I2C_WRITE, .frequency = SN9C102_I2C_100KHZ, .interface = SN9C102_I2C_3WIRES, @@ -154,6 +155,7 @@ static struct sn9c102_sensor tas5130d1b = { int sn9c102_probe_tas5130d1b(struct sn9c102_device* cam) { const struct usb_device_id tas5130d1b_id_table[] = { + { USB_DEVICE(0x0c45, 0x6024), }, { USB_DEVICE(0x0c45, 0x6025), }, { USB_DEVICE(0x0c45, 0x60aa), }, { } -- cgit v1.2.3 From 7e3a0660700ad47ee6e296fe7090d771becfcf96 Mon Sep 17 00:00:00 2001 From: Luca Risolia Date: Mon, 8 Jan 2007 11:34:35 -0300 Subject: V4L/DVB (5063): ZC0301 driver updates. - Implement audio ioctl's and VIDIOC_ENUM_FRAMESIZES - Documentation updates - Generic improvements Signed-off-by: Luca Risolia Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/zc0301.txt | 10 ++--- drivers/media/video/zc0301/zc0301.h | 4 +- drivers/media/video/zc0301/zc0301_core.c | 55 +++++++++++++++++++++++---- drivers/media/video/zc0301/zc0301_pas202bcb.c | 4 +- drivers/media/video/zc0301/zc0301_pb0330.c | 4 +- drivers/media/video/zc0301/zc0301_sensor.h | 12 +++--- 6 files changed, 64 insertions(+), 25 deletions(-) (limited to 'Documentation') diff --git a/Documentation/video4linux/zc0301.txt b/Documentation/video4linux/zc0301.txt index f406f5e80046..befdfdacdc5b 100644 --- a/Documentation/video4linux/zc0301.txt +++ b/Documentation/video4linux/zc0301.txt @@ -23,7 +23,7 @@ Index 1. Copyright ============ -Copyright (C) 2006 by Luca Risolia +Copyright (C) 2006-2007 by Luca Risolia 2. Disclaimer @@ -125,8 +125,9 @@ And finally: 6. Module loading ================= To use the driver, it is necessary to load the "zc0301" module into memory -after every other module required: "videodev", "usbcore" and, depending on -the USB host controller you have, "ehci-hcd", "uhci-hcd" or "ohci-hcd". +after every other module required: "videodev", "v4l2_common", "compat_ioctl32", +"usbcore" and, depending on the USB host controller you have, "ehci-hcd", +"uhci-hcd" or "ohci-hcd". Loading can be done as shown below: @@ -211,12 +212,11 @@ Vendor ID Product ID 0x041e 0x4036 0x041e 0x403a 0x0458 0x7007 -0x0458 0x700C +0x0458 0x700c 0x0458 0x700f 0x046d 0x08ae 0x055f 0xd003 0x055f 0xd004 -0x046d 0x08ae 0x0ac8 0x0301 0x0ac8 0x301b 0x0ac8 0x303b diff --git a/drivers/media/video/zc0301/zc0301.h b/drivers/media/video/zc0301/zc0301.h index b9c93b8c16f7..710f12eb9126 100644 --- a/drivers/media/video/zc0301/zc0301.h +++ b/drivers/media/video/zc0301/zc0301.h @@ -1,7 +1,7 @@ /*************************************************************************** - * V4L2 driver for ZC0301 Image Processor and Control Chip * + * V4L2 driver for ZC0301[P] Image Processor and Control Chip * * * - * Copyright (C) 2006 by Luca Risolia * + * Copyright (C) 2006-2007 by Luca Risolia * * * * 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 * diff --git a/drivers/media/video/zc0301/zc0301_core.c b/drivers/media/video/zc0301/zc0301_core.c index 8da7f15f6290..f1120551c70c 100644 --- a/drivers/media/video/zc0301/zc0301_core.c +++ b/drivers/media/video/zc0301/zc0301_core.c @@ -1,7 +1,7 @@ /*************************************************************************** * Video4Linux2 driver for ZC0301[P] Image Processor and Control Chip * * * - * Copyright (C) 2006 by Luca Risolia * + * Copyright (C) 2006-2007 by Luca Risolia * * * * Informations about the chip internals needed to enable the I2C protocol * * have been taken from the documentation of the ZC030x Video4Linux1 * @@ -52,8 +52,8 @@ #define ZC0301_MODULE_AUTHOR "(C) 2006 Luca Risolia" #define ZC0301_AUTHOR_EMAIL "" #define ZC0301_MODULE_LICENSE "GPL" -#define ZC0301_MODULE_VERSION "1:1.05" -#define ZC0301_MODULE_VERSION_CODE KERNEL_VERSION(1, 0, 5) +#define ZC0301_MODULE_VERSION "1:1.07" +#define ZC0301_MODULE_VERSION_CODE KERNEL_VERSION(1, 1, 7) /*****************************************************************************/ @@ -89,7 +89,7 @@ MODULE_PARM_DESC(force_munmap, "\ndetected camera." "\n 0 = do not force memory unmapping" "\n 1 = force memory unmapping (save memory)" - "\nDefault value is "__MODULE_STRING(SN9C102_FORCE_MUNMAP)"." + "\nDefault value is "__MODULE_STRING(ZC0301_FORCE_MUNMAP)"." "\n"); static unsigned int frame_timeout[] = {[0 ... ZC0301_MAX_DEVICES-1] = @@ -136,7 +136,8 @@ zc0301_request_buffers(struct zc0301_device* cam, u32 count, cam->nbuffers = count; while (cam->nbuffers > 0) { - if ((buff = vmalloc_32(cam->nbuffers * PAGE_ALIGN(imagesize)))) + if ((buff = vmalloc_32_user(cam->nbuffers * + PAGE_ALIGN(imagesize)))) break; cam->nbuffers--; } @@ -430,7 +431,8 @@ static int zc0301_start_transfer(struct zc0301_device* cam) struct usb_host_interface* altsetting = usb_altnum_to_altsetting( usb_ifnum_to_if(udev, 0), ZC0301_ALTERNATE_SETTING); - const unsigned int psz = altsetting->endpoint[0].desc.wMaxPacketSize; + const unsigned int psz = le16_to_cpu(altsetting-> + endpoint[0].desc.wMaxPacketSize); struct urb* urb; s8 i, j; int err = 0; @@ -489,7 +491,7 @@ static int zc0301_start_transfer(struct zc0301_device* cam) return 0; free_urbs: - for (i = 0; i < ZC0301_URBS; i++) + for (i = 0; (i < ZC0301_URBS) && cam->urb[i]; i++) usb_free_urb(cam->urb[i]); free_buffers: @@ -1287,6 +1289,35 @@ zc0301_vidioc_s_crop(struct zc0301_device* cam, void __user * arg) } +static int +zc0301_vidioc_enum_framesizes(struct zc0301_device* cam, void __user * arg) +{ + struct v4l2_frmsizeenum frmsize; + + if (copy_from_user(&frmsize, arg, sizeof(frmsize))) + return -EFAULT; + + if (frmsize.index != 0 && frmsize.index != 1) + return -EINVAL; + + if (frmsize.pixel_format != V4L2_PIX_FMT_JPEG) + return -EINVAL; + + frmsize.type = V4L2_FRMSIZE_TYPE_DISCRETE; + + if (frmsize.index == 1) { + frmsize.discrete.width = cam->sensor.cropcap.defrect.width; + frmsize.discrete.height = cam->sensor.cropcap.defrect.height; + } + memset(&frmsize.reserved, 0, sizeof(frmsize.reserved)); + + if (copy_to_user(arg, &frmsize, sizeof(frmsize))) + return -EFAULT; + + return 0; +} + + static int zc0301_vidioc_enum_fmt(struct zc0301_device* cam, void __user * arg) { @@ -1295,6 +1326,9 @@ zc0301_vidioc_enum_fmt(struct zc0301_device* cam, void __user * arg) if (copy_from_user(&fmtd, arg, sizeof(fmtd))) return -EFAULT; + if (fmtd.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + if (fmtd.index == 0) { strcpy(fmtd.description, "JPEG"); fmtd.pixelformat = V4L2_PIX_FMT_JPEG; @@ -1795,6 +1829,9 @@ static int zc0301_ioctl_v4l2(struct inode* inode, struct file* filp, case VIDIOC_S_FMT: return zc0301_vidioc_try_s_fmt(cam, cmd, arg); + case VIDIOC_ENUM_FRAMESIZES: + return zc0301_vidioc_enum_framesizes(cam, arg); + case VIDIOC_G_JPEGCOMP: return zc0301_vidioc_g_jpegcomp(cam, arg); @@ -1830,6 +1867,7 @@ static int zc0301_ioctl_v4l2(struct inode* inode, struct file* filp, case VIDIOC_QUERYSTD: case VIDIOC_ENUMSTD: case VIDIOC_QUERYMENU: + case VIDIOC_ENUM_FRAMEINTERVALS: return -EINVAL; default: @@ -1876,6 +1914,7 @@ static const struct file_operations zc0301_fops = { .open = zc0301_open, .release = zc0301_release, .ioctl = zc0301_ioctl, + .compat_ioctl = v4l_compat_ioctl32, .read = zc0301_read, .poll = zc0301_poll, .mmap = zc0301_mmap, @@ -1913,7 +1952,7 @@ zc0301_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) mutex_init(&cam->dev_mutex); DBG(2, "ZC0301[P] Image Processor and Control Chip detected " - "(vid/pid 0x%04X/0x%04X)",id->idVendor, id->idProduct); + "(vid/pid 0x%04X:0x%04X)",id->idVendor, id->idProduct); for (i = 0; zc0301_sensor_table[i]; i++) { err = zc0301_sensor_table[i](cam); diff --git a/drivers/media/video/zc0301/zc0301_pas202bcb.c b/drivers/media/video/zc0301/zc0301_pas202bcb.c index ecfd39a56df1..3efb92a0d0da 100644 --- a/drivers/media/video/zc0301/zc0301_pas202bcb.c +++ b/drivers/media/video/zc0301/zc0301_pas202bcb.c @@ -1,8 +1,8 @@ /*************************************************************************** - * Plug-in for PAS202BCB image sensor connected to the ZC0301[P] Image * + * Plug-in for PAS202BCB image sensor connected to the ZC0301 Image * * Processor and Control Chip * * * - * Copyright (C) 2006 by Luca Risolia * + * Copyright (C) 2006-2007 by Luca Risolia * * * * Initialization values of the ZC0301[P] have been taken from the SPCA5XX * * driver maintained by Michel Xhaard * diff --git a/drivers/media/video/zc0301/zc0301_pb0330.c b/drivers/media/video/zc0301/zc0301_pb0330.c index ed8542e6c50f..5784b1d1491c 100644 --- a/drivers/media/video/zc0301/zc0301_pb0330.c +++ b/drivers/media/video/zc0301/zc0301_pb0330.c @@ -1,8 +1,8 @@ /*************************************************************************** - * Plug-in for PB-0330 image sensor connected to the ZC0301[P] Image * + * Plug-in for PB-0330 image sensor connected to the ZC0301P Image * * Processor and Control Chip * * * - * Copyright (C) 2006 by Luca Risolia * + * Copyright (C) 2006-2007 by Luca Risolia * * * * Initialization values of the ZC0301[P] have been taken from the SPCA5XX * * driver maintained by Michel Xhaard * diff --git a/drivers/media/video/zc0301/zc0301_sensor.h b/drivers/media/video/zc0301/zc0301_sensor.h index 3daf049a288a..44e82cff9319 100644 --- a/drivers/media/video/zc0301/zc0301_sensor.h +++ b/drivers/media/video/zc0301/zc0301_sensor.h @@ -1,8 +1,8 @@ /*************************************************************************** - * API for image sensors connected to the ZC0301 Image Processor and * + * API for image sensors connected to the ZC0301[P] Image Processor and * * Control Chip * * * - * Copyright (C) 2006 by Luca Risolia * + * Copyright (C) 2006-2007 by Luca Risolia * * * * 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 * @@ -70,7 +70,7 @@ static const struct usb_device_id zc0301_id_table[] = { \ { ZC0301_USB_DEVICE(0x041e, 0x4036, 0xff), }, /* HV7131 */ \ { ZC0301_USB_DEVICE(0x041e, 0x403a, 0xff), }, /* HV7131 */ \ { ZC0301_USB_DEVICE(0x0458, 0x7007, 0xff), }, /* TAS5130 */ \ - { ZC0301_USB_DEVICE(0x0458, 0x700C, 0xff), }, /* TAS5130 */ \ + { ZC0301_USB_DEVICE(0x0458, 0x700c, 0xff), }, /* TAS5130 */ \ { ZC0301_USB_DEVICE(0x0458, 0x700f, 0xff), }, /* TAS5130 */ \ { ZC0301_USB_DEVICE(0x046d, 0x08ae, 0xff), }, /* PAS202 */ \ { ZC0301_USB_DEVICE(0x055f, 0xd003, 0xff), }, /* TAS5130 */ \ @@ -93,9 +93,9 @@ extern int zc0301_i2c_read(struct zc0301_device*, u16 address, u8 length); /*****************************************************************************/ -#define ZC0301_MAX_CTRLS V4L2_CID_LASTP1-V4L2_CID_BASE+10 -#define ZC0301_V4L2_CID_DAC_MAGNITUDE V4L2_CID_PRIVATE_BASE -#define ZC0301_V4L2_CID_GREEN_BALANCE V4L2_CID_PRIVATE_BASE + 1 +#define ZC0301_MAX_CTRLS (V4L2_CID_LASTP1 - V4L2_CID_BASE + 10) +#define ZC0301_V4L2_CID_DAC_MAGNITUDE (V4L2_CID_PRIVATE_BASE + 0) +#define ZC0301_V4L2_CID_GREEN_BALANCE (V4L2_CID_PRIVATE_BASE + 1) struct zc0301_sensor { char name[32]; -- cgit v1.2.3 From 2656312724d97ebc2e267e0a9740d51ad7aa9a04 Mon Sep 17 00:00:00 2001 From: Luca Risolia Date: Mon, 8 Jan 2007 11:38:36 -0300 Subject: V4L/DVB (5064): ET61X251 driver updates. - Implement audio ioctl's and VIDIOC_ENUM_FRAMESIZES - Documentation updates - Generic improvements Signed-off-by: Luca Risolia Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/et61x251.txt | 7 +- drivers/media/video/et61x251/et61x251.h | 5 +- drivers/media/video/et61x251/et61x251_core.c | 129 ++++++++++++++------- drivers/media/video/et61x251/et61x251_sensor.h | 4 +- drivers/media/video/et61x251/et61x251_tas5130d1b.c | 2 +- 5 files changed, 92 insertions(+), 55 deletions(-) (limited to 'Documentation') diff --git a/Documentation/video4linux/et61x251.txt b/Documentation/video4linux/et61x251.txt index 1bdee8f85b9a..1247566c4de3 100644 --- a/Documentation/video4linux/et61x251.txt +++ b/Documentation/video4linux/et61x251.txt @@ -23,7 +23,7 @@ Index 1. Copyright ============ -Copyright (C) 2006 by Luca Risolia +Copyright (C) 2006-2007 by Luca Risolia 2. Disclaimer @@ -135,8 +135,9 @@ And finally: 6. Module loading ================= To use the driver, it is necessary to load the "et61x251" module into memory -after every other module required: "videodev", "usbcore" and, depending on -the USB host controller you have, "ehci-hcd", "uhci-hcd" or "ohci-hcd". +after every other module required: "videodev", "v4l2_common", "compat_ioctl32", +"usbcore" and, depending on the USB host controller you have, "ehci-hcd", +"uhci-hcd" or "ohci-hcd". Loading can be done as shown below: diff --git a/drivers/media/video/et61x251/et61x251.h b/drivers/media/video/et61x251/et61x251.h index 2e5ca4032489..262f98e12409 100644 --- a/drivers/media/video/et61x251/et61x251.h +++ b/drivers/media/video/et61x251/et61x251.h @@ -171,10 +171,7 @@ struct et61x251_device { struct et61x251_device* et61x251_match_id(struct et61x251_device* cam, const struct usb_device_id *id) { - if (usb_match_id(usb_ifnum_to_if(cam->usbdev, 0), id)) - return cam; - - return NULL; + return usb_match_id(usb_ifnum_to_if(cam->usbdev, 0), id) ? cam : NULL; } diff --git a/drivers/media/video/et61x251/et61x251_core.c b/drivers/media/video/et61x251/et61x251_core.c index 49792ae8c61c..a6525513cd1e 100644 --- a/drivers/media/video/et61x251/et61x251_core.c +++ b/drivers/media/video/et61x251/et61x251_core.c @@ -1,7 +1,7 @@ /*************************************************************************** * V4L2 driver for ET61X[12]51 PC Camera Controllers * * * - * Copyright (C) 2006 by Luca Risolia * + * Copyright (C) 2006-2007 by Luca Risolia * * * * 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 * @@ -48,8 +48,8 @@ #define ET61X251_MODULE_AUTHOR "(C) 2006 Luca Risolia" #define ET61X251_AUTHOR_EMAIL "" #define ET61X251_MODULE_LICENSE "GPL" -#define ET61X251_MODULE_VERSION "1:1.02" -#define ET61X251_MODULE_VERSION_CODE KERNEL_VERSION(1, 0, 2) +#define ET61X251_MODULE_VERSION "1:1.04" +#define ET61X251_MODULE_VERSION_CODE KERNEL_VERSION(1, 1, 4) /*****************************************************************************/ @@ -85,7 +85,7 @@ MODULE_PARM_DESC(force_munmap, "\ndetected camera." "\n 0 = do not force memory unmapping" "\n 1 = force memory unmapping (save memory)" - "\nDefault value is "__MODULE_STRING(SN9C102_FORCE_MUNMAP)"." + "\nDefault value is "__MODULE_STRING(ET61X251_FORCE_MUNMAP)"." "\n"); static unsigned int frame_timeout[] = {[0 ... ET61X251_MAX_DEVICES-1] = @@ -133,7 +133,8 @@ et61x251_request_buffers(struct et61x251_device* cam, u32 count, cam->nbuffers = count; while (cam->nbuffers > 0) { - if ((buff = vmalloc_32(cam->nbuffers * PAGE_ALIGN(imagesize)))) + if ((buff = vmalloc_32_user(cam->nbuffers * + PAGE_ALIGN(imagesize)))) break; cam->nbuffers--; } @@ -543,10 +544,11 @@ static int et61x251_start_transfer(struct et61x251_device* cam) { struct usb_device *udev = cam->usbdev; struct urb* urb; - const unsigned int wMaxPacketSize[] = {0, 256, 384, 512, 640, 768, 832, - 864, 896, 920, 956, 980, 1000, - 1022}; - const unsigned int psz = wMaxPacketSize[ET61X251_ALTERNATE_SETTING]; + struct usb_host_interface* altsetting = usb_altnum_to_altsetting( + usb_ifnum_to_if(udev, 0), + ET61X251_ALTERNATE_SETTING); + const unsigned int psz = le16_to_cpu(altsetting-> + endpoint[0].desc.wMaxPacketSize); s8 i, j; int err = 0; @@ -976,29 +978,31 @@ static CLASS_DEVICE_ATTR(i2c_val, S_IRUGO | S_IWUSR, static int et61x251_create_sysfs(struct et61x251_device* cam) { struct video_device *v4ldev = cam->v4ldev; - int rc; + int err = 0; + + if ((err = video_device_create_file(v4ldev, &class_device_attr_reg))) + goto err_out; + if ((err = video_device_create_file(v4ldev, &class_device_attr_val))) + goto err_reg; - rc = video_device_create_file(v4ldev, &class_device_attr_reg); - if (rc) goto err; - rc = video_device_create_file(v4ldev, &class_device_attr_val); - if (rc) goto err_reg; if (cam->sensor.sysfs_ops) { - rc = video_device_create_file(v4ldev, &class_device_attr_i2c_reg); - if (rc) goto err_val; - rc = video_device_create_file(v4ldev, &class_device_attr_i2c_val); - if (rc) goto err_i2c_reg; + if ((err = video_device_create_file(v4ldev, + &class_device_attr_i2c_reg))) + goto err_val; + if ((err = video_device_create_file(v4ldev, + &class_device_attr_i2c_val))) + goto err_i2c_reg; } - return 0; - err_i2c_reg: + if (cam->sensor.sysfs_ops) video_device_remove_file(v4ldev, &class_device_attr_i2c_reg); err_val: video_device_remove_file(v4ldev, &class_device_attr_val); err_reg: video_device_remove_file(v4ldev, &class_device_attr_reg); -err: - return rc; +err_out: + return err; } #endif /* CONFIG_VIDEO_ADV_DEBUG */ @@ -1767,10 +1771,10 @@ et61x251_vidioc_s_crop(struct et61x251_device* cam, void __user * arg) rect->left = (s->_rect.left & 1L) ? rect->left | 1L : rect->left & ~1L; rect->top = (s->_rect.top & 1L) ? rect->top | 1L : rect->top & ~1L; - if (rect->width < 4) - rect->width = 4; - if (rect->height < 4) - rect->height = 4; + if (rect->width < 16) + rect->width = 16; + if (rect->height < 16) + rect->height = 16; if (rect->width > bounds->width) rect->width = bounds->width; if (rect->height > bounds->height) @@ -1784,8 +1788,8 @@ et61x251_vidioc_s_crop(struct et61x251_device* cam, void __user * arg) if (rect->top + rect->height > bounds->top + bounds->height) rect->top = bounds->top+bounds->height - rect->height; - rect->width &= ~3L; - rect->height &= ~3L; + rect->width &= ~15L; + rect->height &= ~15L; if (ET61X251_PRESERVE_IMGSCALE) { /* Calculate the actual scaling factor */ @@ -1845,6 +1849,35 @@ et61x251_vidioc_s_crop(struct et61x251_device* cam, void __user * arg) } +static int +et61x251_vidioc_enum_framesizes(struct et61x251_device* cam, void __user * arg) +{ + struct v4l2_frmsizeenum frmsize; + + if (copy_from_user(&frmsize, arg, sizeof(frmsize))) + return -EFAULT; + + if (frmsize.index != 0) + return -EINVAL; + + if (frmsize.pixel_format != V4L2_PIX_FMT_ET61X251 && + frmsize.pixel_format != V4L2_PIX_FMT_SBGGR8) + return -EINVAL; + + frmsize.type = V4L2_FRMSIZE_TYPE_STEPWISE; + frmsize.stepwise.min_width = frmsize.stepwise.step_width = 16; + frmsize.stepwise.min_height = frmsize.stepwise.step_height = 16; + frmsize.stepwise.max_width = cam->sensor.cropcap.bounds.width; + frmsize.stepwise.max_height = cam->sensor.cropcap.bounds.height; + memset(&frmsize.reserved, 0, sizeof(frmsize.reserved)); + + if (copy_to_user(arg, &frmsize, sizeof(frmsize))) + return -EFAULT; + + return 0; +} + + static int et61x251_vidioc_enum_fmt(struct et61x251_device* cam, void __user * arg) { @@ -1853,6 +1886,9 @@ et61x251_vidioc_enum_fmt(struct et61x251_device* cam, void __user * arg) if (copy_from_user(&fmtd, arg, sizeof(fmtd))) return -EFAULT; + if (fmtd.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + if (fmtd.index == 0) { strcpy(fmtd.description, "bayer rgb"); fmtd.pixelformat = V4L2_PIX_FMT_SBGGR8; @@ -1934,17 +1970,17 @@ et61x251_vidioc_try_s_fmt(struct et61x251_device* cam, unsigned int cmd, rect.width = scale * pix->width; rect.height = scale * pix->height; - if (rect.width < 4) - rect.width = 4; - if (rect.height < 4) - rect.height = 4; + if (rect.width < 16) + rect.width = 16; + if (rect.height < 16) + rect.height = 16; if (rect.width > bounds->left + bounds->width - rect.left) rect.width = bounds->left + bounds->width - rect.left; if (rect.height > bounds->top + bounds->height - rect.top) rect.height = bounds->top + bounds->height - rect.top; - rect.width &= ~3L; - rect.height &= ~3L; + rect.width &= ~15L; + rect.height &= ~15L; { /* adjust the scaling factor */ u32 a, b; @@ -2378,6 +2414,9 @@ static int et61x251_ioctl_v4l2(struct inode* inode, struct file* filp, case VIDIOC_S_FMT: return et61x251_vidioc_try_s_fmt(cam, cmd, arg); + case VIDIOC_ENUM_FRAMESIZES: + return et61x251_vidioc_enum_framesizes(cam, arg); + case VIDIOC_G_JPEGCOMP: return et61x251_vidioc_g_jpegcomp(cam, arg); @@ -2413,6 +2452,7 @@ static int et61x251_ioctl_v4l2(struct inode* inode, struct file* filp, case VIDIOC_QUERYSTD: case VIDIOC_ENUMSTD: case VIDIOC_QUERYMENU: + case VIDIOC_ENUM_FRAMEINTERVALS: return -EINVAL; default: @@ -2459,6 +2499,7 @@ static const struct file_operations et61x251_fops = { .open = et61x251_open, .release = et61x251_release, .ioctl = et61x251_ioctl, + .compat_ioctl = v4l_compat_ioctl32, .read = et61x251_read, .poll = et61x251_poll, .mmap = et61x251_mmap, @@ -2497,7 +2538,7 @@ et61x251_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) mutex_init(&cam->dev_mutex); DBG(2, "ET61X[12]51 PC Camera Controller detected " - "(vid/pid 0x%04X/0x%04X)",id->idVendor, id->idProduct); + "(vid/pid 0x%04X:0x%04X)",id->idVendor, id->idProduct); for (i = 0; et61x251_sensor_table[i]; i++) { err = et61x251_sensor_table[i](cam); @@ -2550,9 +2591,14 @@ et61x251_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) #ifdef CONFIG_VIDEO_ADV_DEBUG err = et61x251_create_sysfs(cam); - if (err) - goto fail2; - DBG(2, "Optional device control through 'sysfs' interface ready"); + if (!err) + DBG(2, "Optional device control through 'sysfs' " + "interface ready"); + else + DBG(2, "Failed to create 'sysfs' interface for optional " + "device controlling. Error #%d", err); +#else + DBG(2, "Optional device control through 'sysfs' interface disabled"); #endif usb_set_intfdata(intf, cam); @@ -2561,13 +2607,6 @@ et61x251_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) return 0; -#ifdef CONFIG_VIDEO_ADV_DEBUG -fail2: - video_nr[dev_nr] = -1; - dev_nr = (dev_nr < ET61X251_MAX_DEVICES-1) ? dev_nr+1 : 0; - mutex_unlock(&cam->dev_mutex); - video_unregister_device(cam->v4ldev); -#endif fail: if (cam) { kfree(cam->control_buffer); diff --git a/drivers/media/video/et61x251/et61x251_sensor.h b/drivers/media/video/et61x251/et61x251_sensor.h index 65edd08dc386..5fadb5de68bf 100644 --- a/drivers/media/video/et61x251/et61x251_sensor.h +++ b/drivers/media/video/et61x251/et61x251_sensor.h @@ -1,7 +1,7 @@ /*************************************************************************** * API for image sensors connected to ET61X[12]51 PC Camera Controllers * * * - * Copyright (C) 2006 by Luca Risolia * + * Copyright (C) 2006-2007 by Luca Risolia * * * * 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 * @@ -82,7 +82,7 @@ enum et61x251_i2c_rsta { ET61X251_I2C_RSTA_REPEAT = 0x01, /* repeat start */ }; -#define ET61X251_MAX_CTRLS V4L2_CID_LASTP1-V4L2_CID_BASE+10 +#define ET61X251_MAX_CTRLS (V4L2_CID_LASTP1-V4L2_CID_BASE+10) struct et61x251_sensor { char name[32]; diff --git a/drivers/media/video/et61x251/et61x251_tas5130d1b.c b/drivers/media/video/et61x251/et61x251_tas5130d1b.c index a7d65b82b2fb..b06643409842 100644 --- a/drivers/media/video/et61x251/et61x251_tas5130d1b.c +++ b/drivers/media/video/et61x251/et61x251_tas5130d1b.c @@ -2,7 +2,7 @@ * Plug-in for TAS5130D1B image sensor connected to the ET61X[12]51 * * PC Camera Controllers * * * - * Copyright (C) 2006 by Luca Risolia * + * Copyright (C) 2006-2007 by Luca Risolia * * * * 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 * -- cgit v1.2.3 From 43db48d3d2f6326c571984b7b30ab355596bb3cc Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 9 Jan 2007 11:20:59 -0300 Subject: V4L/DVB (5068): Fix authorship references Bill Dirks asked me to update his entries at kernel files, since he change his e-mail. I've also updated a few web broken links or obsolete info to the curent sites where V4L drivers and API are being discussed currently. CC: Bill Dirks Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/CQcam.txt | 6 +++--- Documentation/video4linux/Zoran | 4 ++-- drivers/media/video/v4l1-compat.c | 2 +- drivers/media/video/v4l2-common.c | 2 +- include/linux/videodev2.h | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) (limited to 'Documentation') diff --git a/Documentation/video4linux/CQcam.txt b/Documentation/video4linux/CQcam.txt index ade8651e2443..04986efb731c 100644 --- a/Documentation/video4linux/CQcam.txt +++ b/Documentation/video4linux/CQcam.txt @@ -197,10 +197,10 @@ Use the ../../Maintainers file, particularly the VIDEO FOR LINUX and PARALLEL PORT SUPPORT sections The video4linux page: - http://roadrunner.swansea.linux.org.uk/v4l.shtml + http://linuxtv.org -The video4linux2 page: - http://millennium.diads.com/bdirks/v4l2.htm +The V4L2 API spec: + http://v4l2spec.bytesex.org/ Some web pages about the quickcams: http://www.dkfz-heidelberg.de/Macromol/wedemann/mini-HOWTO-cqcam.html diff --git a/Documentation/video4linux/Zoran b/Documentation/video4linux/Zoran index deb218f77adb..85c575ac4fb9 100644 --- a/Documentation/video4linux/Zoran +++ b/Documentation/video4linux/Zoran @@ -339,9 +339,9 @@ Information - video4linux/mjpeg extensions: (also see below) Information - video4linux2: -http://www.thedirks.org/v4l2/ +http://linuxtv.org +http://v4l2spec.bytesex.org/ /usr/include/linux/videodev2.h -http://www.bytesex.org/v4l/ More information on the video4linux/mjpeg extensions, by Serguei Miridonovi and Rainer Johanni: diff --git a/drivers/media/video/v4l1-compat.c b/drivers/media/video/v4l1-compat.c index 8a13e595304e..d2c1ae0dbfba 100644 --- a/drivers/media/video/v4l1-compat.c +++ b/drivers/media/video/v4l1-compat.c @@ -11,7 +11,7 @@ * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * - * Author: Bill Dirks + * Author: Bill Dirks * et al. * */ diff --git a/drivers/media/video/v4l2-common.c b/drivers/media/video/v4l2-common.c index b8ee37ded3c9..dab87512b9bd 100644 --- a/drivers/media/video/v4l2-common.c +++ b/drivers/media/video/v4l2-common.c @@ -12,7 +12,7 @@ * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * - * Author: Bill Dirks + * Author: Bill Dirks * based on code by Alan Cox, * */ diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index d94e2683be52..112b28c1f63a 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -8,7 +8,7 @@ * * See http://linuxtv.org for more info * - * Author: Bill Dirks + * Author: Bill Dirks * Justin Schoeman * et al. */ -- cgit v1.2.3 From 9de271e66d1172e7fa68ba0a7ecec2f9fb8d78c1 Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Tue, 16 Jan 2007 18:36:40 -0300 Subject: V4L/DVB (5111): Saa7134: add support for Terratec Cinergy HT PCI Add support for Terratec Cinergy HT PCI Signed-off-by: Giorgio Moscardi Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/CARDLIST.saa7134 | 1 + drivers/media/video/saa7134/saa7134-cards.c | 30 +++++++++++++++++++++++++++++ drivers/media/video/saa7134/saa7134-dvb.c | 12 ++++++++++++ drivers/media/video/saa7134/saa7134.h | 1 + 4 files changed, 44 insertions(+) (limited to 'Documentation') diff --git a/Documentation/video4linux/CARDLIST.saa7134 b/Documentation/video4linux/CARDLIST.saa7134 index bcba180194e8..a12246a9bf23 100644 --- a/Documentation/video4linux/CARDLIST.saa7134 +++ b/Documentation/video4linux/CARDLIST.saa7134 @@ -106,3 +106,4 @@ 105 -> Terratec Cinergy HT PCMCIA [153b:1172] 106 -> Encore ENLTV [1131:2342,1131:2341,3016:2344] 107 -> Encore ENLTV-FM [1131:230f] +108 -> Terratec Cinergy HT PCI [153b:1175] diff --git a/drivers/media/video/saa7134/saa7134-cards.c b/drivers/media/video/saa7134/saa7134-cards.c index 0403ae9e5b8a..89f32107f46b 100644 --- a/drivers/media/video/saa7134/saa7134-cards.c +++ b/drivers/media/video/saa7134/saa7134-cards.c @@ -3266,6 +3266,29 @@ struct saa7134_board saa7134_boards[] = { .amux = 0, }, }, + [SAA7134_BOARD_CINERGY_HT_PCI] = { + .name = "Terratec Cinergy HT PCI", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_TDA8290, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .mpeg = SAA7134_MPEG_DVB, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + },{ + .name = name_comp1, + .vmux = 0, + .amux = LINE1, + },{ + .name = name_svideo, + .vmux = 6, + .amux = LINE1, + }}, + }, }; const unsigned int saa7134_bcount = ARRAY_SIZE(saa7134_boards); @@ -3928,6 +3951,12 @@ struct pci_device_id saa7134_pci_tbl[] = { .subvendor = 0x1131, .subdevice = 0x230f, .driver_data = SAA7134_BOARD_ENCORE_ENLTV_FM, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x153b, + .subdevice = 0x1175, + .driver_data = SAA7134_BOARD_CINERGY_HT_PCI, },{ /* --- boards without eeprom + subsystem ID --- */ .vendor = PCI_VENDOR_ID_PHILIPS, @@ -4260,6 +4289,7 @@ int saa7134_board_init2(struct saa7134_dev *dev) } break; case SAA7134_BOARD_CINERGY_HT_PCMCIA: + case SAA7134_BOARD_CINERGY_HT_PCI: /* make the tda10046 find its eeprom */ { u8 data[] = { 0x3c, 0x33, 0x60}; diff --git a/drivers/media/video/saa7134/saa7134-dvb.c b/drivers/media/video/saa7134/saa7134-dvb.c index c33f6a69a247..e3059fd33951 100644 --- a/drivers/media/video/saa7134/saa7134-dvb.c +++ b/drivers/media/video/saa7134/saa7134-dvb.c @@ -1424,6 +1424,18 @@ static int dvb_init(struct saa7134_dev *dev) dev->dvb.frontend->ops.tuner_ops.sleep = cinergy_ht_tuner_sleep; dev->dvb.frontend->ops.tuner_ops.set_params = philips_tiger_tuner_set_params; + } + break; + case SAA7134_BOARD_CINERGY_HT_PCI: + dev->dvb.frontend = dvb_attach(tda10046_attach, + &cinergy_ht_config, + &dev->i2c_adap); + if (dev->dvb.frontend) { + dev->dvb.frontend->ops.i2c_gate_ctrl = tda8290_i2c_gate_ctrl; + dev->dvb.frontend->ops.tuner_ops.init = cinergy_ht_tuner_init; + dev->dvb.frontend->ops.tuner_ops.sleep = cinergy_ht_tuner_sleep; + dev->dvb.frontend->ops.tuner_ops.set_params = md8800_dvbt_pll_set; + } break; default: diff --git a/drivers/media/video/saa7134/saa7134.h b/drivers/media/video/saa7134/saa7134.h index ec08412b1c66..181a1d0fb1e9 100644 --- a/drivers/media/video/saa7134/saa7134.h +++ b/drivers/media/video/saa7134/saa7134.h @@ -234,6 +234,7 @@ struct saa7134_format { #define SAA7134_BOARD_CINERGY_HT_PCMCIA 105 #define SAA7134_BOARD_ENCORE_ENLTV 106 #define SAA7134_BOARD_ENCORE_ENLTV_FM 107 +#define SAA7134_BOARD_CINERGY_HT_PCI 108 #define SAA7134_MAXBOARDS 8 #define SAA7134_INPUT_MAX 8 -- cgit v1.2.3 From a63ad325c337ba634824cefe54f7ac52ee554b72 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 22 Jan 2007 18:27:47 -0300 Subject: V4L/DVB (5119): Various cx2341x documentation updates/fixes. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/cx2341x/fw-dma.txt | 16 +++++++++------- Documentation/video4linux/cx2341x/fw-encoder-api.txt | 10 ++++++---- Documentation/video4linux/cx2341x/fw-memory.txt | 4 +++- 3 files changed, 18 insertions(+), 12 deletions(-) (limited to 'Documentation') diff --git a/Documentation/video4linux/cx2341x/fw-dma.txt b/Documentation/video4linux/cx2341x/fw-dma.txt index 8123e262d5b6..be52b6fd1e9a 100644 --- a/Documentation/video4linux/cx2341x/fw-dma.txt +++ b/Documentation/video4linux/cx2341x/fw-dma.txt @@ -22,6 +22,8 @@ urged to choose a smaller block size and learn the scatter-gather technique. Mailbox #10 is reserved for DMA transfer information. +Note: the hardware expects little-endian data ('intel format'). + Flow ==== @@ -64,7 +66,7 @@ addresses are the physical memory location of the target DMA buffer. Each S-G array element is a struct of three 32-bit words. The first word is the source address, the second is the destination address. Both take up the -entire 32 bits. The lowest 16 bits of the third word is the transfer byte +entire 32 bits. The lowest 18 bits of the third word is the transfer byte count. The high-bit of the third word is the "last" flag. The last-flag tells the card to raise the DMA_DONE interrupt. From hard personal experience, if you forget to set this bit, the card will still "work" but the stream will @@ -78,8 +80,8 @@ Array Element: - 32-bit Source Address - 32-bit Destination Address -- 16-bit reserved (high bit is the last flag) -- 16-bit byte count +- 14-bit reserved (high bit is the last flag) +- 18-bit byte count DMA Transfer Status =================== @@ -87,8 +89,8 @@ DMA Transfer Status Register 0x0004 holds the DMA Transfer Status: Bit -4 Scatter-Gather array error -3 DMA write error -2 DMA read error -1 write completed 0 read completed +1 write completed +2 DMA read error +3 DMA write error +4 Scatter-Gather array error diff --git a/Documentation/video4linux/cx2341x/fw-encoder-api.txt b/Documentation/video4linux/cx2341x/fw-encoder-api.txt index fe02bdb84598..e499cc07fe3d 100644 --- a/Documentation/video4linux/cx2341x/fw-encoder-api.txt +++ b/Documentation/video4linux/cx2341x/fw-encoder-api.txt @@ -498,12 +498,14 @@ Name CX2341X_ENC_GET_PREV_DMA_INFO_MB_9 Enum 203/0xCB Description Returns information on the previous DMA transfer in conjunction with - bit 27 of the interrupt mask. Uses mailbox 9. + bit 27 or 18 of the interrupt mask. Uses mailbox 9. Result[0] Status bits: - Bit 0 set indicates transfer complete - Bit 2 set indicates transfer error - Bit 4 set indicates linked list error + 0 read completed + 1 write completed + 2 DMA read error + 3 DMA write error + 4 Scatter-Gather array error Result[1] DMA type Result[2] diff --git a/Documentation/video4linux/cx2341x/fw-memory.txt b/Documentation/video4linux/cx2341x/fw-memory.txt index d445e457fc1b..9d736fe8de66 100644 --- a/Documentation/video4linux/cx2341x/fw-memory.txt +++ b/Documentation/video4linux/cx2341x/fw-memory.txt @@ -1,6 +1,8 @@ This document describes the cx2341x memory map and documents some of the register space. +Note: the memory long words are little-endian ('intel format'). + Warning! This information was figured out from searching through the memory and registers, this information may not be correct and is certainly not complete, and was not derived from anything more than searching through the memory space with @@ -67,7 +69,7 @@ DMA Registers 0x000-0xff: 0x84 - first write linked list reg, for pci memory addr 0x88 - first write linked list reg, for length of buffer in memory addr (|0x80000000 or this for last link) - 0x8c-0xcc - rest of write linked list reg, 8 sets of 3 total, DMA goes here + 0x8c-0xdc - rest of write linked list reg, 8 sets of 3 total, DMA goes here from linked list addr in reg 0x0c, firmware must push through or something. 0xe0 - first (and only) read linked list reg, for pci memory addr -- cgit v1.2.3 From fec1bc71a314507418e65bcd0f232b3b9f36f435 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 2 Feb 2007 20:42:02 -0300 Subject: V4L/DVB (5179): Cx2341x encoder documentation update. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/cx2341x/fw-encoder-api.txt | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'Documentation') diff --git a/Documentation/video4linux/cx2341x/fw-encoder-api.txt b/Documentation/video4linux/cx2341x/fw-encoder-api.txt index e499cc07fe3d..242104ce5b61 100644 --- a/Documentation/video4linux/cx2341x/fw-encoder-api.txt +++ b/Documentation/video4linux/cx2341x/fw-encoder-api.txt @@ -680,7 +680,7 @@ Description the value. Param[0] Command number: - 1=set initial SCR value when starting encoding. + 1=set initial SCR value when starting encoding (works). 2=set quality mode (apparently some test setting). 3=setup advanced VIM protection handling (supposedly only for the cx23416 for raw YUV). @@ -689,7 +689,11 @@ Param[0] 4=generate artificial PTS timestamps 5=USB flush mode 6=something to do with the quantization matrix - 7=set navigation pack insertion for DVD + 7=set navigation pack insertion for DVD: adds 0xbf (private stream 2) + packets to the MPEG. The size of these packets is 2048 bytes (including + the header of 6 bytes: 0x000001bf + length). The payload is zeroed and + it is up to the application to fill them in. These packets are apparently + inserted every four frames. 8=enable scene change detection (seems to be a failure) 9=set history parameters of the video input module 10=set input field order of VIM -- cgit v1.2.3 From 32ec5332f987435d42371c1c47e310c9cc211cf7 Mon Sep 17 00:00:00 2001 From: Ian Armstrong Date: Sat, 3 Feb 2007 06:37:25 -0300 Subject: V4L/DVB (5184): Add cx23415 decoder register documentation Many thanks to Ian Armstrong for figuring out what all these registers do. Signed-off-by: Ian Armstrong Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- .../video4linux/cx2341x/fw-decoder-regs.txt | 815 +++++++++++++++++++++ 1 file changed, 815 insertions(+) create mode 100644 Documentation/video4linux/cx2341x/fw-decoder-regs.txt (limited to 'Documentation') diff --git a/Documentation/video4linux/cx2341x/fw-decoder-regs.txt b/Documentation/video4linux/cx2341x/fw-decoder-regs.txt new file mode 100644 index 000000000000..db2366c634e8 --- /dev/null +++ b/Documentation/video4linux/cx2341x/fw-decoder-regs.txt @@ -0,0 +1,815 @@ +PVR350 Video decoder registers 0x02002800 -> 0x02002B00 +======================================================= + +This list has been worked out through trial and error. There will be mistakes +and omissions. Some registers have no obvious effect so it's hard to say what +they do, while others interact with each other, or require a certain load +sequence. Horizontal filter setup is one example, with six registers working +in unison and requiring a certain load sequence to correctly configure. The +indexed colour palette is much easier to set at just two registers, but again +it requires a certain load sequence. + +Some registers are fussy about what they are set to. Load in a bad value & the +decoder will fail. A firmware reload will often recover, but sometimes a reset +is required. For registers containing size information, setting them to 0 is +generally a bad idea. For other control registers i.e. 2878, you'll only find +out what values are bad when it hangs. + +-------------------------------------------------------------------------------- +2800 + bit 0 + Decoder enable + 0 = disable + 1 = enable +-------------------------------------------------------------------------------- +2804 + bits 0:31 + Decoder horizontal Y alias register 1 +--------------- +2808 + bits 0:31 + Decoder horizontal Y alias register 2 +--------------- +280C + bits 0:31 + Decoder horizontal Y alias register 3 +--------------- +2810 + bits 0:31 + Decoder horizontal Y alias register 4 +--------------- +2814 + bits 0:31 + Decoder horizontal Y alias register 5 +--------------- +2818 + bits 0:31 + Decoder horizontal Y alias trigger + + These six registers control the horizontal aliasing filter for the Y plane. + The first five registers must all be loaded before accessing the trigger + (2818), as this register actually clocks the data through for the first + five. + + To correctly program set the filter, this whole procedure must be done 16 + times. The actual register contents are copied from a lookup-table in the + firmware which contains 4 different filter settings. + +-------------------------------------------------------------------------------- +281C + bits 0:31 + Decoder horizontal UV alias register 1 +--------------- +2820 + bits 0:31 + Decoder horizontal UV alias register 2 +--------------- +2824 + bits 0:31 + Decoder horizontal UV alias register 3 +--------------- +2828 + bits 0:31 + Decoder horizontal UV alias register 4 +--------------- +282C + bits 0:31 + Decoder horizontal UV alias register 5 +--------------- +2830 + bits 0:31 + Decoder horizontal UV alias trigger + + These six registers control the horizontal aliasing for the UV plane. + Operation is the same as the Y filter, with 2830 being the trigger + register. + +-------------------------------------------------------------------------------- +2834 + bits 0:15 + Decoder Y source width in pixels + + bits 16:31 + Decoder Y destination width in pixels +--------------- +2838 + bits 0:15 + Decoder UV source width in pixels + + bits 16:31 + Decoder UV destination width in pixels + + NOTE: For both registers, the resulting image must be fully visible on + screen. If the image exceeds the right edge both the source and destination + size must be adjusted to reflect the visible portion. For the source width, + you must take into account the scaling when calculating the new value. +-------------------------------------------------------------------------------- + +283C + bits 0:31 + Decoder Y horizontal scaling + Normally = Reg 2854 >> 2 +--------------- +2840 + bits 0:31 + Decoder ?? unknown - horizontal scaling + Usually 0x00080514 +--------------- +2844 + bits 0:31 + Decoder UV horizontal scaling + Normally = Reg 2854 >> 2 +--------------- +2848 + bits 0:31 + Decoder ?? unknown - horizontal scaling + Usually 0x00100514 +--------------- +284C + bits 0:31 + Decoder ?? unknown - Y plane + Usually 0x00200020 +--------------- +2850 + bits 0:31 + Decoder ?? unknown - UV plane + Usually 0x00200020 +--------------- +2854 + bits 0:31 + Decoder 'master' value for horizontal scaling +--------------- +2858 + bits 0:31 + Decoder ?? unknown + Usually 0 +--------------- +285C + bits 0:31 + Decoder ?? unknown + Normally = Reg 2854 >> 1 +--------------- +2860 + bits 0:31 + Decoder ?? unknown + Usually 0 +--------------- +2864 + bits 0:31 + Decoder ?? unknown + Normally = Reg 2854 >> 1 +--------------- +2868 + bits 0:31 + Decoder ?? unknown + Usually 0 + + Most of these registers either control horizontal scaling, or appear linked + to it in some way. Register 2854 contains the 'master' value & the other + registers can be calculated from that one. You must also remember to + correctly set the divider in Reg 2874. + + To enlarge: + Reg 2854 = (source_width * 0x00200000) / destination_width + Reg 2874 = No divide + + To reduce from full size down to half size: + Reg 2854 = (source_width/2 * 0x00200000) / destination width + Reg 2874 = Divide by 2 + + To reduce from half size down to quarter size: + Reg 2854 = (source_width/4 * 0x00200000) / destination width + Reg 2874 = Divide by 4 + + The result is always rounded up. + +-------------------------------------------------------------------------------- +286C + bits 0:15 + Decoder horizontal Y buffer offset + + bits 15:31 + Decoder horizontal UV buffer offset + + Offset into the video image buffer. If the offset is gradually incremented, + the on screen image will move left & wrap around higher up on the right. + +-------------------------------------------------------------------------------- +2870 + bits 0:15 + Decoder horizontal Y output offset + + bits 16:31 + Decoder horizontal UV output offset + + Offsets the actual video output. Controls output alignment of the Y & UV + planes. The higher the value, the greater the shift to the left. Use + reg 2890 to move the image right. + +-------------------------------------------------------------------------------- +2874 + bits 0:1 + Decoder horizontal Y output size divider + 00 = No divide + 01 = Divide by 2 + 10 = Divide by 3 + + bits 4:5 + Decoder horizontal UV output size divider + 00 = No divide + 01 = Divide by 2 + 10 = Divide by 3 + + bit 8 + Decoder ?? unknown + 0 = Normal + 1 = Affects video output levels + + bit 16 + Decoder ?? unknown + 0 = Normal + 1 = Disable horizontal filter + +-------------------------------------------------------------------------------- +2878 + bit 0 + ?? unknown + + bit 1 + osd on/off + 0 = osd off + 1 = osd on + + bit 2 + Decoder + osd video timing + 0 = NTSC + 1 = PAL + + bits 3:4 + ?? unknown + + bit 5 + Decoder + osd + Swaps upper & lower fields + +-------------------------------------------------------------------------------- +287C + bits 0:10 + Decoder & osd ?? unknown + Moves entire screen horizontally. Starts at 0x005 with the screen + shifted heavily to the right. Incrementing in steps of 0x004 will + gradually shift the screen to the left. + + bits 11:31 + ?? unknown + + Normally contents are 0x00101111 (NTSC) or 0x1010111d (PAL) + +-------------------------------------------------------------------------------- +2880 -------- ?? unknown +2884 -------- ?? unknown +-------------------------------------------------------------------------------- +2888 + bit 0 + Decoder + osd ?? unknown + 0 = Normal + 1 = Misaligned fields (Correctable through 289C & 28A4) + + bit 4 + ?? unknown + + bit 8 + ?? unknown + + Warning: Bad values will require a firmware reload to recover. + Known to be bad are 0x000,0x011,0x100,0x111 +-------------------------------------------------------------------------------- +288C + bits 0:15 + osd ?? unknown + Appears to affect the osd position stability. The higher the value the + more unstable it becomes. Decoder output remains stable. + + bits 16:31 + osd ?? unknown + Same as bits 0:15 + +-------------------------------------------------------------------------------- +2890 + bits 0:11 + Decoder output horizontal offset. + + Horizontal offset moves the video image right. A small left shift is + possible, but it's better to use reg 2870 for that due to its greater + range. + + NOTE: Video corruption will occur if video window is shifted off the right + edge. To avoid this read the notes for 2834 & 2838. +-------------------------------------------------------------------------------- +2894 + bits 0:23 + Decoder output video surround colour. + + Contains the colour (in yuv) used to fill the screen when the video is + running in a window. +-------------------------------------------------------------------------------- +2898 + bits 0:23 + Decoder video window colour + Contains the colour (in yuv) used to fill the video window when the + video is turned off. + + bit 24 + Decoder video output + 0 = Video on + 1 = Video off + + bit 28 + Decoder plane order + 0 = Y,UV + 1 = UV,Y + + bit 29 + Decoder second plane byte order + 0 = Normal (UV) + 1 = Swapped (VU) + + In normal usage, the first plane is Y & the second plane is UV. Though the + order of the planes can be swapped, only the byte order of the second plane + can be swapped. This isn't much use for the Y plane, but can be useful for + the UV plane. + +-------------------------------------------------------------------------------- +289C + bits 0:15 + Decoder vertical field offset 1 + + bits 16:31 + Decoder vertical field offset 2 + + Controls field output vertical alignment. The higher the number, the lower + the image on screen. Known starting values are 0x011E0017 (NTSC) & + 0x01500017 (PAL) +-------------------------------------------------------------------------------- +28A0 + bits 0:15 + Decoder & osd width in pixels + + bits 16:31 + Decoder & osd height in pixels + + All output from the decoder & osd are disabled beyond this area. Decoder + output will simply go black outside of this region. If the osd tries to + exceed this area it will become corrupt. +-------------------------------------------------------------------------------- +28A4 + bits 0:11 + osd left shift. + + Has a range of 0x770->0x7FF. With the exception of 0, any value outside of + this range corrupts the osd. +-------------------------------------------------------------------------------- +28A8 + bits 0:15 + osd vertical field offset 1 + + bits 16:31 + osd vertical field offset 2 + + Controls field output vertical alignment. The higher the number, the lower + the image on screen. Known starting values are 0x011E0017 (NTSC) & + 0x01500017 (PAL) +-------------------------------------------------------------------------------- +28AC -------- ?? unknown + | + V +28BC -------- ?? unknown +-------------------------------------------------------------------------------- +28C0 + bit 0 + Current output field + 0 = first field + 1 = second field + + bits 16:31 + Current scanline + The scanline counts from the top line of the first field + through to the last line of the second field. +-------------------------------------------------------------------------------- +28C4 -------- ?? unknown + | + V +28F8 -------- ?? unknown +-------------------------------------------------------------------------------- +28FC + bit 0 + ?? unknown + 0 = Normal + 1 = Breaks decoder & osd output +-------------------------------------------------------------------------------- +2900 + bits 0:31 + Decoder vertical Y alias register 1 +--------------- +2904 + bits 0:31 + Decoder vertical Y alias register 2 +--------------- +2908 + bits 0:31 + Decoder vertical Y alias trigger + + These three registers control the vertical aliasing filter for the Y plane. + Operation is similar to the horizontal Y filter (2804). The only real + difference is that there are only two registers to set before accessing + the trigger register (2908). As for the horizontal filter, the values are + taken from a lookup table in the firmware, and the procedure must be + repeated 16 times to fully program the filter. +-------------------------------------------------------------------------------- +290C + bits 0:31 + Decoder vertical UV alias register 1 +--------------- +2910 + bits 0:31 + Decoder vertical UV alias register 2 +--------------- +2914 + bits 0:31 + Decoder vertical UV alias trigger + + These three registers control the vertical aliasing filter for the UV + plane. Operation is the same as the Y filter, with 2914 being the trigger. +-------------------------------------------------------------------------------- +2918 + bits 0:15 + Decoder Y source height in pixels + + bits 16:31 + Decoder Y destination height in pixels +--------------- +291C + bits 0:15 + Decoder UV source height in pixels divided by 2 + + bits 16:31 + Decoder UV destination height in pixels + + NOTE: For both registers, the resulting image must be fully visible on + screen. If the image exceeds the bottom edge both the source and + destination size must be adjusted to reflect the visible portion. For the + source height, you must take into account the scaling when calculating the + new value. +-------------------------------------------------------------------------------- +2920 + bits 0:31 + Decoder Y vertical scaling + Normally = Reg 2930 >> 2 +--------------- +2924 + bits 0:31 + Decoder Y vertical scaling + Normally = Reg 2920 + 0x514 +--------------- +2928 + bits 0:31 + Decoder UV vertical scaling + When enlarging = Reg 2930 >> 2 + When reducing = Reg 2930 >> 3 +--------------- +292C + bits 0:31 + Decoder UV vertical scaling + Normally = Reg 2928 + 0x514 +--------------- +2930 + bits 0:31 + Decoder 'master' value for vertical scaling +--------------- +2934 + bits 0:31 + Decoder ?? unknown - Y vertical scaling +--------------- +2938 + bits 0:31 + Decoder Y vertical scaling + Normally = Reg 2930 +--------------- +293C + bits 0:31 + Decoder ?? unknown - Y vertical scaling +--------------- +2940 + bits 0:31 + Decoder UV vertical scaling + When enlarging = Reg 2930 >> 1 + When reducing = Reg 2930 +--------------- +2944 + bits 0:31 + Decoder ?? unknown - UV vertical scaling +--------------- +2948 + bits 0:31 + Decoder UV vertical scaling + Normally = Reg 2940 +--------------- +294C + bits 0:31 + Decoder ?? unknown - UV vertical scaling + + Most of these registers either control vertical scaling, or appear linked + to it in some way. Register 2930 contains the 'master' value & all other + registers can be calculated from that one. You must also remember to + correctly set the divider in Reg 296C + + To enlarge: + Reg 2930 = (source_height * 0x00200000) / destination_height + Reg 296C = No divide + + To reduce from full size down to half size: + Reg 2930 = (source_height/2 * 0x00200000) / destination height + Reg 296C = Divide by 2 + + To reduce from half down to quarter. + Reg 2930 = (source_height/4 * 0x00200000) / destination height + Reg 296C = Divide by 4 + +-------------------------------------------------------------------------------- +2950 + bits 0:15 + Decoder Y line index into display buffer, first field + + bits 16:31 + Decoder Y vertical line skip, first field +-------------------------------------------------------------------------------- +2954 + bits 0:15 + Decoder Y line index into display buffer, second field + + bits 16:31 + Decoder Y vertical line skip, second field +-------------------------------------------------------------------------------- +2958 + bits 0:15 + Decoder UV line index into display buffer, first field + + bits 16:31 + Decoder UV vertical line skip, first field +-------------------------------------------------------------------------------- +295C + bits 0:15 + Decoder UV line index into display buffer, second field + + bits 16:31 + Decoder UV vertical line skip, second field +-------------------------------------------------------------------------------- +2960 + bits 0:15 + Decoder destination height minus 1 + + bits 16:31 + Decoder destination height divided by 2 +-------------------------------------------------------------------------------- +2964 + bits 0:15 + Decoder Y vertical offset, second field + + bits 16:31 + Decoder Y vertical offset, first field + + These two registers shift the Y plane up. The higher the number, the + greater the shift. +-------------------------------------------------------------------------------- +2968 + bits 0:15 + Decoder UV vertical offset, second field + + bits 16:31 + Decoder UV vertical offset, first field + + These two registers shift the UV plane up. The higher the number, the + greater the shift. +-------------------------------------------------------------------------------- +296C + bits 0:1 + Decoder vertical Y output size divider + 00 = No divide + 01 = Divide by 2 + 10 = Divide by 4 + + bits 8:9 + Decoder vertical UV output size divider + 00 = No divide + 01 = Divide by 2 + 10 = Divide by 4 +-------------------------------------------------------------------------------- +2970 + bit 0 + Decoder ?? unknown + 0 = Normal + 1 = Affect video output levels + + bit 16 + Decoder ?? unknown + 0 = Normal + 1 = Disable vertical filter + +-------------------------------------------------------------------------------- +2974 -------- ?? unknown + | + V +29EF -------- ?? unknown +-------------------------------------------------------------------------------- +2A00 + bits 0:2 + osd colour mode + 001 = 16 bit (565) + 010 = 15 bit (555) + 011 = 12 bit (444) + 100 = 32 bit (8888) + 101 = 8 bit indexed + + bits 4:5 + osd display bpp + 01 = 8 bit + 10 = 16 bit + 11 = 32 bit + + bit 8 + osd global alpha + 0 = Off + 1 = On + + bit 9 + osd local alpha + 0 = Off + 1 = On + + bit 10 + osd colour key + 0 = Off + 1 = On + + bit 11 + osd ?? unknown + Must be 1 + + bit 13 + osd colour space + 0 = ARGB + 1 = AYVU + + bits 16:31 + osd ?? unknown + Must be 0x001B (some kind of buffer pointer ?) + + When the bits-per-pixel is set to 8, the colour mode is ignored and + assumed to be 8 bit indexed. For 16 & 32 bits-per-pixel the colour depth + is honoured, and when using a colour depth that requires fewer bytes than + allocated the extra bytes are used as padding. So for a 32 bpp with 8 bit + index colour, there are 3 padding bytes per pixel. It's also possible to + select 16bpp with a 32 bit colour mode. This results in the pixel width + being doubled, but the color key will not work as expected in this mode. + + Colour key is as it suggests. You designate a colour which will become + completely transparent. When using 565, 555 or 444 colour modes, the + colour key is always 16 bits wide. The colour to key on is set in Reg 2A18. + + Local alpha is a per-pixel 256 step transparency, with 0 being transparent + and 255 being solid. This is only available in 32 bit & 8 bit indexed + colour modes. + + Global alpha is a 256 step transparency that applies to the entire osd, + with 0 being transparent & 255 being solid. + + It's possible to combine colour key, local alpha & global alpha. +-------------------------------------------------------------------------------- +2A04 + bits 0:15 + osd x coord for left edge + + bits 16:31 + osd y coord for top edge +--------------- +2A08 + bits 0:15 + osd x coord for right edge + + bits 16:31 + osd y coord for bottom edge + + For both registers, (0,0) = top left corner of the display area. These + registers do not control the osd size, only where it's positioned & how + much is visible. The visible osd area cannot exceed the right edge of the + display, otherwise the osd will become corrupt. See reg 2A10 for + setting osd width. +-------------------------------------------------------------------------------- +2A0C + bits 0:31 + osd buffer index + + An index into the osd buffer. Slowly incrementing this moves the osd left, + wrapping around onto the right edge +-------------------------------------------------------------------------------- +2A10 + bits 0:11 + osd buffer 32 bit word width + + Contains the width of the osd measured in 32 bit words. This means that all + colour modes are restricted to a byte width which is divisible by 4. +-------------------------------------------------------------------------------- +2A14 + bits 0:15 + osd height in pixels + + bits 16:32 + osd line index into buffer + osd will start displaying from this line. +-------------------------------------------------------------------------------- +2A18 + bits 0:31 + osd colour key + + Contains the colour value which will be transparent. +-------------------------------------------------------------------------------- +2A1C + bits 0:7 + osd global alpha + + Contains the global alpha value (equiv ivtvfbctl --alpha XX) +-------------------------------------------------------------------------------- +2A20 -------- ?? unknown + | + V +2A2C -------- ?? unknown +-------------------------------------------------------------------------------- +2A30 + bits 0:7 + osd colour to change in indexed palette +--------------- +2A34 + bits 0:31 + osd colour for indexed palette + + To set the new palette, first load the index of the colour to change into + 2A30, then load the new colour into 2A34. The full palette is 256 colours, + so the index range is 0x00-0xFF +-------------------------------------------------------------------------------- +2A38 -------- ?? unknown +2A3C -------- ?? unknown +-------------------------------------------------------------------------------- +2A40 + bits 0:31 + osd ?? unknown + + Affects overall brightness, wrapping around to black +-------------------------------------------------------------------------------- +2A44 + bits 0:31 + osd ?? unknown + + Green tint +-------------------------------------------------------------------------------- +2A48 + bits 0:31 + osd ?? unknown + + Red tint +-------------------------------------------------------------------------------- +2A4C + bits 0:31 + osd ?? unknown + + Affects overall brightness, wrapping around to black +-------------------------------------------------------------------------------- +2A50 + bits 0:31 + osd ?? unknown + + Colour shift +-------------------------------------------------------------------------------- +2A54 + bits 0:31 + osd ?? unknown + + Colour shift +-------------------------------------------------------------------------------- +2A58 -------- ?? unknown + | + V +2AFC -------- ?? unknown +-------------------------------------------------------------------------------- +2B00 + bit 0 + osd filter control + 0 = filter off + 1 = filter on + + bits 1:4 + osd ?? unknown + +-------------------------------------------------------------------------------- + +v0.3 - 2 February 2007 - Ian Armstrong (ian@iarmst.demon.co.uk) + -- cgit v1.2.3 From b5e795f8df42936590ba9c606edc715fe3593284 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Tue, 20 Feb 2007 15:00:53 -0500 Subject: USB: make autosuspend delay a module parameter This patch (as859) makes the default USB autosuspend delay a module parameter of usbcore. By setting the delay value at boot time, users will be able to prevent the system from autosuspending devices which for some reason can't handle it. The patch also stores the autosuspend delay as a per-device value. A later patch will allow the user to change the value, tailoring the delay for each individual device. A delay value of 0 will prevent autosuspend. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- Documentation/kernel-parameters.txt | 7 +++++++ drivers/usb/core/driver.c | 16 ++++++++++------ drivers/usb/core/quirks.c | 6 ++++-- drivers/usb/core/usb.c | 12 ++++++++++++ drivers/usb/core/usb.h | 2 -- include/linux/usb.h | 3 +++ 6 files changed, 36 insertions(+), 10 deletions(-) (limited to 'Documentation') diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index c479d30eeaa3..03eb5ed503f7 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -1758,6 +1758,13 @@ and is between 256 and 4096 characters. It is defined in the file Note that genuine overcurrent events won't be reported either. + usbcore.autosuspend= + [USB] The autosuspend time delay (in seconds) used + for newly-detected USB devices (default 2). This + is the time required before an idle device will be + autosuspended. Devices for which the delay is set + to 0 won't be autosuspended at all. + usbhid.mousepoll= [USBHID] The interval which mice are to be polled at. diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index f9196a0a9412..a420d72a0254 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -963,12 +963,16 @@ static int autosuspend_check(struct usb_device *udev) int i; struct usb_interface *intf; - /* For autosuspend, fail fast if anything is in use. - * Also fail if any interfaces require remote wakeup but it - * isn't available. */ + /* For autosuspend, fail fast if anything is in use or autosuspend + * is disabled. Also fail if any interfaces require remote wakeup + * but it isn't available. + */ udev->do_remote_wakeup = device_may_wakeup(&udev->dev); if (udev->pm_usage_cnt > 0) return -EBUSY; + if (!udev->autosuspend_delay) + return -EPERM; + if (udev->actconfig) { for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) { intf = udev->actconfig->interface[i]; @@ -991,7 +995,7 @@ static int autosuspend_check(struct usb_device *udev) #define autosuspend_check(udev) 0 -#endif +#endif /* CONFIG_USB_SUSPEND */ /** * usb_suspend_both - suspend a USB device and its interfaces @@ -1186,7 +1190,7 @@ static int usb_autopm_do_device(struct usb_device *udev, int inc_usage_cnt) udev->pm_usage_cnt -= inc_usage_cnt; } else if (inc_usage_cnt <= 0 && autosuspend_check(udev) == 0) queue_delayed_work(ksuspend_usb_wq, &udev->autosuspend, - USB_AUTOSUSPEND_DELAY); + udev->autosuspend_delay); usb_pm_unlock(udev); return status; } @@ -1270,7 +1274,7 @@ static int usb_autopm_do_interface(struct usb_interface *intf, intf->pm_usage_cnt -= inc_usage_cnt; } else if (inc_usage_cnt <= 0 && autosuspend_check(udev) == 0) queue_delayed_work(ksuspend_usb_wq, &udev->autosuspend, - USB_AUTOSUSPEND_DELAY); + udev->autosuspend_delay); } usb_pm_unlock(udev); return status; diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c index ea0e48e9f611..0e5c646cb4f6 100644 --- a/drivers/usb/core/quirks.c +++ b/drivers/usb/core/quirks.c @@ -39,8 +39,10 @@ static const struct usb_device_id usb_quirk_list[] = { static void usb_autosuspend_quirk(struct usb_device *udev) { - /* unbalanced resume to prevent autosuspends */ - usb_autoresume_device(udev); +#ifdef CONFIG_USB_SUSPEND + /* disable autosuspend, but allow the user to re-enable it via sysfs */ + udev->autosuspend_delay = 0; +#endif } static const struct usb_device_id *find_id(struct usb_device *udev) diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index 3db721cd557a..54b42ce311c1 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -22,6 +22,7 @@ */ #include +#include #include #include #include @@ -50,6 +51,16 @@ static int nousb; /* Disable USB when built into kernel image */ struct workqueue_struct *ksuspend_usb_wq; /* For autosuspend */ +#ifdef CONFIG_USB_SUSPEND +static int usb_autosuspend_delay = 2; /* Default delay value, + * in seconds */ +module_param_named(autosuspend, usb_autosuspend_delay, uint, 0644); +MODULE_PARM_DESC(autosuspend, "default autosuspend delay"); + +#else +#define usb_autosuspend_delay 0 +#endif + /** * usb_ifnum_to_if - get the interface object with a given interface number @@ -306,6 +317,7 @@ usb_alloc_dev(struct usb_device *parent, struct usb_bus *bus, unsigned port1) #ifdef CONFIG_PM mutex_init(&dev->pm_mutex); INIT_DELAYED_WORK(&dev->autosuspend, usb_autosuspend_work); + dev->autosuspend_delay = usb_autosuspend_delay * HZ; #endif return dev; } diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h index 045cbd111887..b0a35f45b099 100644 --- a/drivers/usb/core/usb.h +++ b/drivers/usb/core/usb.h @@ -65,8 +65,6 @@ static inline void usb_pm_unlock(struct usb_device *udev) {} #ifdef CONFIG_USB_SUSPEND -#define USB_AUTOSUSPEND_DELAY (HZ*2) - extern void usb_autosuspend_device(struct usb_device *udev); extern int usb_autoresume_device(struct usb_device *udev); diff --git a/include/linux/usb.h b/include/linux/usb.h index 37e522eba47f..87dc75a6cee1 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -389,10 +389,13 @@ struct usb_device { int pm_usage_cnt; /* usage counter for autosuspend */ u32 quirks; /* quirks of the whole device */ + #ifdef CONFIG_PM struct delayed_work autosuspend; /* for delayed autosuspends */ struct mutex pm_mutex; /* protects PM operations */ + unsigned autosuspend_delay; /* in jiffies */ + unsigned auto_pm:1; /* autosuspend/resume in progress */ unsigned do_remote_wakeup:1; /* remote wakeup should be enabled */ #endif -- cgit v1.2.3 From cacfd56756c087873f22dc9e2ace5f634775836a Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Tue, 20 Feb 2007 01:03:48 -0800 Subject: [SPARC]: Remove the broken SUN_AURORA driver. The SUN_AURORA driver: - has been marked as BROKEN for more than two years and - is still marked as BROKEN. Drivers that had been marked as BROKEN for such a long time seem to be unlikely to be revived in the forseeable future. But if anyone wants to ever revive this driver, the code is still present in the older kernel releases. Signed-off-by: Adrian Bunk Signed-off-by: David S. Miller --- Documentation/magic-number.txt | 1 - drivers/sbus/char/Kconfig | 7 - drivers/sbus/char/Makefile | 1 - drivers/sbus/char/aurora.c | 2364 ---------------------------------------- drivers/sbus/char/aurora.h | 276 ----- drivers/sbus/char/cd180.h | 240 ---- 6 files changed, 2889 deletions(-) delete mode 100644 drivers/sbus/char/aurora.c delete mode 100644 drivers/sbus/char/aurora.h delete mode 100644 drivers/sbus/char/cd180.h (limited to 'Documentation') diff --git a/Documentation/magic-number.txt b/Documentation/magic-number.txt index af67faccf4de..0e740c812d12 100644 --- a/Documentation/magic-number.txt +++ b/Documentation/magic-number.txt @@ -65,7 +65,6 @@ CMAGIC 0x0111 user include/linux/a.out.h MKISS_DRIVER_MAGIC 0x04bf mkiss_channel drivers/net/mkiss.h RISCOM8_MAGIC 0x0907 riscom_port drivers/char/riscom8.h SPECIALIX_MAGIC 0x0907 specialix_port drivers/char/specialix_io8.h -AURORA_MAGIC 0x0A18 Aurora_port drivers/sbus/char/aurora.h HDLC_MAGIC 0x239e n_hdlc drivers/char/n_hdlc.c APM_BIOS_MAGIC 0x4101 apm_user arch/i386/kernel/apm.c CYCLADES_MAGIC 0x4359 cyclades_port include/linux/cyclades.h diff --git a/drivers/sbus/char/Kconfig b/drivers/sbus/char/Kconfig index 3a8152906bf6..35a73168333f 100644 --- a/drivers/sbus/char/Kconfig +++ b/drivers/sbus/char/Kconfig @@ -46,13 +46,6 @@ config SUN_VIDEOPIX based on the Phillips SAA9051, can handle NTSC and PAL/SECAM and SVIDEO signals. -config SUN_AURORA - tristate "Aurora Multiboard 1600se (EXPERIMENTAL)" - depends on EXPERIMENTAL && BROKEN - help - The Aurora Multiboard is a multi-port high-speed serial controller. - If you have one of these, say Y. - config TADPOLE_TS102_UCTRL tristate "Tadpole TS102 Microcontroller support (EXPERIMENTAL)" depends on EXPERIMENTAL && SPARC32 diff --git a/drivers/sbus/char/Makefile b/drivers/sbus/char/Makefile index 3a5ea1dc789a..7ab060e9a5fe 100644 --- a/drivers/sbus/char/Makefile +++ b/drivers/sbus/char/Makefile @@ -19,7 +19,6 @@ obj-$(CONFIG_SUN_OPENPROMIO) += openprom.o obj-$(CONFIG_SUN_MOSTEK_RTC) += rtc.o obj-$(CONFIG_SUN_BPP) += bpp.o obj-$(CONFIG_SUN_VIDEOPIX) += vfc.o -obj-$(CONFIG_SUN_AURORA) += aurora.o obj-$(CONFIG_TADPOLE_TS102_UCTRL) += uctrl.o obj-$(CONFIG_SUN_JSFLASH) += jsflash.o obj-$(CONFIG_BBC_I2C) += bbc.o diff --git a/drivers/sbus/char/aurora.c b/drivers/sbus/char/aurora.c deleted file mode 100644 index a54b4ac67568..000000000000 --- a/drivers/sbus/char/aurora.c +++ /dev/null @@ -1,2364 +0,0 @@ -/* $Id: aurora.c,v 1.19 2002/01/08 16:00:16 davem Exp $ - * linux/drivers/sbus/char/aurora.c -- Aurora multiport driver - * - * Copyright (c) 1999 by Oliver Aldulea (oli at bv dot ro) - * - * This code is based on the RISCom/8 multiport serial driver written - * by Dmitry Gorodchanin (pgmdsg@ibi.com), based on the Linux serial - * driver, written by Linus Torvalds, Theodore T'so and others. - * The Aurora multiport programming info was obtained mainly from the - * Cirrus Logic CD180 documentation (available on the web), and by - * doing heavy tests on the board. Many thanks to Eddie C. Dost for the - * help on the sbus interface. - * - * 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. - * - * Revision 1.0 - * - * This is the first public release. - * - * Most of the information you need is in the aurora.h file. Please - * read that file before reading this one. - * - * Several parts of the code do not have comments yet. - * - * n.b. The board can support 115.2 bit rates, but only on a few - * ports. The total badwidth of one chip (ports 0-7 or 8-15) is equal - * to OSC_FREQ div 16. In case of my board, each chip can take 6 - * channels of 115.2 kbaud. This information is not well-tested. - * - * Fixed to use tty_get_baud_rate(). - * Theodore Ts'o , 2001-Oct-12 - */ - -#include - -#include -#include -#ifdef AURORA_INT_DEBUG -#include -#endif -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include "aurora.h" -#include "cd180.h" - -unsigned char irqs[4] = { - 0, 0, 0, 0 -}; - -#ifdef AURORA_INT_DEBUG -int irqhit=0; -#endif - -static struct tty_driver *aurora_driver; -static struct Aurora_board aurora_board[AURORA_NBOARD] = { - {0,}, -}; - -static struct Aurora_port aurora_port[AURORA_TNPORTS] = { - { 0, }, -}; - -/* no longer used. static struct Aurora_board * IRQ_to_board[16] = { NULL, } ;*/ -static unsigned char * tmp_buf = NULL; - -DECLARE_TASK_QUEUE(tq_aurora); - -static inline int aurora_paranoia_check(struct Aurora_port const * port, - char *name, const char *routine) -{ -#ifdef AURORA_PARANOIA_CHECK - static const char *badmagic = - KERN_DEBUG "aurora: Warning: bad aurora port magic number for device %s in %s\n"; - static const char *badinfo = - KERN_DEBUG "aurora: Warning: null aurora port for device %s in %s\n"; - - if (!port) { - printk(badinfo, name, routine); - return 1; - } - if (port->magic != AURORA_MAGIC) { - printk(badmagic, name, routine); - return 1; - } -#endif - return 0; -} - -/* - * - * Service functions for aurora driver. - * - */ - -/* Get board number from pointer */ -static inline int board_No (struct Aurora_board const * bp) -{ - return bp - aurora_board; -} - -/* Get port number from pointer */ -static inline int port_No (struct Aurora_port const * port) -{ - return AURORA_PORT(port - aurora_port); -} - -/* Get pointer to board from pointer to port */ -static inline struct Aurora_board * port_Board(struct Aurora_port const * port) -{ - return &aurora_board[AURORA_BOARD(port - aurora_port)]; -} - -/* Wait for Channel Command Register ready */ -static inline void aurora_wait_CCR(struct aurora_reg128 * r) -{ - unsigned long delay; - -#ifdef AURORA_DEBUG -printk("aurora_wait_CCR\n"); -#endif - /* FIXME: need something more descriptive than 100000 :) */ - for (delay = 100000; delay; delay--) - if (!sbus_readb(&r->r[CD180_CCR])) - return; - printk(KERN_DEBUG "aurora: Timeout waiting for CCR.\n"); -} - -/* - * aurora probe functions. - */ - -/* Must be called with enabled interrupts */ -static inline void aurora_long_delay(unsigned long delay) -{ - unsigned long i; - -#ifdef AURORA_DEBUG - printk("aurora_long_delay: start\n"); -#endif - for (i = jiffies + delay; time_before(jiffies, i); ) ; -#ifdef AURORA_DEBUG - printk("aurora_long_delay: end\n"); -#endif -} - -/* Reset and setup CD180 chip */ -static int aurora_init_CD180(struct Aurora_board * bp, int chip) -{ - unsigned long flags; - int id; - -#ifdef AURORA_DEBUG - printk("aurora_init_CD180: start %d:%d\n", - board_No(bp), chip); -#endif - save_flags(flags); cli(); - sbus_writeb(0, &bp->r[chip]->r[CD180_CAR]); - sbus_writeb(0, &bp->r[chip]->r[CD180_GSVR]); - - /* Wait for CCR ready */ - aurora_wait_CCR(bp->r[chip]); - - /* Reset CD180 chip */ - sbus_writeb(CCR_HARDRESET, &bp->r[chip]->r[CD180_CCR]); - udelay(1); - sti(); - id=1000; - while((--id) && - (sbus_readb(&bp->r[chip]->r[CD180_GSVR])!=0xff))udelay(100); - if(!id) { - printk(KERN_ERR "aurora%d: Chip %d failed init.\n", - board_No(bp), chip); - restore_flags(flags); - return(-1); - } - cli(); - sbus_writeb((board_No(bp)<<5)|((chip+1)<<3), - &bp->r[chip]->r[CD180_GSVR]); /* Set ID for this chip */ - sbus_writeb(0x80|bp->ACK_MINT, - &bp->r[chip]->r[CD180_MSMR]); /* Prio for modem intr */ - sbus_writeb(0x80|bp->ACK_TINT, - &bp->r[chip]->r[CD180_TSMR]); /* Prio for transmitter intr */ - sbus_writeb(0x80|bp->ACK_RINT, - &bp->r[chip]->r[CD180_RSMR]); /* Prio for receiver intr */ - /* Setting up prescaler. We need 4 tick per 1 ms */ - sbus_writeb((bp->oscfreq/(1000000/AURORA_TPS)) >> 8, - &bp->r[chip]->r[CD180_PPRH]); - sbus_writeb((bp->oscfreq/(1000000/AURORA_TPS)) & 0xff, - &bp->r[chip]->r[CD180_PPRL]); - - sbus_writeb(SRCR_AUTOPRI|SRCR_GLOBPRI, - &bp->r[chip]->r[CD180_SRCR]); - - id = sbus_readb(&bp->r[chip]->r[CD180_GFRCR]); - printk(KERN_INFO "aurora%d: Chip %d id %02x: ", - board_No(bp), chip,id); - if(sbus_readb(&bp->r[chip]->r[CD180_SRCR]) & 128) { - switch (id) { - case 0x82:printk("CL-CD1864 rev A\n");break; - case 0x83:printk("CL-CD1865 rev A\n");break; - case 0x84:printk("CL-CD1865 rev B\n");break; - case 0x85:printk("CL-CD1865 rev C\n");break; - default:printk("Unknown.\n"); - }; - } else { - switch (id) { - case 0x81:printk("CL-CD180 rev B\n");break; - case 0x82:printk("CL-CD180 rev C\n");break; - default:printk("Unknown.\n"); - }; - } - restore_flags(flags); -#ifdef AURORA_DEBUG - printk("aurora_init_CD180: end\n"); -#endif - return 0; -} - -static int valid_irq(unsigned char irq) -{ -int i; -for(i=0;iprom_name);*/ - if (!strcmp(sdev->prom_name, "sio16")) { -#ifdef AURORA_DEBUG - printk(KERN_INFO "aurora: sio16 at %p\n",sdev); -#endif - if((sdev->reg_addrs[0].reg_size!=1) && - (sdev->reg_addrs[1].reg_size!=128) && - (sdev->reg_addrs[2].reg_size!=128) && - (sdev->reg_addrs[3].reg_size!=4)) { - printk(KERN_ERR "aurora%d: registers' sizes " - "do not match.\n", bn); - break; - } - bp = &aurora_board[bn]; - bp->r0 = (struct aurora_reg1 *) - sbus_ioremap(&sdev->resource[0], 0, - sdev->reg_addrs[0].reg_size, - "sio16"); - if (bp->r0 == NULL) { - printk(KERN_ERR "aurora%d: can't map " - "reg_addrs[0]\n", bn); - break; - } -#ifdef AURORA_DEBUG - printk("Map reg 0: %p\n", bp->r0); -#endif - bp->r[0] = (struct aurora_reg128 *) - sbus_ioremap(&sdev->resource[1], 0, - sdev->reg_addrs[1].reg_size, - "sio16"); - if (bp->r[0] == NULL) { - printk(KERN_ERR "aurora%d: can't map " - "reg_addrs[1]\n", bn); - break; - } -#ifdef AURORA_DEBUG - printk("Map reg 1: %p\n", bp->r[0]); -#endif - bp->r[1] = (struct aurora_reg128 *) - sbus_ioremap(&sdev->resource[2], 0, - sdev->reg_addrs[2].reg_size, - "sio16"); - if (bp->r[1] == NULL) { - printk(KERN_ERR "aurora%d: can't map " - "reg_addrs[2]\n", bn); - break; - } -#ifdef AURORA_DEBUG - printk("Map reg 2: %p\n", bp->r[1]); -#endif - bp->r3 = (struct aurora_reg4 *) - sbus_ioremap(&sdev->resource[3], 0, - sdev->reg_addrs[3].reg_size, - "sio16"); - if (bp->r3 == NULL) { - printk(KERN_ERR "aurora%d: can't map " - "reg_addrs[3]\n", bn); - break; - } -#ifdef AURORA_DEBUG - printk("Map reg 3: %p\n", bp->r3); -#endif - /* Variables setup */ - bp->flags = 0; -#ifdef AURORA_DEBUG - grrr=prom_getint(sdev->prom_node,"intr"); - printk("intr pri %d\n", grrr); -#endif - if ((bp->irq=irqs[bn]) && valid_irq(bp->irq) && - !request_irq(bp->irq|0x30, aurora_interrupt, IRQF_SHARED, "sio16", bp)) { - free_irq(bp->irq|0x30, bp); - } else - if ((bp->irq=prom_getint(sdev->prom_node, "bintr")) && valid_irq(bp->irq) && - !request_irq(bp->irq|0x30, aurora_interrupt, IRQF_SHARED, "sio16", bp)) { - free_irq(bp->irq|0x30, bp); - } else - if ((bp->irq=prom_getint(sdev->prom_node, "intr")) && valid_irq(bp->irq) && - !request_irq(bp->irq|0x30, aurora_interrupt, IRQF_SHARED, "sio16", bp)) { - free_irq(bp->irq|0x30, bp); - } else - for(grrr=0;grrrirq=type_1_irq[grrr])&&!request_irq(bp->irq|0x30, aurora_interrupt, IRQF_SHARED, "sio16", bp)) { - free_irq(bp->irq|0x30, bp); - break; - } else { - printk(KERN_ERR "aurora%d: Could not get an irq for this board !!!\n",bn); - bp->flags=0xff; - } - } - if(bp->flags==0xff)break; - printk(KERN_INFO "aurora%d: irq %d\n",bn,bp->irq&0x0f); - buf[0]=0; - grrr=prom_getproperty(sdev->prom_node,"dtr_rts",buf,sizeof(buf)); - if(!strcmp(buf,"swapped")){ - printk(KERN_INFO "aurora%d: Swapped DTR and RTS\n",bn); - bp->DTR=MSVR_RTS; - bp->RTS=MSVR_DTR; - bp->MSVDTR=CD180_MSVRTS; - bp->MSVRTS=CD180_MSVDTR; - bp->flags|=AURORA_BOARD_DTR_FLOW_OK; - }else{ - #ifdef AURORA_FORCE_DTR_FLOW - printk(KERN_INFO "aurora%d: Forcing swapped DTR-RTS\n",bn); - bp->DTR=MSVR_RTS; - bp->RTS=MSVR_DTR; - bp->MSVDTR=CD180_MSVRTS; - bp->MSVRTS=CD180_MSVDTR; - bp->flags|=AURORA_BOARD_DTR_FLOW_OK; - #else - printk(KERN_INFO "aurora%d: Normal DTR and RTS\n",bn); - bp->DTR=MSVR_DTR; - bp->RTS=MSVR_RTS; - bp->MSVDTR=CD180_MSVDTR; - bp->MSVRTS=CD180_MSVRTS; - #endif - } - bp->oscfreq=prom_getint(sdev->prom_node,"clk")*100; - printk(KERN_INFO "aurora%d: Oscillator: %d Hz\n",bn,bp->oscfreq); - grrr=prom_getproperty(sdev->prom_node,"chip",buf,sizeof(buf)); - printk(KERN_INFO "aurora%d: Chips: %s\n",bn,buf); - grrr=prom_getproperty(sdev->prom_node,"manu",buf,sizeof(buf)); - printk(KERN_INFO "aurora%d: Manufacturer: %s\n",bn,buf); - grrr=prom_getproperty(sdev->prom_node,"model",buf,sizeof(buf)); - printk(KERN_INFO "aurora%d: Model: %s\n",bn,buf); - grrr=prom_getproperty(sdev->prom_node,"rev",buf,sizeof(buf)); - printk(KERN_INFO "aurora%d: Revision: %s\n",bn,buf); - grrr=prom_getproperty(sdev->prom_node,"mode",buf,sizeof(buf)); - printk(KERN_INFO "aurora%d: Mode: %s\n",bn,buf); - #ifdef MODULE - bp->count=0; - #endif - bp->flags = AURORA_BOARD_PRESENT; - /* hardware ack */ - bp->ACK_MINT=1; - bp->ACK_TINT=2; - bp->ACK_RINT=3; - bn++; - } - } - } - return bn; -} - -static void aurora_release_io_range(struct Aurora_board *bp) -{ - sbus_iounmap((unsigned long)bp->r0, 1); - sbus_iounmap((unsigned long)bp->r[0], 128); - sbus_iounmap((unsigned long)bp->r[1], 128); - sbus_iounmap((unsigned long)bp->r3, 4); -} - -static inline void aurora_mark_event(struct Aurora_port * port, int event) -{ -#ifdef AURORA_DEBUG - printk("aurora_mark_event: start\n"); -#endif - set_bit(event, &port->event); - queue_task(&port->tqueue, &tq_aurora); - mark_bh(AURORA_BH); -#ifdef AURORA_DEBUG - printk("aurora_mark_event: end\n"); -#endif -} - -static __inline__ struct Aurora_port * aurora_get_port(struct Aurora_board const * bp, - int chip, - unsigned char const *what) -{ - unsigned char channel; - struct Aurora_port * port; - - channel = ((chip << 3) | - ((sbus_readb(&bp->r[chip]->r[CD180_GSCR]) & GSCR_CHAN) >> GSCR_CHAN_OFF)); - port = &aurora_port[board_No(bp) * AURORA_NPORT * AURORA_NCD180 + channel]; - if (port->flags & ASYNC_INITIALIZED) - return port; - - printk(KERN_DEBUG "aurora%d: %s interrupt from invalid port %d\n", - board_No(bp), what, channel); - return NULL; -} - -static void aurora_receive_exc(struct Aurora_board const * bp, int chip) -{ - struct Aurora_port *port; - struct tty_struct *tty; - unsigned char status; - unsigned char ch; - - if (!(port = aurora_get_port(bp, chip, "Receive_x"))) - return; - - tty = port->tty; - if (tty->flip.count >= TTY_FLIPBUF_SIZE) { -#ifdef AURORA_INTNORM - printk("aurora%d: port %d: Working around flip buffer overflow.\n", - board_No(bp), port_No(port)); -#endif - return; - } - -#ifdef AURORA_REPORT_OVERRUN - status = sbus_readb(&bp->r[chip]->r[CD180_RCSR]); - if (status & RCSR_OE) { - port->overrun++; -#if 1 - printk("aurora%d: port %d: Overrun. Total %ld overruns.\n", - board_No(bp), port_No(port), port->overrun); -#endif - } - status &= port->mark_mask; -#else - status = sbus_readb(&bp->r[chip]->r[CD180_RCSR]) & port->mark_mask; -#endif - ch = sbus_readb(&bp->r[chip]->r[CD180_RDR]); - if (!status) - return; - - if (status & RCSR_TOUT) { -/* printk("aurora%d: port %d: Receiver timeout. Hardware problems ?\n", - board_No(bp), port_No(port));*/ - return; - - } else if (status & RCSR_BREAK) { - printk(KERN_DEBUG "aurora%d: port %d: Handling break...\n", - board_No(bp), port_No(port)); - *tty->flip.flag_buf_ptr++ = TTY_BREAK; - if (port->flags & ASYNC_SAK) - do_SAK(tty); - - } else if (status & RCSR_PE) - *tty->flip.flag_buf_ptr++ = TTY_PARITY; - - else if (status & RCSR_FE) - *tty->flip.flag_buf_ptr++ = TTY_FRAME; - - else if (status & RCSR_OE) - *tty->flip.flag_buf_ptr++ = TTY_OVERRUN; - - else - *tty->flip.flag_buf_ptr++ = 0; - - *tty->flip.char_buf_ptr++ = ch; - tty->flip.count++; - queue_task(&tty->flip.tqueue, &tq_timer); -} - -static void aurora_receive(struct Aurora_board const * bp, int chip) -{ - struct Aurora_port *port; - struct tty_struct *tty; - unsigned char count,cnt; - - if (!(port = aurora_get_port(bp, chip, "Receive"))) - return; - - tty = port->tty; - - count = sbus_readb(&bp->r[chip]->r[CD180_RDCR]); - -#ifdef AURORA_REPORT_FIFO - port->hits[count > 8 ? 9 : count]++; -#endif - - while (count--) { - if (tty->flip.count >= TTY_FLIPBUF_SIZE) { -#ifdef AURORA_INTNORM - printk("aurora%d: port %d: Working around flip buffer overflow.\n", - board_No(bp), port_No(port)); -#endif - break; - } - cnt = sbus_readb(&bp->r[chip]->r[CD180_RDR]); - *tty->flip.char_buf_ptr++ = cnt; - *tty->flip.flag_buf_ptr++ = 0; - tty->flip.count++; - } - queue_task(&tty->flip.tqueue, &tq_timer); -} - -static void aurora_transmit(struct Aurora_board const * bp, int chip) -{ - struct Aurora_port *port; - struct tty_struct *tty; - unsigned char count; - - if (!(port = aurora_get_port(bp, chip, "Transmit"))) - return; - - tty = port->tty; - - if (port->SRER & SRER_TXEMPTY) { - /* FIFO drained */ - sbus_writeb(port_No(port) & 7, - &bp->r[chip]->r[CD180_CAR]); - udelay(1); - port->SRER &= ~SRER_TXEMPTY; - sbus_writeb(port->SRER, &bp->r[chip]->r[CD180_SRER]); - return; - } - - if ((port->xmit_cnt <= 0 && !port->break_length) - || tty->stopped || tty->hw_stopped) { - sbus_writeb(port_No(port) & 7, - &bp->r[chip]->r[CD180_CAR]); - udelay(1); - port->SRER &= ~SRER_TXRDY; - sbus_writeb(port->SRER, - &bp->r[chip]->r[CD180_SRER]); - return; - } - - if (port->break_length) { - if (port->break_length > 0) { - if (port->COR2 & COR2_ETC) { - sbus_writeb(CD180_C_ESC, - &bp->r[chip]->r[CD180_TDR]); - sbus_writeb(CD180_C_SBRK, - &bp->r[chip]->r[CD180_TDR]); - port->COR2 &= ~COR2_ETC; - } - count = min(port->break_length, 0xff); - sbus_writeb(CD180_C_ESC, - &bp->r[chip]->r[CD180_TDR]); - sbus_writeb(CD180_C_DELAY, - &bp->r[chip]->r[CD180_TDR]); - sbus_writeb(count, - &bp->r[chip]->r[CD180_TDR]); - if (!(port->break_length -= count)) - port->break_length--; - } else { - sbus_writeb(CD180_C_ESC, - &bp->r[chip]->r[CD180_TDR]); - sbus_writeb(CD180_C_EBRK, - &bp->r[chip]->r[CD180_TDR]); - sbus_writeb(port->COR2, - &bp->r[chip]->r[CD180_COR2]); - aurora_wait_CCR(bp->r[chip]); - sbus_writeb(CCR_CORCHG2, - &bp->r[chip]->r[CD180_CCR]); - port->break_length = 0; - } - return; - } - - count = CD180_NFIFO; - do { - u8 byte = port->xmit_buf[port->xmit_tail++]; - - sbus_writeb(byte, &bp->r[chip]->r[CD180_TDR]); - port->xmit_tail = port->xmit_tail & (SERIAL_XMIT_SIZE-1); - if (--port->xmit_cnt <= 0) - break; - } while (--count > 0); - - if (port->xmit_cnt <= 0) { - sbus_writeb(port_No(port) & 7, - &bp->r[chip]->r[CD180_CAR]); - udelay(1); - port->SRER &= ~SRER_TXRDY; - sbus_writeb(port->SRER, - &bp->r[chip]->r[CD180_SRER]); - } - if (port->xmit_cnt <= port->wakeup_chars) - aurora_mark_event(port, RS_EVENT_WRITE_WAKEUP); -} - -static void aurora_check_modem(struct Aurora_board const * bp, int chip) -{ - struct Aurora_port *port; - struct tty_struct *tty; - unsigned char mcr; - - if (!(port = aurora_get_port(bp, chip, "Modem"))) - return; - - tty = port->tty; - - mcr = sbus_readb(&bp->r[chip]->r[CD180_MCR]); - if (mcr & MCR_CDCHG) { - if (sbus_readb(&bp->r[chip]->r[CD180_MSVR]) & MSVR_CD) - wake_up_interruptible(&port->open_wait); - else - schedule_task(&port->tqueue_hangup); - } - -/* We don't have such things yet. My aurora board has DTR and RTS swapped, but that doesn't count in this driver. Let's hope - * Aurora didn't made any boards with CTS or DSR broken... - */ -/* #ifdef AURORA_BRAIN_DAMAGED_CTS - if (mcr & MCR_CTSCHG) { - if (aurora_in(bp, CD180_MSVR) & MSVR_CTS) { - tty->hw_stopped = 0; - port->SRER |= SRER_TXRDY; - if (port->xmit_cnt <= port->wakeup_chars) - aurora_mark_event(port, RS_EVENT_WRITE_WAKEUP); - } else { - tty->hw_stopped = 1; - port->SRER &= ~SRER_TXRDY; - } - sbus_writeb(port->SRER, &bp->r[chip]->r[CD180_SRER]); - } - if (mcr & MCR_DSRCHG) { - if (aurora_in(bp, CD180_MSVR) & MSVR_DSR) { - tty->hw_stopped = 0; - port->SRER |= SRER_TXRDY; - if (port->xmit_cnt <= port->wakeup_chars) - aurora_mark_event(port, RS_EVENT_WRITE_WAKEUP); - } else { - tty->hw_stopped = 1; - port->SRER &= ~SRER_TXRDY; - } - sbus_writeb(port->SRER, &bp->r[chip]->r[CD180_SRER]); - } -#endif AURORA_BRAIN_DAMAGED_CTS */ - - /* Clear change bits */ - sbus_writeb(0, &bp->r[chip]->r[CD180_MCR]); -} - -/* The main interrupt processing routine */ -static irqreturn_t aurora_interrupt(int irq, void * dev_id) -{ - unsigned char status; - unsigned char ack,chip/*,chip_id*/; - struct Aurora_board * bp = (struct Aurora_board *) dev_id; - unsigned long loop = 0; - -#ifdef AURORA_INT_DEBUG - printk("IRQ%d %d\n",irq,++irqhit); -#ifdef AURORA_FLOODPRO - if (irqhit>=AURORA_FLOODPRO) - sbus_writeb(8, &bp->r0->r); -#endif -#endif - -/* old bp = IRQ_to_board[irq&0x0f];*/ - - if (!bp || !(bp->flags & AURORA_BOARD_ACTIVE)) - return IRQ_NONE; - -/* The while() below takes care of this. - status = sbus_readb(&bp->r[0]->r[CD180_SRSR]); -#ifdef AURORA_INT_DEBUG - printk("mumu: %02x\n", status); -#endif - if (!(status&SRSR_ANYINT)) - return IRQ_NONE; * Nobody has anything to say, so exit * -*/ - while ((loop++ < 48) && - (status = sbus_readb(&bp->r[0]->r[CD180_SRSR]) & SRSR_ANYINT)){ -#ifdef AURORA_INT_DEBUG - printk("SRSR: %02x\n", status); -#endif - if (status & SRSR_REXT) { - ack = sbus_readb(&bp->r3->r[bp->ACK_RINT]); -#ifdef AURORA_INT_DEBUG - printk("R-ACK %02x\n", ack); -#endif - if ((ack >> 5) == board_No(bp)) { - if ((chip=((ack>>3)&3)-1) < AURORA_NCD180) { - if ((ack&GSVR_ITMASK)==GSVR_IT_RGD) { - aurora_receive(bp,chip); - sbus_writeb(0, - &bp->r[chip]->r[CD180_EOSRR]); - } else if ((ack & GSVR_ITMASK) == GSVR_IT_REXC) { - aurora_receive_exc(bp,chip); - sbus_writeb(0, - &bp->r[chip]->r[CD180_EOSRR]); - } - } - } - } else if (status & SRSR_TEXT) { - ack = sbus_readb(&bp->r3->r[bp->ACK_TINT]); -#ifdef AURORA_INT_DEBUG - printk("T-ACK %02x\n", ack); -#endif - if ((ack >> 5) == board_No(bp)) { - if ((chip=((ack>>3)&3)-1) < AURORA_NCD180) { - if ((ack&GSVR_ITMASK)==GSVR_IT_TX) { - aurora_transmit(bp,chip); - sbus_writeb(0, - &bp->r[chip]->r[CD180_EOSRR]); - } - } - } - } else if (status & SRSR_MEXT) { - ack = sbus_readb(&bp->r3->r[bp->ACK_MINT]); -#ifdef AURORA_INT_DEBUG - printk("M-ACK %02x\n", ack); -#endif - if ((ack >> 5) == board_No(bp)) { - if ((chip = ((ack>>3)&3)-1) < AURORA_NCD180) { - if ((ack&GSVR_ITMASK)==GSVR_IT_MDM) { - aurora_check_modem(bp,chip); - sbus_writeb(0, - &bp->r[chip]->r[CD180_EOSRR]); - } - } - } - } - } -/* I guess this faster code can be used with CD1865, using AUROPRI and GLOBPRI. */ -#if 0 - while ((loop++ < 48)&&(status=bp->r[0]->r[CD180_SRSR]&SRSR_ANYINT)){ -#ifdef AURORA_INT_DEBUG - printk("SRSR: %02x\n",status); -#endif - ack = sbus_readb(&bp->r3->r[0]); -#ifdef AURORA_INT_DEBUG - printk("ACK: %02x\n",ack); -#endif - if ((ack>>5)==board_No(bp)) { - if ((chip=((ack>>3)&3)-1) < AURORA_NCD180) { - ack&=GSVR_ITMASK; - if (ack==GSVR_IT_RGD) { - aurora_receive(bp,chip); - sbus_writeb(0, - &bp->r[chip]->r[CD180_EOSRR]); - } else if (ack==GSVR_IT_REXC) { - aurora_receive_exc(bp,chip); - sbus_writeb(0, - &bp->r[chip]->r[CD180_EOSRR]); - } else if (ack==GSVR_IT_TX) { - aurora_transmit(bp,chip); - sbus_writeb(0, - &bp->r[chip]->r[CD180_EOSRR]); - } else if (ack==GSVR_IT_MDM) { - aurora_check_modem(bp,chip); - sbus_writeb(0, - &bp->r[chip]->r[CD180_EOSRR]); - } - } - } - } -#endif - -/* This is the old handling routine, used in riscom8 for only one CD180. I keep it here for reference. */ -#if 0 - for(chip=0;chipr[chip]->r[CD180_SRSR])) & - (SRSR_TEXT | SRSR_MEXT | SRSR_REXT))) { - - if (status & SRSR_REXT) { - ack = sbus_readb(&bp->r3->r[bp->ACK_RINT]); - if (ack == (chip_id | GSVR_IT_RGD)) { -#ifdef AURORA_INTMSG - printk("RX ACK\n"); -#endif - aurora_receive(bp,chip); - } else if (ack == (chip_id | GSVR_IT_REXC)) { -#ifdef AURORA_INTMSG - printk("RXC ACK\n"); -#endif - aurora_receive_exc(bp,chip); - } else { -#ifdef AURORA_INTNORM - printk("aurora%d-%d: Bad receive ack 0x%02x.\n", - board_No(bp), chip, ack); -#endif - } - } else if (status & SRSR_TEXT) { - ack = sbus_readb(&bp->r3->r[bp->ACK_TINT]); - if (ack == (chip_id | GSVR_IT_TX)){ -#ifdef AURORA_INTMSG - printk("TX ACK\n"); -#endif - aurora_transmit(bp,chip); - } else { -#ifdef AURORA_INTNORM - printk("aurora%d-%d: Bad transmit ack 0x%02x.\n", - board_No(bp), chip, ack); -#endif - } - } else if (status & SRSR_MEXT) { - ack = sbus_readb(&bp->r3->r[bp->ACK_MINT]); - if (ack == (chip_id | GSVR_IT_MDM)){ -#ifdef AURORA_INTMSG - printk("MDM ACK\n"); -#endif - aurora_check_modem(bp,chip); - } else { -#ifdef AURORA_INTNORM - printk("aurora%d-%d: Bad modem ack 0x%02x.\n", - board_No(bp), chip, ack); -#endif - } - } - sbus_writeb(0, &bp->r[chip]->r[CD180_EOSRR]); - } - } -#endif - - return IRQ_HANDLED; -} - -#ifdef AURORA_INT_DEBUG -static void aurora_timer (unsigned long ignored); - -static DEFINE_TIMER(aurora_poll_timer, aurora_timer, 0, 0); - -static void -aurora_timer (unsigned long ignored) -{ - unsigned long flags; - int i; - - save_flags(flags); cli(); - - printk("SRSR: %02x,%02x - ", - sbus_readb(&aurora_board[0].r[0]->r[CD180_SRSR]), - sbus_readb(&aurora_board[0].r[1]->r[CD180_SRSR])); - for (i = 0; i < 4; i++) { - udelay(1); - printk("%02x ", - sbus_readb(&aurora_board[0].r3->r[i])); - } - printk("\n"); - - aurora_poll_timer.expires = jiffies + 300; - add_timer (&aurora_poll_timer); - - restore_flags(flags); -} -#endif - -/* - * Routines for open & close processing. - */ - -/* Called with disabled interrupts */ -static int aurora_setup_board(struct Aurora_board * bp) -{ - int error; - -#ifdef AURORA_ALLIRQ - int i; - for (i = 0; i < AURORA_ALLIRQ; i++) { - error = request_irq(allirq[i]|0x30, aurora_interrupt, IRQF_SHARED, - "sio16", bp); - if (error) - printk(KERN_ERR "IRQ%d request error %d\n", - allirq[i], error); - } -#else - error = request_irq(bp->irq|0x30, aurora_interrupt, IRQF_SHARED, - "sio16", bp); - if (error) { - printk(KERN_ERR "IRQ request error %d\n", error); - return error; - } -#endif - /* Board reset */ - sbus_writeb(0, &bp->r0->r); - udelay(1); - if (bp->flags & AURORA_BOARD_TYPE_2) { - /* unknown yet */ - } else { - sbus_writeb((AURORA_CFG_ENABLE_IO | AURORA_CFG_ENABLE_IRQ | - (((bp->irq)&0x0f)>>2)), - &bp->r0->r); - } - udelay(10000); - - if (aurora_init_CD180(bp,0))error=1;error=0; - if (aurora_init_CD180(bp,1))error++; - if (error == AURORA_NCD180) { - printk(KERN_ERR "Both chips failed initialisation.\n"); - return -EIO; - } - -#ifdef AURORA_INT_DEBUG - aurora_poll_timer.expires= jiffies + 1; - add_timer(&aurora_poll_timer); -#endif -#ifdef AURORA_DEBUG - printk("aurora_setup_board: end\n"); -#endif - return 0; -} - -/* Called with disabled interrupts */ -static void aurora_shutdown_board(struct Aurora_board *bp) -{ - int i; - -#ifdef AURORA_DEBUG - printk("aurora_shutdown_board: start\n"); -#endif - -#ifdef AURORA_INT_DEBUG - del_timer(&aurora_poll_timer); -#endif - -#ifdef AURORA_ALLIRQ - for(i=0;iirq|0x30, bp); -/* IRQ_to_board[bp->irq&0xf] = NULL;*/ -#endif - /* Drop all DTR's */ - for(i=0;i<16;i++){ - sbus_writeb(i & 7, &bp->r[i>>3]->r[CD180_CAR]); - udelay(1); - sbus_writeb(0, &bp->r[i>>3]->r[CD180_MSVR]); - udelay(1); - } - /* Board shutdown */ - sbus_writeb(0, &bp->r0->r); - -#ifdef AURORA_DEBUG - printk("aurora_shutdown_board: end\n"); -#endif -} - -/* Setting up port characteristics. - * Must be called with disabled interrupts - */ -static void aurora_change_speed(struct Aurora_board *bp, struct Aurora_port *port) -{ - struct tty_struct *tty; - unsigned long baud; - long tmp; - unsigned char cor1 = 0, cor3 = 0; - unsigned char mcor1 = 0, mcor2 = 0,chip; - -#ifdef AURORA_DEBUG - printk("aurora_change_speed: start\n"); -#endif - if (!(tty = port->tty) || !tty->termios) - return; - - chip = AURORA_CD180(port_No(port)); - - port->SRER = 0; - port->COR2 = 0; - port->MSVR = MSVR_RTS|MSVR_DTR; - - baud = tty_get_baud_rate(tty); - - /* Select port on the board */ - sbus_writeb(port_No(port) & 7, - &bp->r[chip]->r[CD180_CAR]); - udelay(1); - - if (!baud) { - /* Drop DTR & exit */ - port->MSVR &= ~(bp->DTR|bp->RTS); - sbus_writeb(port->MSVR, - &bp->r[chip]->r[CD180_MSVR]); - return; - } else { - /* Set DTR on */ - port->MSVR |= bp->DTR; - sbus_writeb(port->MSVR, - &bp->r[chip]->r[CD180_MSVR]); - } - - /* Now we must calculate some speed dependent things. */ - - /* Set baud rate for port. */ - tmp = (((bp->oscfreq + baud/2) / baud + - CD180_TPC/2) / CD180_TPC); - -/* tmp = (bp->oscfreq/7)/baud; - if((tmp%10)>4)tmp=tmp/10+1;else tmp=tmp/10;*/ -/* printk("Prescaler period: %d\n",tmp);*/ - - sbus_writeb((tmp >> 8) & 0xff, - &bp->r[chip]->r[CD180_RBPRH]); - sbus_writeb((tmp >> 8) & 0xff, - &bp->r[chip]->r[CD180_TBPRH]); - sbus_writeb(tmp & 0xff, &bp->r[chip]->r[CD180_RBPRL]); - sbus_writeb(tmp & 0xff, &bp->r[chip]->r[CD180_TBPRL]); - - baud = (baud + 5) / 10; /* Estimated CPS */ - - /* Two timer ticks seems enough to wakeup something like SLIP driver */ - tmp = ((baud + HZ/2) / HZ) * 2 - CD180_NFIFO; - port->wakeup_chars = (tmp < 0) ? 0 : ((tmp >= SERIAL_XMIT_SIZE) ? - SERIAL_XMIT_SIZE - 1 : tmp); - - /* Receiver timeout will be transmission time for 1.5 chars */ - tmp = (AURORA_TPS + AURORA_TPS/2 + baud/2) / baud; - tmp = (tmp > 0xff) ? 0xff : tmp; - sbus_writeb(tmp, &bp->r[chip]->r[CD180_RTPR]); - - switch (C_CSIZE(tty)) { - case CS5: - cor1 |= COR1_5BITS; - break; - case CS6: - cor1 |= COR1_6BITS; - break; - case CS7: - cor1 |= COR1_7BITS; - break; - case CS8: - cor1 |= COR1_8BITS; - break; - } - - if (C_CSTOPB(tty)) - cor1 |= COR1_2SB; - - cor1 |= COR1_IGNORE; - if (C_PARENB(tty)) { - cor1 |= COR1_NORMPAR; - if (C_PARODD(tty)) - cor1 |= COR1_ODDP; - if (I_INPCK(tty)) - cor1 &= ~COR1_IGNORE; - } - /* Set marking of some errors */ - port->mark_mask = RCSR_OE | RCSR_TOUT; - if (I_INPCK(tty)) - port->mark_mask |= RCSR_FE | RCSR_PE; - if (I_BRKINT(tty) || I_PARMRK(tty)) - port->mark_mask |= RCSR_BREAK; - if (I_IGNPAR(tty)) - port->mark_mask &= ~(RCSR_FE | RCSR_PE); - if (I_IGNBRK(tty)) { - port->mark_mask &= ~RCSR_BREAK; - if (I_IGNPAR(tty)) - /* Real raw mode. Ignore all */ - port->mark_mask &= ~RCSR_OE; - } - /* Enable Hardware Flow Control */ - if (C_CRTSCTS(tty)) { -/*#ifdef AURORA_BRAIN_DAMAGED_CTS - port->SRER |= SRER_DSR | SRER_CTS; - mcor1 |= MCOR1_DSRZD | MCOR1_CTSZD; - mcor2 |= MCOR2_DSROD | MCOR2_CTSOD; - tty->hw_stopped = !(aurora_in(bp, CD180_MSVR) & (MSVR_CTS|MSVR_DSR)); -#else*/ - port->COR2 |= COR2_CTSAE; -/*#endif*/ - if (bp->flags&AURORA_BOARD_DTR_FLOW_OK) { - mcor1 |= AURORA_RXTH; - } - } - /* Enable Software Flow Control. FIXME: I'm not sure about this */ - /* Some people reported that it works, but I still doubt */ - if (I_IXON(tty)) { - port->COR2 |= COR2_TXIBE; - cor3 |= (COR3_FCT | COR3_SCDE); - if (I_IXANY(tty)) - port->COR2 |= COR2_IXM; - sbus_writeb(START_CHAR(tty), - &bp->r[chip]->r[CD180_SCHR1]); - sbus_writeb(STOP_CHAR(tty), - &bp->r[chip]->r[CD180_SCHR2]); - sbus_writeb(START_CHAR(tty), - &bp->r[chip]->r[CD180_SCHR3]); - sbus_writeb(STOP_CHAR(tty), - &bp->r[chip]->r[CD180_SCHR4]); - } - if (!C_CLOCAL(tty)) { - /* Enable CD check */ - port->SRER |= SRER_CD; - mcor1 |= MCOR1_CDZD; - mcor2 |= MCOR2_CDOD; - } - - if (C_CREAD(tty)) - /* Enable receiver */ - port->SRER |= SRER_RXD; - - /* Set input FIFO size (1-8 bytes) */ - cor3 |= AURORA_RXFIFO; - /* Setting up CD180 channel registers */ - sbus_writeb(cor1, &bp->r[chip]->r[CD180_COR1]); - sbus_writeb(port->COR2, &bp->r[chip]->r[CD180_COR2]); - sbus_writeb(cor3, &bp->r[chip]->r[CD180_COR3]); - /* Make CD180 know about registers change */ - aurora_wait_CCR(bp->r[chip]); - sbus_writeb(CCR_CORCHG1 | CCR_CORCHG2 | CCR_CORCHG3, - &bp->r[chip]->r[CD180_CCR]); - /* Setting up modem option registers */ - sbus_writeb(mcor1, &bp->r[chip]->r[CD180_MCOR1]); - sbus_writeb(mcor2, &bp->r[chip]->r[CD180_MCOR2]); - /* Enable CD180 transmitter & receiver */ - aurora_wait_CCR(bp->r[chip]); - sbus_writeb(CCR_TXEN | CCR_RXEN, &bp->r[chip]->r[CD180_CCR]); - /* Enable interrupts */ - sbus_writeb(port->SRER, &bp->r[chip]->r[CD180_SRER]); - /* And finally set RTS on */ - sbus_writeb(port->MSVR, &bp->r[chip]->r[CD180_MSVR]); -#ifdef AURORA_DEBUG - printk("aurora_change_speed: end\n"); -#endif -} - -/* Must be called with interrupts enabled */ -static int aurora_setup_port(struct Aurora_board *bp, struct Aurora_port *port) -{ - unsigned long flags; - -#ifdef AURORA_DEBUG - printk("aurora_setup_port: start %d\n",port_No(port)); -#endif - if (port->flags & ASYNC_INITIALIZED) - return 0; - - if (!port->xmit_buf) { - /* We may sleep in get_zeroed_page() */ - unsigned long tmp; - - if (!(tmp = get_zeroed_page(GFP_KERNEL))) - return -ENOMEM; - - if (port->xmit_buf) { - free_page(tmp); - return -ERESTARTSYS; - } - port->xmit_buf = (unsigned char *) tmp; - } - - save_flags(flags); cli(); - - if (port->tty) - clear_bit(TTY_IO_ERROR, &port->tty->flags); - -#ifdef MODULE - if ((port->count == 1) && ((++bp->count) == 1)) - bp->flags |= AURORA_BOARD_ACTIVE; -#endif - - port->xmit_cnt = port->xmit_head = port->xmit_tail = 0; - aurora_change_speed(bp, port); - port->flags |= ASYNC_INITIALIZED; - - restore_flags(flags); -#ifdef AURORA_DEBUG - printk("aurora_setup_port: end\n"); -#endif - return 0; -} - -/* Must be called with interrupts disabled */ -static void aurora_shutdown_port(struct Aurora_board *bp, struct Aurora_port *port) -{ - struct tty_struct *tty; - unsigned char chip; - -#ifdef AURORA_DEBUG - printk("aurora_shutdown_port: start\n"); -#endif - if (!(port->flags & ASYNC_INITIALIZED)) - return; - - chip = AURORA_CD180(port_No(port)); - -#ifdef AURORA_REPORT_OVERRUN - printk("aurora%d: port %d: Total %ld overruns were detected.\n", - board_No(bp), port_No(port), port->overrun); -#endif -#ifdef AURORA_REPORT_FIFO - { - int i; - - printk("aurora%d: port %d: FIFO hits [ ", - board_No(bp), port_No(port)); - for (i = 0; i < 10; i++) { - printk("%ld ", port->hits[i]); - } - printk("].\n"); - } -#endif - if (port->xmit_buf) { - free_page((unsigned long) port->xmit_buf); - port->xmit_buf = NULL; - } - - if (!(tty = port->tty) || C_HUPCL(tty)) { - /* Drop DTR */ - port->MSVR &= ~(bp->DTR|bp->RTS); - sbus_writeb(port->MSVR, - &bp->r[chip]->r[CD180_MSVR]); - } - - /* Select port */ - sbus_writeb(port_No(port) & 7, - &bp->r[chip]->r[CD180_CAR]); - udelay(1); - - /* Reset port */ - aurora_wait_CCR(bp->r[chip]); - sbus_writeb(CCR_SOFTRESET, &bp->r[chip]->r[CD180_CCR]); - - /* Disable all interrupts from this port */ - port->SRER = 0; - sbus_writeb(port->SRER, &bp->r[chip]->r[CD180_SRER]); - - if (tty) - set_bit(TTY_IO_ERROR, &tty->flags); - port->flags &= ~ASYNC_INITIALIZED; - -#ifdef MODULE - if (--bp->count < 0) { - printk(KERN_DEBUG "aurora%d: aurora_shutdown_port: " - "bad board count: %d\n", - board_No(bp), bp->count); - bp->count = 0; - } - - if (!bp->count) - bp->flags &= ~AURORA_BOARD_ACTIVE; -#endif - -#ifdef AURORA_DEBUG - printk("aurora_shutdown_port: end\n"); -#endif -} - - -static int block_til_ready(struct tty_struct *tty, struct file * filp, - struct Aurora_port *port) -{ - DECLARE_WAITQUEUE(wait, current); - struct Aurora_board *bp = port_Board(port); - int retval; - int do_clocal = 0; - int CD; - unsigned char chip; - -#ifdef AURORA_DEBUG - printk("block_til_ready: start\n"); -#endif - chip = AURORA_CD180(port_No(port)); - - /* If the device is in the middle of being closed, then block - * until it's done, and then try again. - */ - if (tty_hung_up_p(filp) || port->flags & ASYNC_CLOSING) { - interruptible_sleep_on(&port->close_wait); - if (port->flags & ASYNC_HUP_NOTIFY) - return -EAGAIN; - else - return -ERESTARTSYS; - } - - /* If non-blocking mode is set, or the port is not enabled, - * then make the check up front and then exit. - */ - if ((filp->f_flags & O_NONBLOCK) || - (tty->flags & (1 << TTY_IO_ERROR))) { - port->flags |= ASYNC_NORMAL_ACTIVE; - return 0; - } - - if (C_CLOCAL(tty)) - do_clocal = 1; - - /* Block waiting for the carrier detect and the line to become - * free (i.e., not in use by the callout). While we are in - * this loop, info->count is dropped by one, so that - * rs_close() knows when to free things. We restore it upon - * exit, either normal or abnormal. - */ - retval = 0; - add_wait_queue(&port->open_wait, &wait); - cli(); - if (!tty_hung_up_p(filp)) - port->count--; - sti(); - port->blocked_open++; - while (1) { - cli(); - sbus_writeb(port_No(port) & 7, - &bp->r[chip]->r[CD180_CAR]); - udelay(1); - CD = sbus_readb(&bp->r[chip]->r[CD180_MSVR]) & MSVR_CD; - port->MSVR=bp->RTS; - - /* auto drops DTR */ - sbus_writeb(port->MSVR, &bp->r[chip]->r[CD180_MSVR]); - sti(); - set_current_state(TASK_INTERRUPTIBLE); - if (tty_hung_up_p(filp) || - !(port->flags & ASYNC_INITIALIZED)) { - if (port->flags & ASYNC_HUP_NOTIFY) - retval = -EAGAIN; - else - retval = -ERESTARTSYS; - break; - } - if (!(port->flags & ASYNC_CLOSING) && - (do_clocal || CD)) - break; - if (signal_pending(current)) { - retval = -ERESTARTSYS; - break; - } - schedule(); - } - current->state = TASK_RUNNING; - remove_wait_queue(&port->open_wait, &wait); - if (!tty_hung_up_p(filp)) - port->count++; - port->blocked_open--; - if (retval) - return retval; - - port->flags |= ASYNC_NORMAL_ACTIVE; -#ifdef AURORA_DEBUG - printk("block_til_ready: end\n"); -#endif - return 0; -} - -static int aurora_open(struct tty_struct * tty, struct file * filp) -{ - int board; - int error; - struct Aurora_port * port; - struct Aurora_board * bp; - unsigned long flags; - -#ifdef AURORA_DEBUG - printk("aurora_open: start\n"); -#endif - - board = AURORA_BOARD(tty->index); - if (board > AURORA_NBOARD || - !(aurora_board[board].flags & AURORA_BOARD_PRESENT)) { -#ifdef AURORA_DEBUG - printk("aurora_open: error board %d present %d\n", - board, aurora_board[board].flags & AURORA_BOARD_PRESENT); -#endif - return -ENODEV; - } - - bp = &aurora_board[board]; - port = aurora_port + board * AURORA_NPORT * AURORA_NCD180 + AURORA_PORT(tty->index); - if ((aurora_paranoia_check(port, tty->name, "aurora_open")) { -#ifdef AURORA_DEBUG - printk("aurora_open: error paranoia check\n"); -#endif - return -ENODEV; - } - - port->count++; - tty->driver_data = port; - port->tty = tty; - - if ((error = aurora_setup_port(bp, port))) { -#ifdef AURORA_DEBUG - printk("aurora_open: error aurora_setup_port ret %d\n",error); -#endif - return error; - } - - if ((error = block_til_ready(tty, filp, port))) { -#ifdef AURORA_DEBUG - printk("aurora_open: error block_til_ready ret %d\n",error); -#endif - return error; - } - -#ifdef AURORA_DEBUG - printk("aurora_open: end\n"); -#endif - return 0; -} - -static void aurora_close(struct tty_struct * tty, struct file * filp) -{ - struct Aurora_port *port = (struct Aurora_port *) tty->driver_data; - struct Aurora_board *bp; - unsigned long flags; - unsigned long timeout; - unsigned char chip; - -#ifdef AURORA_DEBUG - printk("aurora_close: start\n"); -#endif - - if (!port || (aurora_paranoia_check(port, tty->name, "close")) - return; - - chip = AURORA_CD180(port_No(port)); - - save_flags(flags); cli(); - if (tty_hung_up_p(filp)) { - restore_flags(flags); - return; - } - - bp = port_Board(port); - if ((tty->count == 1) && (port->count != 1)) { - printk(KERN_DEBUG "aurora%d: aurora_close: bad port count; " - "tty->count is 1, port count is %d\n", - board_No(bp), port->count); - port->count = 1; - } - if (--port->count < 0) { - printk(KERN_DEBUG "aurora%d: aurora_close: bad port " - "count for tty%d: %d\n", - board_No(bp), port_No(port), port->count); - port->count = 0; - } - if (port->count) { - restore_flags(flags); - return; - } - port->flags |= ASYNC_CLOSING; - - /* Now we wait for the transmit buffer to clear; and we notify - * the line discipline to only process XON/XOFF characters. - */ - tty->closing = 1; - if (port->closing_wait != ASYNC_CLOSING_WAIT_NONE){ -#ifdef AURORA_DEBUG - printk("aurora_close: waiting to flush...\n"); -#endif - tty_wait_until_sent(tty, port->closing_wait); - } - - /* At this point we stop accepting input. To do this, we - * disable the receive line status interrupts, and tell the - * interrupt driver to stop checking the data ready bit in the - * line status register. - */ - port->SRER &= ~SRER_RXD; - if (port->flags & ASYNC_INITIALIZED) { - port->SRER &= ~SRER_TXRDY; - port->SRER |= SRER_TXEMPTY; - sbus_writeb(port_No(port) & 7, - &bp->r[chip]->r[CD180_CAR]); - udelay(1); - sbus_writeb(port->SRER, &bp->r[chip]->r[CD180_SRER]); - /* - * Before we drop DTR, make sure the UART transmitter - * has completely drained; this is especially - * important if there is a transmit FIFO! - */ - timeout = jiffies+HZ; - while(port->SRER & SRER_TXEMPTY) { - msleep_interruptible(jiffies_to_msecs(port->timeout)); - if (time_after(jiffies, timeout)) - break; - } - } -#ifdef AURORA_DEBUG - printk("aurora_close: shutdown_port\n"); -#endif - aurora_shutdown_port(bp, port); - if (tty->driver->flush_buffer) - tty->driver->flush_buffer(tty); - tty_ldisc_flush(tty); - tty->closing = 0; - port->event = 0; - port->tty = 0; - if (port->blocked_open) { - if (port->close_delay) { - msleep_interruptible(jiffies_to_msecs(port->close_delay)); - } - wake_up_interruptible(&port->open_wait); - } - port->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING); - wake_up_interruptible(&port->close_wait); - restore_flags(flags); -#ifdef AURORA_DEBUG - printk("aurora_close: end\n"); -#endif -} - -static int aurora_write(struct tty_struct * tty, - const unsigned char *buf, int count) -{ - struct Aurora_port *port = (struct Aurora_port *) tty->driver_data; - struct Aurora_board *bp; - int c, total = 0; - unsigned long flags; - unsigned char chip; - -#ifdef AURORA_DEBUG - printk("aurora_write: start %d\n",count); -#endif - if ((aurora_paranoia_check(port, tty->name, "aurora_write")) - return 0; - - chip = AURORA_CD180(port_No(port)); - - bp = port_Board(port); - - if (!tty || !port->xmit_buf || !tmp_buf) - return 0; - - save_flags(flags); - while (1) { - cli(); - c = min(count, min(SERIAL_XMIT_SIZE - port->xmit_cnt - 1, - SERIAL_XMIT_SIZE - port->xmit_head)); - if (c <= 0) { - restore_flags(flags); - break; - } - memcpy(port->xmit_buf + port->xmit_head, buf, c); - port->xmit_head = (port->xmit_head + c) & (SERIAL_XMIT_SIZE-1); - port->xmit_cnt += c; - restore_flags(flags); - - buf += c; - count -= c; - total += c; - } - - cli(); - if (port->xmit_cnt && !tty->stopped && !tty->hw_stopped && - !(port->SRER & SRER_TXRDY)) { - port->SRER |= SRER_TXRDY; - sbus_writeb(port_No(port) & 7, - &bp->r[chip]->r[CD180_CAR]); - udelay(1); - sbus_writeb(port->SRER, &bp->r[chip]->r[CD180_SRER]); - } - restore_flags(flags); -#ifdef AURORA_DEBUG - printk("aurora_write: end %d\n",total); -#endif - return total; -} - -static void aurora_put_char(struct tty_struct * tty, unsigned char ch) -{ - struct Aurora_port *port = (struct Aurora_port *) tty->driver_data; - unsigned long flags; - -#ifdef AURORA_DEBUG - printk("aurora_put_char: start %c\n",ch); -#endif - if ((aurora_paranoia_check(port, tty->name, "aurora_put_char")) - return; - - if (!tty || !port->xmit_buf) - return; - - save_flags(flags); cli(); - - if (port->xmit_cnt >= SERIAL_XMIT_SIZE - 1) { - restore_flags(flags); - return; - } - - port->xmit_buf[port->xmit_head++] = ch; - port->xmit_head &= SERIAL_XMIT_SIZE - 1; - port->xmit_cnt++; - restore_flags(flags); -#ifdef AURORA_DEBUG - printk("aurora_put_char: end\n"); -#endif -} - -static void aurora_flush_chars(struct tty_struct * tty) -{ - struct Aurora_port *port = (struct Aurora_port *) tty->driver_data; - unsigned long flags; - unsigned char chip; - -/*#ifdef AURORA_DEBUG - printk("aurora_flush_chars: start\n"); -#endif*/ - if ((aurora_paranoia_check(port, tty->name, "aurora_flush_chars")) - return; - - chip = AURORA_CD180(port_No(port)); - - if (port->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped || - !port->xmit_buf) - return; - - save_flags(flags); cli(); - port->SRER |= SRER_TXRDY; - sbus_writeb(port_No(port) & 7, - &port_Board(port)->r[chip]->r[CD180_CAR]); - udelay(1); - sbus_writeb(port->SRER, - &port_Board(port)->r[chip]->r[CD180_SRER]); - restore_flags(flags); -/*#ifdef AURORA_DEBUG - printk("aurora_flush_chars: end\n"); -#endif*/ -} - -static int aurora_write_room(struct tty_struct * tty) -{ - struct Aurora_port *port = (struct Aurora_port *) tty->driver_data; - int ret; - -#ifdef AURORA_DEBUG - printk("aurora_write_room: start\n"); -#endif - if ((aurora_paranoia_check(port, tty->name, "aurora_write_room")) - return 0; - - ret = SERIAL_XMIT_SIZE - port->xmit_cnt - 1; - if (ret < 0) - ret = 0; -#ifdef AURORA_DEBUG - printk("aurora_write_room: end\n"); -#endif - return ret; -} - -static int aurora_chars_in_buffer(struct tty_struct *tty) -{ - struct Aurora_port *port = (struct Aurora_port *) tty->driver_data; - - if ((aurora_paranoia_check(port, tty->name, "aurora_chars_in_buffer")) - return 0; - - return port->xmit_cnt; -} - -static void aurora_flush_buffer(struct tty_struct *tty) -{ - struct Aurora_port *port = (struct Aurora_port *) tty->driver_data; - unsigned long flags; - -#ifdef AURORA_DEBUG - printk("aurora_flush_buffer: start\n"); -#endif - if ((aurora_paranoia_check(port, tty->name, "aurora_flush_buffer")) - return; - - save_flags(flags); cli(); - port->xmit_cnt = port->xmit_head = port->xmit_tail = 0; - restore_flags(flags); - - tty_wakeup(tty); -#ifdef AURORA_DEBUG - printk("aurora_flush_buffer: end\n"); -#endif -} - -static int aurora_tiocmget(struct tty_struct *tty, struct file *file) -{ - struct Aurora_port *port = (struct Aurora_port *) tty->driver_data; - struct Aurora_board * bp; - unsigned char status,chip; - unsigned int result; - unsigned long flags; - -#ifdef AURORA_DEBUG - printk("aurora_get_modem_info: start\n"); -#endif - if ((aurora_paranoia_check(port, tty->name, __FUNCTION__)) - return -ENODEV; - - chip = AURORA_CD180(port_No(port)); - - bp = port_Board(port); - - save_flags(flags); cli(); - - sbus_writeb(port_No(port) & 7, &bp->r[chip]->r[CD180_CAR]); - udelay(1); - - status = sbus_readb(&bp->r[chip]->r[CD180_MSVR]); - result = 0/*bp->r[chip]->r[AURORA_RI] & (1u << port_No(port)) ? 0 : TIOCM_RNG*/; - - restore_flags(flags); - - result |= ((status & bp->RTS) ? TIOCM_RTS : 0) - | ((status & bp->DTR) ? TIOCM_DTR : 0) - | ((status & MSVR_CD) ? TIOCM_CAR : 0) - | ((status & MSVR_DSR) ? TIOCM_DSR : 0) - | ((status & MSVR_CTS) ? TIOCM_CTS : 0); - -#ifdef AURORA_DEBUG - printk("aurora_get_modem_info: end\n"); -#endif - return result; -} - -static int aurora_tiocmset(struct tty_struct *tty, struct file *file, - unsigned int set, unsigned int clear) -{ - struct Aurora_port *port = (struct Aurora_port *) tty->driver_data; - unsigned int arg; - unsigned long flags; - struct Aurora_board *bp = port_Board(port); - unsigned char chip; - -#ifdef AURORA_DEBUG - printk("aurora_set_modem_info: start\n"); -#endif - if ((aurora_paranoia_check(port, tty->name, __FUNCTION__)) - return -ENODEV; - - chip = AURORA_CD180(port_No(port)); - - save_flags(flags); cli(); - if (set & TIOCM_RTS) - port->MSVR |= bp->RTS; - if (set & TIOCM_DTR) - port->MSVR |= bp->DTR; - if (clear & TIOCM_RTS) - port->MSVR &= ~bp->RTS; - if (clear & TIOCM_DTR) - port->MSVR &= ~bp->DTR; - - sbus_writeb(port_No(port) & 7, &bp->r[chip]->r[CD180_CAR]); - udelay(1); - - sbus_writeb(port->MSVR, &bp->r[chip]->r[CD180_MSVR]); - - restore_flags(flags); -#ifdef AURORA_DEBUG - printk("aurora_set_modem_info: end\n"); -#endif - return 0; -} - -static void aurora_send_break(struct Aurora_port * port, unsigned long length) -{ - struct Aurora_board *bp = port_Board(port); - unsigned long flags; - unsigned char chip; - -#ifdef AURORA_DEBUG - printk("aurora_send_break: start\n"); -#endif - chip = AURORA_CD180(port_No(port)); - - save_flags(flags); cli(); - - port->break_length = AURORA_TPS / HZ * length; - port->COR2 |= COR2_ETC; - port->SRER |= SRER_TXRDY; - sbus_writeb(port_No(port) & 7, &bp->r[chip]->r[CD180_CAR]); - udelay(1); - - sbus_writeb(port->COR2, &bp->r[chip]->r[CD180_COR2]); - sbus_writeb(port->SRER, &bp->r[chip]->r[CD180_SRER]); - aurora_wait_CCR(bp->r[chip]); - - sbus_writeb(CCR_CORCHG2, &bp->r[chip]->r[CD180_CCR]); - aurora_wait_CCR(bp->r[chip]); - - restore_flags(flags); -#ifdef AURORA_DEBUG - printk("aurora_send_break: end\n"); -#endif -} - -static int aurora_set_serial_info(struct Aurora_port * port, - struct serial_struct * newinfo) -{ - struct serial_struct tmp; - struct Aurora_board *bp = port_Board(port); - int change_speed; - unsigned long flags; - -#ifdef AURORA_DEBUG - printk("aurora_set_serial_info: start\n"); -#endif - if (copy_from_user(&tmp, newinfo, sizeof(tmp))) - return -EFAULT; -#if 0 - if ((tmp.irq != bp->irq) || - (tmp.port != bp->base) || - (tmp.type != PORT_CIRRUS) || - (tmp.baud_base != (bp->oscfreq + CD180_TPC/2) / CD180_TPC) || - (tmp.custom_divisor != 0) || - (tmp.xmit_fifo_size != CD180_NFIFO) || - (tmp.flags & ~AURORA_LEGAL_FLAGS)) - return -EINVAL; -#endif - - change_speed = ((port->flags & ASYNC_SPD_MASK) != - (tmp.flags & ASYNC_SPD_MASK)); - - if (!capable(CAP_SYS_ADMIN)) { - if ((tmp.close_delay != port->close_delay) || - (tmp.closing_wait != port->closing_wait) || - ((tmp.flags & ~ASYNC_USR_MASK) != - (port->flags & ~ASYNC_USR_MASK))) - return -EPERM; - port->flags = ((port->flags & ~ASYNC_USR_MASK) | - (tmp.flags & ASYNC_USR_MASK)); - } else { - port->flags = ((port->flags & ~ASYNC_FLAGS) | - (tmp.flags & ASYNC_FLAGS)); - port->close_delay = tmp.close_delay; - port->closing_wait = tmp.closing_wait; - } - if (change_speed) { - save_flags(flags); cli(); - aurora_change_speed(bp, port); - restore_flags(flags); - } -#ifdef AURORA_DEBUG - printk("aurora_set_serial_info: end\n"); -#endif - return 0; -} - -extern int aurora_get_serial_info(struct Aurora_port * port, - struct serial_struct * retinfo) -{ - struct serial_struct tmp; - struct Aurora_board *bp = port_Board(port); - -#ifdef AURORA_DEBUG - printk("aurora_get_serial_info: start\n"); -#endif - if (!access_ok(VERIFY_WRITE, (void *) retinfo, sizeof(tmp))) - return -EFAULT; - - memset(&tmp, 0, sizeof(tmp)); - tmp.type = PORT_CIRRUS; - tmp.line = port - aurora_port; - tmp.port = 0; - tmp.irq = bp->irq; - tmp.flags = port->flags; - tmp.baud_base = (bp->oscfreq + CD180_TPC/2) / CD180_TPC; - tmp.close_delay = port->close_delay * HZ/100; - tmp.closing_wait = port->closing_wait * HZ/100; - tmp.xmit_fifo_size = CD180_NFIFO; - copy_to_user(retinfo, &tmp, sizeof(tmp)); -#ifdef AURORA_DEBUG -printk("aurora_get_serial_info: end\n"); -#endif - return 0; -} - -static int aurora_ioctl(struct tty_struct * tty, struct file * filp, - unsigned int cmd, unsigned long arg) - -{ - struct Aurora_port *port = (struct Aurora_port *) tty->driver_data; - int retval; - -#ifdef AURORA_DEBUG - printk("aurora_ioctl: start\n"); -#endif - if ((aurora_paranoia_check(port, tty->name, "aurora_ioctl")) - return -ENODEV; - - switch (cmd) { - case TCSBRK: /* SVID version: non-zero arg --> no break */ - retval = tty_check_change(tty); - if (retval) - return retval; - tty_wait_until_sent(tty, 0); - if (!arg) - aurora_send_break(port, HZ/4); /* 1/4 second */ - return 0; - case TCSBRKP: /* support for POSIX tcsendbreak() */ - retval = tty_check_change(tty); - if (retval) - return retval; - tty_wait_until_sent(tty, 0); - aurora_send_break(port, arg ? arg*(HZ/10) : HZ/4); - return 0; - case TIOCGSOFTCAR: - return put_user(C_CLOCAL(tty) ? 1 : 0, (unsigned long *)arg); - case TIOCSSOFTCAR: - if (get_user(arg,(unsigned long *)arg)) - return -EFAULT; - tty->termios->c_cflag = - ((tty->termios->c_cflag & ~CLOCAL) | - (arg ? CLOCAL : 0)); - return 0; - case TIOCGSERIAL: - return aurora_get_serial_info(port, (struct serial_struct *) arg); - case TIOCSSERIAL: - return aurora_set_serial_info(port, (struct serial_struct *) arg); - default: - return -ENOIOCTLCMD; - }; -#ifdef AURORA_DEBUG - printk("aurora_ioctl: end\n"); -#endif - return 0; -} - -static void aurora_throttle(struct tty_struct * tty) -{ - struct Aurora_port *port = (struct Aurora_port *) tty->driver_data; - struct Aurora_board *bp; - unsigned long flags; - unsigned char chip; - -#ifdef AURORA_DEBUG - printk("aurora_throttle: start\n"); -#endif - if ((aurora_paranoia_check(port, tty->name, "aurora_throttle")) - return; - - bp = port_Board(port); - chip = AURORA_CD180(port_No(port)); - - save_flags(flags); cli(); - port->MSVR &= ~bp->RTS; - sbus_writeb(port_No(port) & 7, &bp->r[chip]->r[CD180_CAR]); - udelay(1); - if (I_IXOFF(tty)) { - aurora_wait_CCR(bp->r[chip]); - sbus_writeb(CCR_SSCH2, &bp->r[chip]->r[CD180_CCR]); - aurora_wait_CCR(bp->r[chip]); - } - sbus_writeb(port->MSVR, &bp->r[chip]->r[CD180_MSVR]); - restore_flags(flags); -#ifdef AURORA_DEBUG - printk("aurora_throttle: end\n"); -#endif -} - -static void aurora_unthrottle(struct tty_struct * tty) -{ - struct Aurora_port *port = (struct Aurora_port *) tty->driver_data; - struct Aurora_board *bp; - unsigned long flags; - unsigned char chip; - -#ifdef AURORA_DEBUG - printk("aurora_unthrottle: start\n"); -#endif - if ((aurora_paranoia_check(port, tty->name, "aurora_unthrottle")) - return; - - bp = port_Board(port); - - chip = AURORA_CD180(port_No(port)); - - save_flags(flags); cli(); - port->MSVR |= bp->RTS; - sbus_writeb(port_No(port) & 7, - &bp->r[chip]->r[CD180_CAR]); - udelay(1); - if (I_IXOFF(tty)) { - aurora_wait_CCR(bp->r[chip]); - sbus_writeb(CCR_SSCH1, - &bp->r[chip]->r[CD180_CCR]); - aurora_wait_CCR(bp->r[chip]); - } - sbus_writeb(port->MSVR, &bp->r[chip]->r[CD180_MSVR]); - restore_flags(flags); -#ifdef AURORA_DEBUG - printk("aurora_unthrottle: end\n"); -#endif -} - -static void aurora_stop(struct tty_struct * tty) -{ - struct Aurora_port *port = (struct Aurora_port *) tty->driver_data; - struct Aurora_board *bp; - unsigned long flags; - unsigned char chip; - -#ifdef AURORA_DEBUG - printk("aurora_stop: start\n"); -#endif - if ((aurora_paranoia_check(port, tty->name, "aurora_stop")) - return; - - bp = port_Board(port); - - chip = AURORA_CD180(port_No(port)); - - save_flags(flags); cli(); - port->SRER &= ~SRER_TXRDY; - sbus_writeb(port_No(port) & 7, - &bp->r[chip]->r[CD180_CAR]); - udelay(1); - sbus_writeb(port->SRER, - &bp->r[chip]->r[CD180_SRER]); - restore_flags(flags); -#ifdef AURORA_DEBUG - printk("aurora_stop: end\n"); -#endif -} - -static void aurora_start(struct tty_struct * tty) -{ - struct Aurora_port *port = (struct Aurora_port *) tty->driver_data; - struct Aurora_board *bp; - unsigned long flags; - unsigned char chip; - -#ifdef AURORA_DEBUG - printk("aurora_start: start\n"); -#endif - if ((aurora_paranoia_check(port, tty->name, "aurora_start")) - return; - - bp = port_Board(port); - - chip = AURORA_CD180(port_No(port)); - - save_flags(flags); cli(); - if (port->xmit_cnt && port->xmit_buf && !(port->SRER & SRER_TXRDY)) { - port->SRER |= SRER_TXRDY; - sbus_writeb(port_No(port) & 7, - &bp->r[chip]->r[CD180_CAR]); - udelay(1); - sbus_writeb(port->SRER, - &bp->r[chip]->r[CD180_SRER]); - } - restore_flags(flags); -#ifdef AURORA_DEBUG - printk("aurora_start: end\n"); -#endif -} - -/* - * This routine is called from the scheduler tqueue when the interrupt - * routine has signalled that a hangup has occurred. The path of - * hangup processing is: - * - * serial interrupt routine -> (scheduler tqueue) -> - * do_aurora_hangup() -> tty->hangup() -> aurora_hangup() - * - */ -static void do_aurora_hangup(void *private_) -{ - struct Aurora_port *port = (struct Aurora_port *) private_; - struct tty_struct *tty; - -#ifdef AURORA_DEBUG - printk("do_aurora_hangup: start\n"); -#endif - tty = port->tty; - if (tty != NULL) { - tty_hangup(tty); /* FIXME: module removal race - AKPM */ -#ifdef AURORA_DEBUG - printk("do_aurora_hangup: end\n"); -#endif - } -} - -static void aurora_hangup(struct tty_struct * tty) -{ - struct Aurora_port *port = (struct Aurora_port *) tty->driver_data; - struct Aurora_board *bp; - -#ifdef AURORA_DEBUG - printk("aurora_hangup: start\n"); -#endif - if ((aurora_paranoia_check(port, tty->name, "aurora_hangup")) - return; - - bp = port_Board(port); - - aurora_shutdown_port(bp, port); - port->event = 0; - port->count = 0; - port->flags &= ~ASYNC_NORMAL_ACTIVE; - port->tty = 0; - wake_up_interruptible(&port->open_wait); -#ifdef AURORA_DEBUG - printk("aurora_hangup: end\n"); -#endif -} - -static void aurora_set_termios(struct tty_struct * tty, struct termios * old_termios) -{ - struct Aurora_port *port = (struct Aurora_port *) tty->driver_data; - unsigned long flags; - -#ifdef AURORA_DEBUG - printk("aurora_set_termios: start\n"); -#endif - if ((aurora_paranoia_check(port, tty->name, "aurora_set_termios")) - return; - - if (tty->termios->c_cflag == old_termios->c_cflag && - tty->termios->c_iflag == old_termios->c_iflag) - return; - - save_flags(flags); cli(); - aurora_change_speed(port_Board(port), port); - restore_flags(flags); - - if ((old_termios->c_cflag & CRTSCTS) && - !(tty->termios->c_cflag & CRTSCTS)) { - tty->hw_stopped = 0; - aurora_start(tty); - } -#ifdef AURORA_DEBUG - printk("aurora_set_termios: end\n"); -#endif -} - -static void do_aurora_bh(void) -{ - run_task_queue(&tq_aurora); -} - -static void do_softint(void *private_) -{ - struct Aurora_port *port = (struct Aurora_port *) private_; - struct tty_struct *tty; - -#ifdef AURORA_DEBUG - printk("do_softint: start\n"); -#endif - tty = port->tty; - if (tty == NULL) - return; - - if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &port->event)) { - tty_wakeup(tty); - } -#ifdef AURORA_DEBUG - printk("do_softint: end\n"); -#endif -} - -static const struct tty_operations aurora_ops = { - .open = aurora_open, - .close = aurora_close, - .write = aurora_write, - .put_char = aurora_put_char, - .flush_chars = aurora_flush_chars, - .write_room = aurora_write_room, - .chars_in_buffer = aurora_chars_in_buffer, - .flush_buffer = aurora_flush_buffer, - .ioctl = aurora_ioctl, - .throttle = aurora_throttle, - .unthrottle = aurora_unthrottle, - .set_termios = aurora_set_termios, - .stop = aurora_stop, - .start = aurora_start, - .hangup = aurora_hangup, - .tiocmget = aurora_tiocmget, - .tiocmset = aurora_tiocmset, -}; - -static int aurora_init_drivers(void) -{ - int error; - int i; - -#ifdef AURORA_DEBUG - printk("aurora_init_drivers: start\n"); -#endif - tmp_buf = (unsigned char *) get_zeroed_page(GFP_KERNEL); - if (tmp_buf == NULL) { - printk(KERN_ERR "aurora: Couldn't get free page.\n"); - return 1; - } - init_bh(AURORA_BH, do_aurora_bh); - aurora_driver = alloc_tty_driver(AURORA_INPORTS); - if (!aurora_driver) { - printk(KERN_ERR "aurora: Couldn't allocate tty driver.\n"); - free_page((unsigned long) tmp_buf); - return 1; - } - aurora_driver->owner = THIS_MODULE; - aurora_driver->name = "ttyA"; - aurora_driver->major = AURORA_MAJOR; - aurora_driver->type = TTY_DRIVER_TYPE_SERIAL; - aurora_driver->subtype = SERIAL_TYPE_NORMAL; - aurora_driver->init_termios = tty_std_termios; - aurora_driver->init_termios.c_cflag = - B9600 | CS8 | CREAD | HUPCL | CLOCAL; - aurora_driver->flags = TTY_DRIVER_REAL_RAW; - tty_set_operations(aurora_driver, &aurora_ops); - error = tty_register_driver(aurora_driver); - if (error) { - put_tty_driver(aurora_driver); - free_page((unsigned long) tmp_buf); - printk(KERN_ERR "aurora: Couldn't register aurora driver, error = %d\n", - error); - return 1; - } - - memset(aurora_port, 0, sizeof(aurora_port)); - for (i = 0; i < AURORA_TNPORTS; i++) { - aurora_port[i].magic = AURORA_MAGIC; - aurora_port[i].tqueue.routine = do_softint; - aurora_port[i].tqueue.data = &aurora_port[i]; - aurora_port[i].tqueue_hangup.routine = do_aurora_hangup; - aurora_port[i].tqueue_hangup.data = &aurora_port[i]; - aurora_port[i].close_delay = 50 * HZ/100; - aurora_port[i].closing_wait = 3000 * HZ/100; - init_waitqueue_head(&aurora_port[i].open_wait); - init_waitqueue_head(&aurora_port[i].close_wait); - } -#ifdef AURORA_DEBUG - printk("aurora_init_drivers: end\n"); -#endif - return 0; -} - -static void aurora_release_drivers(void) -{ -#ifdef AURORA_DEBUG - printk("aurora_release_drivers: start\n"); -#endif - free_page((unsigned long)tmp_buf); - tty_unregister_driver(aurora_driver); - put_tty_driver(aurora_driver); -#ifdef AURORA_DEBUG - printk("aurora_release_drivers: end\n"); -#endif -} - -/* - * Called at boot time. - * - * You can specify IO base for up to RC_NBOARD cards, - * using line "riscom8=0xiobase1,0xiobase2,.." at LILO prompt. - * Note that there will be no probing at default - * addresses in this case. - * - */ -void __init aurora_setup(char *str, int *ints) -{ - int i; - - for(i=0;(i -#include - -#ifdef __KERNEL__ - -/* This is the number of boards to support. I've only tested this driver with - * one board, so it might not work. - */ -#define AURORA_NBOARD 1 - -/* Useful ? Yes. But you can safely comment the warnings if they annoy you - * (let me say that again: the warnings in the code, not this define). - */ -#define AURORA_PARANOIA_CHECK - -/* Well, after many lost nights, I found that the IRQ for this board is - * selected from four built-in values by writing some bits in the - * configuration register. This causes a little problem to occur: which - * IRQ to select ? Which one is the best for the user ? Well, I finally - * decided for the following algorithm: if the "bintr" value is not acceptable - * (not within type_1_irq[], then test the "intr" value, if that fails too, - * try each value from type_1_irq until succeded. Hope it's ok. - * You can safely reorder the irq's. - */ -#define TYPE_1_IRQS 4 -unsigned char type_1_irq[TYPE_1_IRQS] = { - 3, 5, 9, 13 -}; -/* I know something about another method of interrupt setting, but not enough. - * Also, this is for another type of board, so I first have to learn how to - * detect it. -#define TYPE_2_IRQS 3 -unsigned char type_2_irq[TYPE_2_IRQS] = { - 0, 0, 0 ** could anyone find these for me ? (see AURORA_ALLIRQ below) ** - }; -unsigned char type_2_mask[TYPE_2_IRQS] = { - 32, 64, 128 - }; -*/ - -/* The following section should only be modified by those who know what - * they're doing (or don't, but want to help with some feedback). Modifying - * anything raises a _big_ probability for your system to hang, but the - * sacrifice worths. (I sacrificed my ext2fs many, many times...) - */ - -/* This one tries to dump to console the name of almost every function called, - * and many other debugging info. - */ -#undef AURORA_DEBUG - -/* These are the most dangerous and useful defines. They do printk() during - * the interrupt processing routine(s), so if you manage to get "flooded" by - * irq's, start thinking about the "Power off/on" button... - */ -#undef AURORA_INTNORM /* This one enables the "normal" messages, but some - * of them cause flood, so I preffered putting - * them under a define */ -#undef AURORA_INT_DEBUG /* This one is really bad. */ - -/* Here's something helpful: after n irq's, the board will be disabled. This - * prevents irq flooding during debug (no need to think about power - * off/on anymore...) - */ -#define AURORA_FLOODPRO 10 - -/* This one helps finding which irq the board calls, in case of a strange/ - * unsupported board. AURORA_INT_DEBUG should be enabled, because I don't - * think /proc/interrupts or any command will be available in case of an irq - * flood... "allirq" is the list of all free irq's. - */ -/* -#define AURORA_ALLIRQ 6 -int allirq[AURORA_ALLIRQ]={ - 2,3,5,7,9,13 - }; -*/ - -/* These must not be modified. These values are assumed during the code for - * performance optimisations. - */ -#define AURORA_NCD180 2 /* two chips per board */ -#define AURORA_NPORT 8 /* 8 ports per chip */ - -/* several utilities */ -#define AURORA_BOARD(line) (((line) >> 4) & 0x01) -#define AURORA_CD180(line) (((line) >> 3) & 0x01) -#define AURORA_PORT(line) ((line) & 15) - -#define AURORA_TNPORTS (AURORA_NBOARD*AURORA_NCD180*AURORA_NPORT) - -/* Ticks per sec. Used for setting receiver timeout and break length */ -#define AURORA_TPS 4000 - -#define AURORA_MAGIC 0x0A18 - -/* Yeah, after heavy testing I decided it must be 6. - * Sure, You can change it if needed. - */ -#define AURORA_RXFIFO 6 /* Max. receiver FIFO size (1-8) */ - -#define AURORA_RXTH 7 - -struct aurora_reg1 { - __volatile__ unsigned char r; -}; - -struct aurora_reg128 { - __volatile__ unsigned char r[128]; -}; - -struct aurora_reg4 { - __volatile__ unsigned char r[4]; -}; - -struct Aurora_board { - unsigned long flags; - struct aurora_reg1 * r0; /* This is the board configuration - * register (write-only). */ - struct aurora_reg128 * r[2]; /* These are the registers for the - * two chips. */ - struct aurora_reg4 * r3; /* These are used for hardware-based - * acknowledge. Software-based ack is - * not supported by CD180. */ - unsigned int oscfreq; /* The on-board oscillator - * frequency, in Hz. */ - unsigned char irq; -#ifdef MODULE - signed char count; /* counts the use of the board */ -#endif - /* Values for the dtr_rts swapped mode. */ - unsigned char DTR; - unsigned char RTS; - unsigned char MSVDTR; - unsigned char MSVRTS; - /* Values for hardware acknowledge. */ - unsigned char ACK_MINT, ACK_TINT, ACK_RINT; -}; - -/* Board configuration register */ -#define AURORA_CFG_ENABLE_IO 8 -#define AURORA_CFG_ENABLE_IRQ 4 - -/* Board flags */ -#define AURORA_BOARD_PRESENT 0x00000001 -#define AURORA_BOARD_ACTIVE 0x00000002 -#define AURORA_BOARD_TYPE_2 0x00000004 /* don't know how to - * detect this yet */ -#define AURORA_BOARD_DTR_FLOW_OK 0x00000008 - -/* The story goes like this: Cirrus programmed the CD-180 chip to do automatic - * hardware flow control, and do it using CTS and DTR. CTS is ok, but, if you - * have a modem and the chip drops DTR, then the modem will drop the carrier - * (ain't that cute...). Luckily, the guys at Aurora decided to swap DTR and - * RTS, which makes the flow control usable. I hope that all the boards made - * by Aurora have these two signals swapped. If your's doesn't but you have a - * breakout box, you can try to reverse them yourself, then set the following - * flag. - */ -#undef AURORA_FORCE_DTR_FLOW - -/* In fact, a few more words have to be said about hardware flow control. - * This driver handles "output" flow control through the on-board facility - * CTS Auto Enable. For the "input" flow control there are two cases when - * the flow should be controlled. The first case is when the kernel is so - * busy that it cannot process IRQ's in time; this flow control can only be - * activated by the on-board chip, and if the board has RTS and DTR swapped, - * this facility is usable. The second case is when the application is so - * busy that it cannot receive bytes from the kernel, and this flow must be - * activated by software. This second case is not yet implemented in this - * driver. Unfortunately, I estimate that the second case is the one that - * occurs the most. - */ - - -struct Aurora_port { - int magic; - int baud_base; - int flags; - struct tty_struct * tty; - int count; - int blocked_open; - long event; - int timeout; - int close_delay; - unsigned char * xmit_buf; - int custom_divisor; - int xmit_head; - int xmit_tail; - int xmit_cnt; - wait_queue_head_t open_wait; - wait_queue_head_t close_wait; - struct tq_struct tqueue; - struct tq_struct tqueue_hangup; - short wakeup_chars; - short break_length; - unsigned short closing_wait; - unsigned char mark_mask; - unsigned char SRER; - unsigned char MSVR; - unsigned char COR2; -#ifdef AURORA_REPORT_OVERRUN - unsigned long overrun; -#endif -#ifdef AURORA_REPORT_FIFO - unsigned long hits[10]; -#endif -}; - -#endif -#endif /*__LINUX_AURORA_H*/ - diff --git a/drivers/sbus/char/cd180.h b/drivers/sbus/char/cd180.h deleted file mode 100644 index 445b86cc65e7..000000000000 --- a/drivers/sbus/char/cd180.h +++ /dev/null @@ -1,240 +0,0 @@ - -/* Definitions for Cirrus Logic CL-CD180 8-port async mux chip */ -#define CD180_NCH 8 /* Total number of channels */ -#define CD180_TPC 16 /* Ticks per character */ -#define CD180_NFIFO 8 /* TX FIFO size */ - -/* Global registers */ -#define CD180_GFRCR 0x6b /* Global Firmware Revision Code Register */ -#define CD180_SRCR 0x66 /* Service Request Configuration Register */ -#define CD180_PPRH 0x70 /* Prescaler Period Register High */ -#define CD180_PPRL 0x71 /* Prescaler Period Register Low */ -#define CD180_MSMR 0x61 /* Modem Service Match Register */ -#define CD180_TSMR 0x62 /* Transmit Service Match Register */ -#define CD180_RSMR 0x63 /* Receive Service Match Register */ -#define CD180_GSVR 0x40 /* Global Service Vector Register */ -#define CD180_SRSR 0x65 /* Service Request Status Register */ -#define CD180_GSCR 0x41 /* Global Service Channel Register */ -#define CD180_CAR 0x64 /* Channel Access Register */ - -/* Indexed registers */ -#define CD180_RDCR 0x07 /* Receive Data Count Register */ -#define CD180_RDR 0x78 /* Receiver Data Register */ -#define CD180_RCSR 0x7a /* Receiver Character Status Register */ -#define CD180_TDR 0x7b /* Transmit Data Register */ -#define CD180_EOSRR 0x7f /* End of Service Request Register */ - -/* Channel Registers */ -#define CD180_SRER 0x02 /* Service Request Enable Register */ -#define CD180_CCR 0x01 /* Channel Command Register */ -#define CD180_COR1 0x03 /* Channel Option Register 1 */ -#define CD180_COR2 0x04 /* Channel Option Register 2 */ -#define CD180_COR3 0x05 /* Channel Option Register 3 */ -#define CD180_CCSR 0x06 /* Channel Control Status Register */ -#define CD180_RTPR 0x18 /* Receive Timeout Period Register */ -#define CD180_RBPRH 0x31 /* Receive Bit Rate Period Register High */ -#define CD180_RBPRL 0x32 /* Receive Bit Rate Period Register Low */ -#define CD180_TBPRH 0x39 /* Transmit Bit Rate Period Register High */ -#define CD180_TBPRL 0x3a /* Transmit Bit Rate Period Register Low */ -#define CD180_SCHR1 0x09 /* Special Character Register 1 */ -#define CD180_SCHR2 0x0a /* Special Character Register 2 */ -#define CD180_SCHR3 0x0b /* Special Character Register 3 */ -#define CD180_SCHR4 0x0c /* Special Character Register 4 */ -#define CD180_MCR 0x12 /* Modem Change Register */ -#define CD180_MCOR1 0x10 /* Modem Change Option 1 Register */ -#define CD180_MCOR2 0x11 /* Modem Change Option 2 Register */ -#define CD180_MSVR 0x28 /* Modem Signal Value Register */ -#define CD180_MSVRTS 0x29 /* Modem Signal Value RTS */ -#define CD180_MSVDTR 0x2a /* Modem Signal Value DTR */ - -/* Global Interrupt Vector Register (R/W) */ - -#define GSVR_ITMASK 0x07 /* Interrupt type mask */ -#define GSVR_IT_MDM 0x01 /* Modem Signal Change Interrupt */ -#define GSVR_IT_TX 0x02 /* Transmit Data Interrupt */ -#define GSVR_IT_RGD 0x03 /* Receive Good Data Interrupt */ -#define GSVR_IT_REXC 0x07 /* Receive Exception Interrupt */ - - -/* Global Interrupt Channel Register (R/W) */ - -#define GSCR_CHAN 0x1c /* Channel Number Mask */ -#define GSCR_CHAN_OFF 2 /* Channel Number Offset */ - - -/* Channel Address Register (R/W) */ - -#define CAR_CHAN 0x07 /* Channel Number Mask */ - - -/* Receive Character Status Register (R/O) */ - -#define RCSR_TOUT 0x80 /* Rx Timeout */ -#define RCSR_SCDET 0x70 /* Special Character Detected Mask */ -#define RCSR_NO_SC 0x00 /* No Special Characters Detected */ -#define RCSR_SC_1 0x10 /* Special Char 1 (or 1 & 3) Detected */ -#define RCSR_SC_2 0x20 /* Special Char 2 (or 2 & 4) Detected */ -#define RCSR_SC_3 0x30 /* Special Char 3 Detected */ -#define RCSR_SC_4 0x40 /* Special Char 4 Detected */ -#define RCSR_BREAK 0x08 /* Break has been detected */ -#define RCSR_PE 0x04 /* Parity Error */ -#define RCSR_FE 0x02 /* Frame Error */ -#define RCSR_OE 0x01 /* Overrun Error */ - - -/* Channel Command Register (R/W) (commands in groups can be OR-ed) */ - -#define CCR_HARDRESET 0x81 /* Reset the chip */ - -#define CCR_SOFTRESET 0x80 /* Soft Channel Reset */ - -#define CCR_CORCHG1 0x42 /* Channel Option Register 1 Changed */ -#define CCR_CORCHG2 0x44 /* Channel Option Register 2 Changed */ -#define CCR_CORCHG3 0x48 /* Channel Option Register 3 Changed */ - -#define CCR_SSCH1 0x21 /* Send Special Character 1 */ - -#define CCR_SSCH2 0x22 /* Send Special Character 2 */ - -#define CCR_SSCH3 0x23 /* Send Special Character 3 */ - -#define CCR_SSCH4 0x24 /* Send Special Character 4 */ - -#define CCR_TXEN 0x18 /* Enable Transmitter */ -#define CCR_RXEN 0x12 /* Enable Receiver */ - -#define CCR_TXDIS 0x14 /* Disable Transmitter */ -#define CCR_RXDIS 0x11 /* Disable Receiver */ - - -/* Service Request Enable Register (R/W) */ - -#define SRER_DSR 0x80 /* Enable interrupt on DSR change */ -#define SRER_CD 0x40 /* Enable interrupt on CD change */ -#define SRER_CTS 0x20 /* Enable interrupt on CTS change */ -#define SRER_RXD 0x10 /* Enable interrupt on Receive Data */ -#define SRER_RXSC 0x08 /* Enable interrupt on Receive Spec. Char */ -#define SRER_TXRDY 0x04 /* Enable interrupt on TX FIFO empty */ -#define SRER_TXEMPTY 0x02 /* Enable interrupt on TX completely empty */ -#define SRER_RET 0x01 /* Enable interrupt on RX Exc. Timeout */ - - -/* Channel Option Register 1 (R/W) */ - -#define COR1_ODDP 0x80 /* Odd Parity */ -#define COR1_PARMODE 0x60 /* Parity Mode mask */ -#define COR1_NOPAR 0x00 /* No Parity */ -#define COR1_FORCEPAR 0x20 /* Force Parity */ -#define COR1_NORMPAR 0x40 /* Normal Parity */ -#define COR1_IGNORE 0x10 /* Ignore Parity on RX */ -#define COR1_STOPBITS 0x0c /* Number of Stop Bits */ -#define COR1_1SB 0x00 /* 1 Stop Bit */ -#define COR1_15SB 0x04 /* 1.5 Stop Bits */ -#define COR1_2SB 0x08 /* 2 Stop Bits */ -#define COR1_CHARLEN 0x03 /* Character Length */ -#define COR1_5BITS 0x00 /* 5 bits */ -#define COR1_6BITS 0x01 /* 6 bits */ -#define COR1_7BITS 0x02 /* 7 bits */ -#define COR1_8BITS 0x03 /* 8 bits */ - - -/* Channel Option Register 2 (R/W) */ - -#define COR2_IXM 0x80 /* Implied XON mode */ -#define COR2_TXIBE 0x40 /* Enable In-Band (XON/XOFF) Flow Control */ -#define COR2_ETC 0x20 /* Embedded Tx Commands Enable */ -#define COR2_LLM 0x10 /* Local Loopback Mode */ -#define COR2_RLM 0x08 /* Remote Loopback Mode */ -#define COR2_RTSAO 0x04 /* RTS Automatic Output Enable */ -#define COR2_CTSAE 0x02 /* CTS Automatic Enable */ -#define COR2_DSRAE 0x01 /* DSR Automatic Enable */ - - -/* Channel Option Register 3 (R/W) */ - -#define COR3_XONCH 0x80 /* XON is a pair of characters (1 & 3) */ -#define COR3_XOFFCH 0x40 /* XOFF is a pair of characters (2 & 4) */ -#define COR3_FCT 0x20 /* Flow-Control Transparency Mode */ -#define COR3_SCDE 0x10 /* Special Character Detection Enable */ -#define COR3_RXTH 0x0f /* RX FIFO Threshold value (1-8) */ - - -/* Channel Control Status Register (R/O) */ - -#define CCSR_RXEN 0x80 /* Receiver Enabled */ -#define CCSR_RXFLOFF 0x40 /* Receive Flow Off (XOFF was sent) */ -#define CCSR_RXFLON 0x20 /* Receive Flow On (XON was sent) */ -#define CCSR_TXEN 0x08 /* Transmitter Enabled */ -#define CCSR_TXFLOFF 0x04 /* Transmit Flow Off (got XOFF) */ -#define CCSR_TXFLON 0x02 /* Transmit Flow On (got XON) */ - - -/* Modem Change Option Register 1 (R/W) */ - -#define MCOR1_DSRZD 0x80 /* Detect 0->1 transition of DSR */ -#define MCOR1_CDZD 0x40 /* Detect 0->1 transition of CD */ -#define MCOR1_CTSZD 0x20 /* Detect 0->1 transition of CTS */ -#define MCOR1_DTRTH 0x0f /* Auto DTR flow control Threshold (1-8) */ -#define MCOR1_NODTRFC 0x0 /* Automatic DTR flow control disabled */ - - -/* Modem Change Option Register 2 (R/W) */ - -#define MCOR2_DSROD 0x80 /* Detect 1->0 transition of DSR */ -#define MCOR2_CDOD 0x40 /* Detect 1->0 transition of CD */ -#define MCOR2_CTSOD 0x20 /* Detect 1->0 transition of CTS */ - - -/* Modem Change Register (R/W) */ - -#define MCR_DSRCHG 0x80 /* DSR Changed */ -#define MCR_CDCHG 0x40 /* CD Changed */ -#define MCR_CTSCHG 0x20 /* CTS Changed */ - - -/* Modem Signal Value Register (R/W) */ - -#define MSVR_DSR 0x80 /* Current state of DSR input */ -#define MSVR_CD 0x40 /* Current state of CD input */ -#define MSVR_CTS 0x20 /* Current state of CTS input */ -#define MSVR_DTR 0x02 /* Current state of DTR output */ -#define MSVR_RTS 0x01 /* Current state of RTS output */ - - -/* Service Request Status Register */ - -#define SRSR_CMASK 0xC0 /* Current Service Context Mask */ -#define SRSR_CNONE 0x00 /* Not in a service context */ -#define SRSR_CRX 0x40 /* Rx Context */ -#define SRSR_CTX 0x80 /* Tx Context */ -#define SRSR_CMDM 0xC0 /* Modem Context */ -#define SRSR_ANYINT 0x6F /* Any interrupt flag */ -#define SRSR_RINT 0x10 /* Receive Interrupt */ -#define SRSR_TINT 0x04 /* Transmit Interrupt */ -#define SRSR_MINT 0x01 /* Modem Interrupt */ -#define SRSR_REXT 0x20 /* Receive External Interrupt */ -#define SRSR_TEXT 0x08 /* Transmit External Interrupt */ -#define SRSR_MEXT 0x02 /* Modem External Interrupt */ - - -/* Service Request Configuration Register */ - -#define SRCR_PKGTYPE 0x80 -#define SRCR_REGACKEN 0x40 -#define SRCR_DAISYEN 0x20 -#define SRCR_GLOBPRI 0x10 -#define SRCR_UNFAIR 0x08 -#define SRCR_AUTOPRI 0x02 -#define SRCR_PRISEL 0x01 - -/* Values for register-based Interrupt ACKs */ -#define CD180_ACK_MINT 0x75 /* goes to MSMR */ -#define CD180_ACK_TINT 0x76 /* goes to TSMR */ -#define CD180_ACK_RINT 0x77 /* goes to RSMR */ - -/* Escape characters */ - -#define CD180_C_ESC 0x00 /* Escape character */ -#define CD180_C_SBRK 0x81 /* Start sending BREAK */ -#define CD180_C_DELAY 0x82 /* Delay output */ -#define CD180_C_EBRK 0x83 /* Stop sending BREAK */ -- cgit v1.2.3 From 71599cd1c381d1b5f58c35653ac1d3627c6276db Mon Sep 17 00:00:00 2001 From: John Heffner Date: Tue, 27 Feb 2007 10:03:56 -0800 Subject: [TCP]: Document several sysctls. This adds documentation for tcp_moderate_rcvbuf, tcp_no_metrics_save, tcp_base_mss, and tcp_mtu_probing. Signed-off-by: John Heffner Signed-off-by: David S. Miller --- Documentation/networking/ip-sysctl.txt | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) (limited to 'Documentation') diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt index a0f6842368c3..d3aae1f9b4c1 100644 --- a/Documentation/networking/ip-sysctl.txt +++ b/Documentation/networking/ip-sysctl.txt @@ -147,6 +147,11 @@ tcp_available_congestion_control - STRING More congestion control algorithms may be available as modules, but not loaded. +tcp_base_mss - INTEGER + The initial value of search_low to be used by Packetization Layer + Path MTU Discovery (MTU probing). If MTU probing is enabled, + this is the inital MSS used by the connection. + tcp_congestion_control - STRING Set the congestion control algorithm to be used for new connections. The algorithm "reno" is always available, but @@ -243,6 +248,27 @@ tcp_mem - vector of 3 INTEGERs: min, pressure, max Defaults are calculated at boot time from amount of available memory. +tcp_moderate_rcvbuf - BOOLEAN + If set, TCP performs receive buffer autotuning, attempting to + automatically size the buffer (no greater than tcp_rmem[2]) to + match the size required by the path for full throughput. Enabled by + default. + +tcp_mtu_probing - INTEGER + Controls TCP Packetization-Layer Path MTU Discovery. Takes three + values: + 0 - Disabled + 1 - Disabled by default, enabled when an ICMP black hole detected + 2 - Always enabled, use initial MSS of tcp_base_mss. + +tcp_no_metrics_save - BOOLEAN + By default, TCP saves various connection metrics in the route cache + when the connection closes, so that connections established in the + near future can use these to set initial conditions. Usually, this + increases overall performance, but may sometimes cause performance + degredation. If set, TCP will not cache metrics on closing + connections. + tcp_orphan_retries - INTEGER How may times to retry before killing TCP connection, closed by our side. Default value 7 corresponds to ~50sec-16min -- cgit v1.2.3 From 244474b216d319d7317a610fb5d6e194c5ef1460 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Wed, 28 Feb 2007 20:12:35 -0800 Subject: [PATCH] add -mm testing in SubmitChecklist Add -mm testing to SubmitChecklist. Signed-off-by: Randy Dunlap Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/SubmitChecklist | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'Documentation') diff --git a/Documentation/SubmitChecklist b/Documentation/SubmitChecklist index bfbb2718a279..bd23dc0bc0c7 100644 --- a/Documentation/SubmitChecklist +++ b/Documentation/SubmitChecklist @@ -76,3 +76,7 @@ kernel patches. 22: Newly-added code has been compiled with `gcc -W'. This will generate lots of noise, but is good for finding bugs like "warning: comparison between signed and unsigned". + +23: Tested after it has been merged into the -mm patchset to make sure + that it still works with all of the other queued patches and various + changes in the VM, VFS, and other subsystems. -- cgit v1.2.3 From 48dba8ab9b93c3b6b57946bd45ae013402b0b054 Mon Sep 17 00:00:00 2001 From: Vassili Karpov Date: Wed, 28 Feb 2007 20:13:45 -0800 Subject: [PATCH] Documentation: CPU load calculation description Describes how/when the information exported to `/proc/stat' is calculated, and possible problems with this approach. Signed-off-by: Vassili Karpov Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/cpu-load.txt | 113 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 113 insertions(+) create mode 100644 Documentation/cpu-load.txt (limited to 'Documentation') diff --git a/Documentation/cpu-load.txt b/Documentation/cpu-load.txt new file mode 100644 index 000000000000..287224e57cfc --- /dev/null +++ b/Documentation/cpu-load.txt @@ -0,0 +1,113 @@ +CPU load +-------- + +Linux exports various bits of information via `/proc/stat' and +`/proc/uptime' that userland tools, such as top(1), use to calculate +the average time system spent in a particular state, for example: + + $ iostat + Linux 2.6.18.3-exp (linmac) 02/20/2007 + + avg-cpu: %user %nice %system %iowait %steal %idle + 10.01 0.00 2.92 5.44 0.00 81.63 + + ... + +Here the system thinks that over the default sampling period the +system spent 10.01% of the time doing work in user space, 2.92% in the +kernel, and was overall 81.63% of the time idle. + +In most cases the `/proc/stat' information reflects the reality quite +closely, however due to the nature of how/when the kernel collects +this data sometimes it can not be trusted at all. + +So how is this information collected? Whenever timer interrupt is +signalled the kernel looks what kind of task was running at this +moment and increments the counter that corresponds to this tasks +kind/state. The problem with this is that the system could have +switched between various states multiple times between two timer +interrupts yet the counter is incremented only for the last state. + + +Example +------- + +If we imagine the system with one task that periodically burns cycles +in the following manner: + + time line between two timer interrupts +|--------------------------------------| + ^ ^ + |_ something begins working | + |_ something goes to sleep + (only to be awaken quite soon) + +In the above situation the system will be 0% loaded according to the +`/proc/stat' (since the timer interrupt will always happen when the +system is executing the idle handler), but in reality the load is +closer to 99%. + +One can imagine many more situations where this behavior of the kernel +will lead to quite erratic information inside `/proc/stat'. + + +/* gcc -o hog smallhog.c */ +#include +#include +#include +#include +#define HIST 10 + +static volatile sig_atomic_t stop; + +static void sighandler (int signr) +{ + (void) signr; + stop = 1; +} +static unsigned long hog (unsigned long niters) +{ + stop = 0; + while (!stop && --niters); + return niters; +} +int main (void) +{ + int i; + struct itimerval it = { .it_interval = { .tv_sec = 0, .tv_usec = 1 }, + .it_value = { .tv_sec = 0, .tv_usec = 1 } }; + sigset_t set; + unsigned long v[HIST]; + double tmp = 0.0; + unsigned long n; + signal (SIGALRM, &sighandler); + setitimer (ITIMER_REAL, &it, NULL); + + hog (ULONG_MAX); + for (i = 0; i < HIST; ++i) v[i] = ULONG_MAX - hog (ULONG_MAX); + for (i = 0; i < HIST; ++i) tmp += v[i]; + tmp /= HIST; + n = tmp - (tmp / 3.0); + + sigemptyset (&set); + sigaddset (&set, SIGALRM); + + for (;;) { + hog (n); + sigwait (&set, &i); + } + return 0; +} + + +References +---------- + +http://lkml.org/lkml/2007/2/12/6 +Documentation/filesystems/proc.txt (1.8) + + +Thanks +------ + +Con Kolivas, Pavel Machek -- cgit v1.2.3 From 540988eb67f2eee80d0e44bf5f606e388b80500e Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Wed, 28 Feb 2007 00:16:26 +0100 Subject: [ARM] 4238/1: S3C24XX: docs: update suspend and resume Remove some of the explicit use of S3C2410 where it is generic to all the S3C24XX series. Add more info on the CRC code, and add an example of using IRQ_EINT0 to resume from suspend Signed-off-by: Ben Dooks Signed-off-by: Russell King --- Documentation/arm/Samsung-S3C24XX/Suspend.txt | 35 +++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) (limited to 'Documentation') diff --git a/Documentation/arm/Samsung-S3C24XX/Suspend.txt b/Documentation/arm/Samsung-S3C24XX/Suspend.txt index e12bc3284a27..0dab6e32c130 100644 --- a/Documentation/arm/Samsung-S3C24XX/Suspend.txt +++ b/Documentation/arm/Samsung-S3C24XX/Suspend.txt @@ -5,10 +5,10 @@ Introduction ------------ - The S3C2410 supports a low-power suspend mode, where the SDRAM is kept + The S3C24XX supports a low-power suspend mode, where the SDRAM is kept in Self-Refresh mode, and all but the essential peripheral blocks are powered down. For more information on how this works, please look - at the S3C2410 datasheets from Samsung. + at the relevant CPU datasheet from Samsung. Requirements @@ -56,6 +56,27 @@ Machine Support Note, the original method of adding an late_initcall() is wrong, and will end up initialising all compiled machines' pm init! + The following is an example of code used for testing wakeup from + an falling edge on IRQ_EINT0: + + +static irqreturn_t button_irq(int irq, void *pw) +{ + return IRQ_HANDLED; +} + +statuc void __init machine_init(void) +{ + ... + + request_irq(IRQ_EINT0, button_irq, IRQF_TRIGGER_FALLING, + "button-irq-eint0", NULL); + + enable_irq_wake(IRQ_EINT0); + + s3c2410_pm_init(); +} + Debugging --------- @@ -70,6 +91,12 @@ Debugging care should be taken that any external clock sources that the UARTs rely on are still enabled at that point. + 3) If any debugging is placed in the resume path, then it must have the + relevant clocks and peripherals setup before use (ie, bootloader). + + For example, if you transmit a character from the UART, the baud + rate and uart controls must be setup beforehand. + Configuration ------------- @@ -89,6 +116,10 @@ Configuration Allows the entire memory to be checksummed before and after the suspend to see if there has been any corruption of the contents. + Note, the time to calculate the CRC is dependant on the CPU speed + and the size of memory. For an 64Mbyte RAM area on an 200MHz + S3C2410, this can take approximately 4 seconds to complete. + This support requires the CRC32 function to be enabled. -- cgit v1.2.3 From b6209a90eca8c9a464bf9c5b91741fb125185619 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Sat, 3 Mar 2007 17:48:55 +0100 Subject: ide: remove some obsoleted kernel params (v2) Remove * "hdx=serialize" * "idex=noautotune" * "idex=autotune" kernel params, they have been obsoleted for ages. "idex=serialize", "hdx=noautotune" and "hdx=autotune" are still available so there is no funcionality loss caused by this patch. v2: * fix CONFIG_BLK_DEV_4DRIVES=y build broken by version 1 of the patch [ /me wearing brown paper bag ] Signed-off-by: Bartlomiej Zolnierkiewicz --- Documentation/ide.txt | 15 +++------------ drivers/ide/ide.c | 23 ++++++++--------------- drivers/ide/legacy/qd65xx.c | 4 ++-- drivers/ide/pci/opti621.c | 2 +- 4 files changed, 14 insertions(+), 30 deletions(-) (limited to 'Documentation') diff --git a/Documentation/ide.txt b/Documentation/ide.txt index 786c3a766995..82349f5cd3d7 100644 --- a/Documentation/ide.txt +++ b/Documentation/ide.txt @@ -232,7 +232,9 @@ Summary of ide driver parameters for kernel command line "hdx=remap63" : remap the drive: add 63 to all sector numbers (for DM OnTrack) - + + "idex=noautotune" : driver will NOT attempt to tune interface speed + "hdx=autotune" : driver will attempt to tune interface speed to the fastest PIO mode supported, if possible for this drive only. @@ -267,17 +269,6 @@ Summary of ide driver parameters for kernel command line "idex=base,ctl" : specify both base and ctl "idex=base,ctl,irq" : specify base, ctl, and irq number - - "idex=autotune" : driver will attempt to tune interface speed - to the fastest PIO mode supported, - for all drives on this interface. - Not fully supported by all chipset types, - and quite likely to cause trouble with - older/odd IDE drives. - - "idex=noautotune" : driver will NOT attempt to tune interface speed - This is the default for most chipsets, - except the cmd640. "idex=serialize" : do not overlap operations on idex. Please note that you will have to specify this option for diff --git a/drivers/ide/ide.c b/drivers/ide/ide.c index 48a087dfc592..ac1a720f182a 100644 --- a/drivers/ide/ide.c +++ b/drivers/ide/ide.c @@ -1578,7 +1578,7 @@ static int __init ide_setup(char *s) */ if (s[0] == 'h' && s[1] == 'd' && s[2] >= 'a' && s[2] <= max_drive) { const char *hd_words[] = { - "none", "noprobe", "nowerr", "cdrom", "serialize", + "none", "noprobe", "nowerr", "cdrom", "minus5", "autotune", "noautotune", "minus8", "swapdata", "bswap", "noflush", "remap", "remap63", "scsi", NULL }; unit = s[2] - 'a'; @@ -1606,9 +1606,6 @@ static int __init ide_setup(char *s) drive->ready_stat = 0; hwif->noprobe = 0; goto done; - case -5: /* "serialize" */ - printk(" -- USE \"ide%d=serialize\" INSTEAD", hw); - goto do_serialize; case -6: /* "autotune" */ drive->autotune = IDE_TUNE_AUTO; goto obsolete_option; @@ -1669,7 +1666,7 @@ static int __init ide_setup(char *s) * (-8, -9, -10) are reserved to ease the hardcoding. */ static const char *ide_words[] = { - "noprobe", "serialize", "autotune", "noautotune", + "noprobe", "serialize", "minus3", "minus4", "reset", "dma", "ata66", "minus8", "minus9", "minus10", "four", "qd65xx", "ht6560b", "cmd640_vlb", "dtc2278", "umc8672", "ali14xx", NULL }; @@ -1740,12 +1737,17 @@ static int __init ide_setup(char *s) hwif->chipset = mate->chipset = ide_4drives; mate->irq = hwif->irq; memcpy(mate->io_ports, hwif->io_ports, sizeof(hwif->io_ports)); - goto do_serialize; + hwif->mate = mate; + mate->mate = hwif; + hwif->serialized = mate->serialized = 1; + goto obsolete_option; } #endif /* CONFIG_BLK_DEV_4DRIVES */ case -10: /* minus10 */ case -9: /* minus9 */ case -8: /* minus8 */ + case -4: + case -3: goto bad_option; case -7: /* ata66 */ #ifdef CONFIG_BLK_DEV_IDEPCI @@ -1760,16 +1762,7 @@ static int __init ide_setup(char *s) case -5: /* "reset" */ hwif->reset = 1; goto obsolete_option; - case -4: /* "noautotune" */ - hwif->drives[0].autotune = IDE_TUNE_NOAUTO; - hwif->drives[1].autotune = IDE_TUNE_NOAUTO; - goto obsolete_option; - case -3: /* "autotune" */ - hwif->drives[0].autotune = IDE_TUNE_AUTO; - hwif->drives[1].autotune = IDE_TUNE_AUTO; - goto obsolete_option; case -2: /* "serialize" */ - do_serialize: hwif->mate = &ide_hwifs[hw^1]; hwif->mate->mate = hwif; hwif->serialized = hwif->mate->serialized = 1; diff --git a/drivers/ide/legacy/qd65xx.c b/drivers/ide/legacy/qd65xx.c index 584e26c96b46..ab482c85e415 100644 --- a/drivers/ide/legacy/qd65xx.c +++ b/drivers/ide/legacy/qd65xx.c @@ -16,8 +16,8 @@ * Please set local bus speed using kernel parameter idebus * for example, "idebus=33" stands for 33Mhz VLbus * To activate controller support, use "ide0=qd65xx" - * To enable tuning, use "ide0=autotune" - * To enable second channel tuning (qd6580 only), use "ide1=autotune" + * To enable tuning, use "hda=autotune hdb=autotune" + * To enable 2nd channel tuning (qd6580 only), use "hdc=autotune hdd=autotune" */ /* diff --git a/drivers/ide/pci/opti621.c b/drivers/ide/pci/opti621.c index af9aaafa55b6..aede7eee9246 100644 --- a/drivers/ide/pci/opti621.c +++ b/drivers/ide/pci/opti621.c @@ -57,7 +57,7 @@ * There is a 25/33MHz switch in configuration * register, but driver is written for use at any frequency which get * (use idebus=xx to select PCI bus speed). - * Use ide0=autotune for automatical tune of the PIO modes. + * Use hda=autotune and hdb=autotune for automatical tune of the PIO modes. * If you get strange results, do not use this and set PIO manually * by hdparm. * -- cgit v1.2.3 From 849138827c962589ac50496fa7feeb2a2d51b467 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Sat, 3 Mar 2007 17:48:55 +0100 Subject: ide: make legacy IDE VLB modules check for the "probe" kernel params (v2) Legacy IDE VLB host drivers didn't check for "probe" options when compiled as modules, which was obviously wrong as we don't want module to poke at random I/O ports by simply loading it. Fix it by adding "probe" module param to legacy IDE VLB host drivers. v2: * don't obsolete old "ide0=dtc2278/ht6560b/qd65xx/ali14xx/umc8672" IDE driver options yet (per Alan Cox's request) and enhance documentation Signed-off-by: Bartlomiej Zolnierkiewicz --- Documentation/ide.txt | 14 +++++++++----- drivers/ide/Kconfig | 10 +++++----- drivers/ide/ide.c | 10 +++++----- drivers/ide/legacy/ali14xx.c | 9 +++++++++ drivers/ide/legacy/dtc2278.c | 12 ++++++++++-- drivers/ide/legacy/ht6560b.c | 8 ++++++++ drivers/ide/legacy/qd65xx.c | 8 ++++++++ drivers/ide/legacy/umc8672.c | 15 ++++++++++++--- 8 files changed, 66 insertions(+), 20 deletions(-) (limited to 'Documentation') diff --git a/Documentation/ide.txt b/Documentation/ide.txt index 82349f5cd3d7..3bb9f9c98611 100644 --- a/Documentation/ide.txt +++ b/Documentation/ide.txt @@ -294,13 +294,8 @@ The following are valid ONLY on ide0, which usually corresponds to the first ATA interface found on the particular host, and the defaults for the base,ctl ports must not be altered. - "ide0=dtc2278" : probe/support DTC2278 interface - "ide0=ht6560b" : probe/support HT6560B interface "ide0=cmd640_vlb" : *REQUIRED* for VLB cards with the CMD640 chip (not for PCI -- automatically detected) - "ide0=qd65xx" : probe/support qd65xx interface - "ide0=ali14xx" : probe/support ali14xx chipsets (ALI M1439/M1443/M1445) - "ide0=umc8672" : probe/support umc8672 chipsets "ide=doubler" : probe/support IDE doublers on Amiga @@ -308,6 +303,15 @@ There may be more options than shown -- use the source, Luke! Everything else is rejected with a "BAD OPTION" message. +For legacy IDE VLB host drivers (ali14xx/dtc2278/ht6560b/qd65xx/umc8672) +you need to explicitly enable probing by using "probe" kernel parameter, +i.e. to enable probing for ALI M14xx chipsets (ali14xx host driver) use: + +* "ali14xx.probe" boot option when ali14xx driver is built-in the kernel + +* "probe" module parameter when ali14xx driver is compiled as module + ("modprobe ali14xx probe") + ================================================================================ IDE ATAPI streaming tape driver diff --git a/drivers/ide/Kconfig b/drivers/ide/Kconfig index 49234e32fd16..5d134bb75ba1 100644 --- a/drivers/ide/Kconfig +++ b/drivers/ide/Kconfig @@ -1023,7 +1023,7 @@ config BLK_DEV_4DRIVES config BLK_DEV_ALI14XX tristate "ALI M14xx support" help - This driver is enabled at runtime using the "ide0=ali14xx" kernel + This driver is enabled at runtime using the "ali14xx.probe" kernel boot parameter. It enables support for the secondary IDE interface of the ALI M1439/1443/1445/1487/1489 chipsets, and permits faster I/O speeds to be set as well. See the files @@ -1033,7 +1033,7 @@ config BLK_DEV_ALI14XX config BLK_DEV_DTC2278 tristate "DTC-2278 support" help - This driver is enabled at runtime using the "ide0=dtc2278" kernel + This driver is enabled at runtime using the "dtc2278.probe" kernel boot parameter. It enables support for the secondary IDE interface of the DTC-2278 card, and permits faster I/O speeds to be set as well. See the and @@ -1042,7 +1042,7 @@ config BLK_DEV_DTC2278 config BLK_DEV_HT6560B tristate "Holtek HT6560B support" help - This driver is enabled at runtime using the "ide0=ht6560b" kernel + This driver is enabled at runtime using the "ht6560b.probe" kernel boot parameter. It enables support for the secondary IDE interface of the Holtek card, and permits faster I/O speeds to be set as well. See the and @@ -1051,7 +1051,7 @@ config BLK_DEV_HT6560B config BLK_DEV_QD65XX tristate "QDI QD65xx support" help - This driver is enabled at runtime using the "ide0=qd65xx" kernel + This driver is enabled at runtime using the "qd65xx.probe" kernel boot parameter. It permits faster I/O speeds to be set. See the and for more info. @@ -1059,7 +1059,7 @@ config BLK_DEV_QD65XX config BLK_DEV_UMC8672 tristate "UMC-8672 support" help - This driver is enabled at runtime using the "ide0=umc8672" kernel + This driver is enabled at runtime using the "umc8672.probe" kernel boot parameter. It enables support for the secondary IDE interface of the UMC-8672, and permits faster I/O speeds to be set as well. See the files and diff --git a/drivers/ide/ide.c b/drivers/ide/ide.c index ac1a720f182a..dfbd74458522 100644 --- a/drivers/ide/ide.c +++ b/drivers/ide/ide.c @@ -1484,23 +1484,23 @@ static int __init match_parm (char *s, const char *keywords[], int vals[], int m } #ifdef CONFIG_BLK_DEV_ALI14XX -static int __initdata probe_ali14xx; +extern int probe_ali14xx; extern int ali14xx_init(void); #endif #ifdef CONFIG_BLK_DEV_UMC8672 -static int __initdata probe_umc8672; +extern int probe_umc8672; extern int umc8672_init(void); #endif #ifdef CONFIG_BLK_DEV_DTC2278 -static int __initdata probe_dtc2278; +extern int probe_dtc2278; extern int dtc2278_init(void); #endif #ifdef CONFIG_BLK_DEV_HT6560B -static int __initdata probe_ht6560b; +extern int probe_ht6560b; extern int ht6560b_init(void); #endif #ifdef CONFIG_BLK_DEV_QD65XX -static int __initdata probe_qd65xx; +extern int probe_qd65xx; extern int qd65xx_init(void); #endif diff --git a/drivers/ide/legacy/ali14xx.c b/drivers/ide/legacy/ali14xx.c index f9f5b8d54d6d..91961aa03047 100644 --- a/drivers/ide/legacy/ali14xx.c +++ b/drivers/ide/legacy/ali14xx.c @@ -228,9 +228,17 @@ static int __init ali14xx_probe(void) return 0; } +int probe_ali14xx = 0; + +module_param_named(probe, probe_ali14xx, bool, 0); +MODULE_PARM_DESC(probe, "probe for ALI M14xx chipsets"); + /* Can be called directly from ide.c. */ int __init ali14xx_init(void) { + if (probe_ali14xx == 0) + goto out; + /* auto-detect IDE controller port */ if (findPort()) { if (ali14xx_probe()) @@ -238,6 +246,7 @@ int __init ali14xx_init(void) return 0; } printk(KERN_ERR "ali14xx: not found.\n"); +out: return -ENODEV; } diff --git a/drivers/ide/legacy/dtc2278.c b/drivers/ide/legacy/dtc2278.c index f2903b6e43c0..0219ffa64db6 100644 --- a/drivers/ide/legacy/dtc2278.c +++ b/drivers/ide/legacy/dtc2278.c @@ -92,7 +92,7 @@ static void tune_dtc2278 (ide_drive_t *drive, u8 pio) HWIF(drive)->drives[!drive->select.b.unit].io_32bit = 1; } -static int __init probe_dtc2278(void) +static int __init dtc2278_probe(void) { unsigned long flags; ide_hwif_t *hwif, *mate; @@ -143,10 +143,18 @@ static int __init probe_dtc2278(void) return 0; } +int probe_dtc2278 = 0; + +module_param_named(probe, probe_dtc2278, bool, 0); +MODULE_PARM_DESC(probe, "probe for DTC2278xx chipsets"); + /* Can be called directly from ide.c. */ int __init dtc2278_init(void) { - if (probe_dtc2278()) { + if (probe_dtc2278 == 0) + return -ENODEV; + + if (dtc2278_probe()) { printk(KERN_ERR "dtc2278: ide interfaces already in use!\n"); return -EBUSY; } diff --git a/drivers/ide/legacy/ht6560b.c b/drivers/ide/legacy/ht6560b.c index 02d5e20791e3..a2832643c522 100644 --- a/drivers/ide/legacy/ht6560b.c +++ b/drivers/ide/legacy/ht6560b.c @@ -301,12 +301,20 @@ static void tune_ht6560b (ide_drive_t *drive, u8 pio) #endif } +int probe_ht6560b = 0; + +module_param_named(probe, probe_ht6560b, bool, 0); +MODULE_PARM_DESC(probe, "probe for HT6560B chipset"); + /* Can be called directly from ide.c. */ int __init ht6560b_init(void) { ide_hwif_t *hwif, *mate; int t; + if (probe_ht6560b == 0) + return -ENODEV; + hwif = &ide_hwifs[0]; mate = &ide_hwifs[1]; diff --git a/drivers/ide/legacy/qd65xx.c b/drivers/ide/legacy/qd65xx.c index ab482c85e415..2fb8f50f1293 100644 --- a/drivers/ide/legacy/qd65xx.c +++ b/drivers/ide/legacy/qd65xx.c @@ -488,9 +488,17 @@ static int __init qd_probe(int base) return 1; } +int probe_qd65xx = 0; + +module_param_named(probe, probe_qd65xx, bool, 0); +MODULE_PARM_DESC(probe, "probe for QD65xx chipsets"); + /* Can be called directly from ide.c. */ int __init qd65xx_init(void) { + if (probe_qd65xx == 0) + return -ENODEV; + if (qd_probe(0x30)) qd_probe(0xb0); if (ide_hwifs[0].chipset != ide_qd65xx && diff --git a/drivers/ide/legacy/umc8672.c b/drivers/ide/legacy/umc8672.c index 6e2c58c5f6a2..ca7974455578 100644 --- a/drivers/ide/legacy/umc8672.c +++ b/drivers/ide/legacy/umc8672.c @@ -165,12 +165,21 @@ static int __init umc8672_probe(void) return 0; } +int probe_umc8672 = 0; + +module_param_named(probe, probe_umc8672, bool, 0); +MODULE_PARM_DESC(probe, "probe for UMC8672 chipset"); + /* Can be called directly from ide.c. */ int __init umc8672_init(void) { - if (umc8672_probe()) - return -ENODEV; - return 0; + if (probe_umc8672 == 0) + goto out; + + if (umc8672_probe() == 0) + return 0;; +out: + return -ENODEV;; } #ifdef MODULE -- cgit v1.2.3 From e523d93c8487667552dd29ff756d6ea6bce30851 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Wed, 28 Feb 2007 18:30:01 +0900 Subject: doc: Add SH to vdso and earlyprintk in kernel-parameters.txt SH supports both of these options, add it to the docs. Signed-off-by: Paul Mundt --- Documentation/kernel-parameters.txt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'Documentation') diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 03eb5ed503f7..757dd994b879 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -79,6 +79,7 @@ parameter is applicable: Documentation/scsi/. SELINUX SELinux support is enabled. SERIAL Serial support is enabled. + SH SuperH architecture is enabled. SMP The kernel is an SMP kernel. SPARC Sparc architecture is enabled. SWSUSP Software suspend is enabled. @@ -485,7 +486,7 @@ and is between 256 and 4096 characters. It is defined in the file dtc3181e= [HW,SCSI] - earlyprintk= [IA-32,X86-64] + earlyprintk= [IA-32,X86-64,SH] earlyprintk=vga earlyprintk=serial[,ttySn[,baudrate]] @@ -1768,7 +1769,7 @@ and is between 256 and 4096 characters. It is defined in the file usbhid.mousepoll= [USBHID] The interval which mice are to be polled at. - vdso= [IA-32] + vdso= [IA-32,SH] vdso=1: enable VDSO (default) vdso=0: disable VDSO mapping -- cgit v1.2.3 From cc2cccaec88d422e628e6b588078e1dbdc241896 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 5 Mar 2007 00:30:27 -0800 Subject: [PATCH] schedule wext/rtnl for removal Since wext is being replaced as fast as we can (it'll probably stick around for legacy drivers though) and the wext/netlink stuff was never really used, this schedules it for removal. The removal schedule is tight but there are no users of the code, the main user of the wext user interface are the wireless-tools, they only have an alpha version using the netlink interface and even that is incomplete. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville Cc: Jeff Garzik Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/feature-removal-schedule.txt | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'Documentation') diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt index 6a451f47d40f..c3b1430cf603 100644 --- a/Documentation/feature-removal-schedule.txt +++ b/Documentation/feature-removal-schedule.txt @@ -304,3 +304,15 @@ Why: The code says it was obsolete when it was written in 2001. Who: Richard Purdie --------------------------- + +What: Wireless extensions over netlink (CONFIG_NET_WIRELESS_RTNETLINK) +When: with the merge of wireless-dev, 2.6.22 or later +Why: The option/code is + * not enabled on most kernels + * not required by any userspace tools (except an experimental one, + and even there only for some parts, others use ioctl) + * pointless since wext is no longer evolving and the ioctl + interface needs to be kept +Who: Johannes Berg + +--------------------------- -- cgit v1.2.3 From f9c99463b0cd05603d125c915e2886d55a686b82 Mon Sep 17 00:00:00 2001 From: Roland Kletzing Date: Mon, 5 Mar 2007 00:30:54 -0800 Subject: [PATCH] Documentation for io-accounting / reporting via procfs Add some documentation for the new and very useful io-accounting feature. It's being added to Documentation/filesystems/proc.txt Signed-off-by: Roland Kletzing Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/filesystems/proc.txt | 105 +++++++++++++++++++++++++++++++++++++ 1 file changed, 105 insertions(+) (limited to 'Documentation') diff --git a/Documentation/filesystems/proc.txt b/Documentation/filesystems/proc.txt index 72af5de1effb..5484ab5efd4f 100644 --- a/Documentation/filesystems/proc.txt +++ b/Documentation/filesystems/proc.txt @@ -41,6 +41,7 @@ Table of Contents 2.11 /proc/sys/fs/mqueue - POSIX message queues filesystem 2.12 /proc//oom_adj - Adjust the oom-killer score 2.13 /proc//oom_score - Display current oom-killer score + 2.14 /proc//io - Display the IO accounting fields ------------------------------------------------------------------------------ Preface @@ -1990,3 +1991,107 @@ need to recompile the kernel, or even to reboot the system. The files in the command to write value into these files, thereby changing the default settings of the kernel. ------------------------------------------------------------------------------ + +2.14 /proc//io - Display the IO accounting fields +------------------------------------------------------- + +This file contains IO statistics for each running process + +Example +------- + +test:/tmp # dd if=/dev/zero of=/tmp/test.dat & +[1] 3828 + +test:/tmp # cat /proc/3828/io +rchar: 323934931 +wchar: 323929600 +syscr: 632687 +syscw: 632675 +read_bytes: 0 +write_bytes: 323932160 +cancelled_write_bytes: 0 + + +Description +----------- + +rchar +----- + +I/O counter: chars read +The number of bytes which this task has caused to be read from storage. This +is simply the sum of bytes which this process passed to read() and pread(). +It includes things like tty IO and it is unaffected by whether or not actual +physical disk IO was required (the read might have been satisfied from +pagecache) + + +wchar +----- + +I/O counter: chars written +The number of bytes which this task has caused, or shall cause to be written +to disk. Similar caveats apply here as with rchar. + + +syscr +----- + +I/O counter: read syscalls +Attempt to count the number of read I/O operations, i.e. syscalls like read() +and pread(). + + +syscw +----- + +I/O counter: write syscalls +Attempt to count the number of write I/O operations, i.e. syscalls like +write() and pwrite(). + + +read_bytes +---------- + +I/O counter: bytes read +Attempt to count the number of bytes which this process really did cause to +be fetched from the storage layer. Done at the submit_bio() level, so it is +accurate for block-backed filesystems. + + +write_bytes +----------- + +I/O counter: bytes written +Attempt to count the number of bytes which this process caused to be sent to +the storage layer. This is done at page-dirtying time. + + +cancelled_write_bytes +--------------------- + +The big inaccuracy here is truncate. If a process writes 1MB to a file and +then deletes the file, it will in fact perform no writeout. But it will have +been accounted as having caused 1MB of write. +In other words: The number of bytes which this process caused to not happen, +by truncating pagecache. A task can cause "negative" IO too. If this task +truncates some dirty pagecache, some IO which another task has been accounted +for (in it's write_bytes) will not be happening. We _could_ just subtract that +from the truncating task's write_bytes, but there is information loss in doing +that. + + +Note +---- + +At its current implementation state, this is a bit racy on 32-bit machines: if +process A reads process B's /proc/pid/io while process B is updating one of +those 64-bit counters, process A could see an intermediate result. + + +More information about this can be found within the taskstats documentation in +Documentation/accounting. + +------------------------------------------------------------------------------ -- cgit v1.2.3 From 3fc24d850708b8dfd3472b25eac0c32dd7708925 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 16 Feb 2007 13:27:18 +0100 Subject: [ALSA] hda-codec - Define pin configs for MacBooks Define pin configs for MacBook and MacBook Pro with STAC92xx codecs. The latter is detected automatically by checking codec SSID now. Also, fixed the documentation regarding available modeliof sigmatel codec chips. Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- Documentation/sound/alsa/ALSA-Configuration.txt | 8 +++++- sound/pci/hda/patch_sigmatel.c | 33 ++++++++++++++++++++++--- 2 files changed, 37 insertions(+), 4 deletions(-) (limited to 'Documentation') diff --git a/Documentation/sound/alsa/ALSA-Configuration.txt b/Documentation/sound/alsa/ALSA-Configuration.txt index c30ff1bb2d10..e127751d17c2 100644 --- a/Documentation/sound/alsa/ALSA-Configuration.txt +++ b/Documentation/sound/alsa/ALSA-Configuration.txt @@ -895,10 +895,16 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed. can be adjusted. Appearing only when compiled with $CONFIG_SND_DEBUG=y - STAC9200/9205/9220/9221/9254 + STAC9200/9205/9254 + ref Reference board + + STAC9220/9221 ref Reference board 3stack D945 3stack 5stack D945 5stack + SPDIF + macmini Intel Mac Mini + macbook Intel Mac Book + macbook-pro Intel Mac Book Pro STAC9202/9250/9251 ref Reference board, base config diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 54fdf3448133..4c7b03996be9 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -59,6 +59,8 @@ enum { STAC_D945GTP3, STAC_D945GTP5, STAC_MACMINI, + STAC_MACBOOK, + STAC_MACBOOK_PRO, STAC_922X_MODELS }; @@ -521,11 +523,25 @@ static unsigned int d945gtp5_pin_configs[10] = { 0x02a19320, 0x40000100, }; +static unsigned int macbook_pin_configs[10] = { + 0x0321e230, 0x03a1e020, 0x400000fd, 0x9017e110, + 0x400000fe, 0x0381e021, 0x1345e240, 0x13c5e22e, + 0x400000fc, 0x400000fb, +}; + +static unsigned int macbook_pro_pin_configs[10] = { + 0x0221401f, 0x90a70120, 0x01813024, 0x01014010, + 0x400000fd, 0x01016011, 0x1345e240, 0x13c5e22e, + 0x400000fc, 0x400000fb, +}; + static unsigned int *stac922x_brd_tbl[STAC_922X_MODELS] = { [STAC_D945_REF] = ref922x_pin_configs, [STAC_D945GTP3] = d945gtp3_pin_configs, [STAC_D945GTP5] = d945gtp5_pin_configs, [STAC_MACMINI] = d945gtp5_pin_configs, + [STAC_MACBOOK] = macbook_pin_configs, + [STAC_MACBOOK_PRO] = macbook_pro_pin_configs, }; static const char *stac922x_models[STAC_922X_MODELS] = { @@ -533,6 +549,8 @@ static const char *stac922x_models[STAC_922X_MODELS] = { [STAC_D945GTP5] = "5stack", [STAC_D945GTP3] = "3stack", [STAC_MACMINI] = "macmini", + [STAC_MACBOOK] = "macbook", + [STAC_MACBOOK_PRO] = "macbook-pro", }; static struct snd_pci_quirk stac922x_cfg_tbl[] = { @@ -1866,6 +1884,18 @@ static int patch_stac922x(struct hda_codec *codec) spec->board_config = snd_hda_check_board_config(codec, STAC_922X_MODELS, stac922x_models, stac922x_cfg_tbl); + if (spec->board_config == STAC_MACMINI) { + spec->gpio_mute = 1; + /* Intel Macs have all same PCI SSID, so we need to check + * codec SSID to distinguish the exact models + */ + switch (codec->subsystem_id) { + case 0x106b1e00: + spec->board_config = STAC_MACBOOK_PRO; + break; + } + } + again: if (spec->board_config < 0) { snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC922x, " @@ -1906,9 +1936,6 @@ static int patch_stac922x(struct hda_codec *codec) return err; } - if (spec->board_config == STAC_MACMINI) - spec->gpio_mute = 1; - codec->patch_ops = stac92xx_patch_ops; return 0; -- cgit v1.2.3 From 2f24d159d5ac418c946e0d38ada46345753688b1 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 15 Feb 2007 18:56:43 +0100 Subject: [ALSA] cmipci - Allow to disable integrated FM port The driver didn't allow to disable the integrated FM port (if available), and this annoyed people who don't want FM port. Now fm_port=0 disables the FM port unconditionally. fm_port=1 is used for enabling the integrated FM port (as default). Also fixed the documentation about this option. Fix ALSA bug#2491. Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- Documentation/sound/alsa/ALSA-Configuration.txt | 4 +++- sound/pci/cmipci.c | 18 +++++++++++------- 2 files changed, 14 insertions(+), 8 deletions(-) (limited to 'Documentation') diff --git a/Documentation/sound/alsa/ALSA-Configuration.txt b/Documentation/sound/alsa/ALSA-Configuration.txt index e127751d17c2..db398a6441c1 100644 --- a/Documentation/sound/alsa/ALSA-Configuration.txt +++ b/Documentation/sound/alsa/ALSA-Configuration.txt @@ -370,7 +370,9 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed. mpu_port - 0x300,0x310,0x320,0x330 = legacy port, 1 = integrated PCI port, 0 = disable (default) - fm_port - 0x388 (default), 0 = disable (default) + fm_port - 0x388 = legacy port, + 1 = integrated PCI port (default), + 0 = disable soft_ac3 - Software-conversion of raw SPDIF packets (model 033 only) (default = 1) joystick_port - Joystick port address (0 = disable, 1 = auto-detect) diff --git a/sound/pci/cmipci.c b/sound/pci/cmipci.c index 70face7e1048..7d3c5ee0005c 100644 --- a/sound/pci/cmipci.c +++ b/sound/pci/cmipci.c @@ -57,7 +57,7 @@ static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable switches */ static long mpu_port[SNDRV_CARDS]; -static long fm_port[SNDRV_CARDS]; +static long fm_port[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)]=1}; static int soft_ac3[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)]=1}; #ifdef SUPPORT_JOYSTICK static int joystick_port[SNDRV_CARDS]; @@ -2779,6 +2779,9 @@ static int __devinit snd_cmipci_create_fm(struct cmipci *cm, long fm_port) struct snd_opl3 *opl3; int err; + if (!fm_port) + goto disable_fm; + /* first try FM regs in PCI port range */ iosynth = cm->iobase + CM_REG_FM_PCI; err = snd_opl3_create(cm->card, iosynth, iosynth + 2, @@ -2793,7 +2796,7 @@ static int __devinit snd_cmipci_create_fm(struct cmipci *cm, long fm_port) case 0x3C8: val |= CM_FMSEL_3C8; break; case 0x388: val |= CM_FMSEL_388; break; default: - return 0; + goto disable_fm; } snd_cmipci_write(cm, CM_REG_LEGACY_CTRL, val); /* enable FM */ @@ -2803,11 +2806,7 @@ static int __devinit snd_cmipci_create_fm(struct cmipci *cm, long fm_port) OPL3_HW_OPL3, 0, &opl3) < 0) { printk(KERN_ERR "cmipci: no OPL device at %#lx, " "skipping...\n", iosynth); - /* disable FM */ - snd_cmipci_write(cm, CM_REG_LEGACY_CTRL, - val & ~CM_FMSEL_MASK); - snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL, CM_FM_EN); - return 0; + goto disable_fm; } } if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) { @@ -2815,6 +2814,11 @@ static int __devinit snd_cmipci_create_fm(struct cmipci *cm, long fm_port) return err; } return 0; + + disable_fm: + snd_cmipci_clear_bit(cm, CM_REG_LEGACY_CTRL, CM_FMSEL_MASK); + snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL, CM_FM_EN); + return 0; } static int __devinit snd_cmipci_create(struct snd_card *card, struct pci_dev *pci, -- cgit v1.2.3 From 42a7fc4a6598221f1a547a76cdd45a8ab4d90e93 Mon Sep 17 00:00:00 2001 From: Greg Banks Date: Tue, 6 Mar 2007 01:42:23 -0800 Subject: [PATCH] knfsd: provide sunrpc pool_mode module option Provide a module param "pool_mode" for sunrpc.ko which allows a sysadmin to choose the mode for mapping NFS thread service pools to CPUs. Values are: auto choose a mapping mode heuristically global (default, same as the pre-2.6.19 code) a single global pool percpu one pool per CPU pernode one pool per NUMA node Note that since 2.6.19 the hardcoded behaviour has been "auto", this patch makes the default "global". The pool mode can be changed after boot/modprobe using /sys, if the NFS and lockd services have been shut down. A useful side effect of this change is to fix a small memory leak when unloading the module. Signed-off-by: Greg Banks Signed-off-by: Neil Brown Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/kernel-parameters.txt | 16 +++++ net/sunrpc/svc.c | 133 ++++++++++++++++++++++++++++++------ 2 files changed, 130 insertions(+), 19 deletions(-) (limited to 'Documentation') diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 03eb5ed503f7..6e92ba61f7c0 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -1685,6 +1685,22 @@ and is between 256 and 4096 characters. It is defined in the file stifb= [HW] Format: bpp:[:[:...]] + sunrpc.pool_mode= + [NFS] + Control how the NFS server code allocates CPUs to + service thread pools. Depending on how many NICs + you have and where their interrupts are bound, this + option will affect which CPUs will do NFS serving. + Note: this parameter cannot be changed while the + NFS server is running. + + auto the server chooses an appropriate mode + automatically using heuristics + global a single global pool contains all CPUs + percpu one pool for each CPU + pernode one pool for each NUMA node (equivalent + to global on non-NUMA machines) + swiotlb= [IA-64] Number of I/O TLB slabs switches= [HW,M68k] diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c index f960b138236f..b4db53ff1435 100644 --- a/net/sunrpc/svc.c +++ b/net/sunrpc/svc.c @@ -27,22 +27,26 @@ #define RPCDBG_FACILITY RPCDBG_SVCDSP +#define svc_serv_is_pooled(serv) ((serv)->sv_function) + /* * Mode for mapping cpus to pools. */ enum { - SVC_POOL_NONE = -1, /* uninitialised, choose one of the others */ + SVC_POOL_AUTO = -1, /* choose one of the others */ SVC_POOL_GLOBAL, /* no mapping, just a single global pool * (legacy & UP mode) */ SVC_POOL_PERCPU, /* one pool per cpu */ SVC_POOL_PERNODE /* one pool per numa node */ }; +#define SVC_POOL_DEFAULT SVC_POOL_GLOBAL /* * Structure for mapping cpus to pools and vice versa. * Setup once during sunrpc initialisation. */ static struct svc_pool_map { + int count; /* How many svc_servs use us */ int mode; /* Note: int not enum to avoid * warnings about "enumeration value * not handled in switch" */ @@ -50,9 +54,63 @@ static struct svc_pool_map { unsigned int *pool_to; /* maps pool id to cpu or node */ unsigned int *to_pool; /* maps cpu or node to pool id */ } svc_pool_map = { - .mode = SVC_POOL_NONE + .count = 0, + .mode = SVC_POOL_DEFAULT }; +static DEFINE_MUTEX(svc_pool_map_mutex);/* protects svc_pool_map.count only */ + +static int +param_set_pool_mode(const char *val, struct kernel_param *kp) +{ + int *ip = (int *)kp->arg; + struct svc_pool_map *m = &svc_pool_map; + int err; + + mutex_lock(&svc_pool_map_mutex); + + err = -EBUSY; + if (m->count) + goto out; + + err = 0; + if (!strncmp(val, "auto", 4)) + *ip = SVC_POOL_AUTO; + else if (!strncmp(val, "global", 6)) + *ip = SVC_POOL_GLOBAL; + else if (!strncmp(val, "percpu", 6)) + *ip = SVC_POOL_PERCPU; + else if (!strncmp(val, "pernode", 7)) + *ip = SVC_POOL_PERNODE; + else + err = -EINVAL; + +out: + mutex_unlock(&svc_pool_map_mutex); + return err; +} + +static int +param_get_pool_mode(char *buf, struct kernel_param *kp) +{ + int *ip = (int *)kp->arg; + + switch (*ip) + { + case SVC_POOL_AUTO: + return strlcpy(buf, "auto", 20); + case SVC_POOL_GLOBAL: + return strlcpy(buf, "global", 20); + case SVC_POOL_PERCPU: + return strlcpy(buf, "percpu", 20); + case SVC_POOL_PERNODE: + return strlcpy(buf, "pernode", 20); + default: + return sprintf(buf, "%d", *ip); + } +} +module_param_call(pool_mode, param_set_pool_mode, param_get_pool_mode, + &svc_pool_map.mode, 0644); /* * Detect best pool mapping mode heuristically, @@ -166,18 +224,25 @@ svc_pool_map_init_pernode(struct svc_pool_map *m) /* - * Build the global map of cpus to pools and vice versa. + * Add a reference to the global map of cpus to pools (and + * vice versa). Initialise the map if we're the first user. + * Returns the number of pools. */ static unsigned int -svc_pool_map_init(void) +svc_pool_map_get(void) { struct svc_pool_map *m = &svc_pool_map; int npools = -1; - if (m->mode != SVC_POOL_NONE) + mutex_lock(&svc_pool_map_mutex); + + if (m->count++) { + mutex_unlock(&svc_pool_map_mutex); return m->npools; + } - m->mode = svc_pool_map_choose_mode(); + if (m->mode == SVC_POOL_AUTO) + m->mode = svc_pool_map_choose_mode(); switch (m->mode) { case SVC_POOL_PERCPU: @@ -195,9 +260,36 @@ svc_pool_map_init(void) } m->npools = npools; + mutex_unlock(&svc_pool_map_mutex); return m->npools; } + +/* + * Drop a reference to the global map of cpus to pools. + * When the last reference is dropped, the map data is + * freed; this allows the sysadmin to change the pool + * mode using the pool_mode module option without + * rebooting or re-loading sunrpc.ko. + */ +static void +svc_pool_map_put(void) +{ + struct svc_pool_map *m = &svc_pool_map; + + mutex_lock(&svc_pool_map_mutex); + + if (!--m->count) { + m->mode = SVC_POOL_DEFAULT; + kfree(m->to_pool); + kfree(m->pool_to); + m->npools = 0; + } + + mutex_unlock(&svc_pool_map_mutex); +} + + /* * Set the current thread's cpus_allowed mask so that it * will only run on cpus in the given pool. @@ -212,10 +304,9 @@ svc_pool_map_set_cpumask(unsigned int pidx, cpumask_t *oldmask) /* * The caller checks for sv_nrpools > 1, which - * implies that we've been initialized and the - * map mode is not NONE. + * implies that we've been initialized. */ - BUG_ON(m->mode == SVC_POOL_NONE); + BUG_ON(m->count == 0); switch (m->mode) { @@ -246,18 +337,19 @@ svc_pool_for_cpu(struct svc_serv *serv, int cpu) unsigned int pidx = 0; /* - * SVC_POOL_NONE happens in a pure client when + * An uninitialised map happens in a pure client when * lockd is brought up, so silently treat it the * same as SVC_POOL_GLOBAL. */ - - switch (m->mode) { - case SVC_POOL_PERCPU: - pidx = m->to_pool[cpu]; - break; - case SVC_POOL_PERNODE: - pidx = m->to_pool[cpu_to_node(cpu)]; - break; + if (svc_serv_is_pooled(serv)) { + switch (m->mode) { + case SVC_POOL_PERCPU: + pidx = m->to_pool[cpu]; + break; + case SVC_POOL_PERNODE: + pidx = m->to_pool[cpu_to_node(cpu)]; + break; + } } return &serv->sv_pools[pidx % serv->sv_nrpools]; } @@ -347,7 +439,7 @@ svc_create_pooled(struct svc_program *prog, unsigned int bufsize, svc_thread_fn func, int sig, struct module *mod) { struct svc_serv *serv; - unsigned int npools = svc_pool_map_init(); + unsigned int npools = svc_pool_map_get(); serv = __svc_create(prog, bufsize, npools, shutdown); @@ -397,6 +489,9 @@ svc_destroy(struct svc_serv *serv) cache_clean_deferred(serv); + if (svc_serv_is_pooled(serv)) + svc_pool_map_put(); + /* Unregister service with the portmapper */ svc_register(serv, 0, 0); kfree(serv->sv_pools); -- cgit v1.2.3 From 03d926f82800f32642b32ba547c7a002a371a78f Mon Sep 17 00:00:00 2001 From: Bernhard Walle Date: Tue, 6 Mar 2007 02:29:44 -0800 Subject: ACPI: Add kernel-parameters hint that acpi=off doesn't work on IA64. Signed-off-by: Bernhard Walle Signed-off-by: Andrew Morton Signed-off-by: Len Brown --- Documentation/kernel-parameters.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 6e92ba61f7c0..7702e3d397bb 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -125,7 +125,8 @@ and is between 256 and 4096 characters. It is defined in the file See header of drivers/scsi/53c7xx.c. See also Documentation/scsi/ncr53c7xx.txt. - acpi= [HW,ACPI] Advanced Configuration and Power Interface + acpi= [HW,ACPI,X86-64,i386] + Advanced Configuration and Power Interface Format: { force | off | ht | strict | noirq } force -- enable ACPI if default was off off -- disable ACPI if default was on -- cgit v1.2.3 From d1bff9ed3c05859fe4a8d00e51f331f5d45350ed Mon Sep 17 00:00:00 2001 From: Stuart Yoder Date: Mon, 19 Feb 2007 11:25:05 -0600 Subject: [POWERPC] Remove interrupt-controller as a property under /chosen Remove interrupt-controller as a valid property under /chosen in the documentation. There is a consensus that an interrupt-controller property does not belong under /chosen. /chosen is specifically for dynamic properties set at runtime. Signed-off-by: Stuart Yoder Signed-off-by: Paul Mackerras --- Documentation/powerpc/booting-without-of.txt | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'Documentation') diff --git a/Documentation/powerpc/booting-without-of.txt b/Documentation/powerpc/booting-without-of.txt index b41397d6430a..eaa0c3285ac9 100644 --- a/Documentation/powerpc/booting-without-of.txt +++ b/Documentation/powerpc/booting-without-of.txt @@ -832,8 +832,7 @@ address which can extend beyond that limit. This node is a bit "special". Normally, that's where open firmware puts some variable environment information, like the arguments, or - phandle pointers to nodes like the main interrupt controller, or the - default input/output devices. + the default input/output devices. This specification makes a few of these mandatory, but also defines some linux-specific properties that would be normally constructed by @@ -853,14 +852,14 @@ address which can extend beyond that limit. that the kernel tries to find out the default console and has knowledge of various types like 8250 serial ports. You may want to extend this function to add your own. - - interrupt-controller : This is one cell containing a phandle - value that matches the "linux,phandle" property of your main - interrupt controller node. May be used for interrupt routing. - Note that u-boot creates and fills in the chosen node for platforms that use it. + (Note: a practice that is now obsolete was to include a property + under /chosen called interrupt-controller which had a phandle value + that pointed to the main interrupt controller) + f) the /soc node This node is used to represent a system-on-a-chip (SOC) and must be -- cgit v1.2.3 From a55028ff74356895a50359dd9fb74d523b66723f Mon Sep 17 00:00:00 2001 From: Dave Jones Date: Thu, 8 Mar 2007 19:45:26 -0500 Subject: [PATCH] update 'getting sparse' info. - point to the sparse webpage - use git:// instead of rsync:// Signed-off-by: Dave Jones Signed-off-by: Linus Torvalds --- Documentation/sparse.txt | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'Documentation') diff --git a/Documentation/sparse.txt b/Documentation/sparse.txt index f9c99c9a54f9..1a3bdc27d95e 100644 --- a/Documentation/sparse.txt +++ b/Documentation/sparse.txt @@ -45,11 +45,15 @@ special. Getting sparse ~~~~~~~~~~~~~~ -With git, you can just get it from +You can get latest released versions from the Sparse homepage at +http://www.kernel.org/pub/linux/kernel/people/josh/sparse/ - rsync://rsync.kernel.org/pub/scm/devel/sparse/sparse.git +Alternatively, you can get snapshots of the latest development version +of sparse using git to clone.. -and DaveJ has tar-balls at + git://git.kernel.org/pub/scm/linux/kernel/git/josh/sparse.git + +DaveJ has hourly generated tarballs of the git tree available at.. http://www.codemonkey.org.uk/projects/git-snapshots/sparse/ -- cgit v1.2.3 From 27565903e94d548256bf5923653ab2a9668c9b9f Mon Sep 17 00:00:00 2001 From: Stuart Yoder Date: Fri, 2 Mar 2007 13:42:33 -0600 Subject: [POWERPC] Update interrupt info in booting-without-of.txt Create a new section descrbing how interrupts are represented in the device tree. Added more detail. Clarified some things. Signed-off-by: Stuart Yoder Signed-off-by: Paul Mackerras --- Documentation/powerpc/booting-without-of.txt | 123 +++++++++++++++++++-------- 1 file changed, 87 insertions(+), 36 deletions(-) (limited to 'Documentation') diff --git a/Documentation/powerpc/booting-without-of.txt b/Documentation/powerpc/booting-without-of.txt index eaa0c3285ac9..6d5a5a0fa5e8 100644 --- a/Documentation/powerpc/booting-without-of.txt +++ b/Documentation/powerpc/booting-without-of.txt @@ -1108,42 +1108,7 @@ See appendix A for an example partial SOC node definition for the MPC8540. -2) Specifying interrupt information for SOC devices ---------------------------------------------------- - -Each device that is part of an SOC and which generates interrupts -should have the following properties: - - - interrupt-parent : contains the phandle of the interrupt - controller which handles interrupts for this device - - interrupts : a list of tuples representing the interrupt - number and the interrupt sense and level for each interrupt - for this device. - -This information is used by the kernel to build the interrupt table -for the interrupt controllers in the system. - -Sense and level information should be encoded as follows: - - Devices connected to openPIC-compatible controllers should encode - sense and polarity as follows: - - 0 = low to high edge sensitive type enabled - 1 = active low level sensitive type enabled - 2 = active high level sensitive type enabled - 3 = high to low edge sensitive type enabled - - ISA PIC interrupt controllers should adhere to the ISA PIC - encodings listed below: - - 0 = active low level sensitive type enabled - 1 = active high level sensitive type enabled - 2 = high to low edge sensitive type enabled - 3 = low to high edge sensitive type enabled - - - -3) Representing devices without a current OF specification +2) Representing devices without a current OF specification ---------------------------------------------------------- Currently, there are many devices on SOCs that do not have a standard @@ -1732,6 +1697,92 @@ platforms are moved over to use the flattened-device-tree model. More devices will be defined as this spec matures. +VII - Specifying interrupt information for devices +=================================================== + +The device tree represents the busses and devices of a hardware +system in a form similar to the physical bus topology of the +hardware. + +In addition, a logical 'interrupt tree' exists which represents the +hierarchy and routing of interrupts in the hardware. + +The interrupt tree model is fully described in the +document "Open Firmware Recommended Practice: Interrupt +Mapping Version 0.9". The document is available at: +. + +1) interrupts property +---------------------- + +Devices that generate interrupts to a single interrupt controller +should use the conventional OF representation described in the +OF interrupt mapping documentation. + +Each device which generates interrupts must have an 'interrupt' +property. The interrupt property value is an arbitrary number of +of 'interrupt specifier' values which describe the interrupt or +interrupts for the device. + +The encoding of an interrupt specifier is determined by the +interrupt domain in which the device is located in the +interrupt tree. The root of an interrupt domain specifies in +its #interrupt-cells property the number of 32-bit cells +required to encode an interrupt specifier. See the OF interrupt +mapping documentation for a detailed description of domains. + +For example, the binding for the OpenPIC interrupt controller +specifies an #interrupt-cells value of 2 to encode the interrupt +number and level/sense information. All interrupt children in an +OpenPIC interrupt domain use 2 cells per interrupt in their interrupts +property. + +The PCI bus binding specifies a #interrupt-cell value of 1 to encode +which interrupt pin (INTA,INTB,INTC,INTD) is used. + +2) interrupt-parent property +---------------------------- + +The interrupt-parent property is specified to define an explicit +link between a device node and its interrupt parent in +the interrupt tree. The value of interrupt-parent is the +phandle of the parent node. + +If the interrupt-parent property is not defined for a node, it's +interrupt parent is assumed to be an ancestor in the node's +_device tree_ hierarchy. + +3) OpenPIC Interrupt Controllers +-------------------------------- + +OpenPIC interrupt controllers require 2 cells to encode +interrupt information. The first cell defines the interrupt +number. The second cell defines the sense and level +information. + +Sense and level information should be encoded as follows: + + 0 = low to high edge sensitive type enabled + 1 = active low level sensitive type enabled + 2 = active high level sensitive type enabled + 3 = high to low edge sensitive type enabled + +4) ISA Interrupt Controllers +---------------------------- + +ISA PIC interrupt controllers require 2 cells to encode +interrupt information. The first cell defines the interrupt +number. The second cell defines the sense and level +information. + +ISA PIC interrupt controllers should adhere to the ISA PIC +encodings listed below: + + 0 = active low level sensitive type enabled + 1 = active high level sensitive type enabled + 2 = high to low edge sensitive type enabled + 3 = low to high edge sensitive type enabled + Appendix A - Sample SOC node for MPC8540 ======================================== -- cgit v1.2.3 From a1fdcc0d2714b6622e3fd5c00db1635213d6c41a Mon Sep 17 00:00:00 2001 From: Len Brown Date: Sun, 11 Mar 2007 03:26:14 -0400 Subject: ACPI: Add support to parse 2nd MADT When a BIOS bug presents multiple APIC/MADTs, Linux currently uses the 1st and ignores the 2nd. But some machines work better if we use the 2nd. http://bugzilla.kernel.org/show_bug.cgi?id=7465 Add a warning and boot parameter "acpi_apic_instance=2" to allow parsing the 2nd. No change to default behaviour in this patch. Signed-off-by: Len Brown --- Documentation/kernel-parameters.txt | 6 ++++ drivers/acpi/tables.c | 57 +++++++++++++++++++++++++++++++++---- 2 files changed, 58 insertions(+), 5 deletions(-) (limited to 'Documentation') diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 856c8b114e71..22c6b8ccaea5 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -138,6 +138,12 @@ and is between 256 and 4096 characters. It is defined in the file See also Documentation/pm.txt, pci=noacpi + acpi_apic_instance= [ACPI, IOAPIC] + Format: + 2: use 2nd APIC table, if available + 1,0: use 1st APIC table + default: 0 + acpi_sleep= [HW,ACPI] Sleep options Format: { s3_bios, s3_mode } See Documentation/power/video.txt diff --git a/drivers/acpi/tables.c b/drivers/acpi/tables.c index 849e2c361804..c3419182c9a7 100644 --- a/drivers/acpi/tables.c +++ b/drivers/acpi/tables.c @@ -42,7 +42,9 @@ static char *mps_inti_flags_trigger[] = { "dfl", "edge", "res", "level" }; static struct acpi_table_desc initial_tables[ACPI_MAX_TABLES] __initdata; -void acpi_table_print_madt_entry(struct acpi_subtable_header * header) +static int acpi_apic_instance __initdata; + +void acpi_table_print_madt_entry(struct acpi_subtable_header *header) { if (!header) return; @@ -183,8 +185,10 @@ acpi_table_parse_entries(char *id, if (!handler) return -EINVAL; - /* Locate the table (if exists). There should only be one. */ - acpi_get_table(id, 0, &table_header); + if (strncmp(id, ACPI_SIG_MADT, 4) == 0) + acpi_get_table(id, acpi_apic_instance, &table_header); + else + acpi_get_table(id, 0, &table_header); if (!table_header) { printk(KERN_WARNING PREFIX "%4.4s not present\n", id); @@ -237,10 +241,15 @@ acpi_table_parse_madt(enum acpi_madt_type id, int __init acpi_table_parse(char *id, acpi_table_handler handler) { struct acpi_table_header *table = NULL; + if (!handler) return -EINVAL; - acpi_get_table(id, 0, &table); + if (strncmp(id, ACPI_SIG_MADT, 4) == 0) + acpi_get_table(id, acpi_apic_instance, &table); + else + acpi_get_table(id, 0, &table); + if (table) { handler(table); return 0; @@ -248,6 +257,31 @@ int __init acpi_table_parse(char *id, acpi_table_handler handler) return 1; } +/* + * The BIOS is supposed to supply a single APIC/MADT, + * but some report two. Provide a knob to use either. + * (don't you wish instance 0 and 1 were not the same?) + */ +static void __init check_multiple_madt(void) +{ + struct acpi_table_header *table = NULL; + + acpi_get_table(ACPI_SIG_MADT, 2, &table); + if (table) { + printk(KERN_WARNING PREFIX + "BIOS bug: multiple APIC/MADT found," + " using %d\n", acpi_apic_instance); + printk(KERN_WARNING PREFIX + "If \"acpi_apic_instance=%d\" works better, " + "notify linux-acpi@vger.kernel.org\n", + acpi_apic_instance ? 0 : 2); + + } else + acpi_apic_instance = 0; + + return; +} + /* * acpi_table_init() * @@ -257,9 +291,22 @@ int __init acpi_table_parse(char *id, acpi_table_handler handler) * result: sdt_entry[] is initialized */ - int __init acpi_table_init(void) { acpi_initialize_tables(initial_tables, ACPI_MAX_TABLES, 0); + check_multiple_madt(); + return 0; +} + +static int __init acpi_parse_apic_instance(char *str) +{ + + acpi_apic_instance = simple_strtoul(str, NULL, 0); + + printk(KERN_NOTICE PREFIX "Shall use APIC/MADT table %d\n", + acpi_apic_instance); + return 0; } + +early_param("acpi_apic_instance", acpi_parse_apic_instance); -- cgit v1.2.3 From ab97e6cf7be77d1a9ae48d1d0eafde0de21805fc Mon Sep 17 00:00:00 2001 From: Wim Van Sebroeck Date: Sun, 11 Mar 2007 12:59:47 +0000 Subject: [WATCHDOG] i8xx TCO driver - mark for removal Mark the i8xx TCO driver for removal. Signed-off-by: Wim Van Sebroeck --- Documentation/feature-removal-schedule.txt | 8 ++++++++ drivers/char/watchdog/Kconfig | 1 + 2 files changed, 9 insertions(+) (limited to 'Documentation') diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt index c3b1430cf603..0bc8b0b2e103 100644 --- a/Documentation/feature-removal-schedule.txt +++ b/Documentation/feature-removal-schedule.txt @@ -316,3 +316,11 @@ Why: The option/code is Who: Johannes Berg --------------------------- + +What: i8xx_tco watchdog driver +When: in 2.6.22 +Why: the i8xx_tco watchdog driver has been replaced by the iTCO_wdt + watchdog driver. +Who: Wim Van Sebroeck + +--------------------------- diff --git a/drivers/char/watchdog/Kconfig b/drivers/char/watchdog/Kconfig index ea09d0c974ea..e812aa129e28 100644 --- a/drivers/char/watchdog/Kconfig +++ b/drivers/char/watchdog/Kconfig @@ -301,6 +301,7 @@ config I6300ESB_WDT config I8XX_TCO tristate "Intel i8xx TCO Timer/Watchdog" depends on WATCHDOG && (X86 || IA64) && PCI + default n ---help--- Hardware driver for the TCO timer built into the Intel 82801 I/O Controller Hub family. The TCO (Total Cost of Ownership) -- cgit v1.2.3 From 187689913d92b5b3b595486a0cb82706f9175dc6 Mon Sep 17 00:00:00 2001 From: Tobin Davis Date: Mon, 12 Mar 2007 22:20:51 +0100 Subject: [ALSA] hda-codec - more systems for Analog Devices This patch adds support for more systems using Analog Devices codecs. Asus P5B-DLX - AD1988 Toshiba U205 - AD1981 Lenovo M55 - AD1986 Samsung R55 - AD1986 Signed-off-by: Tobin Davis Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- Documentation/sound/alsa/ALSA-Configuration.txt | 1 + sound/pci/hda/patch_analog.c | 34 +++++++++++++++++++++++-- 2 files changed, 33 insertions(+), 2 deletions(-) (limited to 'Documentation') diff --git a/Documentation/sound/alsa/ALSA-Configuration.txt b/Documentation/sound/alsa/ALSA-Configuration.txt index db398a6441c1..c94a4a83ae58 100644 --- a/Documentation/sound/alsa/ALSA-Configuration.txt +++ b/Documentation/sound/alsa/ALSA-Configuration.txt @@ -866,6 +866,7 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed. basic 3-jack (default) hp HP nx6320 thinkpad Lenovo Thinkpad T60/X60/Z60 + toshiba Toshiba U205 AD1986A 6stack 6-jack, separate surrounds (default) diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index 26a84e2f0051..f94f1f22889e 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c @@ -833,12 +833,14 @@ static struct snd_pci_quirk ad1986a_cfg_tbl[] = { SND_PCI_QUIRK(0x1043, 0x81b3, "ASUS P5", AD1986A_3STACK), SND_PCI_QUIRK(0x1043, 0x81cb, "ASUS M2N", AD1986A_3STACK), SND_PCI_QUIRK(0x1043, 0x8234, "ASUS M2N", AD1986A_3STACK), + SND_PCI_QUIRK(0x144d, 0xb03c, "Samsung R55", AD1986A_3STACK), SND_PCI_QUIRK(0x144d, 0xc01e, "FSC V2060", AD1986A_LAPTOP), SND_PCI_QUIRK(0x144d, 0xc023, "Samsung X60", AD1986A_LAPTOP_EAPD), SND_PCI_QUIRK(0x144d, 0xc024, "Samsung R65", AD1986A_LAPTOP_EAPD), SND_PCI_QUIRK(0x144d, 0xc026, "Samsung X11", AD1986A_LAPTOP_EAPD), SND_PCI_QUIRK(0x144d, 0xc504, "Samsung Q35", AD1986A_3STACK), SND_PCI_QUIRK(0x144d, 0xc027, "Samsung Q1", AD1986A_ULTRA), + SND_PCI_QUIRK(0x17aa, 0x1011, "Lenovo M55", AD1986A_LAPTOP), SND_PCI_QUIRK(0x17aa, 0x1017, "Lenovo A60", AD1986A_3STACK), SND_PCI_QUIRK(0x17aa, 0x2066, "Lenovo N100", AD1986A_LAPTOP_EAPD), SND_PCI_QUIRK(0x17c0, 0x2017, "Samsung M50", AD1986A_LAPTOP), @@ -1205,7 +1207,7 @@ static struct hda_verb ad1981_init_verbs[] = { /* * Patch for HP nx6320 * - * nx6320 uses EAPD in the reserve way - EAPD-on means the internal + * nx6320 uses EAPD in the reverse way - EAPD-on means the internal * speaker output enabled _and_ mute-LED off. */ @@ -1373,6 +1375,21 @@ static int ad1981_hp_init(struct hda_codec *codec) return 0; } +/* configuration for Toshiba Laptops */ +static struct hda_verb ad1981_toshiba_init_verbs[] = { + {0x05, AC_VERB_SET_EAPD_BTLENABLE, 0x01 }, /* default on */ + /* pin sensing on HP and Mic jacks */ + {0x06, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_HP_EVENT}, + {0x08, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_MIC_EVENT}, + {} +}; + +static struct snd_kcontrol_new ad1981_toshiba_mixers[] = { + HDA_CODEC_VOLUME("Amp Volume", 0x1a, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Amp Switch", 0x1a, 0x0, HDA_OUTPUT), + { } +}; + /* configuration for Lenovo Thinkpad T60 */ static struct snd_kcontrol_new ad1981_thinkpad_mixers[] = { HDA_CODEC_VOLUME("Master Playback Volume", 0x05, 0x0, HDA_OUTPUT), @@ -1418,6 +1435,7 @@ enum { AD1981_BASIC, AD1981_HP, AD1981_THINKPAD, + AD1981_TOSHIBA, AD1981_MODELS }; @@ -1425,6 +1443,7 @@ static const char *ad1981_models[AD1981_MODELS] = { [AD1981_HP] = "hp", [AD1981_THINKPAD] = "thinkpad", [AD1981_BASIC] = "basic", + [AD1981_TOSHIBA] = "toshiba" }; static struct snd_pci_quirk ad1981_cfg_tbl[] = { @@ -1435,6 +1454,7 @@ static struct snd_pci_quirk ad1981_cfg_tbl[] = { /* Lenovo Thinkpad T60/X60/Z6xx */ SND_PCI_QUIRK(0x17aa, 0, "Lenovo Thinkpad", AD1981_THINKPAD), SND_PCI_QUIRK(0x1014, 0x0597, "Lenovo Z60", AD1981_THINKPAD), + SND_PCI_QUIRK(0x1179, 0x0001, "Toshiba U205", AD1981_TOSHIBA), {} }; @@ -1485,8 +1505,17 @@ static int patch_ad1981(struct hda_codec *codec) spec->mixers[0] = ad1981_thinkpad_mixers; spec->input_mux = &ad1981_thinkpad_capture_source; break; + case AD1981_TOSHIBA: + spec->mixers[0] = ad1981_hp_mixers; + spec->mixers[1] = ad1981_toshiba_mixers; + spec->num_init_verbs = 2; + spec->init_verbs[1] = ad1981_toshiba_init_verbs; + spec->multiout.dig_out_nid = 0; + spec->input_mux = &ad1981_hp_capture_source; + codec->patch_ops.init = ad1981_hp_init; + codec->patch_ops.unsol_event = ad1981_hp_unsol_event; + break; } - return 0; } @@ -2609,6 +2638,7 @@ static const char *ad1988_models[AD1988_MODEL_LAST] = { static struct snd_pci_quirk ad1988_cfg_tbl[] = { SND_PCI_QUIRK(0x1043, 0x81f6, "Asus M2N-SLI", AD1988_6STACK_DIG), + SND_PCI_QUIRK(0x1043, 0x81ec, "Asus P5B-DLX", AD1988_6STACK_DIG), {} }; -- cgit v1.2.3 From 09fe58356d148ff66901ddf639e725ca1a48a0af Mon Sep 17 00:00:00 2001 From: Len Brown Date: Sun, 11 Mar 2007 03:32:00 -0400 Subject: ACPI: parse 2nd MADT by default http://bugzilla.kernel.org/show_bug.cgi?id=7465 Signed-off-by: Len Brown --- Documentation/kernel-parameters.txt | 2 +- drivers/acpi/tables.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'Documentation') diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 22c6b8ccaea5..f0b7a5e5740a 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -142,7 +142,7 @@ and is between 256 and 4096 characters. It is defined in the file Format: 2: use 2nd APIC table, if available 1,0: use 1st APIC table - default: 0 + default: 2 acpi_sleep= [HW,ACPI] Sleep options Format: { s3_bios, s3_mode } diff --git a/drivers/acpi/tables.c b/drivers/acpi/tables.c index c3419182c9a7..96792a6cc164 100644 --- a/drivers/acpi/tables.c +++ b/drivers/acpi/tables.c @@ -42,7 +42,7 @@ static char *mps_inti_flags_trigger[] = { "dfl", "edge", "res", "level" }; static struct acpi_table_desc initial_tables[ACPI_MAX_TABLES] __initdata; -static int acpi_apic_instance __initdata; +static int acpi_apic_instance __initdata = 2; void acpi_table_print_madt_entry(struct acpi_subtable_header *header) { -- cgit v1.2.3 From 6f0778d8726ab647ff80c98c1545fdf839f7bcac Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Thu, 15 Mar 2007 12:38:15 +0100 Subject: [ALSA] hda-codec - Add support for MacBook Pro 1st generation Fix audio on Macbook Pro 1st generation. Signed-off-by: Nicolas Boichat Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- Documentation/sound/alsa/ALSA-Configuration.txt | 3 ++- sound/pci/hda/patch_sigmatel.c | 25 +++++++++++++++++++------ 2 files changed, 21 insertions(+), 7 deletions(-) (limited to 'Documentation') diff --git a/Documentation/sound/alsa/ALSA-Configuration.txt b/Documentation/sound/alsa/ALSA-Configuration.txt index c94a4a83ae58..73e9a174b642 100644 --- a/Documentation/sound/alsa/ALSA-Configuration.txt +++ b/Documentation/sound/alsa/ALSA-Configuration.txt @@ -907,7 +907,8 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed. 5stack D945 5stack + SPDIF macmini Intel Mac Mini macbook Intel Mac Book - macbook-pro Intel Mac Book Pro + macbook-pro-v1 Intel Mac Book Pro 1st generation + macbook-pro Intel Mac Book Pro 2nd generation STAC9202/9250/9251 ref Reference board, base config diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index c77a6b051da2..c94291bc5367 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -60,7 +60,8 @@ enum { STAC_D945GTP5, STAC_MACMINI, STAC_MACBOOK, - STAC_MACBOOK_PRO, + STAC_MACBOOK_PRO_V1, + STAC_MACBOOK_PRO_V2, STAC_922X_MODELS }; @@ -529,7 +530,13 @@ static unsigned int macbook_pin_configs[10] = { 0x400000fc, 0x400000fb, }; -static unsigned int macbook_pro_pin_configs[10] = { +static unsigned int macbook_pro_v1_pin_configs[10] = { + 0x0321e230, 0x03a1e020, 0x9017e110, 0x01014010, + 0x01a19021, 0x0381e021, 0x1345e240, 0x13c5e22e, + 0x02a19320, 0x400000fb +}; + +static unsigned int macbook_pro_v2_pin_configs[10] = { 0x0221401f, 0x90a70120, 0x01813024, 0x01014010, 0x400000fd, 0x01016011, 0x1345e240, 0x13c5e22e, 0x400000fc, 0x400000fb, @@ -541,7 +548,8 @@ static unsigned int *stac922x_brd_tbl[STAC_922X_MODELS] = { [STAC_D945GTP5] = d945gtp5_pin_configs, [STAC_MACMINI] = d945gtp5_pin_configs, [STAC_MACBOOK] = macbook_pin_configs, - [STAC_MACBOOK_PRO] = macbook_pro_pin_configs, + [STAC_MACBOOK_PRO_V1] = macbook_pro_v1_pin_configs, + [STAC_MACBOOK_PRO_V2] = macbook_pro_v2_pin_configs, }; static const char *stac922x_models[STAC_922X_MODELS] = { @@ -550,7 +558,8 @@ static const char *stac922x_models[STAC_922X_MODELS] = { [STAC_D945GTP3] = "3stack", [STAC_MACMINI] = "macmini", [STAC_MACBOOK] = "macbook", - [STAC_MACBOOK_PRO] = "macbook-pro", + [STAC_MACBOOK_PRO_V1] = "macbook-pro-v1", + [STAC_MACBOOK_PRO_V2] = "macbook-pro", }; static struct snd_pci_quirk stac922x_cfg_tbl[] = { @@ -1894,9 +1903,13 @@ static int patch_stac922x(struct hda_codec *codec) /* Intel Macs have all same PCI SSID, so we need to check * codec SSID to distinguish the exact models */ + printk(KERN_INFO "hda_codec: STAC922x, Apple subsys_id=%x\n", codec->subsystem_id); switch (codec->subsystem_id) { - case 0x106b1e00: - spec->board_config = STAC_MACBOOK_PRO; + case 0x106b0200: /* MacBook Pro first generation */ + spec->board_config = STAC_MACBOOK_PRO_V1; + break; + case 0x106b1e00: /* MacBook Pro second generation */ + spec->board_config = STAC_MACBOOK_PRO_V2; break; } } -- cgit v1.2.3 From 0e0293c898c424c52e5d4e7f6923a203d06b9c4b Mon Sep 17 00:00:00 2001 From: David Gibson Date: Wed, 14 Mar 2007 11:50:40 +1100 Subject: [POWERPC] Update documentation for flat device tree format v17 This patch updates booting-without-of.txt to describe version 17 of the flattened device tree format. Version 17 is a small, backwards compatible change from version 16, adding an extra field giving the size of the device tree's structure block. At this time, the kernel has no use for the extra information, however its presence can make life easier for bootloaders or other software manipulating the tree. In addition this patch adds information on the size_dt_strings field of the device tree header, present since version 3 of the flattened tree format, but omitted from the documentation. It also makes changes to consistently refer to versions 16 and 17 as versions 16 and 17 in decimal, rather than version 0x10 which was occasionally used for version 16 previously. Finally, we also add the new field to the definition of the device tree header structure in prom.h Signed-off-by: David Gibson Acked-by: Jon Loeliger Signed-off-by: Paul Mackerras --- Documentation/powerpc/booting-without-of.txt | 29 ++++++++++++++++++++++------ include/asm-powerpc/prom.h | 2 ++ 2 files changed, 25 insertions(+), 6 deletions(-) (limited to 'Documentation') diff --git a/Documentation/powerpc/booting-without-of.txt b/Documentation/powerpc/booting-without-of.txt index 6d5a5a0fa5e8..a1f83f1c24c9 100644 --- a/Documentation/powerpc/booting-without-of.txt +++ b/Documentation/powerpc/booting-without-of.txt @@ -265,6 +265,9 @@ struct boot_param_header { booting on */ /* version 3 fields below */ u32 size_dt_strings; /* size of the strings block */ + + /* version 17 fields below */ + u32 size_dt_struct; /* size of the DT structure block */ }; Along with the constants: @@ -335,10 +338,13 @@ struct boot_param_header { to reallocate it easily at boot and free up the unused flattened structure after expansion. Version 16 introduces a new more "compact" format for the tree itself that is however not backward - compatible. You should always generate a structure of the highest - version defined at the time of your implementation. Currently - that is version 16, unless you explicitly aim at being backward - compatible. + compatible. Version 17 adds an additional field, size_dt_struct, + allowing it to be reallocated or moved more easily (this is + particularly useful for bootloaders which need to make + adjustments to a device tree based on probed information). You + should always generate a structure of the highest version defined + at the time of your implementation. Currently that is version 17, + unless you explicitly aim at being backward compatible. - last_comp_version @@ -347,7 +353,7 @@ struct boot_param_header { is backward compatible with version 1 (that is, a kernel build for version 1 will be able to boot with a version 2 format). You should put a 1 in this field if you generate a device tree of - version 1 to 3, or 0x10 if you generate a tree of version 0x10 + version 1 to 3, or 16 if you generate a tree of version 16 or 17 using the new unit name format. - boot_cpuid_phys @@ -360,6 +366,17 @@ struct boot_param_header { point (see further chapters for more informations on the required device-tree contents) + - size_dt_strings + + This field only exists on version 3 and later headers. It + gives the size of the "strings" section of the device tree (which + starts at the offset given by off_dt_strings). + + - size_dt_struct + + This field only exists on version 17 and later headers. It gives + the size of the "structure" section of the device tree (which + starts at the offset given by off_dt_struct). So the typical layout of a DT block (though the various parts don't need to be in that order) looks like this (addresses go from top to @@ -417,7 +434,7 @@ root node who has no parent. A node has 2 names. The actual node name is generally contained in a property of type "name" in the node property list whose value is a zero terminated string and is mandatory for version 1 to 3 of the -format definition (as it is in Open Firmware). Version 0x10 makes it +format definition (as it is in Open Firmware). Version 16 makes it optional as it can generate it from the unit name defined below. There is also a "unit name" that is used to differentiate nodes with diff --git a/include/asm-powerpc/prom.h b/include/asm-powerpc/prom.h index 1aa15e2e056e..994de8ea3308 100644 --- a/include/asm-powerpc/prom.h +++ b/include/asm-powerpc/prom.h @@ -59,6 +59,8 @@ struct boot_param_header u32 boot_cpuid_phys; /* Physical CPU id we're booting on */ /* version 3 fields below */ u32 dt_strings_size; /* size of the DT strings block */ + /* version 17 fields below */ + u32 dt_struct_size; /* size of the DT structure block */ }; -- cgit v1.2.3 From 28735a7253a6c24364765e80a5428b4a151fccc2 Mon Sep 17 00:00:00 2001 From: David Brownell Date: Fri, 16 Mar 2007 13:38:14 -0800 Subject: [PATCH] gpio_direction_output() needs an initial value It's been pointed out that output GPIOs should have an initial value, to avoid signal glitching ... among other things, it can be some time before a driver is ready. This patch corrects that oversight, fixing - documentation - platforms supporting the GPIO interface - users of that call (just one for now, others are pending) There's only one user of this call for now since most platforms are still using non-generic GPIO setup code, which in most cases already couples the initial value with its "set output mode" request. Note that most platforms are clear about the hardware letting the output value be set before the pin direction is changed, but the s3c241x docs are vague on that topic ... so those chips might not avoid the glitches. Signed-off-by: David Brownell Acked-by: Andrew Victor Acked-by: Milan Svoboda Acked-by: Haavard Skinnemoen Cc: Russell King Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/gpio.txt | 5 ++++- arch/arm/mach-at91/gpio.c | 3 ++- arch/arm/mach-sa1100/generic.c | 3 ++- arch/avr32/mach-at32ap/pio.c | 4 +++- drivers/spi/atmel_spi.c | 2 +- include/asm-arm/arch-at91/gpio.h | 2 +- include/asm-arm/arch-omap/gpio.h | 3 ++- include/asm-arm/arch-pxa/gpio.h | 4 ++-- include/asm-arm/arch-s3c2410/gpio.h | 4 +++- include/asm-arm/arch-sa1100/gpio.h | 2 +- include/asm-avr32/arch-at32ap/gpio.h | 2 +- 11 files changed, 22 insertions(+), 12 deletions(-) (limited to 'Documentation') diff --git a/Documentation/gpio.txt b/Documentation/gpio.txt index 576ce463cf44..989f1130f4f3 100644 --- a/Documentation/gpio.txt +++ b/Documentation/gpio.txt @@ -105,12 +105,15 @@ setting up a platform_device using the GPIO, is mark its direction: /* set as input or output, returning 0 or negative errno */ int gpio_direction_input(unsigned gpio); - int gpio_direction_output(unsigned gpio); + int gpio_direction_output(unsigned gpio, int value); The return value is zero for success, else a negative errno. It should be checked, since the get/set calls don't have error returns and since misconfiguration is possible. (These calls could sleep.) +For output GPIOs, the value provided becomes the initial output value. +This helps avoid signal glitching during system startup. + Setting the direction can fail if the GPIO number is invalid, or when that particular GPIO can't be used in that mode. It's generally a bad idea to rely on boot firmware to have set the direction correctly, since diff --git a/arch/arm/mach-at91/gpio.c b/arch/arm/mach-at91/gpio.c index 44211a0af19a..ba4a1bb3ee40 100644 --- a/arch/arm/mach-at91/gpio.c +++ b/arch/arm/mach-at91/gpio.c @@ -215,13 +215,14 @@ int gpio_direction_input(unsigned pin) } EXPORT_SYMBOL(gpio_direction_input); -int gpio_direction_output(unsigned pin) +int gpio_direction_output(unsigned pin, int value) { void __iomem *pio = pin_to_controller(pin); unsigned mask = pin_to_mask(pin); if (!pio || !(__raw_readl(pio + PIO_PSR) & mask)) return -EINVAL; + __raw_writel(mask, pio + (value ? PIO_SODR : PIO_CODR)); __raw_writel(mask, pio + PIO_OER); return 0; } diff --git a/arch/arm/mach-sa1100/generic.c b/arch/arm/mach-sa1100/generic.c index 192a5a26cf2b..edc349e5fcf7 100644 --- a/arch/arm/mach-sa1100/generic.c +++ b/arch/arm/mach-sa1100/generic.c @@ -153,7 +153,7 @@ int gpio_direction_input(unsigned gpio) EXPORT_SYMBOL(gpio_direction_input); -int gpio_direction_output(unsigned gpio) +int gpio_direction_output(unsigned gpio, int value) { unsigned long flags; @@ -161,6 +161,7 @@ int gpio_direction_output(unsigned gpio) return -EINVAL; local_irq_save(flags); + gpio_set_value(gpio, value); GPDR |= GPIO_GPIO(gpio); local_irq_restore(flags); return 0; diff --git a/arch/avr32/mach-at32ap/pio.c b/arch/avr32/mach-at32ap/pio.c index 9ba5654cde11..1eb99b814f5b 100644 --- a/arch/avr32/mach-at32ap/pio.c +++ b/arch/avr32/mach-at32ap/pio.c @@ -214,7 +214,7 @@ int gpio_direction_input(unsigned int gpio) } EXPORT_SYMBOL(gpio_direction_input); -int gpio_direction_output(unsigned int gpio) +int gpio_direction_output(unsigned int gpio, int value) { struct pio_device *pio; unsigned int pin; @@ -223,6 +223,8 @@ int gpio_direction_output(unsigned int gpio) if (!pio) return -ENODEV; + gpio_set_value(gpio, value); + pin = gpio & 0x1f; pio_writel(pio, OER, 1 << pin); diff --git a/drivers/spi/atmel_spi.c b/drivers/spi/atmel_spi.c index 6fa260d1a9be..66e7bc985797 100644 --- a/drivers/spi/atmel_spi.c +++ b/drivers/spi/atmel_spi.c @@ -425,7 +425,7 @@ static int atmel_spi_setup(struct spi_device *spi) if (ret) return ret; spi->controller_state = (void *)npcs_pin; - gpio_direction_output(npcs_pin); + gpio_direction_output(npcs_pin, !(spi->mode & SPI_CS_HIGH)); } dev_dbg(&spi->dev, diff --git a/include/asm-arm/arch-at91/gpio.h b/include/asm-arm/arch-at91/gpio.h index 98ad2114f43a..0a241e2fb672 100644 --- a/include/asm-arm/arch-at91/gpio.h +++ b/include/asm-arm/arch-at91/gpio.h @@ -223,7 +223,7 @@ static inline void gpio_free(unsigned gpio) } extern int gpio_direction_input(unsigned gpio); -extern int gpio_direction_output(unsigned gpio); +extern int gpio_direction_output(unsigned gpio, int value); static inline int gpio_get_value(unsigned gpio) { diff --git a/include/asm-arm/arch-omap/gpio.h b/include/asm-arm/arch-omap/gpio.h index 3762a6ae6a7f..590917efc94a 100644 --- a/include/asm-arm/arch-omap/gpio.h +++ b/include/asm-arm/arch-omap/gpio.h @@ -113,8 +113,9 @@ static inline int gpio_direction_input(unsigned gpio) return __gpio_set_direction(gpio, 1); } -static inline int gpio_direction_output(unsigned gpio) +static inline int gpio_direction_output(unsigned gpio, int value) { + omap_set_gpio_dataout(gpio, value); return __gpio_set_direction(gpio, 0); } diff --git a/include/asm-arm/arch-pxa/gpio.h b/include/asm-arm/arch-pxa/gpio.h index 3d348a351157..aeba24347f8e 100644 --- a/include/asm-arm/arch-pxa/gpio.h +++ b/include/asm-arm/arch-pxa/gpio.h @@ -43,9 +43,9 @@ static inline int gpio_direction_input(unsigned gpio) return pxa_gpio_mode(gpio | GPIO_IN); } -static inline int gpio_direction_output(unsigned gpio) +static inline int gpio_direction_output(unsigned gpio, int value) { - return pxa_gpio_mode(gpio | GPIO_OUT); + return pxa_gpio_mode(gpio | GPIO_OUT | (value ? 0 : GPIO_DFLT_LOW)); } static inline int __gpio_get_value(unsigned gpio) diff --git a/include/asm-arm/arch-s3c2410/gpio.h b/include/asm-arm/arch-s3c2410/gpio.h index d47ae453f8ca..7583895fd336 100644 --- a/include/asm-arm/arch-s3c2410/gpio.h +++ b/include/asm-arm/arch-s3c2410/gpio.h @@ -44,9 +44,11 @@ static inline int gpio_direction_input(unsigned gpio) return 0; } -static inline int gpio_direction_output(unsigned gpio) +static inline int gpio_direction_output(unsigned gpio, int value) { s3c2410_gpio_cfgpin(gpio, S3C2410_GPIO_OUTPUT); + /* REVISIT can we write the value first, to avoid glitching? */ + s3c2410_gpio_setpin(gpio, value); return 0; } diff --git a/include/asm-arm/arch-sa1100/gpio.h b/include/asm-arm/arch-sa1100/gpio.h index da7575b0e5d0..e7a9d26e22a8 100644 --- a/include/asm-arm/arch-sa1100/gpio.h +++ b/include/asm-arm/arch-sa1100/gpio.h @@ -38,7 +38,7 @@ static inline void gpio_free(unsigned gpio) } extern int gpio_direction_input(unsigned gpio); -extern int gpio_direction_output(unsigned gpio); +extern int gpio_direction_output(unsigned gpio, int value); static inline int gpio_get_value(unsigned gpio) diff --git a/include/asm-avr32/arch-at32ap/gpio.h b/include/asm-avr32/arch-at32ap/gpio.h index fcb756bdaa8e..80a21aa9ae77 100644 --- a/include/asm-avr32/arch-at32ap/gpio.h +++ b/include/asm-avr32/arch-at32ap/gpio.h @@ -10,7 +10,7 @@ int __must_check gpio_request(unsigned int gpio, const char *label); void gpio_free(unsigned int gpio); int gpio_direction_input(unsigned int gpio); -int gpio_direction_output(unsigned int gpio); +int gpio_direction_output(unsigned int gpio, int value); int gpio_get_value(unsigned int gpio); void gpio_set_value(unsigned int gpio, int value); -- cgit v1.2.3 From 58e40308f329275ccf556312a9cd788522f7c224 Mon Sep 17 00:00:00 2001 From: Johannes Schlumberger Date: Wed, 21 Mar 2007 08:55:58 +1100 Subject: [CRYPTO] doc: Fix typo in hash example there is a tiny bug in Documentation/crypto/api-intro.txt. The file has the following example code: struct scatterlist sg[2]; [...] if (crypto_hash_digest(&desc, &sg, 2, result)) which does not match the declaration of crypto_hash_digest() in include/linux/crypto.h. (static inline int crypto_hash_digest(struct hash_desc *desc, struct scatterlist *sg, unsigned int nbytes, u8 *out) The code in the example passes the address of a pointer (an array actually) as the second argument, while the function expects the pointer itself. I have attached a patch to fix this. Signed-off-by: Herbert Xu --- Documentation/crypto/api-intro.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/crypto/api-intro.txt b/Documentation/crypto/api-intro.txt index e41a79aa71ce..9b84b805ab75 100644 --- a/Documentation/crypto/api-intro.txt +++ b/Documentation/crypto/api-intro.txt @@ -60,7 +60,7 @@ Here's an example of how to use the API: desc.tfm = tfm; desc.flags = 0; - if (crypto_hash_digest(&desc, &sg, 2, result)) + if (crypto_hash_digest(&desc, sg, 2, result)) fail(); crypto_free_hash(tfm); -- cgit v1.2.3 From e0a2f28b4dee2a1e4c62dc8389f25defb284e387 Mon Sep 17 00:00:00 2001 From: Scott Wood Date: Fri, 16 Mar 2007 12:28:46 -0500 Subject: [POWERPC] Document the linux,network-index property. To allow more robust association of each network device node with an index (such as is used by the firmware or an EEPROM to indicate MAC addresses), a network device's node may specify the index explicitly. Signed-off-by: Scott Wood Acked-by: David Gibson david@gibson.dropbear.id.au> Signed-off-by: Paul Mackerras --- Documentation/powerpc/booting-without-of.txt | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'Documentation') diff --git a/Documentation/powerpc/booting-without-of.txt b/Documentation/powerpc/booting-without-of.txt index a1f83f1c24c9..88cdb592fdf9 100644 --- a/Documentation/powerpc/booting-without-of.txt +++ b/Documentation/powerpc/booting-without-of.txt @@ -1182,6 +1182,13 @@ platforms are moved over to use the flattened-device-tree model. - phy-handle : The phandle for the PHY connected to this ethernet controller. + Recommended properties: + + - linux,network-index : This is the intended "index" of this + network device. This is used by the bootwrapper to interpret + MAC addresses passed by the firmware when no information other + than indices is available to associate an address with a device. + Example: ethernet@24000 { @@ -1550,6 +1557,12 @@ platforms are moved over to use the flattened-device-tree model. - mac-address : list of bytes representing the ethernet address. - phy-handle : The phandle for the PHY connected to this controller. + Recommended properties: + - linux,network-index : This is the intended "index" of this + network device. This is used by the bootwrapper to interpret + MAC addresses passed by the firmware when no information other + than indices is available to associate an address with a device. + Example: ucc@2000 { device_type = "network"; -- cgit v1.2.3 From acf11faeb1ba1179f695c83c47716e4f6ffdebd8 Mon Sep 17 00:00:00 2001 From: Johannes Weiner Date: Thu, 22 Mar 2007 00:11:18 -0800 Subject: [PATCH] Documentation/sysrq.txt: added short description for 'Q' (timerlist) I added the 'Q' to list. A short description in the `Ok, so what can I use them for'-section, on when or why to use it would be nice! Signed-off-by: Johannes Weiner Cc: Ingo Molnar Cc: Thomas Gleixner Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/sysrq.txt | 2 ++ 1 file changed, 2 insertions(+) (limited to 'Documentation') diff --git a/Documentation/sysrq.txt b/Documentation/sysrq.txt index 452c0f152304..d43aa9d3c105 100644 --- a/Documentation/sysrq.txt +++ b/Documentation/sysrq.txt @@ -93,6 +93,8 @@ On all - write a character to /proc/sysrq-trigger. e.g.: 'p' - Will dump the current registers and flags to your console. +'q' - Will dump a list of all running timers. + 'r' - Turns off keyboard raw mode and sets it to XLATE. 's' - Will attempt to sync all mounted filesystems. -- cgit v1.2.3 From ad62ca2bd89f72e9b80dfaffc463e87bec5e75cb Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Thu, 22 Mar 2007 00:11:21 -0800 Subject: [PATCH] i386: disable local apic timer via command line or dmi quirk The local APIC timer stops to work in deeper C-States. This is handled by the ACPI code and a broadcast mechanism in the clockevents / tick managment code. Some systems do not expose the deeper C-States to the kernel, but switch into deeper C-States behind the kernels back. This delays the local apic timer interrupts for ever and makes the systems unusable. Add a command line option to disable the local apic timer and a dmi quirk for known broken systems. Andi sayeth: While not wrong by itself i think it is still better to use some heuristic -- like "has battery in ACPI" With the DMI table if the problem is more wide spread we will just continue extending it. But anyways should be ok now for .21 although I'm not really happy with it. Signed-off-by: Thomas Gleixner Acked-by: Ingo Molnar Cc: john stultz Grudgingly-acked-by: Andi Kleen Cc: Len Brown Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/kernel-parameters.txt | 2 ++ arch/i386/kernel/apic.c | 52 +++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+) (limited to 'Documentation') diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 856c8b114e71..06377c76e734 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -1117,6 +1117,8 @@ and is between 256 and 4096 characters. It is defined in the file nolapic [IA-32,APIC] Do not enable or use the local APIC. + nolapic_timer [IA-32,APIC] Do not use the local APIC timer. + noltlbs [PPC] Do not use large page/tlb entries for kernel lowmem mapping on PPC40x. diff --git a/arch/i386/kernel/apic.c b/arch/i386/kernel/apic.c index 5cff7970911e..36825117835d 100644 --- a/arch/i386/kernel/apic.c +++ b/arch/i386/kernel/apic.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -61,6 +62,8 @@ static int enable_local_apic __initdata = 0; /* Local APIC timer verification ok */ static int local_apic_timer_verify_ok; +/* Disable local APIC timer from the kernel commandline or via dmi quirk */ +static int local_apic_timer_disabled; /* * Debug level, exported for io_apic.c @@ -265,6 +268,32 @@ static void __devinit setup_APIC_timer(void) clockevents_register_device(levt); } +/* + * Detect systems with known broken BIOS implementations + */ +static int __init lapic_check_broken_bios(struct dmi_system_id *d) +{ + printk(KERN_NOTICE "%s detected: disabling lapic timer.\n", + d->ident); + local_apic_timer_disabled = 1; + return 0; +} + +static struct dmi_system_id __initdata broken_bios_dmi_table[] = { + { + /* + * BIOS exports only C1 state, but uses deeper power + * modes behind the kernels back. + */ + .callback = lapic_check_broken_bios, + .ident = "HP nx6325", + .matches = { + DMI_MATCH(DMI_PRODUCT_NAME, "HP Compaq nx6325"), + }, + }, + {} +}; + /* * In this functions we calibrate APIC bus clocks to the external timer. * @@ -340,6 +369,22 @@ void __init setup_boot_APIC_clock(void) long delta, deltapm; int pm_referenced = 0; + /* Detect know broken systems */ + dmi_check_system(broken_bios_dmi_table); + + /* + * The local apic timer can be disabled via the kernel + * commandline or from the dmi quirk above. Register the lapic + * timer as a dummy clock event source on SMP systems, so the + * broadcast mechanism is used. On UP systems simply ignore it. + */ + if (local_apic_timer_disabled) { + /* No broadcast on UP ! */ + if (num_possible_cpus() > 1) + setup_APIC_timer(); + return; + } + apic_printk(APIC_VERBOSE, "Using local APIC timer interrupts.\n" "calibrating APIC timer ...\n"); @@ -1179,6 +1224,13 @@ static int __init parse_nolapic(char *arg) } early_param("nolapic", parse_nolapic); +static int __init parse_disable_lapic_timer(char *arg) +{ + local_apic_timer_disabled = 1; + return 0; +} +early_param("nolapic_timer", parse_disable_lapic_timer); + static int __init apic_set_verbosity(char *str) { if (strcmp("debug", str) == 0) -- cgit v1.2.3 From e585bef815c0315f2730d7bb4e15b82602454efd Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Fri, 23 Mar 2007 16:08:01 +0100 Subject: [PATCH] i386: add command line option "local_apic_timer_c2_ok" It turned out that it is almost impossible to trust ACPI, BIOS & Co. regarding the C states. This was the reason to switch the local apic timer off in C2 state already. OTOH there are sane and well behaving systems, which get punished by that decision. Allow the user to confirm that the local apic timer is trustworthy in C2 state. This keeps the default behaviour on the safe side. Signed-off-by: Thomas Gleixner Acked-by: Ingo Molnar Signed-off-by: Linus Torvalds --- Documentation/kernel-parameters.txt | 3 +++ arch/i386/kernel/apic.c | 10 ++++++++++ drivers/acpi/processor_idle.c | 3 ++- include/asm-i386/apic.h | 1 + 4 files changed, 16 insertions(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index e39ab0c99fbb..09640a8f7ceb 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -780,6 +780,9 @@ and is between 256 and 4096 characters. It is defined in the file lapic [IA-32,APIC] Enable the local APIC even if BIOS disabled it. + lapic_timer_c2_ok [IA-32,APIC] trust the local apic timer in + C2 power state. + lasi= [HW,SCSI] PARISC LASI driver for the 53c700 chip Format: addr:,irq: diff --git a/arch/i386/kernel/apic.c b/arch/i386/kernel/apic.c index 244c3fe9b8c3..e88415282a6f 100644 --- a/arch/i386/kernel/apic.c +++ b/arch/i386/kernel/apic.c @@ -64,6 +64,9 @@ static int enable_local_apic __initdata = 0; static int local_apic_timer_verify_ok; /* Disable local APIC timer from the kernel commandline or via dmi quirk */ static int local_apic_timer_disabled; +/* Local APIC timer works in C2 */ +int local_apic_timer_c2_ok; +EXPORT_SYMBOL_GPL(local_apic_timer_c2_ok); /* * Debug level, exported for io_apic.c @@ -1232,6 +1235,13 @@ static int __init parse_disable_lapic_timer(char *arg) } early_param("nolapic_timer", parse_disable_lapic_timer); +static int __init parse_lapic_timer_c2_ok(char *arg) +{ + local_apic_timer_c2_ok = 1; + return 0; +} +early_param("lapic_timer_c2_ok", parse_lapic_timer_c2_ok); + static int __init apic_set_verbosity(char *str) { if (strcmp("debug", str) == 0) diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index 60773005b8af..cdf78943af4d 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c @@ -268,6 +268,7 @@ static void acpi_timer_check_state(int state, struct acpi_processor *pr, struct acpi_processor_cx *cx) { struct acpi_processor_power *pwr = &pr->power; + u8 type = local_apic_timer_c2_ok ? ACPI_STATE_C3 : ACPI_STATE_C2; /* * Check, if one of the previous states already marked the lapic @@ -276,7 +277,7 @@ static void acpi_timer_check_state(int state, struct acpi_processor *pr, if (pwr->timer_broadcast_on_state < state) return; - if (cx->type >= ACPI_STATE_C2) + if (cx->type >= type) pr->power.timer_broadcast_on_state = state; } diff --git a/include/asm-i386/apic.h b/include/asm-i386/apic.h index cc6b1652249a..a19810a08ae9 100644 --- a/include/asm-i386/apic.h +++ b/include/asm-i386/apic.h @@ -117,6 +117,7 @@ extern void enable_NMI_through_LVT0 (void * dummy); #define ARCH_APICTIMER_STOPS_ON_C3 1 extern int timer_over_8254; +extern int local_apic_timer_c2_ok; #else /* !CONFIG_X86_LOCAL_APIC */ static inline void lapic_shutdown(void) { } -- cgit v1.2.3 From 2e7c28382b8426c6b7ac6f147177a664065f95f4 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Fri, 23 Mar 2007 11:32:31 -0700 Subject: x86-64: add "local_apic_timer_c2_ok" here too Needed for any architecture that claims ARCH_APICTIMER_STOPS_ON_C3, not just i386. I'm hoping Thomas will clean this up a bit later.. Signed-off-by: Linus Torvalds --- Documentation/kernel-parameters.txt | 2 +- arch/x86_64/kernel/apic.c | 11 +++++++++++ include/asm-x86_64/apic.h | 1 + 3 files changed, 13 insertions(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 09640a8f7ceb..ef2ffded1392 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -780,7 +780,7 @@ and is between 256 and 4096 characters. It is defined in the file lapic [IA-32,APIC] Enable the local APIC even if BIOS disabled it. - lapic_timer_c2_ok [IA-32,APIC] trust the local apic timer in + lapic_timer_c2_ok [IA-32,x86-64,APIC] trust the local apic timer in C2 power state. lasi= [HW,SCSI] PARISC LASI driver for the 53c700 chip diff --git a/arch/x86_64/kernel/apic.c b/arch/x86_64/kernel/apic.c index 723417d924c0..46acf4f2f1ec 100644 --- a/arch/x86_64/kernel/apic.c +++ b/arch/x86_64/kernel/apic.c @@ -47,6 +47,10 @@ int apic_calibrate_pmtmr __initdata; int disable_apic_timer __initdata; +/* Local APIC timer works in C2? */ +int local_apic_timer_c2_ok; +EXPORT_SYMBOL_GPL(local_apic_timer_c2_ok); + static struct resource *ioapic_resources; static struct resource lapic_resource = { .name = "Local APIC", @@ -1192,6 +1196,13 @@ static __init int setup_nolapic(char *str) } early_param("nolapic", setup_nolapic); +static int __init parse_lapic_timer_c2_ok(char *arg) +{ + local_apic_timer_c2_ok = 1; + return 0; +} +early_param("lapic_timer_c2_ok", parse_lapic_timer_c2_ok); + static __init int setup_noapictimer(char *str) { if (str[0] != ' ' && str[0] != 0) diff --git a/include/asm-x86_64/apic.h b/include/asm-x86_64/apic.h index e81d0f289f0b..7cfb39cbd918 100644 --- a/include/asm-x86_64/apic.h +++ b/include/asm-x86_64/apic.h @@ -102,5 +102,6 @@ void switch_ipi_to_APIC_timer(void *cpumask); #define ARCH_APICTIMER_STOPS_ON_C3 1 extern unsigned boot_cpu_id; +extern int local_apic_timer_c2_ok; #endif /* __ASM_APIC_H */ -- cgit v1.2.3 From 954b2e7f4c37cbcdcf4ca7ac47524f3f6bf30119 Mon Sep 17 00:00:00 2001 From: Ralf Baechle Date: Sat, 24 Mar 2007 12:54:26 -0700 Subject: [NET] AX.25 Kconfig and docs updates and fixes o The AX.25 Howto is unmaintained since several years. I've replaced it with a wiki at http://www.linux-ax25.org which provides more uptodate information. o Change default for AX25_DAMA_SLAVE to Y. AX25_DAMA_SLAVE only compiles in support for DAMA but doesn't activate it. I hope this gets Linux distributions to ship their AX.25 kernels with AX25_DAMA_SLAVE enabled. The price for this would be very small. o Delete historic changelog from comments, that's what SCM systems are meant to do. o ---help--- in Kconfig looks so yellingly eye insulting. Use just help. o Rewrite the commented out piece of old Linux 2.4 configuration language to Kconfig for consistency. o Fixup dependencies. Signed-off-by: Ralf Baechle Signed-off-by: David S. Miller --- Documentation/networking/ax25.txt | 18 ++++-------- net/ax25/Kconfig | 61 +++++++++++++++++++++++---------------- 2 files changed, 42 insertions(+), 37 deletions(-) (limited to 'Documentation') diff --git a/Documentation/networking/ax25.txt b/Documentation/networking/ax25.txt index 37c25b0925f0..8257dbf9be57 100644 --- a/Documentation/networking/ax25.txt +++ b/Documentation/networking/ax25.txt @@ -1,16 +1,10 @@ To use the amateur radio protocols within Linux you will need to get a -suitable copy of the AX.25 Utilities. More detailed information about these -and associated programs can be found on http://zone.pspt.fi/~jsn/. - -For more information about the AX.25, NET/ROM and ROSE protocol stacks, see -the AX25-HOWTO written by Terry Dawson -who is also the AX.25 Utilities maintainer. +suitable copy of the AX.25 Utilities. More detailed information about +AX.25, NET/ROM and ROSE, associated programs and and utilities can be +found on http://www.linux-ax25.org. There is an active mailing list for discussing Linux amateur radio matters -called linux-hams. To subscribe to it, send a message to +called linux-hams@vger.kernel.org. To subscribe to it, send a message to majordomo@vger.kernel.org with the words "subscribe linux-hams" in the body -of the message, the subject field is ignored. - -Jonathan G4KLX - -g4klx@g4klx.demon.co.uk +of the message, the subject field is ignored. You don't need to be +subscribed to post but of course that means you might miss an answer. diff --git a/net/ax25/Kconfig b/net/ax25/Kconfig index a8993a041724..43dd86fca4d3 100644 --- a/net/ax25/Kconfig +++ b/net/ax25/Kconfig @@ -1,30 +1,27 @@ # # Amateur Radio protocols and AX.25 device configuration # -# 19971130 Now in an own category to make correct compilation of the -# AX.25 stuff easier... -# Joerg Reuter DL1BKE -# 19980129 Moved to net/ax25/Config.in, sourcing device drivers. menuconfig HAMRADIO depends on NET bool "Amateur Radio support" help If you want to connect your Linux box to an amateur radio, answer Y - here. You want to read and - the AX25-HOWTO, available from . + here. You want to read + and more specifically about AX.25 on Linux + . Note that the answer to this question won't directly affect the kernel: saying N will just cause the configurator to skip all the questions about amateur radio. comment "Packet Radio protocols" - depends on HAMRADIO && NET + depends on HAMRADIO config AX25 tristate "Amateur Radio AX.25 Level 2 protocol" - depends on HAMRADIO && NET - ---help--- + depends on HAMRADIO + help This is the protocol used for computer communication over amateur radio. It is either used by itself for point-to-point links, or to carry other protocols such as tcp/ip. To use it, you need a device @@ -52,6 +49,7 @@ config AX25 config AX25_DAMA_SLAVE bool "AX.25 DAMA Slave support" + default y depends on AX25 help DAMA is a mechanism to prevent collisions when doing AX.25 @@ -59,23 +57,38 @@ config AX25_DAMA_SLAVE from clients (called "slaves") and redistributes it to other slaves. If you say Y here, your Linux box will act as a DAMA slave; this is transparent in that you don't have to do any special DAMA - configuration. (Linux cannot yet act as a DAMA server.) If unsure, - say N. + configuration. Linux cannot yet act as a DAMA server. This option + only compiles DAMA slave support into the kernel. It still needs to + be enabled at runtime. For more about DAMA see + . If unsure, say Y. + +# placeholder until implemented +config AX25_DAMA_MASTER + bool 'AX.25 DAMA Master support' + depends on AX25_DAMA_SLAVE && BROKEN + help + DAMA is a mechanism to prevent collisions when doing AX.25 + networking. A DAMA server (called "master") accepts incoming traffic + from clients (called "slaves") and redistributes it to other slaves. + If you say Y here, your Linux box will act as a DAMA master; this is + transparent in that you don't have to do any special DAMA + configuration. Linux cannot yet act as a DAMA server. This option + only compiles DAMA slave support into the kernel. It still needs to + be explicitly enabled, so if unsure, say Y. -# bool ' AX.25 DAMA Master support' CONFIG_AX25_DAMA_MASTER config NETROM tristate "Amateur Radio NET/ROM protocol" depends on AX25 - ---help--- + help NET/ROM is a network layer protocol on top of AX.25 useful for routing. A comprehensive listing of all the software for Linux amateur radio users as well as information about how to configure an AX.25 port is - contained in the AX25-HOWTO, available from - . You also might want to - check out the file . More - information about digital amateur radio in general is on the WWW at + contained in the Linux Ham Wiki, available from + . You also might want to check out the + file . More information about + digital amateur radio in general is on the WWW at . To compile this driver as a module, choose M here: the @@ -84,27 +97,25 @@ config NETROM config ROSE tristate "Amateur Radio X.25 PLP (Rose)" depends on AX25 - ---help--- + help The Packet Layer Protocol (PLP) is a way to route packets over X.25 connections in general and amateur radio AX.25 connections in particular, essentially an alternative to NET/ROM. A comprehensive listing of all the software for Linux amateur radio users as well as information about how to configure an AX.25 port is - contained in the AX25-HOWTO, available from - . You also might want to - check out the file . More - information about digital amateur radio in general is on the WWW at + contained in the Linux Ham Wiki, available from + . You also might want to check out the + file . More information about + digital amateur radio in general is on the WWW at . To compile this driver as a module, choose M here: the module will be called rose. - menu "AX.25 network device drivers" - depends on HAMRADIO && NET && AX25!=n + depends on HAMRADIO && AX25 source "drivers/net/hamradio/Kconfig" endmenu - -- cgit v1.2.3 From 837ca6ddb440c186eaa8e01b69486581d3457f2c Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Fri, 23 Mar 2007 17:33:54 -0300 Subject: ACPI: ibm-acpi: kill trailing whitespace I shall protect the ibm-acpi city against the invasion of the barbarian blanks! To the unforgiving jaws of sed s/[[:blank:]]\+$// they go! Signed-off-by: Henrique de Moraes Holschuh Signed-off-by: Len Brown --- Documentation/ibm-acpi.txt | 6 +++--- drivers/acpi/ibm_acpi.c | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'Documentation') diff --git a/Documentation/ibm-acpi.txt b/Documentation/ibm-acpi.txt index 0132d363feb5..cdcef016907a 100644 --- a/Documentation/ibm-acpi.txt +++ b/Documentation/ibm-acpi.txt @@ -21,7 +21,7 @@ detailed description): - Fn key combinations - Bluetooth enable and disable - - video output switching, expansion control + - video output switching, expansion control - ThinkLight on and off - limited docking and undocking - UltraBay eject @@ -472,7 +472,7 @@ This feature dumps the values of 256 embedded controller registers. Values which have changed since the last time the registers were dumped are marked with a star: -[root@x40 ibm-acpi]# cat /proc/acpi/ibm/ecdump +[root@x40 ibm-acpi]# cat /proc/acpi/ibm/ecdump EC +00 +01 +02 +03 +04 +05 +06 +07 +08 +09 +0a +0b +0c +0d +0e +0f EC 0x00: a7 47 87 01 fe 96 00 08 01 00 cb 00 00 00 40 00 EC 0x10: 00 00 ff ff f4 3c 87 09 01 ff 42 01 ff ff 0d 00 @@ -503,7 +503,7 @@ vary. The second ensures that the fan-related values do vary, since the fan speed fluctuates a bit. The third will (hopefully) mark the fan register with a star: -[root@x40 ibm-acpi]# cat /proc/acpi/ibm/ecdump +[root@x40 ibm-acpi]# cat /proc/acpi/ibm/ecdump EC +00 +01 +02 +03 +04 +05 +06 +07 +08 +09 +0a +0b +0c +0d +0e +0f EC 0x00: a7 47 87 01 fe 96 00 08 01 00 cb 00 00 00 40 00 EC 0x10: 00 00 ff ff f4 3c 87 09 01 ff 42 01 ff ff 0d 00 diff --git a/drivers/acpi/ibm_acpi.c b/drivers/acpi/ibm_acpi.c index dc1096608f43..60aaed92eef9 100644 --- a/drivers/acpi/ibm_acpi.c +++ b/drivers/acpi/ibm_acpi.c @@ -28,7 +28,7 @@ * 2006-11-22 0.13 new maintainer * changelog now lives in git commit history, and will * not be updated further in-file. - * + * * 2005-08-17 0.12 fix compilation on 2.6.13-rc kernels * 2005-03-17 0.11 support for 600e, 770x * thanks to Jamie Lentin @@ -38,7 +38,7 @@ * experimental brightness control * experimental volume control * experimental fan enable/disable - * 2005-01-16 0.10 fix module loading on R30, R31 + * 2005-01-16 0.10 fix module loading on R30, R31 * 2005-01-16 0.9 support for 570, R30, R31 * ultrabay support on A22p, A3x * limit arg for cmos, led, beep, drop experimental status @@ -161,7 +161,7 @@ IBM_HANDLE(dock, root, "\\_SB.GDCK", /* X30, X31, X40 */ #ifdef CONFIG_ACPI_IBM_BAY IBM_HANDLE(bay, root, "\\_SB.PCI.IDE.SECN.MAST", /* 570 */ "\\_SB.PCI0.IDE0.IDES.IDSM", /* 600e/x, 770e, 770x */ - "\\_SB.PCI0.SATA.SCND.MSTR", /* T60, X60, Z60 */ + "\\_SB.PCI0.SATA.SCND.MSTR", /* T60, X60, Z60 */ "\\_SB.PCI0.IDE0.SCND.MSTR", /* all others */ ); /* A21e, R30, R31 */ -- cgit v1.2.3 From 38f996ed21089fa4ae40526a5f428e3c792ea561 Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Fri, 23 Mar 2007 17:33:59 -0300 Subject: ACPI: ibm-acpi: update documentation Update documentation header, and relocate a hunk of text that was missplaced. Signed-off-by: Henrique de Moraes Holschuh Signed-off-by: Len Brown --- Documentation/ibm-acpi.txt | 85 ++++++++++++++-------------------------------- 1 file changed, 25 insertions(+), 60 deletions(-) (limited to 'Documentation') diff --git a/Documentation/ibm-acpi.txt b/Documentation/ibm-acpi.txt index cdcef016907a..f409f4bbdc47 100644 --- a/Documentation/ibm-acpi.txt +++ b/Documentation/ibm-acpi.txt @@ -1,16 +1,17 @@ IBM ThinkPad ACPI Extras Driver - Version 0.12 - 17 August 2005 + Version 0.13 + 31 December 2006 Borislav Deianov + Henrique de Moraes Holschuh http://ibm-acpi.sf.net/ This is a Linux ACPI driver for the IBM ThinkPad laptops. It supports various features of these laptops which are accessible through the -ACPI framework but not otherwise supported by the generic Linux ACPI -drivers. +ACPI framework but not otherwise fully supported by the generic Linux +ACPI drivers. Status @@ -638,6 +639,26 @@ The ThinkPad's ACPI DSDT code will reprogram the fan on its own when certain conditions are met. It will override any fan programming done through ibm-acpi. +The ibm-acpi kernel driver can be programmed to revert the fan level +to a safe setting if userspace does not issue one of the fan commands: +"enable", "disable", "level" or "watchdog" within a configurable +ammount of time. To do this, use the "watchdog" command. + + echo 'watchdog ' > /proc/acpi/ibm/fan + +Interval is the ammount of time in seconds to wait for one of the +above mentioned fan commands before reseting the fan level to a safe +one. If set to zero, the watchdog is disabled (default). When the +watchdog timer runs out, it does the exact equivalent of the "enable" +fan command. + +Note that the watchdog timer stops after it enables the fan. It will +be rearmed again automatically (using the same interval) when one of +the above mentioned fan commands is received. The fan watchdog is, +therefore, not suitable to protect against fan mode changes made +through means other than the "enable", "disable", and "level" fan +commands. + EXPERIMENTAL: WAN -- /proc/acpi/ibm/wan --------------------------------------- @@ -670,59 +691,3 @@ example: modprobe ibm_acpi hotkey=enable,0xffff video=auto_disable -The ibm-acpi kernel driver can be programmed to revert the fan level -to a safe setting if userspace does not issue one of the fan commands: -"enable", "disable", "level" or "watchdog" within a configurable -ammount of time. To do this, use the "watchdog" command. - - echo 'watchdog ' > /proc/acpi/ibm/fan - -Interval is the ammount of time in seconds to wait for one of the -above mentioned fan commands before reseting the fan level to a safe -one. If set to zero, the watchdog is disabled (default). When the -watchdog timer runs out, it does the exact equivalent of the "enable" -fan command. - -Note that the watchdog timer stops after it enables the fan. It will -be rearmed again automatically (using the same interval) when one of -the above mentioned fan commands is received. The fan watchdog is, -therefore, not suitable to protect against fan mode changes made -through means other than the "enable", "disable", and "level" fan -commands. - - -Example Configuration ---------------------- - -The ACPI support in the kernel is intended to be used in conjunction -with a user-space daemon, acpid. The configuration files for this -daemon control what actions are taken in response to various ACPI -events. An example set of configuration files are included in the -config/ directory of the tarball package available on the web -site. Note that these are provided for illustration purposes only and -may need to be adapted to your particular setup. - -The following utility scripts are used by the example action -scripts (included with ibm-acpi for completeness): - - /usr/local/sbin/idectl -- from the hdparm source distribution, - see http://www.ibiblio.org/pub/Linux/system/hardware - /usr/local/sbin/laptop_mode -- from the Linux kernel source - distribution, see Documentation/laptop-mode.txt - /sbin/service -- comes with Redhat/Fedora distributions - /usr/sbin/hibernate -- from the Software Suspend 2 distribution, - see http://softwaresuspend.berlios.de/ - -Toan T Nguyen notes that Suse uses the -powersave program to suspend ('powersave --suspend-to-ram') or -hibernate ('powersave --suspend-to-disk'). This means that the -hibernate script is not needed on that distribution. - -Henrik Brix Andersen has written a Gentoo ACPI event -handler script for the X31. You can get the latest version from -http://dev.gentoo.org/~brix/files/x31.sh - -David Schweikert has written an alternative blank.sh -script which works on Debian systems. This scripts has now been -extended to also work on Fedora systems and included as the default -blank.sh in the distribution. -- cgit v1.2.3 From 5fabdb94394bef0651479fc14394121c60d5aff7 Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Thu, 22 Mar 2007 16:53:40 -0600 Subject: PCI: Fix up PCI power management doc Update the documentation of PCI power management functions. Signed-off-by: Jonathan Corbet Signed-off-by: Greg Kroah-Hartman --- Documentation/power/pci.txt | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) (limited to 'Documentation') diff --git a/Documentation/power/pci.txt b/Documentation/power/pci.txt index c750f9f2e76e..b6a3cbf7e846 100644 --- a/Documentation/power/pci.txt +++ b/Documentation/power/pci.txt @@ -102,31 +102,28 @@ pci_save_state -------------- Usage: - pci_save_state(dev, buffer); + pci_save_state(struct pci_dev *dev); Description: - Save first 64 bytes of PCI config space. Buffer must be allocated by - caller. + Save first 64 bytes of PCI config space, along with any additional + PCI-Express or PCI-X information. pci_restore_state ----------------- Usage: - pci_restore_state(dev, buffer); + pci_restore_state(struct pci_dev *dev); Description: - Restore previously saved config space. (First 64 bytes only); - - If buffer is NULL, then restore what information we know about the - device from bootup: BARs and interrupt line. + Restore previously saved config space. pci_set_power_state ------------------- Usage: - pci_set_power_state(dev, state); + pci_set_power_state(struct pci_dev *dev, pci_power_t state); Description: Transition device to low power state using PCI PM Capabilities @@ -142,7 +139,7 @@ pci_enable_wake --------------- Usage: - pci_enable_wake(dev, state, enable); + pci_enable_wake(struct pci_dev *dev, pci_power_t state, int enable); Description: Enable device to generate PME# during low power state using PCI PM -- cgit v1.2.3 From 4e381a4f06e3c7b350b55a2636b9d45691780eba Mon Sep 17 00:00:00 2001 From: Len Brown Date: Fri, 30 Mar 2007 14:16:10 -0400 Subject: Revert "ACPI: parse 2nd MADT by default" This reverts commit 09fe58356d148ff66901ddf639e725ca1a48a0af. http://bugzilla.kernel.org/show_bug.cgi?id=8283 Signed-off-by: Len Brown --- Documentation/kernel-parameters.txt | 2 +- drivers/acpi/tables.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'Documentation') diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index ef2ffded1392..12533a958c51 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -142,7 +142,7 @@ and is between 256 and 4096 characters. It is defined in the file Format: 2: use 2nd APIC table, if available 1,0: use 1st APIC table - default: 2 + default: 0 acpi_sleep= [HW,ACPI] Sleep options Format: { s3_bios, s3_mode } diff --git a/drivers/acpi/tables.c b/drivers/acpi/tables.c index 96792a6cc164..c3419182c9a7 100644 --- a/drivers/acpi/tables.c +++ b/drivers/acpi/tables.c @@ -42,7 +42,7 @@ static char *mps_inti_flags_trigger[] = { "dfl", "edge", "res", "level" }; static struct acpi_table_desc initial_tables[ACPI_MAX_TABLES] __initdata; -static int acpi_apic_instance __initdata = 2; +static int acpi_apic_instance __initdata; void acpi_table_print_madt_entry(struct acpi_subtable_header *header) { -- cgit v1.2.3 From f21f85de4b3b9ad4a671fb19a889c16db2ea38b2 Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Thu, 29 Mar 2007 01:58:40 -0300 Subject: ACPI: ibm-acpi: rename driver to thinkpad-acpi Rename the ibm-acpi driver to thinkpad-acpi. ThinkPads are not even made by IBM anymore, so it is high time to rename the driver... The name thinkpad-acpi was used sometime ago by a thinkpad-specific hotkey driver by Erik Rigtorp, around the 2.6.8-2.6.10 time frame. The driver apparently never got merged into mainline (it did make some trips through -mm). ibm-acpi was merged soon after, making its debut in 2.6.10. The reuse of the thinkpad-acpi name shouldn't be a problem as far as user confusion goes, as Erik's thinkpad-acpi apparently didn't get widespread use in the Linux ThinkPad community and most hits for thinkpad-acpi in google point to ibm-acpi anyway. Erik, if you read this, please consider the reuse of the thinkpad-acpi name as a compliment to your effort to make ThinkPads more useful to all of us. Signed-off-by: Henrique de Moraes Holschuh Signed-off-by: Len Brown --- Documentation/ibm-acpi.txt | 693 ---------- Documentation/thinkpad-acpi.txt | 693 ++++++++++ drivers/misc/Makefile | 2 +- drivers/misc/ibm_acpi.c | 2783 --------------------------------------- drivers/misc/ibm_acpi.h | 437 ------ drivers/misc/thinkpad_acpi.c | 2783 +++++++++++++++++++++++++++++++++++++++ drivers/misc/thinkpad_acpi.h | 437 ++++++ 7 files changed, 3914 insertions(+), 3914 deletions(-) delete mode 100644 Documentation/ibm-acpi.txt create mode 100644 Documentation/thinkpad-acpi.txt delete mode 100644 drivers/misc/ibm_acpi.c delete mode 100644 drivers/misc/ibm_acpi.h create mode 100644 drivers/misc/thinkpad_acpi.c create mode 100644 drivers/misc/thinkpad_acpi.h (limited to 'Documentation') diff --git a/Documentation/ibm-acpi.txt b/Documentation/ibm-acpi.txt deleted file mode 100644 index f409f4bbdc47..000000000000 --- a/Documentation/ibm-acpi.txt +++ /dev/null @@ -1,693 +0,0 @@ - IBM ThinkPad ACPI Extras Driver - - Version 0.13 - 31 December 2006 - - Borislav Deianov - Henrique de Moraes Holschuh - http://ibm-acpi.sf.net/ - - -This is a Linux ACPI driver for the IBM ThinkPad laptops. It supports -various features of these laptops which are accessible through the -ACPI framework but not otherwise fully supported by the generic Linux -ACPI drivers. - - -Status ------- - -The features currently supported are the following (see below for -detailed description): - - - Fn key combinations - - Bluetooth enable and disable - - video output switching, expansion control - - ThinkLight on and off - - limited docking and undocking - - UltraBay eject - - CMOS control - - LED control - - ACPI sounds - - temperature sensors - - Experimental: embedded controller register dump - - LCD brightness control - - Volume control - - Experimental: fan speed, fan enable/disable - - Experimental: WAN enable and disable - -A compatibility table by model and feature is maintained on the web -site, http://ibm-acpi.sf.net/. I appreciate any success or failure -reports, especially if they add to or correct the compatibility table. -Please include the following information in your report: - - - ThinkPad model name - - a copy of your DSDT, from /proc/acpi/dsdt - - which driver features work and which don't - - the observed behavior of non-working features - -Any other comments or patches are also more than welcome. - - -Installation ------------- - -If you are compiling this driver as included in the Linux kernel -sources, simply enable the CONFIG_ACPI_IBM option (Power Management / -ACPI / IBM ThinkPad Laptop Extras). - -Features --------- - -The driver creates the /proc/acpi/ibm directory. There is a file under -that directory for each feature described below. Note that while the -driver is still in the alpha stage, the exact proc file format and -commands supported by the various features is guaranteed to change -frequently. - -Driver version -- /proc/acpi/ibm/driver ---------------------------------------- - -The driver name and version. No commands can be written to this file. - -Hot keys -- /proc/acpi/ibm/hotkey ---------------------------------- - -Without this driver, only the Fn-F4 key (sleep button) generates an -ACPI event. With the driver loaded, the hotkey feature enabled and the -mask set (see below), the various hot keys generate ACPI events in the -following format: - - ibm/hotkey HKEY 00000080 0000xxxx - -The last four digits vary depending on the key combination pressed. -All labeled Fn-Fx key combinations generate distinct events. In -addition, the lid microswitch and some docking station buttons may -also generate such events. - -The following commands can be written to this file: - - echo enable > /proc/acpi/ibm/hotkey -- enable the hot keys feature - echo disable > /proc/acpi/ibm/hotkey -- disable the hot keys feature - echo 0xffff > /proc/acpi/ibm/hotkey -- enable all possible hot keys - echo 0x0000 > /proc/acpi/ibm/hotkey -- disable all possible hot keys - ... any other 4-hex-digit mask ... - echo reset > /proc/acpi/ibm/hotkey -- restore the original mask - -The bit mask allows some control over which hot keys generate ACPI -events. Not all bits in the mask can be modified. Not all bits that -can be modified do anything. Not all hot keys can be individually -controlled by the mask. Most recent ThinkPad models honor the -following bits (assuming the hot keys feature has been enabled): - - key bit behavior when set behavior when unset - - Fn-F3 always generates ACPI event - Fn-F4 always generates ACPI event - Fn-F5 0010 generate ACPI event enable/disable Bluetooth - Fn-F7 0040 generate ACPI event switch LCD and external display - Fn-F8 0080 generate ACPI event expand screen or none - Fn-F9 0100 generate ACPI event none - Fn-F12 always generates ACPI event - -Some models do not support all of the above. For example, the T30 does -not support Fn-F5 and Fn-F9. Other models do not support the mask at -all. On those models, hot keys cannot be controlled individually. - -Note that enabling ACPI events for some keys prevents their default -behavior. For example, if events for Fn-F5 are enabled, that key will -no longer enable/disable Bluetooth by itself. This can still be done -from an acpid handler for the ibm/hotkey event. - -Note also that not all Fn key combinations are supported through -ACPI. For example, on the X40, the brightness, volume and "Access IBM" -buttons do not generate ACPI events even with this driver. They *can* -be used through the "ThinkPad Buttons" utility, see -http://www.nongnu.org/tpb/ - -Bluetooth -- /proc/acpi/ibm/bluetooth -------------------------------------- - -This feature shows the presence and current state of a Bluetooth -device. If Bluetooth is installed, the following commands can be used: - - echo enable > /proc/acpi/ibm/bluetooth - echo disable > /proc/acpi/ibm/bluetooth - -Video output control -- /proc/acpi/ibm/video --------------------------------------------- - -This feature allows control over the devices used for video output - -LCD, CRT or DVI (if available). The following commands are available: - - echo lcd_enable > /proc/acpi/ibm/video - echo lcd_disable > /proc/acpi/ibm/video - echo crt_enable > /proc/acpi/ibm/video - echo crt_disable > /proc/acpi/ibm/video - echo dvi_enable > /proc/acpi/ibm/video - echo dvi_disable > /proc/acpi/ibm/video - echo auto_enable > /proc/acpi/ibm/video - echo auto_disable > /proc/acpi/ibm/video - echo expand_toggle > /proc/acpi/ibm/video - echo video_switch > /proc/acpi/ibm/video - -Each video output device can be enabled or disabled individually. -Reading /proc/acpi/ibm/video shows the status of each device. - -Automatic video switching can be enabled or disabled. When automatic -video switching is enabled, certain events (e.g. opening the lid, -docking or undocking) cause the video output device to change -automatically. While this can be useful, it also causes flickering -and, on the X40, video corruption. By disabling automatic switching, -the flickering or video corruption can be avoided. - -The video_switch command cycles through the available video outputs -(it simulates the behavior of Fn-F7). - -Video expansion can be toggled through this feature. This controls -whether the display is expanded to fill the entire LCD screen when a -mode with less than full resolution is used. Note that the current -video expansion status cannot be determined through this feature. - -Note that on many models (particularly those using Radeon graphics -chips) the X driver configures the video card in a way which prevents -Fn-F7 from working. This also disables the video output switching -features of this driver, as it uses the same ACPI methods as -Fn-F7. Video switching on the console should still work. - -UPDATE: There's now a patch for the X.org Radeon driver which -addresses this issue. Some people are reporting success with the patch -while others are still having problems. For more information: - -https://bugs.freedesktop.org/show_bug.cgi?id=2000 - -ThinkLight control -- /proc/acpi/ibm/light ------------------------------------------- - -The current status of the ThinkLight can be found in this file. A few -models which do not make the status available will show it as -"unknown". The available commands are: - - echo on > /proc/acpi/ibm/light - echo off > /proc/acpi/ibm/light - -Docking / undocking -- /proc/acpi/ibm/dock ------------------------------------------- - -Docking and undocking (e.g. with the X4 UltraBase) requires some -actions to be taken by the operating system to safely make or break -the electrical connections with the dock. - -The docking feature of this driver generates the following ACPI events: - - ibm/dock GDCK 00000003 00000001 -- eject request - ibm/dock GDCK 00000003 00000002 -- undocked - ibm/dock GDCK 00000000 00000003 -- docked - -NOTE: These events will only be generated if the laptop was docked -when originally booted. This is due to the current lack of support for -hot plugging of devices in the Linux ACPI framework. If the laptop was -booted while not in the dock, the following message is shown in the -logs: - - Mar 17 01:42:34 aero kernel: ibm_acpi: dock device not present - -In this case, no dock-related events are generated but the dock and -undock commands described below still work. They can be executed -manually or triggered by Fn key combinations (see the example acpid -configuration files included in the driver tarball package available -on the web site). - -When the eject request button on the dock is pressed, the first event -above is generated. The handler for this event should issue the -following command: - - echo undock > /proc/acpi/ibm/dock - -After the LED on the dock goes off, it is safe to eject the laptop. -Note: if you pressed this key by mistake, go ahead and eject the -laptop, then dock it back in. Otherwise, the dock may not function as -expected. - -When the laptop is docked, the third event above is generated. The -handler for this event should issue the following command to fully -enable the dock: - - echo dock > /proc/acpi/ibm/dock - -The contents of the /proc/acpi/ibm/dock file shows the current status -of the dock, as provided by the ACPI framework. - -The docking support in this driver does not take care of enabling or -disabling any other devices you may have attached to the dock. For -example, a CD drive plugged into the UltraBase needs to be disabled or -enabled separately. See the provided example acpid configuration files -for how this can be accomplished. - -There is no support yet for PCI devices that may be attached to a -docking station, e.g. in the ThinkPad Dock II. The driver currently -does not recognize, enable or disable such devices. This means that -the only docking stations currently supported are the X-series -UltraBase docks and "dumb" port replicators like the Mini Dock (the -latter don't need any ACPI support, actually). - -UltraBay eject -- /proc/acpi/ibm/bay ------------------------------------- - -Inserting or ejecting an UltraBay device requires some actions to be -taken by the operating system to safely make or break the electrical -connections with the device. - -This feature generates the following ACPI events: - - ibm/bay MSTR 00000003 00000000 -- eject request - ibm/bay MSTR 00000001 00000000 -- eject lever inserted - -NOTE: These events will only be generated if the UltraBay was present -when the laptop was originally booted (on the X series, the UltraBay -is in the dock, so it may not be present if the laptop was undocked). -This is due to the current lack of support for hot plugging of devices -in the Linux ACPI framework. If the laptop was booted without the -UltraBay, the following message is shown in the logs: - - Mar 17 01:42:34 aero kernel: ibm_acpi: bay device not present - -In this case, no bay-related events are generated but the eject -command described below still works. It can be executed manually or -triggered by a hot key combination. - -Sliding the eject lever generates the first event shown above. The -handler for this event should take whatever actions are necessary to -shut down the device in the UltraBay (e.g. call idectl), then issue -the following command: - - echo eject > /proc/acpi/ibm/bay - -After the LED on the UltraBay goes off, it is safe to pull out the -device. - -When the eject lever is inserted, the second event above is -generated. The handler for this event should take whatever actions are -necessary to enable the UltraBay device (e.g. call idectl). - -The contents of the /proc/acpi/ibm/bay file shows the current status -of the UltraBay, as provided by the ACPI framework. - -EXPERIMENTAL warm eject support on the 600e/x, A22p and A3x (To use -this feature, you need to supply the experimental=1 parameter when -loading the module): - -These models do not have a button near the UltraBay device to request -a hot eject but rather require the laptop to be put to sleep -(suspend-to-ram) before the bay device is ejected or inserted). -The sequence of steps to eject the device is as follows: - - echo eject > /proc/acpi/ibm/bay - put the ThinkPad to sleep - remove the drive - resume from sleep - cat /proc/acpi/ibm/bay should show that the drive was removed - -On the A3x, both the UltraBay 2000 and UltraBay Plus devices are -supported. Use "eject2" instead of "eject" for the second bay. - -Note: the UltraBay eject support on the 600e/x, A22p and A3x is -EXPERIMENTAL and may not work as expected. USE WITH CAUTION! - -CMOS control -- /proc/acpi/ibm/cmos ------------------------------------ - -This feature is used internally by the ACPI firmware to control the -ThinkLight on most newer ThinkPad models. It may also control LCD -brightness, sounds volume and more, but only on some models. - -The commands are non-negative integer numbers: - - echo 0 >/proc/acpi/ibm/cmos - echo 1 >/proc/acpi/ibm/cmos - echo 2 >/proc/acpi/ibm/cmos - ... - -The range of valid numbers is 0 to 21, but not all have an effect and -the behavior varies from model to model. Here is the behavior on the -X40 (tpb is the ThinkPad Buttons utility): - - 0 - no effect but tpb reports "Volume down" - 1 - no effect but tpb reports "Volume up" - 2 - no effect but tpb reports "Mute on" - 3 - simulate pressing the "Access IBM" button - 4 - LCD brightness up - 5 - LCD brightness down - 11 - toggle screen expansion - 12 - ThinkLight on - 13 - ThinkLight off - 14 - no effect but tpb reports ThinkLight status change - -LED control -- /proc/acpi/ibm/led ---------------------------------- - -Some of the LED indicators can be controlled through this feature. The -available commands are: - - echo ' on' >/proc/acpi/ibm/led - echo ' off' >/proc/acpi/ibm/led - echo ' blink' >/proc/acpi/ibm/led - -The range is 0 to 7. The set of LEDs that can be -controlled varies from model to model. Here is the mapping on the X40: - - 0 - power - 1 - battery (orange) - 2 - battery (green) - 3 - UltraBase - 4 - UltraBay - 7 - standby - -All of the above can be turned on and off and can be made to blink. - -ACPI sounds -- /proc/acpi/ibm/beep ----------------------------------- - -The BEEP method is used internally by the ACPI firmware to provide -audible alerts in various situations. This feature allows the same -sounds to be triggered manually. - -The commands are non-negative integer numbers: - - echo >/proc/acpi/ibm/beep - -The valid range is 0 to 17. Not all numbers trigger sounds -and the sounds vary from model to model. Here is the behavior on the -X40: - - 0 - stop a sound in progress (but use 17 to stop 16) - 2 - two beeps, pause, third beep ("low battery") - 3 - single beep - 4 - high, followed by low-pitched beep ("unable") - 5 - single beep - 6 - very high, followed by high-pitched beep ("AC/DC") - 7 - high-pitched beep - 9 - three short beeps - 10 - very long beep - 12 - low-pitched beep - 15 - three high-pitched beeps repeating constantly, stop with 0 - 16 - one medium-pitched beep repeating constantly, stop with 17 - 17 - stop 16 - -Temperature sensors -- /proc/acpi/ibm/thermal ---------------------------------------------- - -Most ThinkPads include six or more separate temperature sensors but -only expose the CPU temperature through the standard ACPI methods. -This feature shows readings from up to eight different sensors on older -ThinkPads, and it has experimental support for up to sixteen different -sensors on newer ThinkPads. Readings from sensors that are not available -return -128. - -No commands can be written to this file. - -EXPERIMENTAL: The 16-sensors feature is marked EXPERIMENTAL because the -implementation directly accesses hardware registers and may not work as -expected. USE WITH CAUTION! To use this feature, you need to supply the -experimental=1 parameter when loading the module. When EXPERIMENTAL -mode is enabled, reading the first 8 sensors on newer ThinkPads will -also use an new experimental thermal sensor access mode. - -For example, on the X40, a typical output may be: -temperatures: 42 42 45 41 36 -128 33 -128 - -EXPERIMENTAL: On the T43/p, a typical output may be: -temperatures: 48 48 36 52 38 -128 31 -128 48 52 48 -128 -128 -128 -128 -128 - -The mapping of thermal sensors to physical locations varies depending on -system-board model (and thus, on ThinkPad model). - -http://thinkwiki.org/wiki/Thermal_Sensors is a public wiki page that -tries to track down these locations for various models. - -Most (newer?) models seem to follow this pattern: - -1: CPU -2: (depends on model) -3: (depends on model) -4: GPU -5: Main battery: main sensor -6: Bay battery: main sensor -7: Main battery: secondary sensor -8: Bay battery: secondary sensor -9-15: (depends on model) - -For the R51 (source: Thomas Gruber): -2: Mini-PCI -3: Internal HDD - -For the T43, T43/p (source: Shmidoax/Thinkwiki.org) -http://thinkwiki.org/wiki/Thermal_Sensors#ThinkPad_T43.2C_T43p -2: System board, left side (near PCMCIA slot), reported as HDAPS temp -3: PCMCIA slot -9: MCH (northbridge) to DRAM Bus -10: ICH (southbridge), under Mini-PCI card, under touchpad -11: Power regulator, underside of system board, below F2 key - -The A31 has a very atypical layout for the thermal sensors -(source: Milos Popovic, http://thinkwiki.org/wiki/Thermal_Sensors#ThinkPad_A31) -1: CPU -2: Main Battery: main sensor -3: Power Converter -4: Bay Battery: main sensor -5: MCH (northbridge) -6: PCMCIA/ambient -7: Main Battery: secondary sensor -8: Bay Battery: secondary sensor - - -EXPERIMENTAL: Embedded controller register dump -- /proc/acpi/ibm/ecdump ------------------------------------------------------------------------- - -This feature is marked EXPERIMENTAL because the implementation -directly accesses hardware registers and may not work as expected. USE -WITH CAUTION! To use this feature, you need to supply the -experimental=1 parameter when loading the module. - -This feature dumps the values of 256 embedded controller -registers. Values which have changed since the last time the registers -were dumped are marked with a star: - -[root@x40 ibm-acpi]# cat /proc/acpi/ibm/ecdump -EC +00 +01 +02 +03 +04 +05 +06 +07 +08 +09 +0a +0b +0c +0d +0e +0f -EC 0x00: a7 47 87 01 fe 96 00 08 01 00 cb 00 00 00 40 00 -EC 0x10: 00 00 ff ff f4 3c 87 09 01 ff 42 01 ff ff 0d 00 -EC 0x20: 00 00 00 00 00 00 00 00 00 00 00 03 43 00 00 80 -EC 0x30: 01 07 1a 00 30 04 00 00 *85 00 00 10 00 50 00 00 -EC 0x40: 00 00 00 00 00 00 14 01 00 04 00 00 00 00 00 00 -EC 0x50: 00 c0 02 0d 00 01 01 02 02 03 03 03 03 *bc *02 *bc -EC 0x60: *02 *bc *02 00 00 00 00 00 00 00 00 00 00 00 00 00 -EC 0x70: 00 00 00 00 00 12 30 40 *24 *26 *2c *27 *20 80 *1f 80 -EC 0x80: 00 00 00 06 *37 *0e 03 00 00 00 0e 07 00 00 00 00 -EC 0x90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -EC 0xa0: *ff 09 ff 09 ff ff *64 00 *00 *00 *a2 41 *ff *ff *e0 00 -EC 0xb0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -EC 0xc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -EC 0xd0: 03 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -EC 0xe0: 00 00 00 00 00 00 00 00 11 20 49 04 24 06 55 03 -EC 0xf0: 31 55 48 54 35 38 57 57 08 2f 45 73 07 65 6c 1a - -This feature can be used to determine the register holding the fan -speed on some models. To do that, do the following: - - - make sure the battery is fully charged - - make sure the fan is running - - run 'cat /proc/acpi/ibm/ecdump' several times, once per second or so - -The first step makes sure various charging-related values don't -vary. The second ensures that the fan-related values do vary, since -the fan speed fluctuates a bit. The third will (hopefully) mark the -fan register with a star: - -[root@x40 ibm-acpi]# cat /proc/acpi/ibm/ecdump -EC +00 +01 +02 +03 +04 +05 +06 +07 +08 +09 +0a +0b +0c +0d +0e +0f -EC 0x00: a7 47 87 01 fe 96 00 08 01 00 cb 00 00 00 40 00 -EC 0x10: 00 00 ff ff f4 3c 87 09 01 ff 42 01 ff ff 0d 00 -EC 0x20: 00 00 00 00 00 00 00 00 00 00 00 03 43 00 00 80 -EC 0x30: 01 07 1a 00 30 04 00 00 85 00 00 10 00 50 00 00 -EC 0x40: 00 00 00 00 00 00 14 01 00 04 00 00 00 00 00 00 -EC 0x50: 00 c0 02 0d 00 01 01 02 02 03 03 03 03 bc 02 bc -EC 0x60: 02 bc 02 00 00 00 00 00 00 00 00 00 00 00 00 00 -EC 0x70: 00 00 00 00 00 12 30 40 24 27 2c 27 21 80 1f 80 -EC 0x80: 00 00 00 06 *be 0d 03 00 00 00 0e 07 00 00 00 00 -EC 0x90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -EC 0xa0: ff 09 ff 09 ff ff 64 00 00 00 a2 41 ff ff e0 00 -EC 0xb0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -EC 0xc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -EC 0xd0: 03 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -EC 0xe0: 00 00 00 00 00 00 00 00 11 20 49 04 24 06 55 03 -EC 0xf0: 31 55 48 54 35 38 57 57 08 2f 45 73 07 65 6c 1a - -Another set of values that varies often is the temperature -readings. Since temperatures don't change vary fast, you can take -several quick dumps to eliminate them. - -You can use a similar method to figure out the meaning of other -embedded controller registers - e.g. make sure nothing else changes -except the charging or discharging battery to determine which -registers contain the current battery capacity, etc. If you experiment -with this, do send me your results (including some complete dumps with -a description of the conditions when they were taken.) - -LCD brightness control -- /proc/acpi/ibm/brightness ---------------------------------------------------- - -This feature allows software control of the LCD brightness on ThinkPad -models which don't have a hardware brightness slider. The available -commands are: - - echo up >/proc/acpi/ibm/brightness - echo down >/proc/acpi/ibm/brightness - echo 'level ' >/proc/acpi/ibm/brightness - -The number range is 0 to 7, although not all of them may be -distinct. The current brightness level is shown in the file. - -Volume control -- /proc/acpi/ibm/volume ---------------------------------------- - -This feature allows volume control on ThinkPad models which don't have -a hardware volume knob. The available commands are: - - echo up >/proc/acpi/ibm/volume - echo down >/proc/acpi/ibm/volume - echo mute >/proc/acpi/ibm/volume - echo 'level ' >/proc/acpi/ibm/volume - -The number range is 0 to 15 although not all of them may be -distinct. The unmute the volume after the mute command, use either the -up or down command (the level command will not unmute the volume). -The current volume level and mute state is shown in the file. - -EXPERIMENTAL: fan speed, fan enable/disable -- /proc/acpi/ibm/fan ------------------------------------------------------------------ - -This feature is marked EXPERIMENTAL because the implementation -directly accesses hardware registers and may not work as expected. USE -WITH CAUTION! To use this feature, you need to supply the -experimental=1 parameter when loading the module. - -This feature attempts to show the current fan speed, control mode and -other fan data that might be available. The speed is read directly -from the hardware registers of the embedded controller. This is known -to work on later R, T and X series ThinkPads but may show a bogus -value on other models. - -Most ThinkPad fans work in "levels". Level 0 stops the fan. The higher -the level, the higher the fan speed, although adjacent levels often map -to the same fan speed. 7 is the highest level, where the fan reaches -the maximum recommended speed. Level "auto" means the EC changes the -fan level according to some internal algorithm, usually based on -readings from the thermal sensors. Level "disengaged" means the EC -disables the speed-locked closed-loop fan control, and drives the fan as -fast as it can go, which might exceed hardware limits, so use this level -with caution. - -The fan usually ramps up or down slowly from one speed to another, -and it is normal for the EC to take several seconds to react to fan -commands. - -The fan may be enabled or disabled with the following commands: - - echo enable >/proc/acpi/ibm/fan - echo disable >/proc/acpi/ibm/fan - -Placing a fan on level 0 is the same as disabling it. Enabling a fan -will try to place it in a safe level if it is too slow or disabled. - -WARNING WARNING WARNING: do not leave the fan disabled unless you are -monitoring all of the temperature sensor readings and you are ready to -enable it if necessary to avoid overheating. - -An enabled fan in level "auto" may stop spinning if the EC decides the -ThinkPad is cool enough and doesn't need the extra airflow. This is -normal, and the EC will spin the fan up if the varios thermal readings -rise too much. - -On the X40, this seems to depend on the CPU and HDD temperatures. -Specifically, the fan is turned on when either the CPU temperature -climbs to 56 degrees or the HDD temperature climbs to 46 degrees. The -fan is turned off when the CPU temperature drops to 49 degrees and the -HDD temperature drops to 41 degrees. These thresholds cannot -currently be controlled. - -The fan level can be controlled with the command: - - echo 'level ' > /proc/acpi/ibm/thermal - -Where is an integer from 0 to 7, or one of the words "auto" -or "disengaged" (without the quotes). Not all ThinkPads support the -"auto" and "disengaged" levels. - -On the X31 and X40 (and ONLY on those models), the fan speed can be -controlled to a certain degree. Once the fan is running, it can be -forced to run faster or slower with the following command: - - echo 'speed ' > /proc/acpi/ibm/thermal - -The sustainable range of fan speeds on the X40 appears to be from -about 3700 to about 7350. Values outside this range either do not have -any effect or the fan speed eventually settles somewhere in that -range. The fan cannot be stopped or started with this command. - -The ThinkPad's ACPI DSDT code will reprogram the fan on its own when -certain conditions are met. It will override any fan programming done -through ibm-acpi. - -The ibm-acpi kernel driver can be programmed to revert the fan level -to a safe setting if userspace does not issue one of the fan commands: -"enable", "disable", "level" or "watchdog" within a configurable -ammount of time. To do this, use the "watchdog" command. - - echo 'watchdog ' > /proc/acpi/ibm/fan - -Interval is the ammount of time in seconds to wait for one of the -above mentioned fan commands before reseting the fan level to a safe -one. If set to zero, the watchdog is disabled (default). When the -watchdog timer runs out, it does the exact equivalent of the "enable" -fan command. - -Note that the watchdog timer stops after it enables the fan. It will -be rearmed again automatically (using the same interval) when one of -the above mentioned fan commands is received. The fan watchdog is, -therefore, not suitable to protect against fan mode changes made -through means other than the "enable", "disable", and "level" fan -commands. - -EXPERIMENTAL: WAN -- /proc/acpi/ibm/wan ---------------------------------------- - -This feature is marked EXPERIMENTAL because the implementation -directly accesses hardware registers and may not work as expected. USE -WITH CAUTION! To use this feature, you need to supply the -experimental=1 parameter when loading the module. - -This feature shows the presence and current state of a WAN (Sierra -Wireless EV-DO) device. If WAN is installed, the following commands can -be used: - - echo enable > /proc/acpi/ibm/wan - echo disable > /proc/acpi/ibm/wan - -It was tested on a Lenovo Thinkpad X60. It should probably work on other -Thinkpad models which come with this module installed. - -Multiple Commands, Module Parameters ------------------------------------- - -Multiple commands can be written to the proc files in one shot by -separating them with commas, for example: - - echo enable,0xffff > /proc/acpi/ibm/hotkey - echo lcd_disable,crt_enable > /proc/acpi/ibm/video - -Commands can also be specified when loading the ibm_acpi module, for -example: - - modprobe ibm_acpi hotkey=enable,0xffff video=auto_disable - diff --git a/Documentation/thinkpad-acpi.txt b/Documentation/thinkpad-acpi.txt new file mode 100644 index 000000000000..f409f4bbdc47 --- /dev/null +++ b/Documentation/thinkpad-acpi.txt @@ -0,0 +1,693 @@ + IBM ThinkPad ACPI Extras Driver + + Version 0.13 + 31 December 2006 + + Borislav Deianov + Henrique de Moraes Holschuh + http://ibm-acpi.sf.net/ + + +This is a Linux ACPI driver for the IBM ThinkPad laptops. It supports +various features of these laptops which are accessible through the +ACPI framework but not otherwise fully supported by the generic Linux +ACPI drivers. + + +Status +------ + +The features currently supported are the following (see below for +detailed description): + + - Fn key combinations + - Bluetooth enable and disable + - video output switching, expansion control + - ThinkLight on and off + - limited docking and undocking + - UltraBay eject + - CMOS control + - LED control + - ACPI sounds + - temperature sensors + - Experimental: embedded controller register dump + - LCD brightness control + - Volume control + - Experimental: fan speed, fan enable/disable + - Experimental: WAN enable and disable + +A compatibility table by model and feature is maintained on the web +site, http://ibm-acpi.sf.net/. I appreciate any success or failure +reports, especially if they add to or correct the compatibility table. +Please include the following information in your report: + + - ThinkPad model name + - a copy of your DSDT, from /proc/acpi/dsdt + - which driver features work and which don't + - the observed behavior of non-working features + +Any other comments or patches are also more than welcome. + + +Installation +------------ + +If you are compiling this driver as included in the Linux kernel +sources, simply enable the CONFIG_ACPI_IBM option (Power Management / +ACPI / IBM ThinkPad Laptop Extras). + +Features +-------- + +The driver creates the /proc/acpi/ibm directory. There is a file under +that directory for each feature described below. Note that while the +driver is still in the alpha stage, the exact proc file format and +commands supported by the various features is guaranteed to change +frequently. + +Driver version -- /proc/acpi/ibm/driver +--------------------------------------- + +The driver name and version. No commands can be written to this file. + +Hot keys -- /proc/acpi/ibm/hotkey +--------------------------------- + +Without this driver, only the Fn-F4 key (sleep button) generates an +ACPI event. With the driver loaded, the hotkey feature enabled and the +mask set (see below), the various hot keys generate ACPI events in the +following format: + + ibm/hotkey HKEY 00000080 0000xxxx + +The last four digits vary depending on the key combination pressed. +All labeled Fn-Fx key combinations generate distinct events. In +addition, the lid microswitch and some docking station buttons may +also generate such events. + +The following commands can be written to this file: + + echo enable > /proc/acpi/ibm/hotkey -- enable the hot keys feature + echo disable > /proc/acpi/ibm/hotkey -- disable the hot keys feature + echo 0xffff > /proc/acpi/ibm/hotkey -- enable all possible hot keys + echo 0x0000 > /proc/acpi/ibm/hotkey -- disable all possible hot keys + ... any other 4-hex-digit mask ... + echo reset > /proc/acpi/ibm/hotkey -- restore the original mask + +The bit mask allows some control over which hot keys generate ACPI +events. Not all bits in the mask can be modified. Not all bits that +can be modified do anything. Not all hot keys can be individually +controlled by the mask. Most recent ThinkPad models honor the +following bits (assuming the hot keys feature has been enabled): + + key bit behavior when set behavior when unset + + Fn-F3 always generates ACPI event + Fn-F4 always generates ACPI event + Fn-F5 0010 generate ACPI event enable/disable Bluetooth + Fn-F7 0040 generate ACPI event switch LCD and external display + Fn-F8 0080 generate ACPI event expand screen or none + Fn-F9 0100 generate ACPI event none + Fn-F12 always generates ACPI event + +Some models do not support all of the above. For example, the T30 does +not support Fn-F5 and Fn-F9. Other models do not support the mask at +all. On those models, hot keys cannot be controlled individually. + +Note that enabling ACPI events for some keys prevents their default +behavior. For example, if events for Fn-F5 are enabled, that key will +no longer enable/disable Bluetooth by itself. This can still be done +from an acpid handler for the ibm/hotkey event. + +Note also that not all Fn key combinations are supported through +ACPI. For example, on the X40, the brightness, volume and "Access IBM" +buttons do not generate ACPI events even with this driver. They *can* +be used through the "ThinkPad Buttons" utility, see +http://www.nongnu.org/tpb/ + +Bluetooth -- /proc/acpi/ibm/bluetooth +------------------------------------- + +This feature shows the presence and current state of a Bluetooth +device. If Bluetooth is installed, the following commands can be used: + + echo enable > /proc/acpi/ibm/bluetooth + echo disable > /proc/acpi/ibm/bluetooth + +Video output control -- /proc/acpi/ibm/video +-------------------------------------------- + +This feature allows control over the devices used for video output - +LCD, CRT or DVI (if available). The following commands are available: + + echo lcd_enable > /proc/acpi/ibm/video + echo lcd_disable > /proc/acpi/ibm/video + echo crt_enable > /proc/acpi/ibm/video + echo crt_disable > /proc/acpi/ibm/video + echo dvi_enable > /proc/acpi/ibm/video + echo dvi_disable > /proc/acpi/ibm/video + echo auto_enable > /proc/acpi/ibm/video + echo auto_disable > /proc/acpi/ibm/video + echo expand_toggle > /proc/acpi/ibm/video + echo video_switch > /proc/acpi/ibm/video + +Each video output device can be enabled or disabled individually. +Reading /proc/acpi/ibm/video shows the status of each device. + +Automatic video switching can be enabled or disabled. When automatic +video switching is enabled, certain events (e.g. opening the lid, +docking or undocking) cause the video output device to change +automatically. While this can be useful, it also causes flickering +and, on the X40, video corruption. By disabling automatic switching, +the flickering or video corruption can be avoided. + +The video_switch command cycles through the available video outputs +(it simulates the behavior of Fn-F7). + +Video expansion can be toggled through this feature. This controls +whether the display is expanded to fill the entire LCD screen when a +mode with less than full resolution is used. Note that the current +video expansion status cannot be determined through this feature. + +Note that on many models (particularly those using Radeon graphics +chips) the X driver configures the video card in a way which prevents +Fn-F7 from working. This also disables the video output switching +features of this driver, as it uses the same ACPI methods as +Fn-F7. Video switching on the console should still work. + +UPDATE: There's now a patch for the X.org Radeon driver which +addresses this issue. Some people are reporting success with the patch +while others are still having problems. For more information: + +https://bugs.freedesktop.org/show_bug.cgi?id=2000 + +ThinkLight control -- /proc/acpi/ibm/light +------------------------------------------ + +The current status of the ThinkLight can be found in this file. A few +models which do not make the status available will show it as +"unknown". The available commands are: + + echo on > /proc/acpi/ibm/light + echo off > /proc/acpi/ibm/light + +Docking / undocking -- /proc/acpi/ibm/dock +------------------------------------------ + +Docking and undocking (e.g. with the X4 UltraBase) requires some +actions to be taken by the operating system to safely make or break +the electrical connections with the dock. + +The docking feature of this driver generates the following ACPI events: + + ibm/dock GDCK 00000003 00000001 -- eject request + ibm/dock GDCK 00000003 00000002 -- undocked + ibm/dock GDCK 00000000 00000003 -- docked + +NOTE: These events will only be generated if the laptop was docked +when originally booted. This is due to the current lack of support for +hot plugging of devices in the Linux ACPI framework. If the laptop was +booted while not in the dock, the following message is shown in the +logs: + + Mar 17 01:42:34 aero kernel: ibm_acpi: dock device not present + +In this case, no dock-related events are generated but the dock and +undock commands described below still work. They can be executed +manually or triggered by Fn key combinations (see the example acpid +configuration files included in the driver tarball package available +on the web site). + +When the eject request button on the dock is pressed, the first event +above is generated. The handler for this event should issue the +following command: + + echo undock > /proc/acpi/ibm/dock + +After the LED on the dock goes off, it is safe to eject the laptop. +Note: if you pressed this key by mistake, go ahead and eject the +laptop, then dock it back in. Otherwise, the dock may not function as +expected. + +When the laptop is docked, the third event above is generated. The +handler for this event should issue the following command to fully +enable the dock: + + echo dock > /proc/acpi/ibm/dock + +The contents of the /proc/acpi/ibm/dock file shows the current status +of the dock, as provided by the ACPI framework. + +The docking support in this driver does not take care of enabling or +disabling any other devices you may have attached to the dock. For +example, a CD drive plugged into the UltraBase needs to be disabled or +enabled separately. See the provided example acpid configuration files +for how this can be accomplished. + +There is no support yet for PCI devices that may be attached to a +docking station, e.g. in the ThinkPad Dock II. The driver currently +does not recognize, enable or disable such devices. This means that +the only docking stations currently supported are the X-series +UltraBase docks and "dumb" port replicators like the Mini Dock (the +latter don't need any ACPI support, actually). + +UltraBay eject -- /proc/acpi/ibm/bay +------------------------------------ + +Inserting or ejecting an UltraBay device requires some actions to be +taken by the operating system to safely make or break the electrical +connections with the device. + +This feature generates the following ACPI events: + + ibm/bay MSTR 00000003 00000000 -- eject request + ibm/bay MSTR 00000001 00000000 -- eject lever inserted + +NOTE: These events will only be generated if the UltraBay was present +when the laptop was originally booted (on the X series, the UltraBay +is in the dock, so it may not be present if the laptop was undocked). +This is due to the current lack of support for hot plugging of devices +in the Linux ACPI framework. If the laptop was booted without the +UltraBay, the following message is shown in the logs: + + Mar 17 01:42:34 aero kernel: ibm_acpi: bay device not present + +In this case, no bay-related events are generated but the eject +command described below still works. It can be executed manually or +triggered by a hot key combination. + +Sliding the eject lever generates the first event shown above. The +handler for this event should take whatever actions are necessary to +shut down the device in the UltraBay (e.g. call idectl), then issue +the following command: + + echo eject > /proc/acpi/ibm/bay + +After the LED on the UltraBay goes off, it is safe to pull out the +device. + +When the eject lever is inserted, the second event above is +generated. The handler for this event should take whatever actions are +necessary to enable the UltraBay device (e.g. call idectl). + +The contents of the /proc/acpi/ibm/bay file shows the current status +of the UltraBay, as provided by the ACPI framework. + +EXPERIMENTAL warm eject support on the 600e/x, A22p and A3x (To use +this feature, you need to supply the experimental=1 parameter when +loading the module): + +These models do not have a button near the UltraBay device to request +a hot eject but rather require the laptop to be put to sleep +(suspend-to-ram) before the bay device is ejected or inserted). +The sequence of steps to eject the device is as follows: + + echo eject > /proc/acpi/ibm/bay + put the ThinkPad to sleep + remove the drive + resume from sleep + cat /proc/acpi/ibm/bay should show that the drive was removed + +On the A3x, both the UltraBay 2000 and UltraBay Plus devices are +supported. Use "eject2" instead of "eject" for the second bay. + +Note: the UltraBay eject support on the 600e/x, A22p and A3x is +EXPERIMENTAL and may not work as expected. USE WITH CAUTION! + +CMOS control -- /proc/acpi/ibm/cmos +----------------------------------- + +This feature is used internally by the ACPI firmware to control the +ThinkLight on most newer ThinkPad models. It may also control LCD +brightness, sounds volume and more, but only on some models. + +The commands are non-negative integer numbers: + + echo 0 >/proc/acpi/ibm/cmos + echo 1 >/proc/acpi/ibm/cmos + echo 2 >/proc/acpi/ibm/cmos + ... + +The range of valid numbers is 0 to 21, but not all have an effect and +the behavior varies from model to model. Here is the behavior on the +X40 (tpb is the ThinkPad Buttons utility): + + 0 - no effect but tpb reports "Volume down" + 1 - no effect but tpb reports "Volume up" + 2 - no effect but tpb reports "Mute on" + 3 - simulate pressing the "Access IBM" button + 4 - LCD brightness up + 5 - LCD brightness down + 11 - toggle screen expansion + 12 - ThinkLight on + 13 - ThinkLight off + 14 - no effect but tpb reports ThinkLight status change + +LED control -- /proc/acpi/ibm/led +--------------------------------- + +Some of the LED indicators can be controlled through this feature. The +available commands are: + + echo ' on' >/proc/acpi/ibm/led + echo ' off' >/proc/acpi/ibm/led + echo ' blink' >/proc/acpi/ibm/led + +The range is 0 to 7. The set of LEDs that can be +controlled varies from model to model. Here is the mapping on the X40: + + 0 - power + 1 - battery (orange) + 2 - battery (green) + 3 - UltraBase + 4 - UltraBay + 7 - standby + +All of the above can be turned on and off and can be made to blink. + +ACPI sounds -- /proc/acpi/ibm/beep +---------------------------------- + +The BEEP method is used internally by the ACPI firmware to provide +audible alerts in various situations. This feature allows the same +sounds to be triggered manually. + +The commands are non-negative integer numbers: + + echo >/proc/acpi/ibm/beep + +The valid range is 0 to 17. Not all numbers trigger sounds +and the sounds vary from model to model. Here is the behavior on the +X40: + + 0 - stop a sound in progress (but use 17 to stop 16) + 2 - two beeps, pause, third beep ("low battery") + 3 - single beep + 4 - high, followed by low-pitched beep ("unable") + 5 - single beep + 6 - very high, followed by high-pitched beep ("AC/DC") + 7 - high-pitched beep + 9 - three short beeps + 10 - very long beep + 12 - low-pitched beep + 15 - three high-pitched beeps repeating constantly, stop with 0 + 16 - one medium-pitched beep repeating constantly, stop with 17 + 17 - stop 16 + +Temperature sensors -- /proc/acpi/ibm/thermal +--------------------------------------------- + +Most ThinkPads include six or more separate temperature sensors but +only expose the CPU temperature through the standard ACPI methods. +This feature shows readings from up to eight different sensors on older +ThinkPads, and it has experimental support for up to sixteen different +sensors on newer ThinkPads. Readings from sensors that are not available +return -128. + +No commands can be written to this file. + +EXPERIMENTAL: The 16-sensors feature is marked EXPERIMENTAL because the +implementation directly accesses hardware registers and may not work as +expected. USE WITH CAUTION! To use this feature, you need to supply the +experimental=1 parameter when loading the module. When EXPERIMENTAL +mode is enabled, reading the first 8 sensors on newer ThinkPads will +also use an new experimental thermal sensor access mode. + +For example, on the X40, a typical output may be: +temperatures: 42 42 45 41 36 -128 33 -128 + +EXPERIMENTAL: On the T43/p, a typical output may be: +temperatures: 48 48 36 52 38 -128 31 -128 48 52 48 -128 -128 -128 -128 -128 + +The mapping of thermal sensors to physical locations varies depending on +system-board model (and thus, on ThinkPad model). + +http://thinkwiki.org/wiki/Thermal_Sensors is a public wiki page that +tries to track down these locations for various models. + +Most (newer?) models seem to follow this pattern: + +1: CPU +2: (depends on model) +3: (depends on model) +4: GPU +5: Main battery: main sensor +6: Bay battery: main sensor +7: Main battery: secondary sensor +8: Bay battery: secondary sensor +9-15: (depends on model) + +For the R51 (source: Thomas Gruber): +2: Mini-PCI +3: Internal HDD + +For the T43, T43/p (source: Shmidoax/Thinkwiki.org) +http://thinkwiki.org/wiki/Thermal_Sensors#ThinkPad_T43.2C_T43p +2: System board, left side (near PCMCIA slot), reported as HDAPS temp +3: PCMCIA slot +9: MCH (northbridge) to DRAM Bus +10: ICH (southbridge), under Mini-PCI card, under touchpad +11: Power regulator, underside of system board, below F2 key + +The A31 has a very atypical layout for the thermal sensors +(source: Milos Popovic, http://thinkwiki.org/wiki/Thermal_Sensors#ThinkPad_A31) +1: CPU +2: Main Battery: main sensor +3: Power Converter +4: Bay Battery: main sensor +5: MCH (northbridge) +6: PCMCIA/ambient +7: Main Battery: secondary sensor +8: Bay Battery: secondary sensor + + +EXPERIMENTAL: Embedded controller register dump -- /proc/acpi/ibm/ecdump +------------------------------------------------------------------------ + +This feature is marked EXPERIMENTAL because the implementation +directly accesses hardware registers and may not work as expected. USE +WITH CAUTION! To use this feature, you need to supply the +experimental=1 parameter when loading the module. + +This feature dumps the values of 256 embedded controller +registers. Values which have changed since the last time the registers +were dumped are marked with a star: + +[root@x40 ibm-acpi]# cat /proc/acpi/ibm/ecdump +EC +00 +01 +02 +03 +04 +05 +06 +07 +08 +09 +0a +0b +0c +0d +0e +0f +EC 0x00: a7 47 87 01 fe 96 00 08 01 00 cb 00 00 00 40 00 +EC 0x10: 00 00 ff ff f4 3c 87 09 01 ff 42 01 ff ff 0d 00 +EC 0x20: 00 00 00 00 00 00 00 00 00 00 00 03 43 00 00 80 +EC 0x30: 01 07 1a 00 30 04 00 00 *85 00 00 10 00 50 00 00 +EC 0x40: 00 00 00 00 00 00 14 01 00 04 00 00 00 00 00 00 +EC 0x50: 00 c0 02 0d 00 01 01 02 02 03 03 03 03 *bc *02 *bc +EC 0x60: *02 *bc *02 00 00 00 00 00 00 00 00 00 00 00 00 00 +EC 0x70: 00 00 00 00 00 12 30 40 *24 *26 *2c *27 *20 80 *1f 80 +EC 0x80: 00 00 00 06 *37 *0e 03 00 00 00 0e 07 00 00 00 00 +EC 0x90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +EC 0xa0: *ff 09 ff 09 ff ff *64 00 *00 *00 *a2 41 *ff *ff *e0 00 +EC 0xb0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +EC 0xc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +EC 0xd0: 03 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +EC 0xe0: 00 00 00 00 00 00 00 00 11 20 49 04 24 06 55 03 +EC 0xf0: 31 55 48 54 35 38 57 57 08 2f 45 73 07 65 6c 1a + +This feature can be used to determine the register holding the fan +speed on some models. To do that, do the following: + + - make sure the battery is fully charged + - make sure the fan is running + - run 'cat /proc/acpi/ibm/ecdump' several times, once per second or so + +The first step makes sure various charging-related values don't +vary. The second ensures that the fan-related values do vary, since +the fan speed fluctuates a bit. The third will (hopefully) mark the +fan register with a star: + +[root@x40 ibm-acpi]# cat /proc/acpi/ibm/ecdump +EC +00 +01 +02 +03 +04 +05 +06 +07 +08 +09 +0a +0b +0c +0d +0e +0f +EC 0x00: a7 47 87 01 fe 96 00 08 01 00 cb 00 00 00 40 00 +EC 0x10: 00 00 ff ff f4 3c 87 09 01 ff 42 01 ff ff 0d 00 +EC 0x20: 00 00 00 00 00 00 00 00 00 00 00 03 43 00 00 80 +EC 0x30: 01 07 1a 00 30 04 00 00 85 00 00 10 00 50 00 00 +EC 0x40: 00 00 00 00 00 00 14 01 00 04 00 00 00 00 00 00 +EC 0x50: 00 c0 02 0d 00 01 01 02 02 03 03 03 03 bc 02 bc +EC 0x60: 02 bc 02 00 00 00 00 00 00 00 00 00 00 00 00 00 +EC 0x70: 00 00 00 00 00 12 30 40 24 27 2c 27 21 80 1f 80 +EC 0x80: 00 00 00 06 *be 0d 03 00 00 00 0e 07 00 00 00 00 +EC 0x90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +EC 0xa0: ff 09 ff 09 ff ff 64 00 00 00 a2 41 ff ff e0 00 +EC 0xb0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +EC 0xc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +EC 0xd0: 03 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +EC 0xe0: 00 00 00 00 00 00 00 00 11 20 49 04 24 06 55 03 +EC 0xf0: 31 55 48 54 35 38 57 57 08 2f 45 73 07 65 6c 1a + +Another set of values that varies often is the temperature +readings. Since temperatures don't change vary fast, you can take +several quick dumps to eliminate them. + +You can use a similar method to figure out the meaning of other +embedded controller registers - e.g. make sure nothing else changes +except the charging or discharging battery to determine which +registers contain the current battery capacity, etc. If you experiment +with this, do send me your results (including some complete dumps with +a description of the conditions when they were taken.) + +LCD brightness control -- /proc/acpi/ibm/brightness +--------------------------------------------------- + +This feature allows software control of the LCD brightness on ThinkPad +models which don't have a hardware brightness slider. The available +commands are: + + echo up >/proc/acpi/ibm/brightness + echo down >/proc/acpi/ibm/brightness + echo 'level ' >/proc/acpi/ibm/brightness + +The number range is 0 to 7, although not all of them may be +distinct. The current brightness level is shown in the file. + +Volume control -- /proc/acpi/ibm/volume +--------------------------------------- + +This feature allows volume control on ThinkPad models which don't have +a hardware volume knob. The available commands are: + + echo up >/proc/acpi/ibm/volume + echo down >/proc/acpi/ibm/volume + echo mute >/proc/acpi/ibm/volume + echo 'level ' >/proc/acpi/ibm/volume + +The number range is 0 to 15 although not all of them may be +distinct. The unmute the volume after the mute command, use either the +up or down command (the level command will not unmute the volume). +The current volume level and mute state is shown in the file. + +EXPERIMENTAL: fan speed, fan enable/disable -- /proc/acpi/ibm/fan +----------------------------------------------------------------- + +This feature is marked EXPERIMENTAL because the implementation +directly accesses hardware registers and may not work as expected. USE +WITH CAUTION! To use this feature, you need to supply the +experimental=1 parameter when loading the module. + +This feature attempts to show the current fan speed, control mode and +other fan data that might be available. The speed is read directly +from the hardware registers of the embedded controller. This is known +to work on later R, T and X series ThinkPads but may show a bogus +value on other models. + +Most ThinkPad fans work in "levels". Level 0 stops the fan. The higher +the level, the higher the fan speed, although adjacent levels often map +to the same fan speed. 7 is the highest level, where the fan reaches +the maximum recommended speed. Level "auto" means the EC changes the +fan level according to some internal algorithm, usually based on +readings from the thermal sensors. Level "disengaged" means the EC +disables the speed-locked closed-loop fan control, and drives the fan as +fast as it can go, which might exceed hardware limits, so use this level +with caution. + +The fan usually ramps up or down slowly from one speed to another, +and it is normal for the EC to take several seconds to react to fan +commands. + +The fan may be enabled or disabled with the following commands: + + echo enable >/proc/acpi/ibm/fan + echo disable >/proc/acpi/ibm/fan + +Placing a fan on level 0 is the same as disabling it. Enabling a fan +will try to place it in a safe level if it is too slow or disabled. + +WARNING WARNING WARNING: do not leave the fan disabled unless you are +monitoring all of the temperature sensor readings and you are ready to +enable it if necessary to avoid overheating. + +An enabled fan in level "auto" may stop spinning if the EC decides the +ThinkPad is cool enough and doesn't need the extra airflow. This is +normal, and the EC will spin the fan up if the varios thermal readings +rise too much. + +On the X40, this seems to depend on the CPU and HDD temperatures. +Specifically, the fan is turned on when either the CPU temperature +climbs to 56 degrees or the HDD temperature climbs to 46 degrees. The +fan is turned off when the CPU temperature drops to 49 degrees and the +HDD temperature drops to 41 degrees. These thresholds cannot +currently be controlled. + +The fan level can be controlled with the command: + + echo 'level ' > /proc/acpi/ibm/thermal + +Where is an integer from 0 to 7, or one of the words "auto" +or "disengaged" (without the quotes). Not all ThinkPads support the +"auto" and "disengaged" levels. + +On the X31 and X40 (and ONLY on those models), the fan speed can be +controlled to a certain degree. Once the fan is running, it can be +forced to run faster or slower with the following command: + + echo 'speed ' > /proc/acpi/ibm/thermal + +The sustainable range of fan speeds on the X40 appears to be from +about 3700 to about 7350. Values outside this range either do not have +any effect or the fan speed eventually settles somewhere in that +range. The fan cannot be stopped or started with this command. + +The ThinkPad's ACPI DSDT code will reprogram the fan on its own when +certain conditions are met. It will override any fan programming done +through ibm-acpi. + +The ibm-acpi kernel driver can be programmed to revert the fan level +to a safe setting if userspace does not issue one of the fan commands: +"enable", "disable", "level" or "watchdog" within a configurable +ammount of time. To do this, use the "watchdog" command. + + echo 'watchdog ' > /proc/acpi/ibm/fan + +Interval is the ammount of time in seconds to wait for one of the +above mentioned fan commands before reseting the fan level to a safe +one. If set to zero, the watchdog is disabled (default). When the +watchdog timer runs out, it does the exact equivalent of the "enable" +fan command. + +Note that the watchdog timer stops after it enables the fan. It will +be rearmed again automatically (using the same interval) when one of +the above mentioned fan commands is received. The fan watchdog is, +therefore, not suitable to protect against fan mode changes made +through means other than the "enable", "disable", and "level" fan +commands. + +EXPERIMENTAL: WAN -- /proc/acpi/ibm/wan +--------------------------------------- + +This feature is marked EXPERIMENTAL because the implementation +directly accesses hardware registers and may not work as expected. USE +WITH CAUTION! To use this feature, you need to supply the +experimental=1 parameter when loading the module. + +This feature shows the presence and current state of a WAN (Sierra +Wireless EV-DO) device. If WAN is installed, the following commands can +be used: + + echo enable > /proc/acpi/ibm/wan + echo disable > /proc/acpi/ibm/wan + +It was tested on a Lenovo Thinkpad X60. It should probably work on other +Thinkpad models which come with this module installed. + +Multiple Commands, Module Parameters +------------------------------------ + +Multiple commands can be written to the proc files in one shot by +separating them with commas, for example: + + echo enable,0xffff > /proc/acpi/ibm/hotkey + echo lcd_disable,crt_enable > /proc/acpi/ibm/video + +Commands can also be specified when loading the ibm_acpi module, for +example: + + modprobe ibm_acpi hotkey=enable,0xffff video=auto_disable + diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 848b398482d9..ebf4ff2f858e 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -12,4 +12,4 @@ obj-$(CONFIG_TIFM_CORE) += tifm_core.o obj-$(CONFIG_TIFM_7XX1) += tifm_7xx1.o obj-$(CONFIG_SGI_IOC4) += ioc4.o obj-$(CONFIG_SONY_LAPTOP) += sony-laptop.o -obj-$(CONFIG_ACPI_IBM) += ibm_acpi.o +obj-$(CONFIG_ACPI_IBM) += thinkpad_acpi.o diff --git a/drivers/misc/ibm_acpi.c b/drivers/misc/ibm_acpi.c deleted file mode 100644 index ae03b8f6f7be..000000000000 --- a/drivers/misc/ibm_acpi.c +++ /dev/null @@ -1,2783 +0,0 @@ -/* - * ibm_acpi.c - IBM ThinkPad ACPI Extras - * - * - * Copyright (C) 2004-2005 Borislav Deianov - * Copyright (C) 2006-2007 Henrique de Moraes Holschuh - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA. - */ - -#define IBM_VERSION "0.13" - -/* - * Changelog: - * - * 2006-11-22 0.13 new maintainer - * changelog now lives in git commit history, and will - * not be updated further in-file. - * - * 2005-08-17 0.12 fix compilation on 2.6.13-rc kernels - * 2005-03-17 0.11 support for 600e, 770x - * thanks to Jamie Lentin - * support for 770e, G41 - * G40 and G41 don't have a thinklight - * temperatures no longer experimental - * experimental brightness control - * experimental volume control - * experimental fan enable/disable - * 2005-01-16 0.10 fix module loading on R30, R31 - * 2005-01-16 0.9 support for 570, R30, R31 - * ultrabay support on A22p, A3x - * limit arg for cmos, led, beep, drop experimental status - * more capable led control on A21e, A22p, T20-22, X20 - * experimental temperatures and fan speed - * experimental embedded controller register dump - * mark more functions as __init, drop incorrect __exit - * use MODULE_VERSION - * thanks to Henrik Brix Andersen - * fix parameter passing on module loading - * thanks to Rusty Russell - * thanks to Jim Radford - * 2004-11-08 0.8 fix init error case, don't return from a macro - * thanks to Chris Wright - * 2004-10-23 0.7 fix module loading on A21e, A22p, T20, T21, X20 - * fix led control on A21e - * 2004-10-19 0.6 use acpi_bus_register_driver() to claim HKEY device - * 2004-10-18 0.5 thinklight support on A21e, G40, R32, T20, T21, X20 - * proc file format changed - * video_switch command - * experimental cmos control - * experimental led control - * experimental acpi sounds - * 2004-09-16 0.4 support for module parameters - * hotkey mask can be prefixed by 0x - * video output switching - * video expansion control - * ultrabay eject support - * removed lcd brightness/on/off control, didn't work - * 2004-08-17 0.3 support for R40 - * lcd off, brightness control - * thinklight on/off - * 2004-08-14 0.2 support for T series, X20 - * bluetooth enable/disable - * hotkey events disabled by default - * removed fan control, currently useless - * 2004-08-09 0.1 initial release, support for X series - */ - -#include "ibm_acpi.h" - -MODULE_AUTHOR("Borislav Deianov, Henrique de Moraes Holschuh"); -MODULE_DESCRIPTION(IBM_DESC); -MODULE_VERSION(IBM_VERSION); -MODULE_LICENSE("GPL"); - -#define __unused __attribute__ ((unused)) - -/**************************************************************************** - **************************************************************************** - * - * ACPI Helpers and device model - * - **************************************************************************** - ****************************************************************************/ - -/************************************************************************* - * ACPI basic handles - */ - -static acpi_handle root_handle = NULL; - -#define IBM_HANDLE(object, parent, paths...) \ - static acpi_handle object##_handle; \ - static acpi_handle *object##_parent = &parent##_handle; \ - static char *object##_path; \ - static char *object##_paths[] = { paths } - -IBM_HANDLE(ec, root, "\\_SB.PCI0.ISA.EC0", /* 240, 240x */ - "\\_SB.PCI.ISA.EC", /* 570 */ - "\\_SB.PCI0.ISA0.EC0", /* 600e/x, 770e, 770x */ - "\\_SB.PCI0.ISA.EC", /* A21e, A2xm/p, T20-22, X20-21 */ - "\\_SB.PCI0.AD4S.EC0", /* i1400, R30 */ - "\\_SB.PCI0.ICH3.EC0", /* R31 */ - "\\_SB.PCI0.LPC.EC", /* all others */ - ); - -IBM_HANDLE(ecrd, ec, "ECRD"); /* 570 */ -IBM_HANDLE(ecwr, ec, "ECWR"); /* 570 */ - - -/************************************************************************* - * Misc ACPI handles - */ - -IBM_HANDLE(cmos, root, "\\UCMS", /* R50, R50e, R50p, R51, T4x, X31, X40 */ - "\\CMOS", /* A3x, G4x, R32, T23, T30, X22-24, X30 */ - "\\CMS", /* R40, R40e */ - ); /* all others */ - -IBM_HANDLE(hkey, ec, "\\_SB.HKEY", /* 600e/x, 770e, 770x */ - "^HKEY", /* R30, R31 */ - "HKEY", /* all others */ - ); /* 570 */ - - -/************************************************************************* - * ACPI helpers - */ - -static int acpi_evalf(acpi_handle handle, - void *res, char *method, char *fmt, ...) -{ - char *fmt0 = fmt; - struct acpi_object_list params; - union acpi_object in_objs[IBM_MAX_ACPI_ARGS]; - struct acpi_buffer result, *resultp; - union acpi_object out_obj; - acpi_status status; - va_list ap; - char res_type; - int success; - int quiet; - - if (!*fmt) { - printk(IBM_ERR "acpi_evalf() called with empty format\n"); - return 0; - } - - if (*fmt == 'q') { - quiet = 1; - fmt++; - } else - quiet = 0; - - res_type = *(fmt++); - - params.count = 0; - params.pointer = &in_objs[0]; - - va_start(ap, fmt); - while (*fmt) { - char c = *(fmt++); - switch (c) { - case 'd': /* int */ - in_objs[params.count].integer.value = va_arg(ap, int); - in_objs[params.count++].type = ACPI_TYPE_INTEGER; - break; - /* add more types as needed */ - default: - printk(IBM_ERR "acpi_evalf() called " - "with invalid format character '%c'\n", c); - return 0; - } - } - va_end(ap); - - if (res_type != 'v') { - result.length = sizeof(out_obj); - result.pointer = &out_obj; - resultp = &result; - } else - resultp = NULL; - - status = acpi_evaluate_object(handle, method, ¶ms, resultp); - - switch (res_type) { - case 'd': /* int */ - if (res) - *(int *)res = out_obj.integer.value; - success = status == AE_OK && out_obj.type == ACPI_TYPE_INTEGER; - break; - case 'v': /* void */ - success = status == AE_OK; - break; - /* add more types as needed */ - default: - printk(IBM_ERR "acpi_evalf() called " - "with invalid format character '%c'\n", res_type); - return 0; - } - - if (!success && !quiet) - printk(IBM_ERR "acpi_evalf(%s, %s, ...) failed: %d\n", - method, fmt0, status); - - return success; -} - -static void __unused acpi_print_int(acpi_handle handle, char *method) -{ - int i; - - if (acpi_evalf(handle, &i, method, "d")) - printk(IBM_INFO "%s = 0x%x\n", method, i); - else - printk(IBM_ERR "error calling %s\n", method); -} - -static int acpi_ec_read(int i, u8 * p) -{ - int v; - - if (ecrd_handle) { - if (!acpi_evalf(ecrd_handle, &v, NULL, "dd", i)) - return 0; - *p = v; - } else { - if (ec_read(i, p) < 0) - return 0; - } - - return 1; -} - -static int acpi_ec_write(int i, u8 v) -{ - if (ecwr_handle) { - if (!acpi_evalf(ecwr_handle, NULL, NULL, "vdd", i, v)) - return 0; - } else { - if (ec_write(i, v) < 0) - return 0; - } - - return 1; -} - -static int _sta(acpi_handle handle) -{ - int status; - - if (!handle || !acpi_evalf(handle, &status, "_STA", "d")) - status = 0; - - return status; -} - -/************************************************************************* - * ACPI device model - */ - -static void __init ibm_handle_init(char *name, - acpi_handle * handle, acpi_handle parent, - char **paths, int num_paths, char **path) -{ - int i; - acpi_status status; - - for (i = 0; i < num_paths; i++) { - status = acpi_get_handle(parent, paths[i], handle); - if (ACPI_SUCCESS(status)) { - *path = paths[i]; - return; - } - } - - *handle = NULL; -} - -static void dispatch_notify(acpi_handle handle, u32 event, void *data) -{ - struct ibm_struct *ibm = data; - - if (!ibm || !ibm->notify) - return; - - ibm->notify(ibm, event); -} - -static int __init setup_notify(struct ibm_struct *ibm) -{ - acpi_status status; - int ret; - - if (!*ibm->handle) - return 0; - - ret = acpi_bus_get_device(*ibm->handle, &ibm->device); - if (ret < 0) { - printk(IBM_ERR "%s device not present\n", ibm->name); - return -ENODEV; - } - - acpi_driver_data(ibm->device) = ibm; - sprintf(acpi_device_class(ibm->device), "%s/%s", IBM_NAME, ibm->name); - - status = acpi_install_notify_handler(*ibm->handle, ibm->type, - dispatch_notify, ibm); - if (ACPI_FAILURE(status)) { - if (status == AE_ALREADY_EXISTS) { - printk(IBM_NOTICE "another device driver is already handling %s events\n", - ibm->name); - } else { - printk(IBM_ERR "acpi_install_notify_handler(%s) failed: %d\n", - ibm->name, status); - } - return -ENODEV; - } - ibm->notify_installed = 1; - return 0; -} - -static int __init ibm_device_add(struct acpi_device *device) -{ - return 0; -} - -static int __init register_ibmacpi_subdriver(struct ibm_struct *ibm) -{ - int ret; - - ibm->driver = kzalloc(sizeof(struct acpi_driver), GFP_KERNEL); - if (!ibm->driver) { - printk(IBM_ERR "kmalloc(ibm->driver) failed\n"); - return -1; - } - - sprintf(ibm->driver->name, "%s_%s", IBM_NAME, ibm->name); - ibm->driver->ids = ibm->hid; - ibm->driver->ops.add = &ibm_device_add; - - ret = acpi_bus_register_driver(ibm->driver); - if (ret < 0) { - printk(IBM_ERR "acpi_bus_register_driver(%s) failed: %d\n", - ibm->hid, ret); - kfree(ibm->driver); - } - - return ret; -} - - -/**************************************************************************** - **************************************************************************** - * - * Procfs Helpers - * - **************************************************************************** - ****************************************************************************/ - -static int dispatch_read(char *page, char **start, off_t off, int count, - int *eof, void *data) -{ - struct ibm_struct *ibm = data; - int len; - - if (!ibm || !ibm->read) - return -EINVAL; - - len = ibm->read(page); - if (len < 0) - return len; - - if (len <= off + count) - *eof = 1; - *start = page + off; - len -= off; - if (len > count) - len = count; - if (len < 0) - len = 0; - - return len; -} - -static int dispatch_write(struct file *file, const char __user * userbuf, - unsigned long count, void *data) -{ - struct ibm_struct *ibm = data; - char *kernbuf; - int ret; - - if (!ibm || !ibm->write) - return -EINVAL; - - kernbuf = kmalloc(count + 2, GFP_KERNEL); - if (!kernbuf) - return -ENOMEM; - - if (copy_from_user(kernbuf, userbuf, count)) { - kfree(kernbuf); - return -EFAULT; - } - - kernbuf[count] = 0; - strcat(kernbuf, ","); - ret = ibm->write(kernbuf); - if (ret == 0) - ret = count; - - kfree(kernbuf); - - return ret; -} - -static char *next_cmd(char **cmds) -{ - char *start = *cmds; - char *end; - - while ((end = strchr(start, ',')) && end == start) - start = end + 1; - - if (!end) - return NULL; - - *end = 0; - *cmds = end + 1; - return start; -} - - -/**************************************************************************** - **************************************************************************** - * - * Subdrivers - * - **************************************************************************** - ****************************************************************************/ - -/************************************************************************* - * ibm-acpi init subdriver - */ - -static int ibm_acpi_driver_init(void) -{ - printk(IBM_INFO "%s v%s\n", IBM_DESC, IBM_VERSION); - printk(IBM_INFO "%s\n", IBM_URL); - - if (ibm_thinkpad_ec_found) - printk(IBM_INFO "ThinkPad EC firmware %s\n", - ibm_thinkpad_ec_found); - - return 0; -} - -static int ibm_acpi_driver_read(char *p) -{ - int len = 0; - - len += sprintf(p + len, "driver:\t\t%s\n", IBM_DESC); - len += sprintf(p + len, "version:\t%s\n", IBM_VERSION); - - return len; -} - -/************************************************************************* - * Hotkey subdriver - */ - -static int hotkey_supported; -static int hotkey_mask_supported; -static int hotkey_orig_status; -static int hotkey_orig_mask; - -static int hotkey_init(void) -{ - /* hotkey not supported on 570 */ - hotkey_supported = hkey_handle != NULL; - - if (hotkey_supported) { - /* mask not supported on 570, 600e/x, 770e, 770x, A21e, A2xm/p, - A30, R30, R31, T20-22, X20-21, X22-24 */ - hotkey_mask_supported = - acpi_evalf(hkey_handle, NULL, "DHKN", "qv"); - - if (!hotkey_get(&hotkey_orig_status, &hotkey_orig_mask)) - return -ENODEV; - } - - return 0; -} - -static void hotkey_exit(void) -{ - if (hotkey_supported) - hotkey_set(hotkey_orig_status, hotkey_orig_mask); -} - -static void hotkey_notify(struct ibm_struct *ibm, u32 event) -{ - int hkey; - - if (acpi_evalf(hkey_handle, &hkey, "MHKP", "d")) - acpi_bus_generate_event(ibm->device, event, hkey); - else { - printk(IBM_ERR "unknown hotkey event %d\n", event); - acpi_bus_generate_event(ibm->device, event, 0); - } -} - -static int hotkey_get(int *status, int *mask) -{ - if (!acpi_evalf(hkey_handle, status, "DHKC", "d")) - return 0; - - if (hotkey_mask_supported) - if (!acpi_evalf(hkey_handle, mask, "DHKN", "d")) - return 0; - - return 1; -} - -static int hotkey_set(int status, int mask) -{ - int i; - - if (!acpi_evalf(hkey_handle, NULL, "MHKC", "vd", status)) - return 0; - - if (hotkey_mask_supported) - for (i = 0; i < 32; i++) { - int bit = ((1 << i) & mask) != 0; - if (!acpi_evalf(hkey_handle, - NULL, "MHKM", "vdd", i + 1, bit)) - return 0; - } - - return 1; -} - -static int hotkey_read(char *p) -{ - int status, mask; - int len = 0; - - if (!hotkey_supported) { - len += sprintf(p + len, "status:\t\tnot supported\n"); - return len; - } - - if (!hotkey_get(&status, &mask)) - return -EIO; - - len += sprintf(p + len, "status:\t\t%s\n", enabled(status, 0)); - if (hotkey_mask_supported) { - len += sprintf(p + len, "mask:\t\t0x%04x\n", mask); - len += sprintf(p + len, - "commands:\tenable, disable, reset, \n"); - } else { - len += sprintf(p + len, "mask:\t\tnot supported\n"); - len += sprintf(p + len, "commands:\tenable, disable, reset\n"); - } - - return len; -} - -static int hotkey_write(char *buf) -{ - int status, mask; - char *cmd; - int do_cmd = 0; - - if (!hotkey_supported) - return -ENODEV; - - if (!hotkey_get(&status, &mask)) - return -EIO; - - while ((cmd = next_cmd(&buf))) { - if (strlencmp(cmd, "enable") == 0) { - status = 1; - } else if (strlencmp(cmd, "disable") == 0) { - status = 0; - } else if (strlencmp(cmd, "reset") == 0) { - status = hotkey_orig_status; - mask = hotkey_orig_mask; - } else if (sscanf(cmd, "0x%x", &mask) == 1) { - /* mask set */ - } else if (sscanf(cmd, "%x", &mask) == 1) { - /* mask set */ - } else - return -EINVAL; - do_cmd = 1; - } - - if (do_cmd && !hotkey_set(status, mask)) - return -EIO; - - return 0; -} - -/************************************************************************* - * Bluetooth subdriver - */ - -static int bluetooth_supported; - -static int bluetooth_init(void) -{ - /* bluetooth not supported on 570, 600e/x, 770e, 770x, A21e, A2xm/p, - G4x, R30, R31, R40e, R50e, T20-22, X20-21 */ - bluetooth_supported = hkey_handle && - acpi_evalf(hkey_handle, NULL, "GBDC", "qv"); - - return 0; -} - -static int bluetooth_status(void) -{ - int status; - - if (!bluetooth_supported || - !acpi_evalf(hkey_handle, &status, "GBDC", "d")) - status = 0; - - return status; -} - -static int bluetooth_read(char *p) -{ - int len = 0; - int status = bluetooth_status(); - - if (!bluetooth_supported) - len += sprintf(p + len, "status:\t\tnot supported\n"); - else if (!(status & 1)) - len += sprintf(p + len, "status:\t\tnot installed\n"); - else { - len += sprintf(p + len, "status:\t\t%s\n", enabled(status, 1)); - len += sprintf(p + len, "commands:\tenable, disable\n"); - } - - return len; -} - -static int bluetooth_write(char *buf) -{ - int status = bluetooth_status(); - char *cmd; - int do_cmd = 0; - - if (!bluetooth_supported) - return -ENODEV; - - while ((cmd = next_cmd(&buf))) { - if (strlencmp(cmd, "enable") == 0) { - status |= 2; - } else if (strlencmp(cmd, "disable") == 0) { - status &= ~2; - } else - return -EINVAL; - do_cmd = 1; - } - - if (do_cmd && !acpi_evalf(hkey_handle, NULL, "SBDC", "vd", status)) - return -EIO; - - return 0; -} - -/************************************************************************* - * Wan subdriver - */ - -static int wan_supported; - -static int wan_init(void) -{ - wan_supported = hkey_handle && - acpi_evalf(hkey_handle, NULL, "GWAN", "qv"); - - return 0; -} - -static int wan_status(void) -{ - int status; - - if (!wan_supported || !acpi_evalf(hkey_handle, &status, "GWAN", "d")) - status = 0; - - return status; -} - -static int wan_read(char *p) -{ - int len = 0; - int status = wan_status(); - - if (!wan_supported) - len += sprintf(p + len, "status:\t\tnot supported\n"); - else if (!(status & 1)) - len += sprintf(p + len, "status:\t\tnot installed\n"); - else { - len += sprintf(p + len, "status:\t\t%s\n", enabled(status, 1)); - len += sprintf(p + len, "commands:\tenable, disable\n"); - } - - return len; -} - -static int wan_write(char *buf) -{ - int status = wan_status(); - char *cmd; - int do_cmd = 0; - - if (!wan_supported) - return -ENODEV; - - while ((cmd = next_cmd(&buf))) { - if (strlencmp(cmd, "enable") == 0) { - status |= 2; - } else if (strlencmp(cmd, "disable") == 0) { - status &= ~2; - } else - return -EINVAL; - do_cmd = 1; - } - - if (do_cmd && !acpi_evalf(hkey_handle, NULL, "SWAN", "vd", status)) - return -EIO; - - return 0; -} - -/************************************************************************* - * Video subdriver - */ - -static enum video_access_mode video_supported; -static int video_orig_autosw; - -IBM_HANDLE(vid, root, "\\_SB.PCI.AGP.VGA", /* 570 */ - "\\_SB.PCI0.AGP0.VID0", /* 600e/x, 770x */ - "\\_SB.PCI0.VID0", /* 770e */ - "\\_SB.PCI0.VID", /* A21e, G4x, R50e, X30, X40 */ - "\\_SB.PCI0.AGP.VID", /* all others */ - ); /* R30, R31 */ - -IBM_HANDLE(vid2, root, "\\_SB.PCI0.AGPB.VID"); /* G41 */ - -static int video_init(void) -{ - int ivga; - - if (vid2_handle && acpi_evalf(NULL, &ivga, "\\IVGA", "d") && ivga) - /* G41, assume IVGA doesn't change */ - vid_handle = vid2_handle; - - if (!vid_handle) - /* video switching not supported on R30, R31 */ - video_supported = IBMACPI_VIDEO_NONE; - else if (acpi_evalf(vid_handle, &video_orig_autosw, "SWIT", "qd")) - /* 570 */ - video_supported = IBMACPI_VIDEO_570; - else if (acpi_evalf(vid_handle, &video_orig_autosw, "^VADL", "qd")) - /* 600e/x, 770e, 770x */ - video_supported = IBMACPI_VIDEO_770; - else - /* all others */ - video_supported = IBMACPI_VIDEO_NEW; - - return 0; -} - -static void video_exit(void) -{ - acpi_evalf(vid_handle, NULL, "_DOS", "vd", video_orig_autosw); -} - -static int video_status(void) -{ - int status = 0; - int i; - - if (video_supported == IBMACPI_VIDEO_570) { - if (acpi_evalf(NULL, &i, "\\_SB.PHS", "dd", 0x87)) - status = i & 3; - } else if (video_supported == IBMACPI_VIDEO_770) { - if (acpi_evalf(NULL, &i, "\\VCDL", "d")) - status |= 0x01 * i; - if (acpi_evalf(NULL, &i, "\\VCDC", "d")) - status |= 0x02 * i; - } else if (video_supported == IBMACPI_VIDEO_NEW) { - acpi_evalf(NULL, NULL, "\\VUPS", "vd", 1); - if (acpi_evalf(NULL, &i, "\\VCDC", "d")) - status |= 0x02 * i; - - acpi_evalf(NULL, NULL, "\\VUPS", "vd", 0); - if (acpi_evalf(NULL, &i, "\\VCDL", "d")) - status |= 0x01 * i; - if (acpi_evalf(NULL, &i, "\\VCDD", "d")) - status |= 0x08 * i; - } - - return status; -} - -static int video_autosw(void) -{ - int autosw = 0; - - if (video_supported == IBMACPI_VIDEO_570) - acpi_evalf(vid_handle, &autosw, "SWIT", "d"); - else if (video_supported == IBMACPI_VIDEO_770 || - video_supported == IBMACPI_VIDEO_NEW) - acpi_evalf(vid_handle, &autosw, "^VDEE", "d"); - - return autosw & 1; -} - -static int video_switch(void) -{ - int autosw = video_autosw(); - int ret; - - if (!acpi_evalf(vid_handle, NULL, "_DOS", "vd", 1)) - return -EIO; - ret = video_supported == IBMACPI_VIDEO_570 ? - acpi_evalf(ec_handle, NULL, "_Q16", "v") : - acpi_evalf(vid_handle, NULL, "VSWT", "v"); - acpi_evalf(vid_handle, NULL, "_DOS", "vd", autosw); - - return ret; -} - -static int video_expand(void) -{ - if (video_supported == IBMACPI_VIDEO_570) - return acpi_evalf(ec_handle, NULL, "_Q17", "v"); - else if (video_supported == IBMACPI_VIDEO_770) - return acpi_evalf(vid_handle, NULL, "VEXP", "v"); - else - return acpi_evalf(NULL, NULL, "\\VEXP", "v"); -} - -static int video_switch2(int status) -{ - int ret; - - if (video_supported == IBMACPI_VIDEO_570) { - ret = acpi_evalf(NULL, NULL, - "\\_SB.PHS2", "vdd", 0x8b, status | 0x80); - } else if (video_supported == IBMACPI_VIDEO_770) { - int autosw = video_autosw(); - if (!acpi_evalf(vid_handle, NULL, "_DOS", "vd", 1)) - return -EIO; - - ret = acpi_evalf(vid_handle, NULL, - "ASWT", "vdd", status * 0x100, 0); - - acpi_evalf(vid_handle, NULL, "_DOS", "vd", autosw); - } else { - ret = acpi_evalf(NULL, NULL, "\\VUPS", "vd", 0x80) && - acpi_evalf(NULL, NULL, "\\VSDS", "vdd", status, 1); - } - - return ret; -} - -static int video_read(char *p) -{ - int status = video_status(); - int autosw = video_autosw(); - int len = 0; - - if (!video_supported) { - len += sprintf(p + len, "status:\t\tnot supported\n"); - return len; - } - - len += sprintf(p + len, "status:\t\tsupported\n"); - len += sprintf(p + len, "lcd:\t\t%s\n", enabled(status, 0)); - len += sprintf(p + len, "crt:\t\t%s\n", enabled(status, 1)); - if (video_supported == IBMACPI_VIDEO_NEW) - len += sprintf(p + len, "dvi:\t\t%s\n", enabled(status, 3)); - len += sprintf(p + len, "auto:\t\t%s\n", enabled(autosw, 0)); - len += sprintf(p + len, "commands:\tlcd_enable, lcd_disable\n"); - len += sprintf(p + len, "commands:\tcrt_enable, crt_disable\n"); - if (video_supported == IBMACPI_VIDEO_NEW) - len += sprintf(p + len, "commands:\tdvi_enable, dvi_disable\n"); - len += sprintf(p + len, "commands:\tauto_enable, auto_disable\n"); - len += sprintf(p + len, "commands:\tvideo_switch, expand_toggle\n"); - - return len; -} - -static int video_write(char *buf) -{ - char *cmd; - int enable, disable, status; - - if (!video_supported) - return -ENODEV; - - enable = disable = 0; - - while ((cmd = next_cmd(&buf))) { - if (strlencmp(cmd, "lcd_enable") == 0) { - enable |= 0x01; - } else if (strlencmp(cmd, "lcd_disable") == 0) { - disable |= 0x01; - } else if (strlencmp(cmd, "crt_enable") == 0) { - enable |= 0x02; - } else if (strlencmp(cmd, "crt_disable") == 0) { - disable |= 0x02; - } else if (video_supported == IBMACPI_VIDEO_NEW && - strlencmp(cmd, "dvi_enable") == 0) { - enable |= 0x08; - } else if (video_supported == IBMACPI_VIDEO_NEW && - strlencmp(cmd, "dvi_disable") == 0) { - disable |= 0x08; - } else if (strlencmp(cmd, "auto_enable") == 0) { - if (!acpi_evalf(vid_handle, NULL, "_DOS", "vd", 1)) - return -EIO; - } else if (strlencmp(cmd, "auto_disable") == 0) { - if (!acpi_evalf(vid_handle, NULL, "_DOS", "vd", 0)) - return -EIO; - } else if (strlencmp(cmd, "video_switch") == 0) { - if (!video_switch()) - return -EIO; - } else if (strlencmp(cmd, "expand_toggle") == 0) { - if (!video_expand()) - return -EIO; - } else - return -EINVAL; - } - - if (enable || disable) { - status = (video_status() & 0x0f & ~disable) | enable; - if (!video_switch2(status)) - return -EIO; - } - - return 0; -} - -/************************************************************************* - * Light (thinklight) subdriver - */ - -static int light_supported; -static int light_status_supported; - -IBM_HANDLE(lght, root, "\\LGHT"); /* A21e, A2xm/p, T20-22, X20-21 */ -IBM_HANDLE(ledb, ec, "LEDB"); /* G4x */ - -static int light_init(void) -{ - /* light not supported on 570, 600e/x, 770e, 770x, G4x, R30, R31 */ - light_supported = (cmos_handle || lght_handle) && !ledb_handle; - - if (light_supported) - /* light status not supported on - 570, 600e/x, 770e, 770x, G4x, R30, R31, R32, X20 */ - light_status_supported = acpi_evalf(ec_handle, NULL, - "KBLT", "qv"); - - return 0; -} - -static int light_read(char *p) -{ - int len = 0; - int status = 0; - - if (!light_supported) { - len += sprintf(p + len, "status:\t\tnot supported\n"); - } else if (!light_status_supported) { - len += sprintf(p + len, "status:\t\tunknown\n"); - len += sprintf(p + len, "commands:\ton, off\n"); - } else { - if (!acpi_evalf(ec_handle, &status, "KBLT", "d")) - return -EIO; - len += sprintf(p + len, "status:\t\t%s\n", onoff(status, 0)); - len += sprintf(p + len, "commands:\ton, off\n"); - } - - return len; -} - -static int light_write(char *buf) -{ - int cmos_cmd, lght_cmd; - char *cmd; - int success; - - if (!light_supported) - return -ENODEV; - - while ((cmd = next_cmd(&buf))) { - if (strlencmp(cmd, "on") == 0) { - cmos_cmd = 0x0c; - lght_cmd = 1; - } else if (strlencmp(cmd, "off") == 0) { - cmos_cmd = 0x0d; - lght_cmd = 0; - } else - return -EINVAL; - - success = cmos_handle ? - acpi_evalf(cmos_handle, NULL, NULL, "vd", cmos_cmd) : - acpi_evalf(lght_handle, NULL, NULL, "vd", lght_cmd); - if (!success) - return -EIO; - } - - return 0; -} - -/************************************************************************* - * Dock subdriver - */ - -/* don't list other alternatives as we install a notify handler on the 570 */ -IBM_HANDLE(pci, root, "\\_SB.PCI"); /* 570 */ - -#ifdef CONFIG_ACPI_IBM_DOCK - -IBM_HANDLE(dock, root, "\\_SB.GDCK", /* X30, X31, X40 */ - "\\_SB.PCI0.DOCK", /* 600e/x,770e,770x,A2xm/p,T20-22,X20-21 */ - "\\_SB.PCI0.PCI1.DOCK", /* all others */ - "\\_SB.PCI.ISA.SLCE", /* 570 */ - ); /* A21e,G4x,R30,R31,R32,R40,R40e,R50e */ - -#define dock_docked() (_sta(dock_handle) & 1) - -static void dock_notify(struct ibm_struct *ibm, u32 event) -{ - int docked = dock_docked(); - int pci = ibm->hid && strstr(ibm->hid, IBM_PCI_HID); - - if (event == 1 && !pci) /* 570 */ - acpi_bus_generate_event(ibm->device, event, 1); /* button */ - else if (event == 1 && pci) /* 570 */ - acpi_bus_generate_event(ibm->device, event, 3); /* dock */ - else if (event == 3 && docked) - acpi_bus_generate_event(ibm->device, event, 1); /* button */ - else if (event == 3 && !docked) - acpi_bus_generate_event(ibm->device, event, 2); /* undock */ - else if (event == 0 && docked) - acpi_bus_generate_event(ibm->device, event, 3); /* dock */ - else { - printk(IBM_ERR "unknown dock event %d, status %d\n", - event, _sta(dock_handle)); - acpi_bus_generate_event(ibm->device, event, 0); /* unknown */ - } -} - -static int dock_read(char *p) -{ - int len = 0; - int docked = dock_docked(); - - if (!dock_handle) - len += sprintf(p + len, "status:\t\tnot supported\n"); - else if (!docked) - len += sprintf(p + len, "status:\t\tundocked\n"); - else { - len += sprintf(p + len, "status:\t\tdocked\n"); - len += sprintf(p + len, "commands:\tdock, undock\n"); - } - - return len; -} - -static int dock_write(char *buf) -{ - char *cmd; - - if (!dock_docked()) - return -ENODEV; - - while ((cmd = next_cmd(&buf))) { - if (strlencmp(cmd, "undock") == 0) { - if (!acpi_evalf(dock_handle, NULL, "_DCK", "vd", 0) || - !acpi_evalf(dock_handle, NULL, "_EJ0", "vd", 1)) - return -EIO; - } else if (strlencmp(cmd, "dock") == 0) { - if (!acpi_evalf(dock_handle, NULL, "_DCK", "vd", 1)) - return -EIO; - } else - return -EINVAL; - } - - return 0; -} - -#endif /* CONFIG_ACPI_IBM_DOCK */ - -/************************************************************************* - * Bay subdriver - */ - -#ifdef CONFIG_ACPI_IBM_BAY -static int bay_status_supported; -static int bay_status2_supported; -static int bay_eject_supported; -static int bay_eject2_supported; - -IBM_HANDLE(bay, root, "\\_SB.PCI.IDE.SECN.MAST", /* 570 */ - "\\_SB.PCI0.IDE0.IDES.IDSM", /* 600e/x, 770e, 770x */ - "\\_SB.PCI0.SATA.SCND.MSTR", /* T60, X60, Z60 */ - "\\_SB.PCI0.IDE0.SCND.MSTR", /* all others */ - ); /* A21e, R30, R31 */ -IBM_HANDLE(bay_ej, bay, "_EJ3", /* 600e/x, A2xm/p, A3x */ - "_EJ0", /* all others */ - ); /* 570,A21e,G4x,R30,R31,R32,R40e,R50e */ -IBM_HANDLE(bay2, root, "\\_SB.PCI0.IDE0.PRIM.SLAV", /* A3x, R32 */ - "\\_SB.PCI0.IDE0.IDEP.IDPS", /* 600e/x, 770e, 770x */ - ); /* all others */ -IBM_HANDLE(bay2_ej, bay2, "_EJ3", /* 600e/x, 770e, A3x */ - "_EJ0", /* 770x */ - ); /* all others */ - -static int bay_init(void) -{ - bay_status_supported = bay_handle && - acpi_evalf(bay_handle, NULL, "_STA", "qv"); - bay_status2_supported = bay2_handle && - acpi_evalf(bay2_handle, NULL, "_STA", "qv"); - - bay_eject_supported = bay_handle && bay_ej_handle && - (strlencmp(bay_ej_path, "_EJ0") == 0 || experimental); - bay_eject2_supported = bay2_handle && bay2_ej_handle && - (strlencmp(bay2_ej_path, "_EJ0") == 0 || experimental); - - return 0; -} - -static void bay_notify(struct ibm_struct *ibm, u32 event) -{ - acpi_bus_generate_event(ibm->device, event, 0); -} - -#define bay_occupied(b) (_sta(b##_handle) & 1) - -static int bay_read(char *p) -{ - int len = 0; - int occupied = bay_occupied(bay); - int occupied2 = bay_occupied(bay2); - int eject, eject2; - - len += sprintf(p + len, "status:\t\t%s\n", bay_status_supported ? - (occupied ? "occupied" : "unoccupied") : - "not supported"); - if (bay_status2_supported) - len += sprintf(p + len, "status2:\t%s\n", occupied2 ? - "occupied" : "unoccupied"); - - eject = bay_eject_supported && occupied; - eject2 = bay_eject2_supported && occupied2; - - if (eject && eject2) - len += sprintf(p + len, "commands:\teject, eject2\n"); - else if (eject) - len += sprintf(p + len, "commands:\teject\n"); - else if (eject2) - len += sprintf(p + len, "commands:\teject2\n"); - - return len; -} - -static int bay_write(char *buf) -{ - char *cmd; - - if (!bay_eject_supported && !bay_eject2_supported) - return -ENODEV; - - while ((cmd = next_cmd(&buf))) { - if (bay_eject_supported && strlencmp(cmd, "eject") == 0) { - if (!acpi_evalf(bay_ej_handle, NULL, NULL, "vd", 1)) - return -EIO; - } else if (bay_eject2_supported && - strlencmp(cmd, "eject2") == 0) { - if (!acpi_evalf(bay2_ej_handle, NULL, NULL, "vd", 1)) - return -EIO; - } else - return -EINVAL; - } - - return 0; -} -#endif /* CONFIG_ACPI_IBM_BAY */ - -/************************************************************************* - * CMOS subdriver - */ - -static int cmos_eval(int cmos_cmd) -{ - if (cmos_handle) - return acpi_evalf(cmos_handle, NULL, NULL, "vd", cmos_cmd); - else - return 1; -} - -static int cmos_read(char *p) -{ - int len = 0; - - /* cmos not supported on 570, 600e/x, 770e, 770x, A21e, A2xm/p, - R30, R31, T20-22, X20-21 */ - if (!cmos_handle) - len += sprintf(p + len, "status:\t\tnot supported\n"); - else { - len += sprintf(p + len, "status:\t\tsupported\n"); - len += sprintf(p + len, "commands:\t ( is 0-21)\n"); - } - - return len; -} - -static int cmos_write(char *buf) -{ - char *cmd; - int cmos_cmd; - - if (!cmos_handle) - return -EINVAL; - - while ((cmd = next_cmd(&buf))) { - if (sscanf(cmd, "%u", &cmos_cmd) == 1 && - cmos_cmd >= 0 && cmos_cmd <= 21) { - /* cmos_cmd set */ - } else - return -EINVAL; - - if (!cmos_eval(cmos_cmd)) - return -EIO; - } - - return 0; -} - - -/************************************************************************* - * LED subdriver - */ - -static enum led_access_mode led_supported; - -IBM_HANDLE(led, ec, "SLED", /* 570 */ - "SYSL", /* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20-21 */ - "LED", /* all others */ - ); /* R30, R31 */ - -static int led_init(void) -{ - if (!led_handle) - /* led not supported on R30, R31 */ - led_supported = IBMACPI_LED_NONE; - else if (strlencmp(led_path, "SLED") == 0) - /* 570 */ - led_supported = IBMACPI_LED_570; - else if (strlencmp(led_path, "SYSL") == 0) - /* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20-21 */ - led_supported = IBMACPI_LED_OLD; - else - /* all others */ - led_supported = IBMACPI_LED_NEW; - - return 0; -} - -#define led_status(s) ((s) == 0 ? "off" : ((s) == 1 ? "on" : "blinking")) - -static int led_read(char *p) -{ - int len = 0; - - if (!led_supported) { - len += sprintf(p + len, "status:\t\tnot supported\n"); - return len; - } - len += sprintf(p + len, "status:\t\tsupported\n"); - - if (led_supported == IBMACPI_LED_570) { - /* 570 */ - int i, status; - for (i = 0; i < 8; i++) { - if (!acpi_evalf(ec_handle, - &status, "GLED", "dd", 1 << i)) - return -EIO; - len += sprintf(p + len, "%d:\t\t%s\n", - i, led_status(status)); - } - } - - len += sprintf(p + len, "commands:\t" - " on, off, blink ( is 0-7)\n"); - - return len; -} - -/* off, on, blink */ -static const int led_sled_arg1[] = { 0, 1, 3 }; -static const int led_exp_hlbl[] = { 0, 0, 1 }; /* led# * */ -static const int led_exp_hlcl[] = { 0, 1, 1 }; /* led# * */ -static const int led_led_arg1[] = { 0, 0x80, 0xc0 }; - -static int led_write(char *buf) -{ - char *cmd; - int led, ind, ret; - - if (!led_supported) - return -ENODEV; - - while ((cmd = next_cmd(&buf))) { - if (sscanf(cmd, "%d", &led) != 1 || led < 0 || led > 7) - return -EINVAL; - - if (strstr(cmd, "off")) { - ind = 0; - } else if (strstr(cmd, "on")) { - ind = 1; - } else if (strstr(cmd, "blink")) { - ind = 2; - } else - return -EINVAL; - - if (led_supported == IBMACPI_LED_570) { - /* 570 */ - led = 1 << led; - if (!acpi_evalf(led_handle, NULL, NULL, "vdd", - led, led_sled_arg1[ind])) - return -EIO; - } else if (led_supported == IBMACPI_LED_OLD) { - /* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20 */ - led = 1 << led; - ret = ec_write(IBMACPI_LED_EC_HLMS, led); - if (ret >= 0) - ret = - ec_write(IBMACPI_LED_EC_HLBL, - led * led_exp_hlbl[ind]); - if (ret >= 0) - ret = - ec_write(IBMACPI_LED_EC_HLCL, - led * led_exp_hlcl[ind]); - if (ret < 0) - return ret; - } else { - /* all others */ - if (!acpi_evalf(led_handle, NULL, NULL, "vdd", - led, led_led_arg1[ind])) - return -EIO; - } - } - - return 0; -} - -/************************************************************************* - * Beep subdriver - */ - -IBM_HANDLE(beep, ec, "BEEP"); /* all except R30, R31 */ - -static int beep_read(char *p) -{ - int len = 0; - - if (!beep_handle) - len += sprintf(p + len, "status:\t\tnot supported\n"); - else { - len += sprintf(p + len, "status:\t\tsupported\n"); - len += sprintf(p + len, "commands:\t ( is 0-17)\n"); - } - - return len; -} - -static int beep_write(char *buf) -{ - char *cmd; - int beep_cmd; - - if (!beep_handle) - return -ENODEV; - - while ((cmd = next_cmd(&buf))) { - if (sscanf(cmd, "%u", &beep_cmd) == 1 && - beep_cmd >= 0 && beep_cmd <= 17) { - /* beep_cmd set */ - } else - return -EINVAL; - if (!acpi_evalf(beep_handle, NULL, NULL, "vdd", beep_cmd, 0)) - return -EIO; - } - - return 0; -} - -/************************************************************************* - * Thermal subdriver - */ - -static enum thermal_access_mode thermal_read_mode; - -static int thermal_init(void) -{ - u8 t, ta1, ta2; - int i; - int acpi_tmp7 = acpi_evalf(ec_handle, NULL, "TMP7", "qv"); - - if (ibm_thinkpad_ec_found && experimental) { - /* - * Direct EC access mode: sensors at registers - * 0x78-0x7F, 0xC0-0xC7. Registers return 0x00 for - * non-implemented, thermal sensors return 0x80 when - * not available - */ - - ta1 = ta2 = 0; - for (i = 0; i < 8; i++) { - if (likely(acpi_ec_read(0x78 + i, &t))) { - ta1 |= t; - } else { - ta1 = 0; - break; - } - if (likely(acpi_ec_read(0xC0 + i, &t))) { - ta2 |= t; - } else { - ta1 = 0; - break; - } - } - if (ta1 == 0) { - /* This is sheer paranoia, but we handle it anyway */ - if (acpi_tmp7) { - printk(IBM_ERR - "ThinkPad ACPI EC access misbehaving, " - "falling back to ACPI TMPx access mode\n"); - thermal_read_mode = IBMACPI_THERMAL_ACPI_TMP07; - } else { - printk(IBM_ERR - "ThinkPad ACPI EC access misbehaving, " - "disabling thermal sensors access\n"); - thermal_read_mode = IBMACPI_THERMAL_NONE; - } - } else { - thermal_read_mode = - (ta2 != 0) ? - IBMACPI_THERMAL_TPEC_16 : IBMACPI_THERMAL_TPEC_8; - } - } else if (acpi_tmp7) { - if (acpi_evalf(ec_handle, NULL, "UPDT", "qv")) { - /* 600e/x, 770e, 770x */ - thermal_read_mode = IBMACPI_THERMAL_ACPI_UPDT; - } else { - /* Standard ACPI TMPx access, max 8 sensors */ - thermal_read_mode = IBMACPI_THERMAL_ACPI_TMP07; - } - } else { - /* temperatures not supported on 570, G4x, R30, R31, R32 */ - thermal_read_mode = IBMACPI_THERMAL_NONE; - } - - return 0; -} - -static int thermal_get_sensors(struct ibm_thermal_sensors_struct *s) -{ - int i, t; - s8 tmp; - char tmpi[] = "TMPi"; - - if (!s) - return -EINVAL; - - switch (thermal_read_mode) { -#if IBMACPI_MAX_THERMAL_SENSORS >= 16 - case IBMACPI_THERMAL_TPEC_16: - for (i = 0; i < 8; i++) { - if (!acpi_ec_read(0xC0 + i, &tmp)) - return -EIO; - s->temp[i + 8] = tmp * 1000; - } - /* fallthrough */ -#endif - case IBMACPI_THERMAL_TPEC_8: - for (i = 0; i < 8; i++) { - if (!acpi_ec_read(0x78 + i, &tmp)) - return -EIO; - s->temp[i] = tmp * 1000; - } - return (thermal_read_mode == IBMACPI_THERMAL_TPEC_16) ? 16 : 8; - - case IBMACPI_THERMAL_ACPI_UPDT: - if (!acpi_evalf(ec_handle, NULL, "UPDT", "v")) - return -EIO; - for (i = 0; i < 8; i++) { - tmpi[3] = '0' + i; - if (!acpi_evalf(ec_handle, &t, tmpi, "d")) - return -EIO; - s->temp[i] = (t - 2732) * 100; - } - return 8; - - case IBMACPI_THERMAL_ACPI_TMP07: - for (i = 0; i < 8; i++) { - tmpi[3] = '0' + i; - if (!acpi_evalf(ec_handle, &t, tmpi, "d")) - return -EIO; - s->temp[i] = t * 1000; - } - return 8; - - case IBMACPI_THERMAL_NONE: - default: - return 0; - } -} - -static int thermal_read(char *p) -{ - int len = 0; - int n, i; - struct ibm_thermal_sensors_struct t; - - n = thermal_get_sensors(&t); - if (unlikely(n < 0)) - return n; - - len += sprintf(p + len, "temperatures:\t"); - - if (n > 0) { - for (i = 0; i < (n - 1); i++) - len += sprintf(p + len, "%d ", t.temp[i] / 1000); - len += sprintf(p + len, "%d\n", t.temp[i] / 1000); - } else - len += sprintf(p + len, "not supported\n"); - - return len; -} - -/************************************************************************* - * EC Dump subdriver - */ - -static u8 ecdump_regs[256]; - -static int ecdump_read(char *p) -{ - int len = 0; - int i, j; - u8 v; - - len += sprintf(p + len, "EC " - " +00 +01 +02 +03 +04 +05 +06 +07" - " +08 +09 +0a +0b +0c +0d +0e +0f\n"); - for (i = 0; i < 256; i += 16) { - len += sprintf(p + len, "EC 0x%02x:", i); - for (j = 0; j < 16; j++) { - if (!acpi_ec_read(i + j, &v)) - break; - if (v != ecdump_regs[i + j]) - len += sprintf(p + len, " *%02x", v); - else - len += sprintf(p + len, " %02x", v); - ecdump_regs[i + j] = v; - } - len += sprintf(p + len, "\n"); - if (j != 16) - break; - } - - /* These are way too dangerous to advertise openly... */ -#if 0 - len += sprintf(p + len, "commands:\t0x 0x" - " ( is 00-ff, is 00-ff)\n"); - len += sprintf(p + len, "commands:\t0x " - " ( is 00-ff, is 0-255)\n"); -#endif - return len; -} - -static int ecdump_write(char *buf) -{ - char *cmd; - int i, v; - - while ((cmd = next_cmd(&buf))) { - if (sscanf(cmd, "0x%x 0x%x", &i, &v) == 2) { - /* i and v set */ - } else if (sscanf(cmd, "0x%x %u", &i, &v) == 2) { - /* i and v set */ - } else - return -EINVAL; - if (i >= 0 && i < 256 && v >= 0 && v < 256) { - if (!acpi_ec_write(i, v)) - return -EIO; - } else - return -EINVAL; - } - - return 0; -} - -/************************************************************************* - * Backlight/brightness subdriver - */ - -static struct backlight_device *ibm_backlight_device = NULL; - -static struct backlight_ops ibm_backlight_data = { - .get_brightness = brightness_get, - .update_status = brightness_update_status, -}; - -static int brightness_init(void) -{ - int b; - - b = brightness_get(NULL); - if (b < 0) - return b; - - ibm_backlight_device = backlight_device_register("ibm", NULL, NULL, - &ibm_backlight_data); - if (IS_ERR(ibm_backlight_device)) { - printk(IBM_ERR "Could not register backlight device\n"); - return PTR_ERR(ibm_backlight_device); - } - - ibm_backlight_device->props.max_brightness = 7; - ibm_backlight_device->props.brightness = b; - backlight_update_status(ibm_backlight_device); - - return 0; -} - -static void brightness_exit(void) -{ - if (ibm_backlight_device) { - backlight_device_unregister(ibm_backlight_device); - ibm_backlight_device = NULL; - } -} - -static int brightness_update_status(struct backlight_device *bd) -{ - return brightness_set( - (bd->props.fb_blank == FB_BLANK_UNBLANK && - bd->props.power == FB_BLANK_UNBLANK) ? - bd->props.brightness : 0); -} - -static int brightness_get(struct backlight_device *bd) -{ - u8 level; - if (!acpi_ec_read(brightness_offset, &level)) - return -EIO; - - level &= 0x7; - - return level; -} - -static int brightness_set(int value) -{ - int cmos_cmd, inc, i; - int current_value = brightness_get(NULL); - - value &= 7; - - cmos_cmd = value > current_value ? TP_CMOS_BRIGHTNESS_UP : TP_CMOS_BRIGHTNESS_DOWN; - inc = value > current_value ? 1 : -1; - for (i = current_value; i != value; i += inc) { - if (!cmos_eval(cmos_cmd)) - return -EIO; - if (!acpi_ec_write(brightness_offset, i + inc)) - return -EIO; - } - - return 0; -} - -static int brightness_read(char *p) -{ - int len = 0; - int level; - - if ((level = brightness_get(NULL)) < 0) { - len += sprintf(p + len, "level:\t\tunreadable\n"); - } else { - len += sprintf(p + len, "level:\t\t%d\n", level & 0x7); - len += sprintf(p + len, "commands:\tup, down\n"); - len += sprintf(p + len, "commands:\tlevel " - " ( is 0-7)\n"); - } - - return len; -} - -static int brightness_write(char *buf) -{ - int level; - int new_level; - char *cmd; - - while ((cmd = next_cmd(&buf))) { - if ((level = brightness_get(NULL)) < 0) - return level; - level &= 7; - - if (strlencmp(cmd, "up") == 0) { - new_level = level == 7 ? 7 : level + 1; - } else if (strlencmp(cmd, "down") == 0) { - new_level = level == 0 ? 0 : level - 1; - } else if (sscanf(cmd, "level %d", &new_level) == 1 && - new_level >= 0 && new_level <= 7) { - /* new_level set */ - } else - return -EINVAL; - - brightness_set(new_level); - } - - return 0; -} - -/************************************************************************* - * Volume subdriver - */ - -static int volume_read(char *p) -{ - int len = 0; - u8 level; - - if (!acpi_ec_read(volume_offset, &level)) { - len += sprintf(p + len, "level:\t\tunreadable\n"); - } else { - len += sprintf(p + len, "level:\t\t%d\n", level & 0xf); - len += sprintf(p + len, "mute:\t\t%s\n", onoff(level, 6)); - len += sprintf(p + len, "commands:\tup, down, mute\n"); - len += sprintf(p + len, "commands:\tlevel " - " ( is 0-15)\n"); - } - - return len; -} - -static int volume_write(char *buf) -{ - int cmos_cmd, inc, i; - u8 level, mute; - int new_level, new_mute; - char *cmd; - - while ((cmd = next_cmd(&buf))) { - if (!acpi_ec_read(volume_offset, &level)) - return -EIO; - new_mute = mute = level & 0x40; - new_level = level = level & 0xf; - - if (strlencmp(cmd, "up") == 0) { - if (mute) - new_mute = 0; - else - new_level = level == 15 ? 15 : level + 1; - } else if (strlencmp(cmd, "down") == 0) { - if (mute) - new_mute = 0; - else - new_level = level == 0 ? 0 : level - 1; - } else if (sscanf(cmd, "level %d", &new_level) == 1 && - new_level >= 0 && new_level <= 15) { - /* new_level set */ - } else if (strlencmp(cmd, "mute") == 0) { - new_mute = 0x40; - } else - return -EINVAL; - - if (new_level != level) { /* mute doesn't change */ - cmos_cmd = new_level > level ? TP_CMOS_VOLUME_UP : TP_CMOS_VOLUME_DOWN; - inc = new_level > level ? 1 : -1; - - if (mute && (!cmos_eval(cmos_cmd) || - !acpi_ec_write(volume_offset, level))) - return -EIO; - - for (i = level; i != new_level; i += inc) - if (!cmos_eval(cmos_cmd) || - !acpi_ec_write(volume_offset, i + inc)) - return -EIO; - - if (mute && (!cmos_eval(TP_CMOS_VOLUME_MUTE) || - !acpi_ec_write(volume_offset, - new_level + mute))) - return -EIO; - } - - if (new_mute != mute) { /* level doesn't change */ - cmos_cmd = new_mute ? TP_CMOS_VOLUME_MUTE : TP_CMOS_VOLUME_UP; - - if (!cmos_eval(cmos_cmd) || - !acpi_ec_write(volume_offset, level + new_mute)) - return -EIO; - } - } - - return 0; -} - - -/************************************************************************* - * Fan subdriver - */ - -/* - * FAN ACCESS MODES - * - * IBMACPI_FAN_RD_ACPI_GFAN: - * ACPI GFAN method: returns fan level - * - * see IBMACPI_FAN_WR_ACPI_SFAN - * EC 0x2f not available if GFAN exists - * - * IBMACPI_FAN_WR_ACPI_SFAN: - * ACPI SFAN method: sets fan level, 0 (stop) to 7 (max) - * - * EC 0x2f might be available *for reading*, but never for writing. - * - * IBMACPI_FAN_WR_TPEC: - * ThinkPad EC register 0x2f (HFSP): fan control loop mode Supported - * on almost all ThinkPads - * - * Fan speed changes of any sort (including those caused by the - * disengaged mode) are usually done slowly by the firmware as the - * maximum ammount of fan duty cycle change per second seems to be - * limited. - * - * Reading is not available if GFAN exists. - * Writing is not available if SFAN exists. - * - * Bits - * 7 automatic mode engaged; - * (default operation mode of the ThinkPad) - * fan level is ignored in this mode. - * 6 disengage mode (takes precedence over bit 7); - * not available on all thinkpads. May disable - * the tachometer, and speeds up fan to 100% duty-cycle, - * which speeds it up far above the standard RPM - * levels. It is not impossible that it could cause - * hardware damage. - * 5-3 unused in some models. Extra bits for fan level - * in others, but still useless as all values above - * 7 map to the same speed as level 7 in these models. - * 2-0 fan level (0..7 usually) - * 0x00 = stop - * 0x07 = max (set when temperatures critical) - * Some ThinkPads may have other levels, see - * IBMACPI_FAN_WR_ACPI_FANS (X31/X40/X41) - * - * FIRMWARE BUG: on some models, EC 0x2f might not be initialized at - * boot. Apparently the EC does not intialize it, so unless ACPI DSDT - * does so, its initial value is meaningless (0x07). - * - * For firmware bugs, refer to: - * http://thinkwiki.org/wiki/Embedded_Controller_Firmware#Firmware_Issues - * - * ---- - * - * ThinkPad EC register 0x84 (LSB), 0x85 (MSB): - * Main fan tachometer reading (in RPM) - * - * This register is present on all ThinkPads with a new-style EC, and - * it is known not to be present on the A21m/e, and T22, as there is - * something else in offset 0x84 according to the ACPI DSDT. Other - * ThinkPads from this same time period (and earlier) probably lack the - * tachometer as well. - * - * Unfortunately a lot of ThinkPads with new-style ECs but whose firwmare - * was never fixed by IBM to report the EC firmware version string - * probably support the tachometer (like the early X models), so - * detecting it is quite hard. We need more data to know for sure. - * - * FIRMWARE BUG: always read 0x84 first, otherwise incorrect readings - * might result. - * - * FIRMWARE BUG: when EC 0x2f bit 6 is set (disengaged mode), this - * register is not invalidated in ThinkPads that disable tachometer - * readings. Thus, the tachometer readings go stale. - * - * For firmware bugs, refer to: - * http://thinkwiki.org/wiki/Embedded_Controller_Firmware#Firmware_Issues - * - * IBMACPI_FAN_WR_ACPI_FANS: - * ThinkPad X31, X40, X41. Not available in the X60. - * - * FANS ACPI handle: takes three arguments: low speed, medium speed, - * high speed. ACPI DSDT seems to map these three speeds to levels - * as follows: STOP LOW LOW MED MED HIGH HIGH HIGH HIGH - * (this map is stored on FAN0..FAN8 as "0,1,1,2,2,3,3,3,3") - * - * The speeds are stored on handles - * (FANA:FAN9), (FANC:FANB), (FANE:FAND). - * - * There are three default speed sets, acessible as handles: - * FS1L,FS1M,FS1H; FS2L,FS2M,FS2H; FS3L,FS3M,FS3H - * - * ACPI DSDT switches which set is in use depending on various - * factors. - * - * IBMACPI_FAN_WR_TPEC is also available and should be used to - * command the fan. The X31/X40/X41 seems to have 8 fan levels, - * but the ACPI tables just mention level 7. - */ - -static enum fan_status_access_mode fan_status_access_mode; -static enum fan_control_access_mode fan_control_access_mode; -static enum fan_control_commands fan_control_commands; - -static int fan_control_status_known; -static u8 fan_control_initial_status; - -static void fan_watchdog_fire(struct work_struct *ignored); -static int fan_watchdog_maxinterval; -static DECLARE_DELAYED_WORK(fan_watchdog_task, fan_watchdog_fire); - -IBM_HANDLE(fans, ec, "FANS"); /* X31, X40, X41 */ -IBM_HANDLE(gfan, ec, "GFAN", /* 570 */ - "\\FSPD", /* 600e/x, 770e, 770x */ - ); /* all others */ -IBM_HANDLE(sfan, ec, "SFAN", /* 570 */ - "JFNS", /* 770x-JL */ - ); /* all others */ - -static int fan_init(void) -{ - fan_status_access_mode = IBMACPI_FAN_NONE; - fan_control_access_mode = IBMACPI_FAN_WR_NONE; - fan_control_commands = 0; - fan_control_status_known = 1; - fan_watchdog_maxinterval = 0; - - if (gfan_handle) { - /* 570, 600e/x, 770e, 770x */ - fan_status_access_mode = IBMACPI_FAN_RD_ACPI_GFAN; - } else { - /* all other ThinkPads: note that even old-style - * ThinkPad ECs supports the fan control register */ - if (likely(acpi_ec_read(fan_status_offset, - &fan_control_initial_status))) { - fan_status_access_mode = IBMACPI_FAN_RD_TPEC; - - /* In some ThinkPads, neither the EC nor the ACPI - * DSDT initialize the fan status, and it ends up - * being set to 0x07 when it *could* be either - * 0x07 or 0x80. - * - * Enable for TP-1Y (T43), TP-78 (R51e), - * TP-76 (R52), TP-70 (T43, R52), which are known - * to be buggy. */ - if (fan_control_initial_status == 0x07 && - ibm_thinkpad_ec_found && - ((ibm_thinkpad_ec_found[0] == '1' && - ibm_thinkpad_ec_found[1] == 'Y') || - (ibm_thinkpad_ec_found[0] == '7' && - (ibm_thinkpad_ec_found[1] == '6' || - ibm_thinkpad_ec_found[1] == '8' || - ibm_thinkpad_ec_found[1] == '0')) - )) { - printk(IBM_NOTICE - "fan_init: initial fan status is " - "unknown, assuming it is in auto " - "mode\n"); - fan_control_status_known = 0; - } - } else { - printk(IBM_ERR - "ThinkPad ACPI EC access misbehaving, " - "fan status and control unavailable\n"); - return 0; - } - } - - if (sfan_handle) { - /* 570, 770x-JL */ - fan_control_access_mode = IBMACPI_FAN_WR_ACPI_SFAN; - fan_control_commands |= - IBMACPI_FAN_CMD_LEVEL | IBMACPI_FAN_CMD_ENABLE; - } else { - if (!gfan_handle) { - /* gfan without sfan means no fan control */ - /* all other models implement TP EC 0x2f control */ - - if (fans_handle) { - /* X31, X40, X41 */ - fan_control_access_mode = - IBMACPI_FAN_WR_ACPI_FANS; - fan_control_commands |= - IBMACPI_FAN_CMD_SPEED | - IBMACPI_FAN_CMD_LEVEL | - IBMACPI_FAN_CMD_ENABLE; - } else { - fan_control_access_mode = IBMACPI_FAN_WR_TPEC; - fan_control_commands |= - IBMACPI_FAN_CMD_LEVEL | - IBMACPI_FAN_CMD_ENABLE; - } - } - } - - return 0; -} - -static int fan_get_status(u8 *status) -{ - u8 s; - - /* TODO: - * Add IBMACPI_FAN_RD_ACPI_FANS ? */ - - switch (fan_status_access_mode) { - case IBMACPI_FAN_RD_ACPI_GFAN: - /* 570, 600e/x, 770e, 770x */ - - if (unlikely(!acpi_evalf(gfan_handle, &s, NULL, "d"))) - return -EIO; - - if (likely(status)) - *status = s & 0x07; - - break; - - case IBMACPI_FAN_RD_TPEC: - /* all except 570, 600e/x, 770e, 770x */ - if (unlikely(!acpi_ec_read(fan_status_offset, &s))) - return -EIO; - - if (likely(status)) - *status = s; - - break; - - default: - return -ENXIO; - } - - return 0; -} - -static void fan_exit(void) -{ - cancel_delayed_work(&fan_watchdog_task); - flush_scheduled_work(); -} - -static int fan_get_speed(unsigned int *speed) -{ - u8 hi, lo; - - switch (fan_status_access_mode) { - case IBMACPI_FAN_RD_TPEC: - /* all except 570, 600e/x, 770e, 770x */ - if (unlikely(!acpi_ec_read(fan_rpm_offset, &lo) || - !acpi_ec_read(fan_rpm_offset + 1, &hi))) - return -EIO; - - if (likely(speed)) - *speed = (hi << 8) | lo; - - break; - - default: - return -ENXIO; - } - - return 0; -} - -static void fan_watchdog_fire(struct work_struct *ignored) -{ - printk(IBM_NOTICE "fan watchdog: enabling fan\n"); - if (fan_set_enable()) { - printk(IBM_ERR "fan watchdog: error while enabling fan\n"); - /* reschedule for later */ - fan_watchdog_reset(); - } -} - -static void fan_watchdog_reset(void) -{ - static int fan_watchdog_active = 0; - - if (fan_watchdog_active) - cancel_delayed_work(&fan_watchdog_task); - - if (fan_watchdog_maxinterval > 0) { - fan_watchdog_active = 1; - if (!schedule_delayed_work(&fan_watchdog_task, - msecs_to_jiffies(fan_watchdog_maxinterval - * 1000))) { - printk(IBM_ERR "failed to schedule the fan watchdog, " - "watchdog will not trigger\n"); - } - } else - fan_watchdog_active = 0; -} - -static int fan_set_level(int level) -{ - switch (fan_control_access_mode) { - case IBMACPI_FAN_WR_ACPI_SFAN: - if (level >= 0 && level <= 7) { - if (!acpi_evalf(sfan_handle, NULL, NULL, "vd", level)) - return -EIO; - } else - return -EINVAL; - break; - - case IBMACPI_FAN_WR_ACPI_FANS: - case IBMACPI_FAN_WR_TPEC: - if ((level != IBMACPI_FAN_EC_AUTO) && - (level != IBMACPI_FAN_EC_DISENGAGED) && - ((level < 0) || (level > 7))) - return -EINVAL; - - if (!acpi_ec_write(fan_status_offset, level)) - return -EIO; - else - fan_control_status_known = 1; - break; - - default: - return -ENXIO; - } - return 0; -} - -static int fan_set_enable(void) -{ - u8 s; - int rc; - - switch (fan_control_access_mode) { - case IBMACPI_FAN_WR_ACPI_FANS: - case IBMACPI_FAN_WR_TPEC: - if ((rc = fan_get_status(&s)) < 0) - return rc; - - /* Don't go out of emergency fan mode */ - if (s != 7) - s = IBMACPI_FAN_EC_AUTO; - - if (!acpi_ec_write(fan_status_offset, s)) - return -EIO; - else - fan_control_status_known = 1; - break; - - case IBMACPI_FAN_WR_ACPI_SFAN: - if ((rc = fan_get_status(&s)) < 0) - return rc; - - s &= 0x07; - - /* Set fan to at least level 4 */ - if (s < 4) - s = 4; - - if (!acpi_evalf(sfan_handle, NULL, NULL, "vd", s)) - return -EIO; - break; - - default: - return -ENXIO; - } - return 0; -} - -static int fan_set_disable(void) -{ - switch (fan_control_access_mode) { - case IBMACPI_FAN_WR_ACPI_FANS: - case IBMACPI_FAN_WR_TPEC: - if (!acpi_ec_write(fan_status_offset, 0x00)) - return -EIO; - else - fan_control_status_known = 1; - break; - - case IBMACPI_FAN_WR_ACPI_SFAN: - if (!acpi_evalf(sfan_handle, NULL, NULL, "vd", 0x00)) - return -EIO; - break; - - default: - return -ENXIO; - } - return 0; -} - -static int fan_set_speed(int speed) -{ - switch (fan_control_access_mode) { - case IBMACPI_FAN_WR_ACPI_FANS: - if (speed >= 0 && speed <= 65535) { - if (!acpi_evalf(fans_handle, NULL, NULL, "vddd", - speed, speed, speed)) - return -EIO; - } else - return -EINVAL; - break; - - default: - return -ENXIO; - } - return 0; -} - -static int fan_read(char *p) -{ - int len = 0; - int rc; - u8 status; - unsigned int speed = 0; - - switch (fan_status_access_mode) { - case IBMACPI_FAN_RD_ACPI_GFAN: - /* 570, 600e/x, 770e, 770x */ - if ((rc = fan_get_status(&status)) < 0) - return rc; - - len += sprintf(p + len, "status:\t\t%s\n" - "level:\t\t%d\n", - (status != 0) ? "enabled" : "disabled", status); - break; - - case IBMACPI_FAN_RD_TPEC: - /* all except 570, 600e/x, 770e, 770x */ - if ((rc = fan_get_status(&status)) < 0) - return rc; - - if (unlikely(!fan_control_status_known)) { - if (status != fan_control_initial_status) - fan_control_status_known = 1; - else - /* Return most likely status. In fact, it - * might be the only possible status */ - status = IBMACPI_FAN_EC_AUTO; - } - - len += sprintf(p + len, "status:\t\t%s\n", - (status != 0) ? "enabled" : "disabled"); - - /* No ThinkPad boots on disengaged mode, we can safely - * assume the tachometer is online if fan control status - * was unknown */ - if ((rc = fan_get_speed(&speed)) < 0) - return rc; - - len += sprintf(p + len, "speed:\t\t%d\n", speed); - - if (status & IBMACPI_FAN_EC_DISENGAGED) - /* Disengaged mode takes precedence */ - len += sprintf(p + len, "level:\t\tdisengaged\n"); - else if (status & IBMACPI_FAN_EC_AUTO) - len += sprintf(p + len, "level:\t\tauto\n"); - else - len += sprintf(p + len, "level:\t\t%d\n", status); - break; - - case IBMACPI_FAN_NONE: - default: - len += sprintf(p + len, "status:\t\tnot supported\n"); - } - - if (fan_control_commands & IBMACPI_FAN_CMD_LEVEL) { - len += sprintf(p + len, "commands:\tlevel "); - - switch (fan_control_access_mode) { - case IBMACPI_FAN_WR_ACPI_SFAN: - len += sprintf(p + len, " ( is 0-7)\n"); - break; - - default: - len += sprintf(p + len, " ( is 0-7, " - "auto, disengaged)\n"); - break; - } - } - - if (fan_control_commands & IBMACPI_FAN_CMD_ENABLE) - len += sprintf(p + len, "commands:\tenable, disable\n" - "commands:\twatchdog ( is 0 (off), " - "1-120 (seconds))\n"); - - if (fan_control_commands & IBMACPI_FAN_CMD_SPEED) - len += sprintf(p + len, "commands:\tspeed " - " ( is 0-65535)\n"); - - return len; -} - -static int fan_write_cmd_level(const char *cmd, int *rc) -{ - int level; - - if (strlencmp(cmd, "level auto") == 0) - level = IBMACPI_FAN_EC_AUTO; - else if (strlencmp(cmd, "level disengaged") == 0) - level = IBMACPI_FAN_EC_DISENGAGED; - else if (sscanf(cmd, "level %d", &level) != 1) - return 0; - - if ((*rc = fan_set_level(level)) == -ENXIO) - printk(IBM_ERR "level command accepted for unsupported " - "access mode %d", fan_control_access_mode); - - return 1; -} - -static int fan_write_cmd_enable(const char *cmd, int *rc) -{ - if (strlencmp(cmd, "enable") != 0) - return 0; - - if ((*rc = fan_set_enable()) == -ENXIO) - printk(IBM_ERR "enable command accepted for unsupported " - "access mode %d", fan_control_access_mode); - - return 1; -} - -static int fan_write_cmd_disable(const char *cmd, int *rc) -{ - if (strlencmp(cmd, "disable") != 0) - return 0; - - if ((*rc = fan_set_disable()) == -ENXIO) - printk(IBM_ERR "disable command accepted for unsupported " - "access mode %d", fan_control_access_mode); - - return 1; -} - -static int fan_write_cmd_speed(const char *cmd, int *rc) -{ - int speed; - - /* TODO: - * Support speed ? */ - - if (sscanf(cmd, "speed %d", &speed) != 1) - return 0; - - if ((*rc = fan_set_speed(speed)) == -ENXIO) - printk(IBM_ERR "speed command accepted for unsupported " - "access mode %d", fan_control_access_mode); - - return 1; -} - -static int fan_write_cmd_watchdog(const char *cmd, int *rc) -{ - int interval; - - if (sscanf(cmd, "watchdog %d", &interval) != 1) - return 0; - - if (interval < 0 || interval > 120) - *rc = -EINVAL; - else - fan_watchdog_maxinterval = interval; - - return 1; -} - -static int fan_write(char *buf) -{ - char *cmd; - int rc = 0; - - while (!rc && (cmd = next_cmd(&buf))) { - if (!((fan_control_commands & IBMACPI_FAN_CMD_LEVEL) && - fan_write_cmd_level(cmd, &rc)) && - !((fan_control_commands & IBMACPI_FAN_CMD_ENABLE) && - (fan_write_cmd_enable(cmd, &rc) || - fan_write_cmd_disable(cmd, &rc) || - fan_write_cmd_watchdog(cmd, &rc))) && - !((fan_control_commands & IBMACPI_FAN_CMD_SPEED) && - fan_write_cmd_speed(cmd, &rc)) - ) - rc = -EINVAL; - else if (!rc) - fan_watchdog_reset(); - } - - return rc; -} - -/**************************************************************************** - **************************************************************************** - * - * Infrastructure - * - **************************************************************************** - ****************************************************************************/ - -/* /proc support */ -static struct proc_dir_entry *proc_dir = NULL; - -/* Subdriver registry */ -static struct ibm_struct ibms[] = { - { - .name = "driver", - .init = ibm_acpi_driver_init, - .read = ibm_acpi_driver_read, - }, - { - .name = "hotkey", - .hid = IBM_HKEY_HID, - .init = hotkey_init, - .read = hotkey_read, - .write = hotkey_write, - .exit = hotkey_exit, - .notify = hotkey_notify, - .handle = &hkey_handle, - .type = ACPI_DEVICE_NOTIFY, - }, - { - .name = "bluetooth", - .init = bluetooth_init, - .read = bluetooth_read, - .write = bluetooth_write, - }, - { - .name = "wan", - .init = wan_init, - .read = wan_read, - .write = wan_write, - .experimental = 1, - }, - { - .name = "video", - .init = video_init, - .read = video_read, - .write = video_write, - .exit = video_exit, - }, - { - .name = "light", - .init = light_init, - .read = light_read, - .write = light_write, - }, -#ifdef CONFIG_ACPI_IBM_DOCK - { - .name = "dock", - .read = dock_read, - .write = dock_write, - .notify = dock_notify, - .handle = &dock_handle, - .type = ACPI_SYSTEM_NOTIFY, - }, - { - .name = "dock", - .hid = IBM_PCI_HID, - .notify = dock_notify, - .handle = &pci_handle, - .type = ACPI_SYSTEM_NOTIFY, - }, -#endif -#ifdef CONFIG_ACPI_IBM_BAY - { - .name = "bay", - .init = bay_init, - .read = bay_read, - .write = bay_write, - .notify = bay_notify, - .handle = &bay_handle, - .type = ACPI_SYSTEM_NOTIFY, - }, -#endif /* CONFIG_ACPI_IBM_BAY */ - { - .name = "cmos", - .read = cmos_read, - .write = cmos_write, - }, - { - .name = "led", - .init = led_init, - .read = led_read, - .write = led_write, - }, - { - .name = "beep", - .read = beep_read, - .write = beep_write, - }, - { - .name = "thermal", - .init = thermal_init, - .read = thermal_read, - }, - { - .name = "ecdump", - .read = ecdump_read, - .write = ecdump_write, - .experimental = 1, - }, - { - .name = "brightness", - .read = brightness_read, - .write = brightness_write, - .init = brightness_init, - .exit = brightness_exit, - }, - { - .name = "volume", - .read = volume_read, - .write = volume_write, - }, - { - .name = "fan", - .read = fan_read, - .write = fan_write, - .init = fan_init, - .exit = fan_exit, - .experimental = 1, - }, -}; - -/* - * Module and infrastructure proble, init and exit handling - */ - -static int __init ibm_init(struct ibm_struct *ibm) -{ - int ret; - struct proc_dir_entry *entry; - - if (ibm->experimental && !experimental) - return 0; - - if (ibm->hid) { - ret = register_ibmacpi_subdriver(ibm); - if (ret < 0) - return ret; - ibm->driver_registered = 1; - } - - if (ibm->init) { - ret = ibm->init(); - if (ret != 0) - return ret; - ibm->init_called = 1; - } - - if (ibm->read) { - entry = create_proc_entry(ibm->name, - S_IFREG | S_IRUGO | S_IWUSR, - proc_dir); - if (!entry) { - printk(IBM_ERR "unable to create proc entry %s\n", - ibm->name); - return -ENODEV; - } - entry->owner = THIS_MODULE; - entry->data = ibm; - entry->read_proc = &dispatch_read; - if (ibm->write) - entry->write_proc = &dispatch_write; - ibm->proc_created = 1; - } - - if (ibm->notify) { - ret = setup_notify(ibm); - if (ret == -ENODEV) { - printk(IBM_NOTICE "disabling subdriver %s\n", - ibm->name); - ibm_exit(ibm); - return 0; - } - if (ret < 0) - return ret; - } - - return 0; -} - -static void ibm_exit(struct ibm_struct *ibm) -{ - if (ibm->notify_installed) - acpi_remove_notify_handler(*ibm->handle, ibm->type, - dispatch_notify); - - if (ibm->proc_created) - remove_proc_entry(ibm->name, proc_dir); - - if (ibm->init_called && ibm->exit) - ibm->exit(); - - if (ibm->driver_registered) { - acpi_bus_unregister_driver(ibm->driver); - kfree(ibm->driver); - } -} - -/* Probing */ - -static char *ibm_thinkpad_ec_found = NULL; - -static char* __init check_dmi_for_ec(void) -{ - struct dmi_device *dev = NULL; - char ec_fw_string[18]; - - /* - * ThinkPad T23 or newer, A31 or newer, R50e or newer, - * X32 or newer, all Z series; Some models must have an - * up-to-date BIOS or they will not be detected. - * - * See http://thinkwiki.org/wiki/List_of_DMI_IDs - */ - while ((dev = dmi_find_device(DMI_DEV_TYPE_OEM_STRING, NULL, dev))) { - if (sscanf(dev->name, - "IBM ThinkPad Embedded Controller -[%17c", - ec_fw_string) == 1) { - ec_fw_string[sizeof(ec_fw_string) - 1] = 0; - ec_fw_string[strcspn(ec_fw_string, " ]")] = 0; - return kstrdup(ec_fw_string, GFP_KERNEL); - } - } - return NULL; -} - -/* Module init, exit, parameters */ - -static int __init set_ibm_param(const char *val, struct kernel_param *kp) -{ - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(ibms); i++) - if (strcmp(ibms[i].name, kp->name) == 0 && ibms[i].write) { - if (strlen(val) > sizeof(ibms[i].param) - 2) - return -ENOSPC; - strcpy(ibms[i].param, val); - strcat(ibms[i].param, ","); - return 0; - } - - return -EINVAL; -} - -static int experimental; -module_param(experimental, int, 0); - -#define IBM_PARAM(feature) \ - module_param_call(feature, set_ibm_param, NULL, NULL, 0) - -IBM_PARAM(hotkey); -IBM_PARAM(bluetooth); -IBM_PARAM(video); -IBM_PARAM(light); -#ifdef CONFIG_ACPI_IBM_DOCK -IBM_PARAM(dock); -#endif -#ifdef CONFIG_ACPI_IBM_BAY -IBM_PARAM(bay); -#endif /* CONFIG_ACPI_IBM_BAY */ -IBM_PARAM(cmos); -IBM_PARAM(led); -IBM_PARAM(beep); -IBM_PARAM(ecdump); -IBM_PARAM(brightness); -IBM_PARAM(volume); -IBM_PARAM(fan); - -static int __init acpi_ibm_init(void) -{ - int ret, i; - - if (acpi_disabled) - return -ENODEV; - - /* ec is required because many other handles are relative to it */ - IBM_HANDLE_INIT(ec); - if (!ec_handle) { - printk(IBM_ERR "ec object not found\n"); - return -ENODEV; - } - - /* Models with newer firmware report the EC in DMI */ - ibm_thinkpad_ec_found = check_dmi_for_ec(); - - /* these handles are not required */ - IBM_HANDLE_INIT(vid); - IBM_HANDLE_INIT(vid2); - IBM_HANDLE_INIT(ledb); - IBM_HANDLE_INIT(led); - IBM_HANDLE_INIT(hkey); - IBM_HANDLE_INIT(lght); - IBM_HANDLE_INIT(cmos); -#ifdef CONFIG_ACPI_IBM_DOCK - IBM_HANDLE_INIT(dock); -#endif - IBM_HANDLE_INIT(pci); -#ifdef CONFIG_ACPI_IBM_BAY - IBM_HANDLE_INIT(bay); - if (bay_handle) - IBM_HANDLE_INIT(bay_ej); - IBM_HANDLE_INIT(bay2); - if (bay2_handle) - IBM_HANDLE_INIT(bay2_ej); -#endif /* CONFIG_ACPI_IBM_BAY */ - IBM_HANDLE_INIT(beep); - IBM_HANDLE_INIT(ecrd); - IBM_HANDLE_INIT(ecwr); - IBM_HANDLE_INIT(fans); - IBM_HANDLE_INIT(gfan); - IBM_HANDLE_INIT(sfan); - - proc_dir = proc_mkdir(IBM_DIR, acpi_root_dir); - if (!proc_dir) { - printk(IBM_ERR "unable to create proc dir %s", IBM_DIR); - acpi_ibm_exit(); - return -ENODEV; - } - proc_dir->owner = THIS_MODULE; - - for (i = 0; i < ARRAY_SIZE(ibms); i++) { - ret = ibm_init(&ibms[i]); - if (ret >= 0 && *ibms[i].param) - ret = ibms[i].write(ibms[i].param); - if (ret < 0) { - acpi_ibm_exit(); - return ret; - } - } - - return 0; -} - -static void acpi_ibm_exit(void) -{ - int i; - - for (i = ARRAY_SIZE(ibms) - 1; i >= 0; i--) - ibm_exit(&ibms[i]); - - if (proc_dir) - remove_proc_entry(IBM_DIR, acpi_root_dir); - - if (ibm_thinkpad_ec_found) - kfree(ibm_thinkpad_ec_found); -} - -module_init(acpi_ibm_init); -module_exit(acpi_ibm_exit); diff --git a/drivers/misc/ibm_acpi.h b/drivers/misc/ibm_acpi.h deleted file mode 100644 index 7ebaaa40e183..000000000000 --- a/drivers/misc/ibm_acpi.h +++ /dev/null @@ -1,437 +0,0 @@ -/* - * ibm_acpi.h - IBM ThinkPad ACPI Extras - * - * - * Copyright (C) 2004-2005 Borislav Deianov - * Copyright (C) 2006-2007 Henrique de Moraes Holschuh - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA. - */ - -#ifndef __IBM_ACPI_H__ -#define __IBM_ACPI_H__ - -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include -#include - -#include -#include - - -/**************************************************************************** - * Main driver - */ - -#define IBM_NAME "ibm" -#define IBM_DESC "IBM ThinkPad ACPI Extras" -#define IBM_FILE "ibm_acpi" -#define IBM_URL "http://ibm-acpi.sf.net/" - -#define IBM_DIR IBM_NAME - -#define IBM_LOG IBM_FILE ": " -#define IBM_ERR KERN_ERR IBM_LOG -#define IBM_NOTICE KERN_NOTICE IBM_LOG -#define IBM_INFO KERN_INFO IBM_LOG -#define IBM_DEBUG KERN_DEBUG IBM_LOG - -#define IBM_MAX_ACPI_ARGS 3 - -/* ThinkPad CMOS commands */ -#define TP_CMOS_VOLUME_DOWN 0 -#define TP_CMOS_VOLUME_UP 1 -#define TP_CMOS_VOLUME_MUTE 2 -#define TP_CMOS_BRIGHTNESS_UP 4 -#define TP_CMOS_BRIGHTNESS_DOWN 5 - -#define onoff(status,bit) ((status) & (1 << (bit)) ? "on" : "off") -#define enabled(status,bit) ((status) & (1 << (bit)) ? "enabled" : "disabled") -#define strlencmp(a,b) (strncmp((a), (b), strlen(b))) - -/* ACPI HIDs */ -#define IBM_HKEY_HID "IBM0068" -#define IBM_PCI_HID "PNP0A03" - -/* ACPI helpers */ -static int acpi_evalf(acpi_handle handle, - void *res, char *method, char *fmt, ...); -static int acpi_ec_read(int i, u8 * p); -static int acpi_ec_write(int i, u8 v); -static int _sta(acpi_handle handle); - -/* ACPI handles */ -static acpi_handle root_handle; /* root namespace */ -static acpi_handle ec_handle; /* EC */ -static acpi_handle ecrd_handle, ecwr_handle; /* 570 EC access */ -static acpi_handle cmos_handle, hkey_handle; /* basic thinkpad handles */ - -static void ibm_handle_init(char *name, - acpi_handle * handle, acpi_handle parent, - char **paths, int num_paths, char **path); -#define IBM_HANDLE_INIT(object) \ - ibm_handle_init(#object, &object##_handle, *object##_parent, \ - object##_paths, ARRAY_SIZE(object##_paths), &object##_path) - -/* procfs support */ -static struct proc_dir_entry *proc_dir; -static int ibm_acpi_driver_init(void); -static int ibm_acpi_driver_read(char *p); - -/* procfs helpers */ -static int dispatch_read(char *page, char **start, off_t off, int count, - int *eof, void *data); -static int dispatch_write(struct file *file, const char __user * userbuf, - unsigned long count, void *data); -static char *next_cmd(char **cmds); - -/* Module */ -static int experimental; -static char *ibm_thinkpad_ec_found; - -static char* check_dmi_for_ec(void); -static int acpi_ibm_init(void); -static void acpi_ibm_exit(void); - - -/**************************************************************************** - * Subdrivers - */ - -struct ibm_struct { - char *name; - char param[32]; - - char *hid; - struct acpi_driver *driver; - - int (*init) (void); - int (*read) (char *); - int (*write) (char *); - void (*exit) (void); - - void (*notify) (struct ibm_struct *, u32); - acpi_handle *handle; - int type; - struct acpi_device *device; - - int driver_registered; - int proc_created; - int init_called; - int notify_installed; - - int experimental; -}; - -static struct ibm_struct ibms[]; -static int set_ibm_param(const char *val, struct kernel_param *kp); -static int ibm_init(struct ibm_struct *ibm); -static void ibm_exit(struct ibm_struct *ibm); - -/* ACPI devices */ -static void dispatch_notify(acpi_handle handle, u32 event, void *data); -static int setup_notify(struct ibm_struct *ibm); -static int ibm_device_add(struct acpi_device *device); -static int register_ibmacpi_subdriver(struct ibm_struct *ibm); - - -/* - * Bay subdriver - */ - -#ifdef CONFIG_ACPI_IBM_BAY -static int bay_status_supported, bay_eject_supported; -static int bay_status2_supported, bay_eject2_supported; - -static acpi_handle bay_handle, bay_ej_handle; -static acpi_handle bay2_handle, bay2_ej_handle; - -static int bay_init(void); -static void bay_notify(struct ibm_struct *ibm, u32 event); -static int bay_read(char *p); -static int bay_write(char *buf); -#endif /* CONFIG_ACPI_IBM_BAY */ - - -/* - * Beep subdriver - */ - -static acpi_handle beep_handle; - -static int beep_read(char *p); -static int beep_write(char *buf); - - -/* - * Bluetooth subdriver - */ - -static int bluetooth_supported; - -static int bluetooth_init(void); -static int bluetooth_status(void); -static int bluetooth_read(char *p); -static int bluetooth_write(char *buf); - - -/* - * Brightness (backlight) subdriver - */ - -static struct backlight_device *ibm_backlight_device; -static int brightness_offset = 0x31; - -static int brightness_init(void); -static void brightness_exit(void); -static int brightness_get(struct backlight_device *bd); -static int brightness_set(int value); -static int brightness_update_status(struct backlight_device *bd); -static int brightness_read(char *p); -static int brightness_write(char *buf); - - -/* - * CMOS subdriver - */ - -static int cmos_eval(int cmos_cmd); -static int cmos_read(char *p); -static int cmos_write(char *buf); - - -/* - * Dock subdriver - */ - -static acpi_handle pci_handle; -#ifdef CONFIG_ACPI_IBM_DOCK -static acpi_handle dock_handle; - -static void dock_notify(struct ibm_struct *ibm, u32 event); -static int dock_read(char *p); -static int dock_write(char *buf); -#endif /* CONFIG_ACPI_IBM_DOCK */ - - -/* - * EC dump subdriver - */ - -static int ecdump_read(char *p) ; -static int ecdump_write(char *buf); - - -/* - * Fan subdriver - */ - -enum { /* Fan control constants */ - fan_status_offset = 0x2f, /* EC register 0x2f */ - fan_rpm_offset = 0x84, /* EC register 0x84: LSB, 0x85 MSB (RPM) - * 0x84 must be read before 0x85 */ - - IBMACPI_FAN_EC_DISENGAGED = 0x40, /* EC mode: tachometer - * disengaged */ - IBMACPI_FAN_EC_AUTO = 0x80, /* EC mode: auto fan - * control */ -}; - -enum fan_status_access_mode { - IBMACPI_FAN_NONE = 0, /* No fan status or control */ - IBMACPI_FAN_RD_ACPI_GFAN, /* Use ACPI GFAN */ - IBMACPI_FAN_RD_TPEC, /* Use ACPI EC regs 0x2f, 0x84-0x85 */ -}; - -enum fan_control_access_mode { - IBMACPI_FAN_WR_NONE = 0, /* No fan control */ - IBMACPI_FAN_WR_ACPI_SFAN, /* Use ACPI SFAN */ - IBMACPI_FAN_WR_TPEC, /* Use ACPI EC reg 0x2f */ - IBMACPI_FAN_WR_ACPI_FANS, /* Use ACPI FANS and EC reg 0x2f */ -}; - -enum fan_control_commands { - IBMACPI_FAN_CMD_SPEED = 0x0001, /* speed command */ - IBMACPI_FAN_CMD_LEVEL = 0x0002, /* level command */ - IBMACPI_FAN_CMD_ENABLE = 0x0004, /* enable/disable cmd, - * and also watchdog cmd */ -}; - -static enum fan_status_access_mode fan_status_access_mode; -static enum fan_control_access_mode fan_control_access_mode; -static enum fan_control_commands fan_control_commands; -static int fan_control_status_known; -static u8 fan_control_initial_status; -static int fan_watchdog_maxinterval; - -static acpi_handle fans_handle, gfan_handle, sfan_handle; - -static int fan_init(void); -static void fan_exit(void); -static int fan_get_status(u8 *status); -static int fan_get_speed(unsigned int *speed); -static void fan_watchdog_fire(struct work_struct *ignored); -static void fan_watchdog_reset(void); -static int fan_set_level(int level); -static int fan_set_enable(void); -static int fan_set_disable(void); -static int fan_set_speed(int speed); -static int fan_read(char *p); -static int fan_write(char *buf); -static int fan_write_cmd_level(const char *cmd, int *rc); -static int fan_write_cmd_enable(const char *cmd, int *rc); -static int fan_write_cmd_disable(const char *cmd, int *rc); -static int fan_write_cmd_speed(const char *cmd, int *rc); -static int fan_write_cmd_watchdog(const char *cmd, int *rc); - - -/* - * Hotkey subdriver - */ - -static int hotkey_supported; -static int hotkey_mask_supported; -static int hotkey_orig_status; -static int hotkey_orig_mask; - -static int hotkey_init(void); -static void hotkey_exit(void); -static int hotkey_get(int *status, int *mask); -static int hotkey_set(int status, int mask); -static void hotkey_notify(struct ibm_struct *ibm, u32 event); -static int hotkey_read(char *p); -static int hotkey_write(char *buf); - - -/* - * LED subdriver - */ - -enum led_access_mode { - IBMACPI_LED_NONE = 0, - IBMACPI_LED_570, /* 570 */ - IBMACPI_LED_OLD, /* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20-21 */ - IBMACPI_LED_NEW, /* all others */ -}; - -enum { /* For IBMACPI_LED_OLD */ - IBMACPI_LED_EC_HLCL = 0x0c, /* EC reg to get led to power on */ - IBMACPI_LED_EC_HLBL = 0x0d, /* EC reg to blink a lit led */ - IBMACPI_LED_EC_HLMS = 0x0e, /* EC reg to select led to command */ -}; - -static enum led_access_mode led_supported; -static acpi_handle led_handle; - -static int led_init(void); -static int led_read(char *p); -static int led_write(char *buf); - -/* - * Light (thinklight) subdriver - */ - -static int light_supported; -static int light_status_supported; -static acpi_handle lght_handle, ledb_handle; - -static int light_init(void); -static int light_read(char *p); -static int light_write(char *buf); - - -/* - * Thermal subdriver - */ - -enum thermal_access_mode { - IBMACPI_THERMAL_NONE = 0, /* No thermal support */ - IBMACPI_THERMAL_ACPI_TMP07, /* Use ACPI TMP0-7 */ - IBMACPI_THERMAL_ACPI_UPDT, /* Use ACPI TMP0-7 with UPDT */ - IBMACPI_THERMAL_TPEC_8, /* Use ACPI EC regs, 8 sensors */ - IBMACPI_THERMAL_TPEC_16, /* Use ACPI EC regs, 16 sensors */ -}; - -#define IBMACPI_MAX_THERMAL_SENSORS 16 /* Max thermal sensors supported */ -struct ibm_thermal_sensors_struct { - s32 temp[IBMACPI_MAX_THERMAL_SENSORS]; -}; - -static int thermal_init(void); -static int thermal_get_sensors(struct ibm_thermal_sensors_struct *s); -static int thermal_read(char *p); - - -/* - * Video subdriver - */ - -enum video_access_mode { - IBMACPI_VIDEO_NONE = 0, - IBMACPI_VIDEO_570, /* 570 */ - IBMACPI_VIDEO_770, /* 600e/x, 770e, 770x */ - IBMACPI_VIDEO_NEW, /* all others */ -}; - -static enum video_access_mode video_supported; -static int video_orig_autosw; -static acpi_handle vid_handle, vid2_handle; - -static int video_init(void); -static void video_exit(void); -static int video_status(void); -static int video_autosw(void); -static int video_switch(void); -static int video_switch2(int status); -static int video_expand(void); -static int video_read(char *p); -static int video_write(char *buf); - - -/* - * Volume subdriver - */ - -static int volume_offset = 0x30; - -static int volume_read(char *p); -static int volume_write(char *buf); - - -/* - * Wan subdriver - */ - -static int wan_supported; - -static int wan_init(void); -static int wan_status(void); -static int wan_read(char *p); -static int wan_write(char *buf); - - -#endif /* __IBM_ACPI_H */ diff --git a/drivers/misc/thinkpad_acpi.c b/drivers/misc/thinkpad_acpi.c new file mode 100644 index 000000000000..2836516ece86 --- /dev/null +++ b/drivers/misc/thinkpad_acpi.c @@ -0,0 +1,2783 @@ +/* + * ibm_acpi.c - IBM ThinkPad ACPI Extras + * + * + * Copyright (C) 2004-2005 Borislav Deianov + * Copyright (C) 2006-2007 Henrique de Moraes Holschuh + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#define IBM_VERSION "0.13" + +/* + * Changelog: + * + * 2006-11-22 0.13 new maintainer + * changelog now lives in git commit history, and will + * not be updated further in-file. + * + * 2005-08-17 0.12 fix compilation on 2.6.13-rc kernels + * 2005-03-17 0.11 support for 600e, 770x + * thanks to Jamie Lentin + * support for 770e, G41 + * G40 and G41 don't have a thinklight + * temperatures no longer experimental + * experimental brightness control + * experimental volume control + * experimental fan enable/disable + * 2005-01-16 0.10 fix module loading on R30, R31 + * 2005-01-16 0.9 support for 570, R30, R31 + * ultrabay support on A22p, A3x + * limit arg for cmos, led, beep, drop experimental status + * more capable led control on A21e, A22p, T20-22, X20 + * experimental temperatures and fan speed + * experimental embedded controller register dump + * mark more functions as __init, drop incorrect __exit + * use MODULE_VERSION + * thanks to Henrik Brix Andersen + * fix parameter passing on module loading + * thanks to Rusty Russell + * thanks to Jim Radford + * 2004-11-08 0.8 fix init error case, don't return from a macro + * thanks to Chris Wright + * 2004-10-23 0.7 fix module loading on A21e, A22p, T20, T21, X20 + * fix led control on A21e + * 2004-10-19 0.6 use acpi_bus_register_driver() to claim HKEY device + * 2004-10-18 0.5 thinklight support on A21e, G40, R32, T20, T21, X20 + * proc file format changed + * video_switch command + * experimental cmos control + * experimental led control + * experimental acpi sounds + * 2004-09-16 0.4 support for module parameters + * hotkey mask can be prefixed by 0x + * video output switching + * video expansion control + * ultrabay eject support + * removed lcd brightness/on/off control, didn't work + * 2004-08-17 0.3 support for R40 + * lcd off, brightness control + * thinklight on/off + * 2004-08-14 0.2 support for T series, X20 + * bluetooth enable/disable + * hotkey events disabled by default + * removed fan control, currently useless + * 2004-08-09 0.1 initial release, support for X series + */ + +#include "thinkpad_acpi.h" + +MODULE_AUTHOR("Borislav Deianov, Henrique de Moraes Holschuh"); +MODULE_DESCRIPTION(IBM_DESC); +MODULE_VERSION(IBM_VERSION); +MODULE_LICENSE("GPL"); + +#define __unused __attribute__ ((unused)) + +/**************************************************************************** + **************************************************************************** + * + * ACPI Helpers and device model + * + **************************************************************************** + ****************************************************************************/ + +/************************************************************************* + * ACPI basic handles + */ + +static acpi_handle root_handle = NULL; + +#define IBM_HANDLE(object, parent, paths...) \ + static acpi_handle object##_handle; \ + static acpi_handle *object##_parent = &parent##_handle; \ + static char *object##_path; \ + static char *object##_paths[] = { paths } + +IBM_HANDLE(ec, root, "\\_SB.PCI0.ISA.EC0", /* 240, 240x */ + "\\_SB.PCI.ISA.EC", /* 570 */ + "\\_SB.PCI0.ISA0.EC0", /* 600e/x, 770e, 770x */ + "\\_SB.PCI0.ISA.EC", /* A21e, A2xm/p, T20-22, X20-21 */ + "\\_SB.PCI0.AD4S.EC0", /* i1400, R30 */ + "\\_SB.PCI0.ICH3.EC0", /* R31 */ + "\\_SB.PCI0.LPC.EC", /* all others */ + ); + +IBM_HANDLE(ecrd, ec, "ECRD"); /* 570 */ +IBM_HANDLE(ecwr, ec, "ECWR"); /* 570 */ + + +/************************************************************************* + * Misc ACPI handles + */ + +IBM_HANDLE(cmos, root, "\\UCMS", /* R50, R50e, R50p, R51, T4x, X31, X40 */ + "\\CMOS", /* A3x, G4x, R32, T23, T30, X22-24, X30 */ + "\\CMS", /* R40, R40e */ + ); /* all others */ + +IBM_HANDLE(hkey, ec, "\\_SB.HKEY", /* 600e/x, 770e, 770x */ + "^HKEY", /* R30, R31 */ + "HKEY", /* all others */ + ); /* 570 */ + + +/************************************************************************* + * ACPI helpers + */ + +static int acpi_evalf(acpi_handle handle, + void *res, char *method, char *fmt, ...) +{ + char *fmt0 = fmt; + struct acpi_object_list params; + union acpi_object in_objs[IBM_MAX_ACPI_ARGS]; + struct acpi_buffer result, *resultp; + union acpi_object out_obj; + acpi_status status; + va_list ap; + char res_type; + int success; + int quiet; + + if (!*fmt) { + printk(IBM_ERR "acpi_evalf() called with empty format\n"); + return 0; + } + + if (*fmt == 'q') { + quiet = 1; + fmt++; + } else + quiet = 0; + + res_type = *(fmt++); + + params.count = 0; + params.pointer = &in_objs[0]; + + va_start(ap, fmt); + while (*fmt) { + char c = *(fmt++); + switch (c) { + case 'd': /* int */ + in_objs[params.count].integer.value = va_arg(ap, int); + in_objs[params.count++].type = ACPI_TYPE_INTEGER; + break; + /* add more types as needed */ + default: + printk(IBM_ERR "acpi_evalf() called " + "with invalid format character '%c'\n", c); + return 0; + } + } + va_end(ap); + + if (res_type != 'v') { + result.length = sizeof(out_obj); + result.pointer = &out_obj; + resultp = &result; + } else + resultp = NULL; + + status = acpi_evaluate_object(handle, method, ¶ms, resultp); + + switch (res_type) { + case 'd': /* int */ + if (res) + *(int *)res = out_obj.integer.value; + success = status == AE_OK && out_obj.type == ACPI_TYPE_INTEGER; + break; + case 'v': /* void */ + success = status == AE_OK; + break; + /* add more types as needed */ + default: + printk(IBM_ERR "acpi_evalf() called " + "with invalid format character '%c'\n", res_type); + return 0; + } + + if (!success && !quiet) + printk(IBM_ERR "acpi_evalf(%s, %s, ...) failed: %d\n", + method, fmt0, status); + + return success; +} + +static void __unused acpi_print_int(acpi_handle handle, char *method) +{ + int i; + + if (acpi_evalf(handle, &i, method, "d")) + printk(IBM_INFO "%s = 0x%x\n", method, i); + else + printk(IBM_ERR "error calling %s\n", method); +} + +static int acpi_ec_read(int i, u8 * p) +{ + int v; + + if (ecrd_handle) { + if (!acpi_evalf(ecrd_handle, &v, NULL, "dd", i)) + return 0; + *p = v; + } else { + if (ec_read(i, p) < 0) + return 0; + } + + return 1; +} + +static int acpi_ec_write(int i, u8 v) +{ + if (ecwr_handle) { + if (!acpi_evalf(ecwr_handle, NULL, NULL, "vdd", i, v)) + return 0; + } else { + if (ec_write(i, v) < 0) + return 0; + } + + return 1; +} + +static int _sta(acpi_handle handle) +{ + int status; + + if (!handle || !acpi_evalf(handle, &status, "_STA", "d")) + status = 0; + + return status; +} + +/************************************************************************* + * ACPI device model + */ + +static void __init ibm_handle_init(char *name, + acpi_handle * handle, acpi_handle parent, + char **paths, int num_paths, char **path) +{ + int i; + acpi_status status; + + for (i = 0; i < num_paths; i++) { + status = acpi_get_handle(parent, paths[i], handle); + if (ACPI_SUCCESS(status)) { + *path = paths[i]; + return; + } + } + + *handle = NULL; +} + +static void dispatch_notify(acpi_handle handle, u32 event, void *data) +{ + struct ibm_struct *ibm = data; + + if (!ibm || !ibm->notify) + return; + + ibm->notify(ibm, event); +} + +static int __init setup_notify(struct ibm_struct *ibm) +{ + acpi_status status; + int ret; + + if (!*ibm->handle) + return 0; + + ret = acpi_bus_get_device(*ibm->handle, &ibm->device); + if (ret < 0) { + printk(IBM_ERR "%s device not present\n", ibm->name); + return -ENODEV; + } + + acpi_driver_data(ibm->device) = ibm; + sprintf(acpi_device_class(ibm->device), "%s/%s", IBM_NAME, ibm->name); + + status = acpi_install_notify_handler(*ibm->handle, ibm->type, + dispatch_notify, ibm); + if (ACPI_FAILURE(status)) { + if (status == AE_ALREADY_EXISTS) { + printk(IBM_NOTICE "another device driver is already handling %s events\n", + ibm->name); + } else { + printk(IBM_ERR "acpi_install_notify_handler(%s) failed: %d\n", + ibm->name, status); + } + return -ENODEV; + } + ibm->notify_installed = 1; + return 0; +} + +static int __init ibm_device_add(struct acpi_device *device) +{ + return 0; +} + +static int __init register_ibmacpi_subdriver(struct ibm_struct *ibm) +{ + int ret; + + ibm->driver = kzalloc(sizeof(struct acpi_driver), GFP_KERNEL); + if (!ibm->driver) { + printk(IBM_ERR "kmalloc(ibm->driver) failed\n"); + return -1; + } + + sprintf(ibm->driver->name, "%s_%s", IBM_NAME, ibm->name); + ibm->driver->ids = ibm->hid; + ibm->driver->ops.add = &ibm_device_add; + + ret = acpi_bus_register_driver(ibm->driver); + if (ret < 0) { + printk(IBM_ERR "acpi_bus_register_driver(%s) failed: %d\n", + ibm->hid, ret); + kfree(ibm->driver); + } + + return ret; +} + + +/**************************************************************************** + **************************************************************************** + * + * Procfs Helpers + * + **************************************************************************** + ****************************************************************************/ + +static int dispatch_read(char *page, char **start, off_t off, int count, + int *eof, void *data) +{ + struct ibm_struct *ibm = data; + int len; + + if (!ibm || !ibm->read) + return -EINVAL; + + len = ibm->read(page); + if (len < 0) + return len; + + if (len <= off + count) + *eof = 1; + *start = page + off; + len -= off; + if (len > count) + len = count; + if (len < 0) + len = 0; + + return len; +} + +static int dispatch_write(struct file *file, const char __user * userbuf, + unsigned long count, void *data) +{ + struct ibm_struct *ibm = data; + char *kernbuf; + int ret; + + if (!ibm || !ibm->write) + return -EINVAL; + + kernbuf = kmalloc(count + 2, GFP_KERNEL); + if (!kernbuf) + return -ENOMEM; + + if (copy_from_user(kernbuf, userbuf, count)) { + kfree(kernbuf); + return -EFAULT; + } + + kernbuf[count] = 0; + strcat(kernbuf, ","); + ret = ibm->write(kernbuf); + if (ret == 0) + ret = count; + + kfree(kernbuf); + + return ret; +} + +static char *next_cmd(char **cmds) +{ + char *start = *cmds; + char *end; + + while ((end = strchr(start, ',')) && end == start) + start = end + 1; + + if (!end) + return NULL; + + *end = 0; + *cmds = end + 1; + return start; +} + + +/**************************************************************************** + **************************************************************************** + * + * Subdrivers + * + **************************************************************************** + ****************************************************************************/ + +/************************************************************************* + * ibm-acpi init subdriver + */ + +static int ibm_acpi_driver_init(void) +{ + printk(IBM_INFO "%s v%s\n", IBM_DESC, IBM_VERSION); + printk(IBM_INFO "%s\n", IBM_URL); + + if (ibm_thinkpad_ec_found) + printk(IBM_INFO "ThinkPad EC firmware %s\n", + ibm_thinkpad_ec_found); + + return 0; +} + +static int ibm_acpi_driver_read(char *p) +{ + int len = 0; + + len += sprintf(p + len, "driver:\t\t%s\n", IBM_DESC); + len += sprintf(p + len, "version:\t%s\n", IBM_VERSION); + + return len; +} + +/************************************************************************* + * Hotkey subdriver + */ + +static int hotkey_supported; +static int hotkey_mask_supported; +static int hotkey_orig_status; +static int hotkey_orig_mask; + +static int hotkey_init(void) +{ + /* hotkey not supported on 570 */ + hotkey_supported = hkey_handle != NULL; + + if (hotkey_supported) { + /* mask not supported on 570, 600e/x, 770e, 770x, A21e, A2xm/p, + A30, R30, R31, T20-22, X20-21, X22-24 */ + hotkey_mask_supported = + acpi_evalf(hkey_handle, NULL, "DHKN", "qv"); + + if (!hotkey_get(&hotkey_orig_status, &hotkey_orig_mask)) + return -ENODEV; + } + + return 0; +} + +static void hotkey_exit(void) +{ + if (hotkey_supported) + hotkey_set(hotkey_orig_status, hotkey_orig_mask); +} + +static void hotkey_notify(struct ibm_struct *ibm, u32 event) +{ + int hkey; + + if (acpi_evalf(hkey_handle, &hkey, "MHKP", "d")) + acpi_bus_generate_event(ibm->device, event, hkey); + else { + printk(IBM_ERR "unknown hotkey event %d\n", event); + acpi_bus_generate_event(ibm->device, event, 0); + } +} + +static int hotkey_get(int *status, int *mask) +{ + if (!acpi_evalf(hkey_handle, status, "DHKC", "d")) + return 0; + + if (hotkey_mask_supported) + if (!acpi_evalf(hkey_handle, mask, "DHKN", "d")) + return 0; + + return 1; +} + +static int hotkey_set(int status, int mask) +{ + int i; + + if (!acpi_evalf(hkey_handle, NULL, "MHKC", "vd", status)) + return 0; + + if (hotkey_mask_supported) + for (i = 0; i < 32; i++) { + int bit = ((1 << i) & mask) != 0; + if (!acpi_evalf(hkey_handle, + NULL, "MHKM", "vdd", i + 1, bit)) + return 0; + } + + return 1; +} + +static int hotkey_read(char *p) +{ + int status, mask; + int len = 0; + + if (!hotkey_supported) { + len += sprintf(p + len, "status:\t\tnot supported\n"); + return len; + } + + if (!hotkey_get(&status, &mask)) + return -EIO; + + len += sprintf(p + len, "status:\t\t%s\n", enabled(status, 0)); + if (hotkey_mask_supported) { + len += sprintf(p + len, "mask:\t\t0x%04x\n", mask); + len += sprintf(p + len, + "commands:\tenable, disable, reset, \n"); + } else { + len += sprintf(p + len, "mask:\t\tnot supported\n"); + len += sprintf(p + len, "commands:\tenable, disable, reset\n"); + } + + return len; +} + +static int hotkey_write(char *buf) +{ + int status, mask; + char *cmd; + int do_cmd = 0; + + if (!hotkey_supported) + return -ENODEV; + + if (!hotkey_get(&status, &mask)) + return -EIO; + + while ((cmd = next_cmd(&buf))) { + if (strlencmp(cmd, "enable") == 0) { + status = 1; + } else if (strlencmp(cmd, "disable") == 0) { + status = 0; + } else if (strlencmp(cmd, "reset") == 0) { + status = hotkey_orig_status; + mask = hotkey_orig_mask; + } else if (sscanf(cmd, "0x%x", &mask) == 1) { + /* mask set */ + } else if (sscanf(cmd, "%x", &mask) == 1) { + /* mask set */ + } else + return -EINVAL; + do_cmd = 1; + } + + if (do_cmd && !hotkey_set(status, mask)) + return -EIO; + + return 0; +} + +/************************************************************************* + * Bluetooth subdriver + */ + +static int bluetooth_supported; + +static int bluetooth_init(void) +{ + /* bluetooth not supported on 570, 600e/x, 770e, 770x, A21e, A2xm/p, + G4x, R30, R31, R40e, R50e, T20-22, X20-21 */ + bluetooth_supported = hkey_handle && + acpi_evalf(hkey_handle, NULL, "GBDC", "qv"); + + return 0; +} + +static int bluetooth_status(void) +{ + int status; + + if (!bluetooth_supported || + !acpi_evalf(hkey_handle, &status, "GBDC", "d")) + status = 0; + + return status; +} + +static int bluetooth_read(char *p) +{ + int len = 0; + int status = bluetooth_status(); + + if (!bluetooth_supported) + len += sprintf(p + len, "status:\t\tnot supported\n"); + else if (!(status & 1)) + len += sprintf(p + len, "status:\t\tnot installed\n"); + else { + len += sprintf(p + len, "status:\t\t%s\n", enabled(status, 1)); + len += sprintf(p + len, "commands:\tenable, disable\n"); + } + + return len; +} + +static int bluetooth_write(char *buf) +{ + int status = bluetooth_status(); + char *cmd; + int do_cmd = 0; + + if (!bluetooth_supported) + return -ENODEV; + + while ((cmd = next_cmd(&buf))) { + if (strlencmp(cmd, "enable") == 0) { + status |= 2; + } else if (strlencmp(cmd, "disable") == 0) { + status &= ~2; + } else + return -EINVAL; + do_cmd = 1; + } + + if (do_cmd && !acpi_evalf(hkey_handle, NULL, "SBDC", "vd", status)) + return -EIO; + + return 0; +} + +/************************************************************************* + * Wan subdriver + */ + +static int wan_supported; + +static int wan_init(void) +{ + wan_supported = hkey_handle && + acpi_evalf(hkey_handle, NULL, "GWAN", "qv"); + + return 0; +} + +static int wan_status(void) +{ + int status; + + if (!wan_supported || !acpi_evalf(hkey_handle, &status, "GWAN", "d")) + status = 0; + + return status; +} + +static int wan_read(char *p) +{ + int len = 0; + int status = wan_status(); + + if (!wan_supported) + len += sprintf(p + len, "status:\t\tnot supported\n"); + else if (!(status & 1)) + len += sprintf(p + len, "status:\t\tnot installed\n"); + else { + len += sprintf(p + len, "status:\t\t%s\n", enabled(status, 1)); + len += sprintf(p + len, "commands:\tenable, disable\n"); + } + + return len; +} + +static int wan_write(char *buf) +{ + int status = wan_status(); + char *cmd; + int do_cmd = 0; + + if (!wan_supported) + return -ENODEV; + + while ((cmd = next_cmd(&buf))) { + if (strlencmp(cmd, "enable") == 0) { + status |= 2; + } else if (strlencmp(cmd, "disable") == 0) { + status &= ~2; + } else + return -EINVAL; + do_cmd = 1; + } + + if (do_cmd && !acpi_evalf(hkey_handle, NULL, "SWAN", "vd", status)) + return -EIO; + + return 0; +} + +/************************************************************************* + * Video subdriver + */ + +static enum video_access_mode video_supported; +static int video_orig_autosw; + +IBM_HANDLE(vid, root, "\\_SB.PCI.AGP.VGA", /* 570 */ + "\\_SB.PCI0.AGP0.VID0", /* 600e/x, 770x */ + "\\_SB.PCI0.VID0", /* 770e */ + "\\_SB.PCI0.VID", /* A21e, G4x, R50e, X30, X40 */ + "\\_SB.PCI0.AGP.VID", /* all others */ + ); /* R30, R31 */ + +IBM_HANDLE(vid2, root, "\\_SB.PCI0.AGPB.VID"); /* G41 */ + +static int video_init(void) +{ + int ivga; + + if (vid2_handle && acpi_evalf(NULL, &ivga, "\\IVGA", "d") && ivga) + /* G41, assume IVGA doesn't change */ + vid_handle = vid2_handle; + + if (!vid_handle) + /* video switching not supported on R30, R31 */ + video_supported = IBMACPI_VIDEO_NONE; + else if (acpi_evalf(vid_handle, &video_orig_autosw, "SWIT", "qd")) + /* 570 */ + video_supported = IBMACPI_VIDEO_570; + else if (acpi_evalf(vid_handle, &video_orig_autosw, "^VADL", "qd")) + /* 600e/x, 770e, 770x */ + video_supported = IBMACPI_VIDEO_770; + else + /* all others */ + video_supported = IBMACPI_VIDEO_NEW; + + return 0; +} + +static void video_exit(void) +{ + acpi_evalf(vid_handle, NULL, "_DOS", "vd", video_orig_autosw); +} + +static int video_status(void) +{ + int status = 0; + int i; + + if (video_supported == IBMACPI_VIDEO_570) { + if (acpi_evalf(NULL, &i, "\\_SB.PHS", "dd", 0x87)) + status = i & 3; + } else if (video_supported == IBMACPI_VIDEO_770) { + if (acpi_evalf(NULL, &i, "\\VCDL", "d")) + status |= 0x01 * i; + if (acpi_evalf(NULL, &i, "\\VCDC", "d")) + status |= 0x02 * i; + } else if (video_supported == IBMACPI_VIDEO_NEW) { + acpi_evalf(NULL, NULL, "\\VUPS", "vd", 1); + if (acpi_evalf(NULL, &i, "\\VCDC", "d")) + status |= 0x02 * i; + + acpi_evalf(NULL, NULL, "\\VUPS", "vd", 0); + if (acpi_evalf(NULL, &i, "\\VCDL", "d")) + status |= 0x01 * i; + if (acpi_evalf(NULL, &i, "\\VCDD", "d")) + status |= 0x08 * i; + } + + return status; +} + +static int video_autosw(void) +{ + int autosw = 0; + + if (video_supported == IBMACPI_VIDEO_570) + acpi_evalf(vid_handle, &autosw, "SWIT", "d"); + else if (video_supported == IBMACPI_VIDEO_770 || + video_supported == IBMACPI_VIDEO_NEW) + acpi_evalf(vid_handle, &autosw, "^VDEE", "d"); + + return autosw & 1; +} + +static int video_switch(void) +{ + int autosw = video_autosw(); + int ret; + + if (!acpi_evalf(vid_handle, NULL, "_DOS", "vd", 1)) + return -EIO; + ret = video_supported == IBMACPI_VIDEO_570 ? + acpi_evalf(ec_handle, NULL, "_Q16", "v") : + acpi_evalf(vid_handle, NULL, "VSWT", "v"); + acpi_evalf(vid_handle, NULL, "_DOS", "vd", autosw); + + return ret; +} + +static int video_expand(void) +{ + if (video_supported == IBMACPI_VIDEO_570) + return acpi_evalf(ec_handle, NULL, "_Q17", "v"); + else if (video_supported == IBMACPI_VIDEO_770) + return acpi_evalf(vid_handle, NULL, "VEXP", "v"); + else + return acpi_evalf(NULL, NULL, "\\VEXP", "v"); +} + +static int video_switch2(int status) +{ + int ret; + + if (video_supported == IBMACPI_VIDEO_570) { + ret = acpi_evalf(NULL, NULL, + "\\_SB.PHS2", "vdd", 0x8b, status | 0x80); + } else if (video_supported == IBMACPI_VIDEO_770) { + int autosw = video_autosw(); + if (!acpi_evalf(vid_handle, NULL, "_DOS", "vd", 1)) + return -EIO; + + ret = acpi_evalf(vid_handle, NULL, + "ASWT", "vdd", status * 0x100, 0); + + acpi_evalf(vid_handle, NULL, "_DOS", "vd", autosw); + } else { + ret = acpi_evalf(NULL, NULL, "\\VUPS", "vd", 0x80) && + acpi_evalf(NULL, NULL, "\\VSDS", "vdd", status, 1); + } + + return ret; +} + +static int video_read(char *p) +{ + int status = video_status(); + int autosw = video_autosw(); + int len = 0; + + if (!video_supported) { + len += sprintf(p + len, "status:\t\tnot supported\n"); + return len; + } + + len += sprintf(p + len, "status:\t\tsupported\n"); + len += sprintf(p + len, "lcd:\t\t%s\n", enabled(status, 0)); + len += sprintf(p + len, "crt:\t\t%s\n", enabled(status, 1)); + if (video_supported == IBMACPI_VIDEO_NEW) + len += sprintf(p + len, "dvi:\t\t%s\n", enabled(status, 3)); + len += sprintf(p + len, "auto:\t\t%s\n", enabled(autosw, 0)); + len += sprintf(p + len, "commands:\tlcd_enable, lcd_disable\n"); + len += sprintf(p + len, "commands:\tcrt_enable, crt_disable\n"); + if (video_supported == IBMACPI_VIDEO_NEW) + len += sprintf(p + len, "commands:\tdvi_enable, dvi_disable\n"); + len += sprintf(p + len, "commands:\tauto_enable, auto_disable\n"); + len += sprintf(p + len, "commands:\tvideo_switch, expand_toggle\n"); + + return len; +} + +static int video_write(char *buf) +{ + char *cmd; + int enable, disable, status; + + if (!video_supported) + return -ENODEV; + + enable = disable = 0; + + while ((cmd = next_cmd(&buf))) { + if (strlencmp(cmd, "lcd_enable") == 0) { + enable |= 0x01; + } else if (strlencmp(cmd, "lcd_disable") == 0) { + disable |= 0x01; + } else if (strlencmp(cmd, "crt_enable") == 0) { + enable |= 0x02; + } else if (strlencmp(cmd, "crt_disable") == 0) { + disable |= 0x02; + } else if (video_supported == IBMACPI_VIDEO_NEW && + strlencmp(cmd, "dvi_enable") == 0) { + enable |= 0x08; + } else if (video_supported == IBMACPI_VIDEO_NEW && + strlencmp(cmd, "dvi_disable") == 0) { + disable |= 0x08; + } else if (strlencmp(cmd, "auto_enable") == 0) { + if (!acpi_evalf(vid_handle, NULL, "_DOS", "vd", 1)) + return -EIO; + } else if (strlencmp(cmd, "auto_disable") == 0) { + if (!acpi_evalf(vid_handle, NULL, "_DOS", "vd", 0)) + return -EIO; + } else if (strlencmp(cmd, "video_switch") == 0) { + if (!video_switch()) + return -EIO; + } else if (strlencmp(cmd, "expand_toggle") == 0) { + if (!video_expand()) + return -EIO; + } else + return -EINVAL; + } + + if (enable || disable) { + status = (video_status() & 0x0f & ~disable) | enable; + if (!video_switch2(status)) + return -EIO; + } + + return 0; +} + +/************************************************************************* + * Light (thinklight) subdriver + */ + +static int light_supported; +static int light_status_supported; + +IBM_HANDLE(lght, root, "\\LGHT"); /* A21e, A2xm/p, T20-22, X20-21 */ +IBM_HANDLE(ledb, ec, "LEDB"); /* G4x */ + +static int light_init(void) +{ + /* light not supported on 570, 600e/x, 770e, 770x, G4x, R30, R31 */ + light_supported = (cmos_handle || lght_handle) && !ledb_handle; + + if (light_supported) + /* light status not supported on + 570, 600e/x, 770e, 770x, G4x, R30, R31, R32, X20 */ + light_status_supported = acpi_evalf(ec_handle, NULL, + "KBLT", "qv"); + + return 0; +} + +static int light_read(char *p) +{ + int len = 0; + int status = 0; + + if (!light_supported) { + len += sprintf(p + len, "status:\t\tnot supported\n"); + } else if (!light_status_supported) { + len += sprintf(p + len, "status:\t\tunknown\n"); + len += sprintf(p + len, "commands:\ton, off\n"); + } else { + if (!acpi_evalf(ec_handle, &status, "KBLT", "d")) + return -EIO; + len += sprintf(p + len, "status:\t\t%s\n", onoff(status, 0)); + len += sprintf(p + len, "commands:\ton, off\n"); + } + + return len; +} + +static int light_write(char *buf) +{ + int cmos_cmd, lght_cmd; + char *cmd; + int success; + + if (!light_supported) + return -ENODEV; + + while ((cmd = next_cmd(&buf))) { + if (strlencmp(cmd, "on") == 0) { + cmos_cmd = 0x0c; + lght_cmd = 1; + } else if (strlencmp(cmd, "off") == 0) { + cmos_cmd = 0x0d; + lght_cmd = 0; + } else + return -EINVAL; + + success = cmos_handle ? + acpi_evalf(cmos_handle, NULL, NULL, "vd", cmos_cmd) : + acpi_evalf(lght_handle, NULL, NULL, "vd", lght_cmd); + if (!success) + return -EIO; + } + + return 0; +} + +/************************************************************************* + * Dock subdriver + */ + +/* don't list other alternatives as we install a notify handler on the 570 */ +IBM_HANDLE(pci, root, "\\_SB.PCI"); /* 570 */ + +#ifdef CONFIG_ACPI_IBM_DOCK + +IBM_HANDLE(dock, root, "\\_SB.GDCK", /* X30, X31, X40 */ + "\\_SB.PCI0.DOCK", /* 600e/x,770e,770x,A2xm/p,T20-22,X20-21 */ + "\\_SB.PCI0.PCI1.DOCK", /* all others */ + "\\_SB.PCI.ISA.SLCE", /* 570 */ + ); /* A21e,G4x,R30,R31,R32,R40,R40e,R50e */ + +#define dock_docked() (_sta(dock_handle) & 1) + +static void dock_notify(struct ibm_struct *ibm, u32 event) +{ + int docked = dock_docked(); + int pci = ibm->hid && strstr(ibm->hid, IBM_PCI_HID); + + if (event == 1 && !pci) /* 570 */ + acpi_bus_generate_event(ibm->device, event, 1); /* button */ + else if (event == 1 && pci) /* 570 */ + acpi_bus_generate_event(ibm->device, event, 3); /* dock */ + else if (event == 3 && docked) + acpi_bus_generate_event(ibm->device, event, 1); /* button */ + else if (event == 3 && !docked) + acpi_bus_generate_event(ibm->device, event, 2); /* undock */ + else if (event == 0 && docked) + acpi_bus_generate_event(ibm->device, event, 3); /* dock */ + else { + printk(IBM_ERR "unknown dock event %d, status %d\n", + event, _sta(dock_handle)); + acpi_bus_generate_event(ibm->device, event, 0); /* unknown */ + } +} + +static int dock_read(char *p) +{ + int len = 0; + int docked = dock_docked(); + + if (!dock_handle) + len += sprintf(p + len, "status:\t\tnot supported\n"); + else if (!docked) + len += sprintf(p + len, "status:\t\tundocked\n"); + else { + len += sprintf(p + len, "status:\t\tdocked\n"); + len += sprintf(p + len, "commands:\tdock, undock\n"); + } + + return len; +} + +static int dock_write(char *buf) +{ + char *cmd; + + if (!dock_docked()) + return -ENODEV; + + while ((cmd = next_cmd(&buf))) { + if (strlencmp(cmd, "undock") == 0) { + if (!acpi_evalf(dock_handle, NULL, "_DCK", "vd", 0) || + !acpi_evalf(dock_handle, NULL, "_EJ0", "vd", 1)) + return -EIO; + } else if (strlencmp(cmd, "dock") == 0) { + if (!acpi_evalf(dock_handle, NULL, "_DCK", "vd", 1)) + return -EIO; + } else + return -EINVAL; + } + + return 0; +} + +#endif /* CONFIG_ACPI_IBM_DOCK */ + +/************************************************************************* + * Bay subdriver + */ + +#ifdef CONFIG_ACPI_IBM_BAY +static int bay_status_supported; +static int bay_status2_supported; +static int bay_eject_supported; +static int bay_eject2_supported; + +IBM_HANDLE(bay, root, "\\_SB.PCI.IDE.SECN.MAST", /* 570 */ + "\\_SB.PCI0.IDE0.IDES.IDSM", /* 600e/x, 770e, 770x */ + "\\_SB.PCI0.SATA.SCND.MSTR", /* T60, X60, Z60 */ + "\\_SB.PCI0.IDE0.SCND.MSTR", /* all others */ + ); /* A21e, R30, R31 */ +IBM_HANDLE(bay_ej, bay, "_EJ3", /* 600e/x, A2xm/p, A3x */ + "_EJ0", /* all others */ + ); /* 570,A21e,G4x,R30,R31,R32,R40e,R50e */ +IBM_HANDLE(bay2, root, "\\_SB.PCI0.IDE0.PRIM.SLAV", /* A3x, R32 */ + "\\_SB.PCI0.IDE0.IDEP.IDPS", /* 600e/x, 770e, 770x */ + ); /* all others */ +IBM_HANDLE(bay2_ej, bay2, "_EJ3", /* 600e/x, 770e, A3x */ + "_EJ0", /* 770x */ + ); /* all others */ + +static int bay_init(void) +{ + bay_status_supported = bay_handle && + acpi_evalf(bay_handle, NULL, "_STA", "qv"); + bay_status2_supported = bay2_handle && + acpi_evalf(bay2_handle, NULL, "_STA", "qv"); + + bay_eject_supported = bay_handle && bay_ej_handle && + (strlencmp(bay_ej_path, "_EJ0") == 0 || experimental); + bay_eject2_supported = bay2_handle && bay2_ej_handle && + (strlencmp(bay2_ej_path, "_EJ0") == 0 || experimental); + + return 0; +} + +static void bay_notify(struct ibm_struct *ibm, u32 event) +{ + acpi_bus_generate_event(ibm->device, event, 0); +} + +#define bay_occupied(b) (_sta(b##_handle) & 1) + +static int bay_read(char *p) +{ + int len = 0; + int occupied = bay_occupied(bay); + int occupied2 = bay_occupied(bay2); + int eject, eject2; + + len += sprintf(p + len, "status:\t\t%s\n", bay_status_supported ? + (occupied ? "occupied" : "unoccupied") : + "not supported"); + if (bay_status2_supported) + len += sprintf(p + len, "status2:\t%s\n", occupied2 ? + "occupied" : "unoccupied"); + + eject = bay_eject_supported && occupied; + eject2 = bay_eject2_supported && occupied2; + + if (eject && eject2) + len += sprintf(p + len, "commands:\teject, eject2\n"); + else if (eject) + len += sprintf(p + len, "commands:\teject\n"); + else if (eject2) + len += sprintf(p + len, "commands:\teject2\n"); + + return len; +} + +static int bay_write(char *buf) +{ + char *cmd; + + if (!bay_eject_supported && !bay_eject2_supported) + return -ENODEV; + + while ((cmd = next_cmd(&buf))) { + if (bay_eject_supported && strlencmp(cmd, "eject") == 0) { + if (!acpi_evalf(bay_ej_handle, NULL, NULL, "vd", 1)) + return -EIO; + } else if (bay_eject2_supported && + strlencmp(cmd, "eject2") == 0) { + if (!acpi_evalf(bay2_ej_handle, NULL, NULL, "vd", 1)) + return -EIO; + } else + return -EINVAL; + } + + return 0; +} +#endif /* CONFIG_ACPI_IBM_BAY */ + +/************************************************************************* + * CMOS subdriver + */ + +static int cmos_eval(int cmos_cmd) +{ + if (cmos_handle) + return acpi_evalf(cmos_handle, NULL, NULL, "vd", cmos_cmd); + else + return 1; +} + +static int cmos_read(char *p) +{ + int len = 0; + + /* cmos not supported on 570, 600e/x, 770e, 770x, A21e, A2xm/p, + R30, R31, T20-22, X20-21 */ + if (!cmos_handle) + len += sprintf(p + len, "status:\t\tnot supported\n"); + else { + len += sprintf(p + len, "status:\t\tsupported\n"); + len += sprintf(p + len, "commands:\t ( is 0-21)\n"); + } + + return len; +} + +static int cmos_write(char *buf) +{ + char *cmd; + int cmos_cmd; + + if (!cmos_handle) + return -EINVAL; + + while ((cmd = next_cmd(&buf))) { + if (sscanf(cmd, "%u", &cmos_cmd) == 1 && + cmos_cmd >= 0 && cmos_cmd <= 21) { + /* cmos_cmd set */ + } else + return -EINVAL; + + if (!cmos_eval(cmos_cmd)) + return -EIO; + } + + return 0; +} + + +/************************************************************************* + * LED subdriver + */ + +static enum led_access_mode led_supported; + +IBM_HANDLE(led, ec, "SLED", /* 570 */ + "SYSL", /* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20-21 */ + "LED", /* all others */ + ); /* R30, R31 */ + +static int led_init(void) +{ + if (!led_handle) + /* led not supported on R30, R31 */ + led_supported = IBMACPI_LED_NONE; + else if (strlencmp(led_path, "SLED") == 0) + /* 570 */ + led_supported = IBMACPI_LED_570; + else if (strlencmp(led_path, "SYSL") == 0) + /* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20-21 */ + led_supported = IBMACPI_LED_OLD; + else + /* all others */ + led_supported = IBMACPI_LED_NEW; + + return 0; +} + +#define led_status(s) ((s) == 0 ? "off" : ((s) == 1 ? "on" : "blinking")) + +static int led_read(char *p) +{ + int len = 0; + + if (!led_supported) { + len += sprintf(p + len, "status:\t\tnot supported\n"); + return len; + } + len += sprintf(p + len, "status:\t\tsupported\n"); + + if (led_supported == IBMACPI_LED_570) { + /* 570 */ + int i, status; + for (i = 0; i < 8; i++) { + if (!acpi_evalf(ec_handle, + &status, "GLED", "dd", 1 << i)) + return -EIO; + len += sprintf(p + len, "%d:\t\t%s\n", + i, led_status(status)); + } + } + + len += sprintf(p + len, "commands:\t" + " on, off, blink ( is 0-7)\n"); + + return len; +} + +/* off, on, blink */ +static const int led_sled_arg1[] = { 0, 1, 3 }; +static const int led_exp_hlbl[] = { 0, 0, 1 }; /* led# * */ +static const int led_exp_hlcl[] = { 0, 1, 1 }; /* led# * */ +static const int led_led_arg1[] = { 0, 0x80, 0xc0 }; + +static int led_write(char *buf) +{ + char *cmd; + int led, ind, ret; + + if (!led_supported) + return -ENODEV; + + while ((cmd = next_cmd(&buf))) { + if (sscanf(cmd, "%d", &led) != 1 || led < 0 || led > 7) + return -EINVAL; + + if (strstr(cmd, "off")) { + ind = 0; + } else if (strstr(cmd, "on")) { + ind = 1; + } else if (strstr(cmd, "blink")) { + ind = 2; + } else + return -EINVAL; + + if (led_supported == IBMACPI_LED_570) { + /* 570 */ + led = 1 << led; + if (!acpi_evalf(led_handle, NULL, NULL, "vdd", + led, led_sled_arg1[ind])) + return -EIO; + } else if (led_supported == IBMACPI_LED_OLD) { + /* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20 */ + led = 1 << led; + ret = ec_write(IBMACPI_LED_EC_HLMS, led); + if (ret >= 0) + ret = + ec_write(IBMACPI_LED_EC_HLBL, + led * led_exp_hlbl[ind]); + if (ret >= 0) + ret = + ec_write(IBMACPI_LED_EC_HLCL, + led * led_exp_hlcl[ind]); + if (ret < 0) + return ret; + } else { + /* all others */ + if (!acpi_evalf(led_handle, NULL, NULL, "vdd", + led, led_led_arg1[ind])) + return -EIO; + } + } + + return 0; +} + +/************************************************************************* + * Beep subdriver + */ + +IBM_HANDLE(beep, ec, "BEEP"); /* all except R30, R31 */ + +static int beep_read(char *p) +{ + int len = 0; + + if (!beep_handle) + len += sprintf(p + len, "status:\t\tnot supported\n"); + else { + len += sprintf(p + len, "status:\t\tsupported\n"); + len += sprintf(p + len, "commands:\t ( is 0-17)\n"); + } + + return len; +} + +static int beep_write(char *buf) +{ + char *cmd; + int beep_cmd; + + if (!beep_handle) + return -ENODEV; + + while ((cmd = next_cmd(&buf))) { + if (sscanf(cmd, "%u", &beep_cmd) == 1 && + beep_cmd >= 0 && beep_cmd <= 17) { + /* beep_cmd set */ + } else + return -EINVAL; + if (!acpi_evalf(beep_handle, NULL, NULL, "vdd", beep_cmd, 0)) + return -EIO; + } + + return 0; +} + +/************************************************************************* + * Thermal subdriver + */ + +static enum thermal_access_mode thermal_read_mode; + +static int thermal_init(void) +{ + u8 t, ta1, ta2; + int i; + int acpi_tmp7 = acpi_evalf(ec_handle, NULL, "TMP7", "qv"); + + if (ibm_thinkpad_ec_found && experimental) { + /* + * Direct EC access mode: sensors at registers + * 0x78-0x7F, 0xC0-0xC7. Registers return 0x00 for + * non-implemented, thermal sensors return 0x80 when + * not available + */ + + ta1 = ta2 = 0; + for (i = 0; i < 8; i++) { + if (likely(acpi_ec_read(0x78 + i, &t))) { + ta1 |= t; + } else { + ta1 = 0; + break; + } + if (likely(acpi_ec_read(0xC0 + i, &t))) { + ta2 |= t; + } else { + ta1 = 0; + break; + } + } + if (ta1 == 0) { + /* This is sheer paranoia, but we handle it anyway */ + if (acpi_tmp7) { + printk(IBM_ERR + "ThinkPad ACPI EC access misbehaving, " + "falling back to ACPI TMPx access mode\n"); + thermal_read_mode = IBMACPI_THERMAL_ACPI_TMP07; + } else { + printk(IBM_ERR + "ThinkPad ACPI EC access misbehaving, " + "disabling thermal sensors access\n"); + thermal_read_mode = IBMACPI_THERMAL_NONE; + } + } else { + thermal_read_mode = + (ta2 != 0) ? + IBMACPI_THERMAL_TPEC_16 : IBMACPI_THERMAL_TPEC_8; + } + } else if (acpi_tmp7) { + if (acpi_evalf(ec_handle, NULL, "UPDT", "qv")) { + /* 600e/x, 770e, 770x */ + thermal_read_mode = IBMACPI_THERMAL_ACPI_UPDT; + } else { + /* Standard ACPI TMPx access, max 8 sensors */ + thermal_read_mode = IBMACPI_THERMAL_ACPI_TMP07; + } + } else { + /* temperatures not supported on 570, G4x, R30, R31, R32 */ + thermal_read_mode = IBMACPI_THERMAL_NONE; + } + + return 0; +} + +static int thermal_get_sensors(struct ibm_thermal_sensors_struct *s) +{ + int i, t; + s8 tmp; + char tmpi[] = "TMPi"; + + if (!s) + return -EINVAL; + + switch (thermal_read_mode) { +#if IBMACPI_MAX_THERMAL_SENSORS >= 16 + case IBMACPI_THERMAL_TPEC_16: + for (i = 0; i < 8; i++) { + if (!acpi_ec_read(0xC0 + i, &tmp)) + return -EIO; + s->temp[i + 8] = tmp * 1000; + } + /* fallthrough */ +#endif + case IBMACPI_THERMAL_TPEC_8: + for (i = 0; i < 8; i++) { + if (!acpi_ec_read(0x78 + i, &tmp)) + return -EIO; + s->temp[i] = tmp * 1000; + } + return (thermal_read_mode == IBMACPI_THERMAL_TPEC_16) ? 16 : 8; + + case IBMACPI_THERMAL_ACPI_UPDT: + if (!acpi_evalf(ec_handle, NULL, "UPDT", "v")) + return -EIO; + for (i = 0; i < 8; i++) { + tmpi[3] = '0' + i; + if (!acpi_evalf(ec_handle, &t, tmpi, "d")) + return -EIO; + s->temp[i] = (t - 2732) * 100; + } + return 8; + + case IBMACPI_THERMAL_ACPI_TMP07: + for (i = 0; i < 8; i++) { + tmpi[3] = '0' + i; + if (!acpi_evalf(ec_handle, &t, tmpi, "d")) + return -EIO; + s->temp[i] = t * 1000; + } + return 8; + + case IBMACPI_THERMAL_NONE: + default: + return 0; + } +} + +static int thermal_read(char *p) +{ + int len = 0; + int n, i; + struct ibm_thermal_sensors_struct t; + + n = thermal_get_sensors(&t); + if (unlikely(n < 0)) + return n; + + len += sprintf(p + len, "temperatures:\t"); + + if (n > 0) { + for (i = 0; i < (n - 1); i++) + len += sprintf(p + len, "%d ", t.temp[i] / 1000); + len += sprintf(p + len, "%d\n", t.temp[i] / 1000); + } else + len += sprintf(p + len, "not supported\n"); + + return len; +} + +/************************************************************************* + * EC Dump subdriver + */ + +static u8 ecdump_regs[256]; + +static int ecdump_read(char *p) +{ + int len = 0; + int i, j; + u8 v; + + len += sprintf(p + len, "EC " + " +00 +01 +02 +03 +04 +05 +06 +07" + " +08 +09 +0a +0b +0c +0d +0e +0f\n"); + for (i = 0; i < 256; i += 16) { + len += sprintf(p + len, "EC 0x%02x:", i); + for (j = 0; j < 16; j++) { + if (!acpi_ec_read(i + j, &v)) + break; + if (v != ecdump_regs[i + j]) + len += sprintf(p + len, " *%02x", v); + else + len += sprintf(p + len, " %02x", v); + ecdump_regs[i + j] = v; + } + len += sprintf(p + len, "\n"); + if (j != 16) + break; + } + + /* These are way too dangerous to advertise openly... */ +#if 0 + len += sprintf(p + len, "commands:\t0x 0x" + " ( is 00-ff, is 00-ff)\n"); + len += sprintf(p + len, "commands:\t0x " + " ( is 00-ff, is 0-255)\n"); +#endif + return len; +} + +static int ecdump_write(char *buf) +{ + char *cmd; + int i, v; + + while ((cmd = next_cmd(&buf))) { + if (sscanf(cmd, "0x%x 0x%x", &i, &v) == 2) { + /* i and v set */ + } else if (sscanf(cmd, "0x%x %u", &i, &v) == 2) { + /* i and v set */ + } else + return -EINVAL; + if (i >= 0 && i < 256 && v >= 0 && v < 256) { + if (!acpi_ec_write(i, v)) + return -EIO; + } else + return -EINVAL; + } + + return 0; +} + +/************************************************************************* + * Backlight/brightness subdriver + */ + +static struct backlight_device *ibm_backlight_device = NULL; + +static struct backlight_ops ibm_backlight_data = { + .get_brightness = brightness_get, + .update_status = brightness_update_status, +}; + +static int brightness_init(void) +{ + int b; + + b = brightness_get(NULL); + if (b < 0) + return b; + + ibm_backlight_device = backlight_device_register("ibm", NULL, NULL, + &ibm_backlight_data); + if (IS_ERR(ibm_backlight_device)) { + printk(IBM_ERR "Could not register backlight device\n"); + return PTR_ERR(ibm_backlight_device); + } + + ibm_backlight_device->props.max_brightness = 7; + ibm_backlight_device->props.brightness = b; + backlight_update_status(ibm_backlight_device); + + return 0; +} + +static void brightness_exit(void) +{ + if (ibm_backlight_device) { + backlight_device_unregister(ibm_backlight_device); + ibm_backlight_device = NULL; + } +} + +static int brightness_update_status(struct backlight_device *bd) +{ + return brightness_set( + (bd->props.fb_blank == FB_BLANK_UNBLANK && + bd->props.power == FB_BLANK_UNBLANK) ? + bd->props.brightness : 0); +} + +static int brightness_get(struct backlight_device *bd) +{ + u8 level; + if (!acpi_ec_read(brightness_offset, &level)) + return -EIO; + + level &= 0x7; + + return level; +} + +static int brightness_set(int value) +{ + int cmos_cmd, inc, i; + int current_value = brightness_get(NULL); + + value &= 7; + + cmos_cmd = value > current_value ? TP_CMOS_BRIGHTNESS_UP : TP_CMOS_BRIGHTNESS_DOWN; + inc = value > current_value ? 1 : -1; + for (i = current_value; i != value; i += inc) { + if (!cmos_eval(cmos_cmd)) + return -EIO; + if (!acpi_ec_write(brightness_offset, i + inc)) + return -EIO; + } + + return 0; +} + +static int brightness_read(char *p) +{ + int len = 0; + int level; + + if ((level = brightness_get(NULL)) < 0) { + len += sprintf(p + len, "level:\t\tunreadable\n"); + } else { + len += sprintf(p + len, "level:\t\t%d\n", level & 0x7); + len += sprintf(p + len, "commands:\tup, down\n"); + len += sprintf(p + len, "commands:\tlevel " + " ( is 0-7)\n"); + } + + return len; +} + +static int brightness_write(char *buf) +{ + int level; + int new_level; + char *cmd; + + while ((cmd = next_cmd(&buf))) { + if ((level = brightness_get(NULL)) < 0) + return level; + level &= 7; + + if (strlencmp(cmd, "up") == 0) { + new_level = level == 7 ? 7 : level + 1; + } else if (strlencmp(cmd, "down") == 0) { + new_level = level == 0 ? 0 : level - 1; + } else if (sscanf(cmd, "level %d", &new_level) == 1 && + new_level >= 0 && new_level <= 7) { + /* new_level set */ + } else + return -EINVAL; + + brightness_set(new_level); + } + + return 0; +} + +/************************************************************************* + * Volume subdriver + */ + +static int volume_read(char *p) +{ + int len = 0; + u8 level; + + if (!acpi_ec_read(volume_offset, &level)) { + len += sprintf(p + len, "level:\t\tunreadable\n"); + } else { + len += sprintf(p + len, "level:\t\t%d\n", level & 0xf); + len += sprintf(p + len, "mute:\t\t%s\n", onoff(level, 6)); + len += sprintf(p + len, "commands:\tup, down, mute\n"); + len += sprintf(p + len, "commands:\tlevel " + " ( is 0-15)\n"); + } + + return len; +} + +static int volume_write(char *buf) +{ + int cmos_cmd, inc, i; + u8 level, mute; + int new_level, new_mute; + char *cmd; + + while ((cmd = next_cmd(&buf))) { + if (!acpi_ec_read(volume_offset, &level)) + return -EIO; + new_mute = mute = level & 0x40; + new_level = level = level & 0xf; + + if (strlencmp(cmd, "up") == 0) { + if (mute) + new_mute = 0; + else + new_level = level == 15 ? 15 : level + 1; + } else if (strlencmp(cmd, "down") == 0) { + if (mute) + new_mute = 0; + else + new_level = level == 0 ? 0 : level - 1; + } else if (sscanf(cmd, "level %d", &new_level) == 1 && + new_level >= 0 && new_level <= 15) { + /* new_level set */ + } else if (strlencmp(cmd, "mute") == 0) { + new_mute = 0x40; + } else + return -EINVAL; + + if (new_level != level) { /* mute doesn't change */ + cmos_cmd = new_level > level ? TP_CMOS_VOLUME_UP : TP_CMOS_VOLUME_DOWN; + inc = new_level > level ? 1 : -1; + + if (mute && (!cmos_eval(cmos_cmd) || + !acpi_ec_write(volume_offset, level))) + return -EIO; + + for (i = level; i != new_level; i += inc) + if (!cmos_eval(cmos_cmd) || + !acpi_ec_write(volume_offset, i + inc)) + return -EIO; + + if (mute && (!cmos_eval(TP_CMOS_VOLUME_MUTE) || + !acpi_ec_write(volume_offset, + new_level + mute))) + return -EIO; + } + + if (new_mute != mute) { /* level doesn't change */ + cmos_cmd = new_mute ? TP_CMOS_VOLUME_MUTE : TP_CMOS_VOLUME_UP; + + if (!cmos_eval(cmos_cmd) || + !acpi_ec_write(volume_offset, level + new_mute)) + return -EIO; + } + } + + return 0; +} + + +/************************************************************************* + * Fan subdriver + */ + +/* + * FAN ACCESS MODES + * + * IBMACPI_FAN_RD_ACPI_GFAN: + * ACPI GFAN method: returns fan level + * + * see IBMACPI_FAN_WR_ACPI_SFAN + * EC 0x2f not available if GFAN exists + * + * IBMACPI_FAN_WR_ACPI_SFAN: + * ACPI SFAN method: sets fan level, 0 (stop) to 7 (max) + * + * EC 0x2f might be available *for reading*, but never for writing. + * + * IBMACPI_FAN_WR_TPEC: + * ThinkPad EC register 0x2f (HFSP): fan control loop mode Supported + * on almost all ThinkPads + * + * Fan speed changes of any sort (including those caused by the + * disengaged mode) are usually done slowly by the firmware as the + * maximum ammount of fan duty cycle change per second seems to be + * limited. + * + * Reading is not available if GFAN exists. + * Writing is not available if SFAN exists. + * + * Bits + * 7 automatic mode engaged; + * (default operation mode of the ThinkPad) + * fan level is ignored in this mode. + * 6 disengage mode (takes precedence over bit 7); + * not available on all thinkpads. May disable + * the tachometer, and speeds up fan to 100% duty-cycle, + * which speeds it up far above the standard RPM + * levels. It is not impossible that it could cause + * hardware damage. + * 5-3 unused in some models. Extra bits for fan level + * in others, but still useless as all values above + * 7 map to the same speed as level 7 in these models. + * 2-0 fan level (0..7 usually) + * 0x00 = stop + * 0x07 = max (set when temperatures critical) + * Some ThinkPads may have other levels, see + * IBMACPI_FAN_WR_ACPI_FANS (X31/X40/X41) + * + * FIRMWARE BUG: on some models, EC 0x2f might not be initialized at + * boot. Apparently the EC does not intialize it, so unless ACPI DSDT + * does so, its initial value is meaningless (0x07). + * + * For firmware bugs, refer to: + * http://thinkwiki.org/wiki/Embedded_Controller_Firmware#Firmware_Issues + * + * ---- + * + * ThinkPad EC register 0x84 (LSB), 0x85 (MSB): + * Main fan tachometer reading (in RPM) + * + * This register is present on all ThinkPads with a new-style EC, and + * it is known not to be present on the A21m/e, and T22, as there is + * something else in offset 0x84 according to the ACPI DSDT. Other + * ThinkPads from this same time period (and earlier) probably lack the + * tachometer as well. + * + * Unfortunately a lot of ThinkPads with new-style ECs but whose firwmare + * was never fixed by IBM to report the EC firmware version string + * probably support the tachometer (like the early X models), so + * detecting it is quite hard. We need more data to know for sure. + * + * FIRMWARE BUG: always read 0x84 first, otherwise incorrect readings + * might result. + * + * FIRMWARE BUG: when EC 0x2f bit 6 is set (disengaged mode), this + * register is not invalidated in ThinkPads that disable tachometer + * readings. Thus, the tachometer readings go stale. + * + * For firmware bugs, refer to: + * http://thinkwiki.org/wiki/Embedded_Controller_Firmware#Firmware_Issues + * + * IBMACPI_FAN_WR_ACPI_FANS: + * ThinkPad X31, X40, X41. Not available in the X60. + * + * FANS ACPI handle: takes three arguments: low speed, medium speed, + * high speed. ACPI DSDT seems to map these three speeds to levels + * as follows: STOP LOW LOW MED MED HIGH HIGH HIGH HIGH + * (this map is stored on FAN0..FAN8 as "0,1,1,2,2,3,3,3,3") + * + * The speeds are stored on handles + * (FANA:FAN9), (FANC:FANB), (FANE:FAND). + * + * There are three default speed sets, acessible as handles: + * FS1L,FS1M,FS1H; FS2L,FS2M,FS2H; FS3L,FS3M,FS3H + * + * ACPI DSDT switches which set is in use depending on various + * factors. + * + * IBMACPI_FAN_WR_TPEC is also available and should be used to + * command the fan. The X31/X40/X41 seems to have 8 fan levels, + * but the ACPI tables just mention level 7. + */ + +static enum fan_status_access_mode fan_status_access_mode; +static enum fan_control_access_mode fan_control_access_mode; +static enum fan_control_commands fan_control_commands; + +static int fan_control_status_known; +static u8 fan_control_initial_status; + +static void fan_watchdog_fire(struct work_struct *ignored); +static int fan_watchdog_maxinterval; +static DECLARE_DELAYED_WORK(fan_watchdog_task, fan_watchdog_fire); + +IBM_HANDLE(fans, ec, "FANS"); /* X31, X40, X41 */ +IBM_HANDLE(gfan, ec, "GFAN", /* 570 */ + "\\FSPD", /* 600e/x, 770e, 770x */ + ); /* all others */ +IBM_HANDLE(sfan, ec, "SFAN", /* 570 */ + "JFNS", /* 770x-JL */ + ); /* all others */ + +static int fan_init(void) +{ + fan_status_access_mode = IBMACPI_FAN_NONE; + fan_control_access_mode = IBMACPI_FAN_WR_NONE; + fan_control_commands = 0; + fan_control_status_known = 1; + fan_watchdog_maxinterval = 0; + + if (gfan_handle) { + /* 570, 600e/x, 770e, 770x */ + fan_status_access_mode = IBMACPI_FAN_RD_ACPI_GFAN; + } else { + /* all other ThinkPads: note that even old-style + * ThinkPad ECs supports the fan control register */ + if (likely(acpi_ec_read(fan_status_offset, + &fan_control_initial_status))) { + fan_status_access_mode = IBMACPI_FAN_RD_TPEC; + + /* In some ThinkPads, neither the EC nor the ACPI + * DSDT initialize the fan status, and it ends up + * being set to 0x07 when it *could* be either + * 0x07 or 0x80. + * + * Enable for TP-1Y (T43), TP-78 (R51e), + * TP-76 (R52), TP-70 (T43, R52), which are known + * to be buggy. */ + if (fan_control_initial_status == 0x07 && + ibm_thinkpad_ec_found && + ((ibm_thinkpad_ec_found[0] == '1' && + ibm_thinkpad_ec_found[1] == 'Y') || + (ibm_thinkpad_ec_found[0] == '7' && + (ibm_thinkpad_ec_found[1] == '6' || + ibm_thinkpad_ec_found[1] == '8' || + ibm_thinkpad_ec_found[1] == '0')) + )) { + printk(IBM_NOTICE + "fan_init: initial fan status is " + "unknown, assuming it is in auto " + "mode\n"); + fan_control_status_known = 0; + } + } else { + printk(IBM_ERR + "ThinkPad ACPI EC access misbehaving, " + "fan status and control unavailable\n"); + return 0; + } + } + + if (sfan_handle) { + /* 570, 770x-JL */ + fan_control_access_mode = IBMACPI_FAN_WR_ACPI_SFAN; + fan_control_commands |= + IBMACPI_FAN_CMD_LEVEL | IBMACPI_FAN_CMD_ENABLE; + } else { + if (!gfan_handle) { + /* gfan without sfan means no fan control */ + /* all other models implement TP EC 0x2f control */ + + if (fans_handle) { + /* X31, X40, X41 */ + fan_control_access_mode = + IBMACPI_FAN_WR_ACPI_FANS; + fan_control_commands |= + IBMACPI_FAN_CMD_SPEED | + IBMACPI_FAN_CMD_LEVEL | + IBMACPI_FAN_CMD_ENABLE; + } else { + fan_control_access_mode = IBMACPI_FAN_WR_TPEC; + fan_control_commands |= + IBMACPI_FAN_CMD_LEVEL | + IBMACPI_FAN_CMD_ENABLE; + } + } + } + + return 0; +} + +static int fan_get_status(u8 *status) +{ + u8 s; + + /* TODO: + * Add IBMACPI_FAN_RD_ACPI_FANS ? */ + + switch (fan_status_access_mode) { + case IBMACPI_FAN_RD_ACPI_GFAN: + /* 570, 600e/x, 770e, 770x */ + + if (unlikely(!acpi_evalf(gfan_handle, &s, NULL, "d"))) + return -EIO; + + if (likely(status)) + *status = s & 0x07; + + break; + + case IBMACPI_FAN_RD_TPEC: + /* all except 570, 600e/x, 770e, 770x */ + if (unlikely(!acpi_ec_read(fan_status_offset, &s))) + return -EIO; + + if (likely(status)) + *status = s; + + break; + + default: + return -ENXIO; + } + + return 0; +} + +static void fan_exit(void) +{ + cancel_delayed_work(&fan_watchdog_task); + flush_scheduled_work(); +} + +static int fan_get_speed(unsigned int *speed) +{ + u8 hi, lo; + + switch (fan_status_access_mode) { + case IBMACPI_FAN_RD_TPEC: + /* all except 570, 600e/x, 770e, 770x */ + if (unlikely(!acpi_ec_read(fan_rpm_offset, &lo) || + !acpi_ec_read(fan_rpm_offset + 1, &hi))) + return -EIO; + + if (likely(speed)) + *speed = (hi << 8) | lo; + + break; + + default: + return -ENXIO; + } + + return 0; +} + +static void fan_watchdog_fire(struct work_struct *ignored) +{ + printk(IBM_NOTICE "fan watchdog: enabling fan\n"); + if (fan_set_enable()) { + printk(IBM_ERR "fan watchdog: error while enabling fan\n"); + /* reschedule for later */ + fan_watchdog_reset(); + } +} + +static void fan_watchdog_reset(void) +{ + static int fan_watchdog_active = 0; + + if (fan_watchdog_active) + cancel_delayed_work(&fan_watchdog_task); + + if (fan_watchdog_maxinterval > 0) { + fan_watchdog_active = 1; + if (!schedule_delayed_work(&fan_watchdog_task, + msecs_to_jiffies(fan_watchdog_maxinterval + * 1000))) { + printk(IBM_ERR "failed to schedule the fan watchdog, " + "watchdog will not trigger\n"); + } + } else + fan_watchdog_active = 0; +} + +static int fan_set_level(int level) +{ + switch (fan_control_access_mode) { + case IBMACPI_FAN_WR_ACPI_SFAN: + if (level >= 0 && level <= 7) { + if (!acpi_evalf(sfan_handle, NULL, NULL, "vd", level)) + return -EIO; + } else + return -EINVAL; + break; + + case IBMACPI_FAN_WR_ACPI_FANS: + case IBMACPI_FAN_WR_TPEC: + if ((level != IBMACPI_FAN_EC_AUTO) && + (level != IBMACPI_FAN_EC_DISENGAGED) && + ((level < 0) || (level > 7))) + return -EINVAL; + + if (!acpi_ec_write(fan_status_offset, level)) + return -EIO; + else + fan_control_status_known = 1; + break; + + default: + return -ENXIO; + } + return 0; +} + +static int fan_set_enable(void) +{ + u8 s; + int rc; + + switch (fan_control_access_mode) { + case IBMACPI_FAN_WR_ACPI_FANS: + case IBMACPI_FAN_WR_TPEC: + if ((rc = fan_get_status(&s)) < 0) + return rc; + + /* Don't go out of emergency fan mode */ + if (s != 7) + s = IBMACPI_FAN_EC_AUTO; + + if (!acpi_ec_write(fan_status_offset, s)) + return -EIO; + else + fan_control_status_known = 1; + break; + + case IBMACPI_FAN_WR_ACPI_SFAN: + if ((rc = fan_get_status(&s)) < 0) + return rc; + + s &= 0x07; + + /* Set fan to at least level 4 */ + if (s < 4) + s = 4; + + if (!acpi_evalf(sfan_handle, NULL, NULL, "vd", s)) + return -EIO; + break; + + default: + return -ENXIO; + } + return 0; +} + +static int fan_set_disable(void) +{ + switch (fan_control_access_mode) { + case IBMACPI_FAN_WR_ACPI_FANS: + case IBMACPI_FAN_WR_TPEC: + if (!acpi_ec_write(fan_status_offset, 0x00)) + return -EIO; + else + fan_control_status_known = 1; + break; + + case IBMACPI_FAN_WR_ACPI_SFAN: + if (!acpi_evalf(sfan_handle, NULL, NULL, "vd", 0x00)) + return -EIO; + break; + + default: + return -ENXIO; + } + return 0; +} + +static int fan_set_speed(int speed) +{ + switch (fan_control_access_mode) { + case IBMACPI_FAN_WR_ACPI_FANS: + if (speed >= 0 && speed <= 65535) { + if (!acpi_evalf(fans_handle, NULL, NULL, "vddd", + speed, speed, speed)) + return -EIO; + } else + return -EINVAL; + break; + + default: + return -ENXIO; + } + return 0; +} + +static int fan_read(char *p) +{ + int len = 0; + int rc; + u8 status; + unsigned int speed = 0; + + switch (fan_status_access_mode) { + case IBMACPI_FAN_RD_ACPI_GFAN: + /* 570, 600e/x, 770e, 770x */ + if ((rc = fan_get_status(&status)) < 0) + return rc; + + len += sprintf(p + len, "status:\t\t%s\n" + "level:\t\t%d\n", + (status != 0) ? "enabled" : "disabled", status); + break; + + case IBMACPI_FAN_RD_TPEC: + /* all except 570, 600e/x, 770e, 770x */ + if ((rc = fan_get_status(&status)) < 0) + return rc; + + if (unlikely(!fan_control_status_known)) { + if (status != fan_control_initial_status) + fan_control_status_known = 1; + else + /* Return most likely status. In fact, it + * might be the only possible status */ + status = IBMACPI_FAN_EC_AUTO; + } + + len += sprintf(p + len, "status:\t\t%s\n", + (status != 0) ? "enabled" : "disabled"); + + /* No ThinkPad boots on disengaged mode, we can safely + * assume the tachometer is online if fan control status + * was unknown */ + if ((rc = fan_get_speed(&speed)) < 0) + return rc; + + len += sprintf(p + len, "speed:\t\t%d\n", speed); + + if (status & IBMACPI_FAN_EC_DISENGAGED) + /* Disengaged mode takes precedence */ + len += sprintf(p + len, "level:\t\tdisengaged\n"); + else if (status & IBMACPI_FAN_EC_AUTO) + len += sprintf(p + len, "level:\t\tauto\n"); + else + len += sprintf(p + len, "level:\t\t%d\n", status); + break; + + case IBMACPI_FAN_NONE: + default: + len += sprintf(p + len, "status:\t\tnot supported\n"); + } + + if (fan_control_commands & IBMACPI_FAN_CMD_LEVEL) { + len += sprintf(p + len, "commands:\tlevel "); + + switch (fan_control_access_mode) { + case IBMACPI_FAN_WR_ACPI_SFAN: + len += sprintf(p + len, " ( is 0-7)\n"); + break; + + default: + len += sprintf(p + len, " ( is 0-7, " + "auto, disengaged)\n"); + break; + } + } + + if (fan_control_commands & IBMACPI_FAN_CMD_ENABLE) + len += sprintf(p + len, "commands:\tenable, disable\n" + "commands:\twatchdog ( is 0 (off), " + "1-120 (seconds))\n"); + + if (fan_control_commands & IBMACPI_FAN_CMD_SPEED) + len += sprintf(p + len, "commands:\tspeed " + " ( is 0-65535)\n"); + + return len; +} + +static int fan_write_cmd_level(const char *cmd, int *rc) +{ + int level; + + if (strlencmp(cmd, "level auto") == 0) + level = IBMACPI_FAN_EC_AUTO; + else if (strlencmp(cmd, "level disengaged") == 0) + level = IBMACPI_FAN_EC_DISENGAGED; + else if (sscanf(cmd, "level %d", &level) != 1) + return 0; + + if ((*rc = fan_set_level(level)) == -ENXIO) + printk(IBM_ERR "level command accepted for unsupported " + "access mode %d", fan_control_access_mode); + + return 1; +} + +static int fan_write_cmd_enable(const char *cmd, int *rc) +{ + if (strlencmp(cmd, "enable") != 0) + return 0; + + if ((*rc = fan_set_enable()) == -ENXIO) + printk(IBM_ERR "enable command accepted for unsupported " + "access mode %d", fan_control_access_mode); + + return 1; +} + +static int fan_write_cmd_disable(const char *cmd, int *rc) +{ + if (strlencmp(cmd, "disable") != 0) + return 0; + + if ((*rc = fan_set_disable()) == -ENXIO) + printk(IBM_ERR "disable command accepted for unsupported " + "access mode %d", fan_control_access_mode); + + return 1; +} + +static int fan_write_cmd_speed(const char *cmd, int *rc) +{ + int speed; + + /* TODO: + * Support speed ? */ + + if (sscanf(cmd, "speed %d", &speed) != 1) + return 0; + + if ((*rc = fan_set_speed(speed)) == -ENXIO) + printk(IBM_ERR "speed command accepted for unsupported " + "access mode %d", fan_control_access_mode); + + return 1; +} + +static int fan_write_cmd_watchdog(const char *cmd, int *rc) +{ + int interval; + + if (sscanf(cmd, "watchdog %d", &interval) != 1) + return 0; + + if (interval < 0 || interval > 120) + *rc = -EINVAL; + else + fan_watchdog_maxinterval = interval; + + return 1; +} + +static int fan_write(char *buf) +{ + char *cmd; + int rc = 0; + + while (!rc && (cmd = next_cmd(&buf))) { + if (!((fan_control_commands & IBMACPI_FAN_CMD_LEVEL) && + fan_write_cmd_level(cmd, &rc)) && + !((fan_control_commands & IBMACPI_FAN_CMD_ENABLE) && + (fan_write_cmd_enable(cmd, &rc) || + fan_write_cmd_disable(cmd, &rc) || + fan_write_cmd_watchdog(cmd, &rc))) && + !((fan_control_commands & IBMACPI_FAN_CMD_SPEED) && + fan_write_cmd_speed(cmd, &rc)) + ) + rc = -EINVAL; + else if (!rc) + fan_watchdog_reset(); + } + + return rc; +} + +/**************************************************************************** + **************************************************************************** + * + * Infrastructure + * + **************************************************************************** + ****************************************************************************/ + +/* /proc support */ +static struct proc_dir_entry *proc_dir = NULL; + +/* Subdriver registry */ +static struct ibm_struct ibms[] = { + { + .name = "driver", + .init = ibm_acpi_driver_init, + .read = ibm_acpi_driver_read, + }, + { + .name = "hotkey", + .hid = IBM_HKEY_HID, + .init = hotkey_init, + .read = hotkey_read, + .write = hotkey_write, + .exit = hotkey_exit, + .notify = hotkey_notify, + .handle = &hkey_handle, + .type = ACPI_DEVICE_NOTIFY, + }, + { + .name = "bluetooth", + .init = bluetooth_init, + .read = bluetooth_read, + .write = bluetooth_write, + }, + { + .name = "wan", + .init = wan_init, + .read = wan_read, + .write = wan_write, + .experimental = 1, + }, + { + .name = "video", + .init = video_init, + .read = video_read, + .write = video_write, + .exit = video_exit, + }, + { + .name = "light", + .init = light_init, + .read = light_read, + .write = light_write, + }, +#ifdef CONFIG_ACPI_IBM_DOCK + { + .name = "dock", + .read = dock_read, + .write = dock_write, + .notify = dock_notify, + .handle = &dock_handle, + .type = ACPI_SYSTEM_NOTIFY, + }, + { + .name = "dock", + .hid = IBM_PCI_HID, + .notify = dock_notify, + .handle = &pci_handle, + .type = ACPI_SYSTEM_NOTIFY, + }, +#endif +#ifdef CONFIG_ACPI_IBM_BAY + { + .name = "bay", + .init = bay_init, + .read = bay_read, + .write = bay_write, + .notify = bay_notify, + .handle = &bay_handle, + .type = ACPI_SYSTEM_NOTIFY, + }, +#endif /* CONFIG_ACPI_IBM_BAY */ + { + .name = "cmos", + .read = cmos_read, + .write = cmos_write, + }, + { + .name = "led", + .init = led_init, + .read = led_read, + .write = led_write, + }, + { + .name = "beep", + .read = beep_read, + .write = beep_write, + }, + { + .name = "thermal", + .init = thermal_init, + .read = thermal_read, + }, + { + .name = "ecdump", + .read = ecdump_read, + .write = ecdump_write, + .experimental = 1, + }, + { + .name = "brightness", + .read = brightness_read, + .write = brightness_write, + .init = brightness_init, + .exit = brightness_exit, + }, + { + .name = "volume", + .read = volume_read, + .write = volume_write, + }, + { + .name = "fan", + .read = fan_read, + .write = fan_write, + .init = fan_init, + .exit = fan_exit, + .experimental = 1, + }, +}; + +/* + * Module and infrastructure proble, init and exit handling + */ + +static int __init ibm_init(struct ibm_struct *ibm) +{ + int ret; + struct proc_dir_entry *entry; + + if (ibm->experimental && !experimental) + return 0; + + if (ibm->hid) { + ret = register_ibmacpi_subdriver(ibm); + if (ret < 0) + return ret; + ibm->driver_registered = 1; + } + + if (ibm->init) { + ret = ibm->init(); + if (ret != 0) + return ret; + ibm->init_called = 1; + } + + if (ibm->read) { + entry = create_proc_entry(ibm->name, + S_IFREG | S_IRUGO | S_IWUSR, + proc_dir); + if (!entry) { + printk(IBM_ERR "unable to create proc entry %s\n", + ibm->name); + return -ENODEV; + } + entry->owner = THIS_MODULE; + entry->data = ibm; + entry->read_proc = &dispatch_read; + if (ibm->write) + entry->write_proc = &dispatch_write; + ibm->proc_created = 1; + } + + if (ibm->notify) { + ret = setup_notify(ibm); + if (ret == -ENODEV) { + printk(IBM_NOTICE "disabling subdriver %s\n", + ibm->name); + ibm_exit(ibm); + return 0; + } + if (ret < 0) + return ret; + } + + return 0; +} + +static void ibm_exit(struct ibm_struct *ibm) +{ + if (ibm->notify_installed) + acpi_remove_notify_handler(*ibm->handle, ibm->type, + dispatch_notify); + + if (ibm->proc_created) + remove_proc_entry(ibm->name, proc_dir); + + if (ibm->init_called && ibm->exit) + ibm->exit(); + + if (ibm->driver_registered) { + acpi_bus_unregister_driver(ibm->driver); + kfree(ibm->driver); + } +} + +/* Probing */ + +static char *ibm_thinkpad_ec_found = NULL; + +static char* __init check_dmi_for_ec(void) +{ + struct dmi_device *dev = NULL; + char ec_fw_string[18]; + + /* + * ThinkPad T23 or newer, A31 or newer, R50e or newer, + * X32 or newer, all Z series; Some models must have an + * up-to-date BIOS or they will not be detected. + * + * See http://thinkwiki.org/wiki/List_of_DMI_IDs + */ + while ((dev = dmi_find_device(DMI_DEV_TYPE_OEM_STRING, NULL, dev))) { + if (sscanf(dev->name, + "IBM ThinkPad Embedded Controller -[%17c", + ec_fw_string) == 1) { + ec_fw_string[sizeof(ec_fw_string) - 1] = 0; + ec_fw_string[strcspn(ec_fw_string, " ]")] = 0; + return kstrdup(ec_fw_string, GFP_KERNEL); + } + } + return NULL; +} + +/* Module init, exit, parameters */ + +static int __init set_ibm_param(const char *val, struct kernel_param *kp) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(ibms); i++) + if (strcmp(ibms[i].name, kp->name) == 0 && ibms[i].write) { + if (strlen(val) > sizeof(ibms[i].param) - 2) + return -ENOSPC; + strcpy(ibms[i].param, val); + strcat(ibms[i].param, ","); + return 0; + } + + return -EINVAL; +} + +static int experimental; +module_param(experimental, int, 0); + +#define IBM_PARAM(feature) \ + module_param_call(feature, set_ibm_param, NULL, NULL, 0) + +IBM_PARAM(hotkey); +IBM_PARAM(bluetooth); +IBM_PARAM(video); +IBM_PARAM(light); +#ifdef CONFIG_ACPI_IBM_DOCK +IBM_PARAM(dock); +#endif +#ifdef CONFIG_ACPI_IBM_BAY +IBM_PARAM(bay); +#endif /* CONFIG_ACPI_IBM_BAY */ +IBM_PARAM(cmos); +IBM_PARAM(led); +IBM_PARAM(beep); +IBM_PARAM(ecdump); +IBM_PARAM(brightness); +IBM_PARAM(volume); +IBM_PARAM(fan); + +static int __init acpi_ibm_init(void) +{ + int ret, i; + + if (acpi_disabled) + return -ENODEV; + + /* ec is required because many other handles are relative to it */ + IBM_HANDLE_INIT(ec); + if (!ec_handle) { + printk(IBM_ERR "ec object not found\n"); + return -ENODEV; + } + + /* Models with newer firmware report the EC in DMI */ + ibm_thinkpad_ec_found = check_dmi_for_ec(); + + /* these handles are not required */ + IBM_HANDLE_INIT(vid); + IBM_HANDLE_INIT(vid2); + IBM_HANDLE_INIT(ledb); + IBM_HANDLE_INIT(led); + IBM_HANDLE_INIT(hkey); + IBM_HANDLE_INIT(lght); + IBM_HANDLE_INIT(cmos); +#ifdef CONFIG_ACPI_IBM_DOCK + IBM_HANDLE_INIT(dock); +#endif + IBM_HANDLE_INIT(pci); +#ifdef CONFIG_ACPI_IBM_BAY + IBM_HANDLE_INIT(bay); + if (bay_handle) + IBM_HANDLE_INIT(bay_ej); + IBM_HANDLE_INIT(bay2); + if (bay2_handle) + IBM_HANDLE_INIT(bay2_ej); +#endif /* CONFIG_ACPI_IBM_BAY */ + IBM_HANDLE_INIT(beep); + IBM_HANDLE_INIT(ecrd); + IBM_HANDLE_INIT(ecwr); + IBM_HANDLE_INIT(fans); + IBM_HANDLE_INIT(gfan); + IBM_HANDLE_INIT(sfan); + + proc_dir = proc_mkdir(IBM_DIR, acpi_root_dir); + if (!proc_dir) { + printk(IBM_ERR "unable to create proc dir %s", IBM_DIR); + acpi_ibm_exit(); + return -ENODEV; + } + proc_dir->owner = THIS_MODULE; + + for (i = 0; i < ARRAY_SIZE(ibms); i++) { + ret = ibm_init(&ibms[i]); + if (ret >= 0 && *ibms[i].param) + ret = ibms[i].write(ibms[i].param); + if (ret < 0) { + acpi_ibm_exit(); + return ret; + } + } + + return 0; +} + +static void acpi_ibm_exit(void) +{ + int i; + + for (i = ARRAY_SIZE(ibms) - 1; i >= 0; i--) + ibm_exit(&ibms[i]); + + if (proc_dir) + remove_proc_entry(IBM_DIR, acpi_root_dir); + + if (ibm_thinkpad_ec_found) + kfree(ibm_thinkpad_ec_found); +} + +module_init(acpi_ibm_init); +module_exit(acpi_ibm_exit); diff --git a/drivers/misc/thinkpad_acpi.h b/drivers/misc/thinkpad_acpi.h new file mode 100644 index 000000000000..7ebaaa40e183 --- /dev/null +++ b/drivers/misc/thinkpad_acpi.h @@ -0,0 +1,437 @@ +/* + * ibm_acpi.h - IBM ThinkPad ACPI Extras + * + * + * Copyright (C) 2004-2005 Borislav Deianov + * Copyright (C) 2006-2007 Henrique de Moraes Holschuh + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#ifndef __IBM_ACPI_H__ +#define __IBM_ACPI_H__ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + + +/**************************************************************************** + * Main driver + */ + +#define IBM_NAME "ibm" +#define IBM_DESC "IBM ThinkPad ACPI Extras" +#define IBM_FILE "ibm_acpi" +#define IBM_URL "http://ibm-acpi.sf.net/" + +#define IBM_DIR IBM_NAME + +#define IBM_LOG IBM_FILE ": " +#define IBM_ERR KERN_ERR IBM_LOG +#define IBM_NOTICE KERN_NOTICE IBM_LOG +#define IBM_INFO KERN_INFO IBM_LOG +#define IBM_DEBUG KERN_DEBUG IBM_LOG + +#define IBM_MAX_ACPI_ARGS 3 + +/* ThinkPad CMOS commands */ +#define TP_CMOS_VOLUME_DOWN 0 +#define TP_CMOS_VOLUME_UP 1 +#define TP_CMOS_VOLUME_MUTE 2 +#define TP_CMOS_BRIGHTNESS_UP 4 +#define TP_CMOS_BRIGHTNESS_DOWN 5 + +#define onoff(status,bit) ((status) & (1 << (bit)) ? "on" : "off") +#define enabled(status,bit) ((status) & (1 << (bit)) ? "enabled" : "disabled") +#define strlencmp(a,b) (strncmp((a), (b), strlen(b))) + +/* ACPI HIDs */ +#define IBM_HKEY_HID "IBM0068" +#define IBM_PCI_HID "PNP0A03" + +/* ACPI helpers */ +static int acpi_evalf(acpi_handle handle, + void *res, char *method, char *fmt, ...); +static int acpi_ec_read(int i, u8 * p); +static int acpi_ec_write(int i, u8 v); +static int _sta(acpi_handle handle); + +/* ACPI handles */ +static acpi_handle root_handle; /* root namespace */ +static acpi_handle ec_handle; /* EC */ +static acpi_handle ecrd_handle, ecwr_handle; /* 570 EC access */ +static acpi_handle cmos_handle, hkey_handle; /* basic thinkpad handles */ + +static void ibm_handle_init(char *name, + acpi_handle * handle, acpi_handle parent, + char **paths, int num_paths, char **path); +#define IBM_HANDLE_INIT(object) \ + ibm_handle_init(#object, &object##_handle, *object##_parent, \ + object##_paths, ARRAY_SIZE(object##_paths), &object##_path) + +/* procfs support */ +static struct proc_dir_entry *proc_dir; +static int ibm_acpi_driver_init(void); +static int ibm_acpi_driver_read(char *p); + +/* procfs helpers */ +static int dispatch_read(char *page, char **start, off_t off, int count, + int *eof, void *data); +static int dispatch_write(struct file *file, const char __user * userbuf, + unsigned long count, void *data); +static char *next_cmd(char **cmds); + +/* Module */ +static int experimental; +static char *ibm_thinkpad_ec_found; + +static char* check_dmi_for_ec(void); +static int acpi_ibm_init(void); +static void acpi_ibm_exit(void); + + +/**************************************************************************** + * Subdrivers + */ + +struct ibm_struct { + char *name; + char param[32]; + + char *hid; + struct acpi_driver *driver; + + int (*init) (void); + int (*read) (char *); + int (*write) (char *); + void (*exit) (void); + + void (*notify) (struct ibm_struct *, u32); + acpi_handle *handle; + int type; + struct acpi_device *device; + + int driver_registered; + int proc_created; + int init_called; + int notify_installed; + + int experimental; +}; + +static struct ibm_struct ibms[]; +static int set_ibm_param(const char *val, struct kernel_param *kp); +static int ibm_init(struct ibm_struct *ibm); +static void ibm_exit(struct ibm_struct *ibm); + +/* ACPI devices */ +static void dispatch_notify(acpi_handle handle, u32 event, void *data); +static int setup_notify(struct ibm_struct *ibm); +static int ibm_device_add(struct acpi_device *device); +static int register_ibmacpi_subdriver(struct ibm_struct *ibm); + + +/* + * Bay subdriver + */ + +#ifdef CONFIG_ACPI_IBM_BAY +static int bay_status_supported, bay_eject_supported; +static int bay_status2_supported, bay_eject2_supported; + +static acpi_handle bay_handle, bay_ej_handle; +static acpi_handle bay2_handle, bay2_ej_handle; + +static int bay_init(void); +static void bay_notify(struct ibm_struct *ibm, u32 event); +static int bay_read(char *p); +static int bay_write(char *buf); +#endif /* CONFIG_ACPI_IBM_BAY */ + + +/* + * Beep subdriver + */ + +static acpi_handle beep_handle; + +static int beep_read(char *p); +static int beep_write(char *buf); + + +/* + * Bluetooth subdriver + */ + +static int bluetooth_supported; + +static int bluetooth_init(void); +static int bluetooth_status(void); +static int bluetooth_read(char *p); +static int bluetooth_write(char *buf); + + +/* + * Brightness (backlight) subdriver + */ + +static struct backlight_device *ibm_backlight_device; +static int brightness_offset = 0x31; + +static int brightness_init(void); +static void brightness_exit(void); +static int brightness_get(struct backlight_device *bd); +static int brightness_set(int value); +static int brightness_update_status(struct backlight_device *bd); +static int brightness_read(char *p); +static int brightness_write(char *buf); + + +/* + * CMOS subdriver + */ + +static int cmos_eval(int cmos_cmd); +static int cmos_read(char *p); +static int cmos_write(char *buf); + + +/* + * Dock subdriver + */ + +static acpi_handle pci_handle; +#ifdef CONFIG_ACPI_IBM_DOCK +static acpi_handle dock_handle; + +static void dock_notify(struct ibm_struct *ibm, u32 event); +static int dock_read(char *p); +static int dock_write(char *buf); +#endif /* CONFIG_ACPI_IBM_DOCK */ + + +/* + * EC dump subdriver + */ + +static int ecdump_read(char *p) ; +static int ecdump_write(char *buf); + + +/* + * Fan subdriver + */ + +enum { /* Fan control constants */ + fan_status_offset = 0x2f, /* EC register 0x2f */ + fan_rpm_offset = 0x84, /* EC register 0x84: LSB, 0x85 MSB (RPM) + * 0x84 must be read before 0x85 */ + + IBMACPI_FAN_EC_DISENGAGED = 0x40, /* EC mode: tachometer + * disengaged */ + IBMACPI_FAN_EC_AUTO = 0x80, /* EC mode: auto fan + * control */ +}; + +enum fan_status_access_mode { + IBMACPI_FAN_NONE = 0, /* No fan status or control */ + IBMACPI_FAN_RD_ACPI_GFAN, /* Use ACPI GFAN */ + IBMACPI_FAN_RD_TPEC, /* Use ACPI EC regs 0x2f, 0x84-0x85 */ +}; + +enum fan_control_access_mode { + IBMACPI_FAN_WR_NONE = 0, /* No fan control */ + IBMACPI_FAN_WR_ACPI_SFAN, /* Use ACPI SFAN */ + IBMACPI_FAN_WR_TPEC, /* Use ACPI EC reg 0x2f */ + IBMACPI_FAN_WR_ACPI_FANS, /* Use ACPI FANS and EC reg 0x2f */ +}; + +enum fan_control_commands { + IBMACPI_FAN_CMD_SPEED = 0x0001, /* speed command */ + IBMACPI_FAN_CMD_LEVEL = 0x0002, /* level command */ + IBMACPI_FAN_CMD_ENABLE = 0x0004, /* enable/disable cmd, + * and also watchdog cmd */ +}; + +static enum fan_status_access_mode fan_status_access_mode; +static enum fan_control_access_mode fan_control_access_mode; +static enum fan_control_commands fan_control_commands; +static int fan_control_status_known; +static u8 fan_control_initial_status; +static int fan_watchdog_maxinterval; + +static acpi_handle fans_handle, gfan_handle, sfan_handle; + +static int fan_init(void); +static void fan_exit(void); +static int fan_get_status(u8 *status); +static int fan_get_speed(unsigned int *speed); +static void fan_watchdog_fire(struct work_struct *ignored); +static void fan_watchdog_reset(void); +static int fan_set_level(int level); +static int fan_set_enable(void); +static int fan_set_disable(void); +static int fan_set_speed(int speed); +static int fan_read(char *p); +static int fan_write(char *buf); +static int fan_write_cmd_level(const char *cmd, int *rc); +static int fan_write_cmd_enable(const char *cmd, int *rc); +static int fan_write_cmd_disable(const char *cmd, int *rc); +static int fan_write_cmd_speed(const char *cmd, int *rc); +static int fan_write_cmd_watchdog(const char *cmd, int *rc); + + +/* + * Hotkey subdriver + */ + +static int hotkey_supported; +static int hotkey_mask_supported; +static int hotkey_orig_status; +static int hotkey_orig_mask; + +static int hotkey_init(void); +static void hotkey_exit(void); +static int hotkey_get(int *status, int *mask); +static int hotkey_set(int status, int mask); +static void hotkey_notify(struct ibm_struct *ibm, u32 event); +static int hotkey_read(char *p); +static int hotkey_write(char *buf); + + +/* + * LED subdriver + */ + +enum led_access_mode { + IBMACPI_LED_NONE = 0, + IBMACPI_LED_570, /* 570 */ + IBMACPI_LED_OLD, /* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20-21 */ + IBMACPI_LED_NEW, /* all others */ +}; + +enum { /* For IBMACPI_LED_OLD */ + IBMACPI_LED_EC_HLCL = 0x0c, /* EC reg to get led to power on */ + IBMACPI_LED_EC_HLBL = 0x0d, /* EC reg to blink a lit led */ + IBMACPI_LED_EC_HLMS = 0x0e, /* EC reg to select led to command */ +}; + +static enum led_access_mode led_supported; +static acpi_handle led_handle; + +static int led_init(void); +static int led_read(char *p); +static int led_write(char *buf); + +/* + * Light (thinklight) subdriver + */ + +static int light_supported; +static int light_status_supported; +static acpi_handle lght_handle, ledb_handle; + +static int light_init(void); +static int light_read(char *p); +static int light_write(char *buf); + + +/* + * Thermal subdriver + */ + +enum thermal_access_mode { + IBMACPI_THERMAL_NONE = 0, /* No thermal support */ + IBMACPI_THERMAL_ACPI_TMP07, /* Use ACPI TMP0-7 */ + IBMACPI_THERMAL_ACPI_UPDT, /* Use ACPI TMP0-7 with UPDT */ + IBMACPI_THERMAL_TPEC_8, /* Use ACPI EC regs, 8 sensors */ + IBMACPI_THERMAL_TPEC_16, /* Use ACPI EC regs, 16 sensors */ +}; + +#define IBMACPI_MAX_THERMAL_SENSORS 16 /* Max thermal sensors supported */ +struct ibm_thermal_sensors_struct { + s32 temp[IBMACPI_MAX_THERMAL_SENSORS]; +}; + +static int thermal_init(void); +static int thermal_get_sensors(struct ibm_thermal_sensors_struct *s); +static int thermal_read(char *p); + + +/* + * Video subdriver + */ + +enum video_access_mode { + IBMACPI_VIDEO_NONE = 0, + IBMACPI_VIDEO_570, /* 570 */ + IBMACPI_VIDEO_770, /* 600e/x, 770e, 770x */ + IBMACPI_VIDEO_NEW, /* all others */ +}; + +static enum video_access_mode video_supported; +static int video_orig_autosw; +static acpi_handle vid_handle, vid2_handle; + +static int video_init(void); +static void video_exit(void); +static int video_status(void); +static int video_autosw(void); +static int video_switch(void); +static int video_switch2(int status); +static int video_expand(void); +static int video_read(char *p); +static int video_write(char *buf); + + +/* + * Volume subdriver + */ + +static int volume_offset = 0x30; + +static int volume_read(char *p); +static int volume_write(char *buf); + + +/* + * Wan subdriver + */ + +static int wan_supported; + +static int wan_init(void); +static int wan_status(void); +static int wan_read(char *p); +static int wan_write(char *buf); + + +#endif /* __IBM_ACPI_H */ -- cgit v1.2.3 From 643f12dbb660e139fbaea268f3e3ce4d7d594b8f Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Thu, 29 Mar 2007 01:58:43 -0300 Subject: ACPI: thinkpad-acpi: cleanup after rename Cleanup documentation, driver strings and other misc stuff, now that the driver is named "thinkpad-acpi". Signed-off-by: Henrique de Moraes Holschuh Signed-off-by: Len Brown --- Documentation/thinkpad-acpi.txt | 46 ++++++++++++++++++++++++----------------- drivers/misc/thinkpad_acpi.c | 18 +++++++++------- drivers/misc/thinkpad_acpi.h | 21 ++++++++++--------- 3 files changed, 49 insertions(+), 36 deletions(-) (limited to 'Documentation') diff --git a/Documentation/thinkpad-acpi.txt b/Documentation/thinkpad-acpi.txt index f409f4bbdc47..af18d294cf1a 100644 --- a/Documentation/thinkpad-acpi.txt +++ b/Documentation/thinkpad-acpi.txt @@ -1,17 +1,22 @@ - IBM ThinkPad ACPI Extras Driver + ThinkPad ACPI Extras Driver - Version 0.13 - 31 December 2006 + Version 0.14 + March 26th, 2007 Borislav Deianov Henrique de Moraes Holschuh http://ibm-acpi.sf.net/ -This is a Linux ACPI driver for the IBM ThinkPad laptops. It supports -various features of these laptops which are accessible through the -ACPI framework but not otherwise fully supported by the generic Linux -ACPI drivers. +This is a Linux driver for the IBM and Lenovo ThinkPad laptops. It +supports various features of these laptops which are accessible +through the ACPI and ACPI EC framework, but not otherwise fully +supported by the generic Linux ACPI drivers. + +This driver used to be named ibm-acpi until kernel 2.6.21 and release +0.13-20070314. It used to be in the drivers/acpi tree, but it was +moved to the drivers/misc tree and renamed to thinkpad-acpi for kernel +2.6.22, and release 0.14. Status @@ -43,6 +48,8 @@ Please include the following information in your report: - ThinkPad model name - a copy of your DSDT, from /proc/acpi/dsdt + - a copy of the output of dmidecode, with serial numbers + and UUIDs masked off - which driver features work and which don't - the observed behavior of non-working features @@ -53,8 +60,9 @@ Installation ------------ If you are compiling this driver as included in the Linux kernel -sources, simply enable the CONFIG_ACPI_IBM option (Power Management / -ACPI / IBM ThinkPad Laptop Extras). +sources, simply enable the CONFIG_THINKPAD_ACPI option, and optionally +enable the CONFIG_THINKPAD_ACPI_BAY option if you want the +thinkpad-specific bay functionality. Features -------- @@ -210,7 +218,7 @@ hot plugging of devices in the Linux ACPI framework. If the laptop was booted while not in the dock, the following message is shown in the logs: - Mar 17 01:42:34 aero kernel: ibm_acpi: dock device not present + Mar 17 01:42:34 aero kernel: thinkpad_acpi: dock device not present In this case, no dock-related events are generated but the dock and undock commands described below still work. They can be executed @@ -270,7 +278,7 @@ This is due to the current lack of support for hot plugging of devices in the Linux ACPI framework. If the laptop was booted without the UltraBay, the following message is shown in the logs: - Mar 17 01:42:34 aero kernel: ibm_acpi: bay device not present + Mar 17 01:42:34 aero kernel: thinkpad_acpi: bay device not present In this case, no bay-related events are generated but the eject command described below still works. It can be executed manually or @@ -637,12 +645,12 @@ range. The fan cannot be stopped or started with this command. The ThinkPad's ACPI DSDT code will reprogram the fan on its own when certain conditions are met. It will override any fan programming done -through ibm-acpi. +through thinkpad-acpi. -The ibm-acpi kernel driver can be programmed to revert the fan level -to a safe setting if userspace does not issue one of the fan commands: -"enable", "disable", "level" or "watchdog" within a configurable -ammount of time. To do this, use the "watchdog" command. +The thinkpad-acpi kernel driver can be programmed to revert the fan +level to a safe setting if userspace does not issue one of the fan +commands: "enable", "disable", "level" or "watchdog" within a +configurable ammount of time. To do this, use the "watchdog" command. echo 'watchdog ' > /proc/acpi/ibm/fan @@ -686,8 +694,8 @@ separating them with commas, for example: echo enable,0xffff > /proc/acpi/ibm/hotkey echo lcd_disable,crt_enable > /proc/acpi/ibm/video -Commands can also be specified when loading the ibm_acpi module, for -example: +Commands can also be specified when loading the thinkpad-acpi module, +for example: - modprobe ibm_acpi hotkey=enable,0xffff video=auto_disable + modprobe thinkpad_acpi hotkey=enable,0xffff video=auto_disable diff --git a/drivers/misc/thinkpad_acpi.c b/drivers/misc/thinkpad_acpi.c index 90ffc4670a01..ddaedf80d873 100644 --- a/drivers/misc/thinkpad_acpi.c +++ b/drivers/misc/thinkpad_acpi.c @@ -1,5 +1,5 @@ /* - * ibm_acpi.c - IBM ThinkPad ACPI Extras + * thinkpad_acpi.c - ThinkPad ACPI Extras * * * Copyright (C) 2004-2005 Borislav Deianov @@ -21,10 +21,12 @@ * 02110-1301, USA. */ -#define IBM_VERSION "0.13" +#define IBM_VERSION "0.14" /* * Changelog: + * 2007-03-27 0.14 renamed to thinkpad_acpi and moved to + * drivers/misc. * * 2006-11-22 0.13 new maintainer * changelog now lives in git commit history, and will @@ -318,7 +320,9 @@ static int __init setup_notify(struct ibm_struct *ibm) } acpi_driver_data(ibm->device) = ibm; - sprintf(acpi_device_class(ibm->device), "%s/%s", IBM_NAME, ibm->name); + sprintf(acpi_device_class(ibm->device), "%s/%s", + IBM_ACPI_EVENT_PREFIX, + ibm->name); status = acpi_install_notify_handler(*ibm->handle, ibm->type, dispatch_notify, ibm); @@ -458,7 +462,7 @@ static char *next_cmd(char **cmds) * ibm-acpi init subdriver */ -static int ibm_acpi_driver_init(void) +static int thinkpad_acpi_driver_init(void) { printk(IBM_INFO "%s v%s\n", IBM_DESC, IBM_VERSION); printk(IBM_INFO "%s\n", IBM_URL); @@ -470,7 +474,7 @@ static int ibm_acpi_driver_init(void) return 0; } -static int ibm_acpi_driver_read(char *p) +static int thinkpad_acpi_driver_read(char *p) { int len = 0; @@ -2440,8 +2444,8 @@ static struct proc_dir_entry *proc_dir = NULL; static struct ibm_struct ibms[] = { { .name = "driver", - .init = ibm_acpi_driver_init, - .read = ibm_acpi_driver_read, + .init = thinkpad_acpi_driver_init, + .read = thinkpad_acpi_driver_read, }, { .name = "hotkey", diff --git a/drivers/misc/thinkpad_acpi.h b/drivers/misc/thinkpad_acpi.h index ee1b93a2bbdd..015c02beb203 100644 --- a/drivers/misc/thinkpad_acpi.h +++ b/drivers/misc/thinkpad_acpi.h @@ -1,5 +1,5 @@ /* - * ibm_acpi.h - IBM ThinkPad ACPI Extras + * thinkpad_acpi.h - ThinkPad ACPI Extras * * * Copyright (C) 2004-2005 Borislav Deianov @@ -21,8 +21,8 @@ * 02110-1301, USA. */ -#ifndef __IBM_ACPI_H__ -#define __IBM_ACPI_H__ +#ifndef __THINKPAD_ACPI_H__ +#define __THINKPAD_ACPI_H__ #include #include @@ -47,12 +47,13 @@ * Main driver */ -#define IBM_NAME "ibm" -#define IBM_DESC "IBM ThinkPad ACPI Extras" -#define IBM_FILE "ibm_acpi" +#define IBM_NAME "thinkpad" +#define IBM_DESC "ThinkPad ACPI Extras" +#define IBM_FILE "thinkpad_acpi" #define IBM_URL "http://ibm-acpi.sf.net/" -#define IBM_DIR IBM_NAME +#define IBM_DIR "ibm" +#define IBM_ACPI_EVENT_PREFIX "ibm" #define IBM_LOG IBM_FILE ": " #define IBM_ERR KERN_ERR IBM_LOG @@ -99,8 +100,8 @@ static void ibm_handle_init(char *name, /* procfs support */ static struct proc_dir_entry *proc_dir; -static int ibm_acpi_driver_init(void); -static int ibm_acpi_driver_read(char *p); +static int thinkpad_acpi_driver_init(void); +static int thinkpad_acpi_driver_read(char *p); /* procfs helpers */ static int dispatch_read(char *page, char **start, off_t off, int count, @@ -434,4 +435,4 @@ static int wan_read(char *p); static int wan_write(char *buf); -#endif /* __IBM_ACPI_H */ +#endif /* __THINKPAD_ACPI_H */ -- cgit v1.2.3 From 2400ff77e7025bf6ffb71afdcbfbdd9aa47dfc36 Mon Sep 17 00:00:00 2001 From: Simon Horman Date: Sun, 1 Apr 2007 23:49:40 -0700 Subject: [PATCH] CPUSETS: add mems to basic usage documentation It seems that there must be at least one node in mems and at least one CPU in cpus in order to be able to assign tasks to a cpuset. This makes sense. And I think it would also make sense to include a mems setting in the basic usage section of the documentation. I also wonder if something logged to dmsg, explaining why a write failed, would be a good enhancement. I ended up having rummage arround in cpuset.c in order to work out why my configuration was failing. Signed-off-by: Simon Horman Acked-by: Paul Jackson Cc: Paul Menage Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/cpusets.txt | 3 +++ 1 file changed, 3 insertions(+) (limited to 'Documentation') diff --git a/Documentation/cpusets.txt b/Documentation/cpusets.txt index 842f0d1ab216..f2c0a6842930 100644 --- a/Documentation/cpusets.txt +++ b/Documentation/cpusets.txt @@ -557,6 +557,9 @@ Set some flags: Add some cpus: # /bin/echo 0-7 > cpus +Add some mems: +# /bin/echo 0-7 > mems + Now attach your shell to this cpuset: # /bin/echo $$ > tasks -- cgit v1.2.3 From 199c1167f5e8123a9b0a9ab1e8b78a6aa4b2733f Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Thu, 22 Mar 2007 00:40:06 +0100 Subject: ieee1394: change deprecation status of dv1394 Nobody ported ffmpeg from dv1394 to rawiso yet, and there is no justification to remove dv1394 right now. Nevertheless, a strong deprecation of this ABI makes a lot of sense, especially as Kristian H's drivers shape up to be an attractive alternative to the existing ones. But we don't have a schedule at the moment. Signed-off-by: Stefan Richter --- Documentation/ABI/obsolete/dv1394 | 9 +++++++++ Documentation/feature-removal-schedule.txt | 11 ----------- drivers/ieee1394/Kconfig | 6 +++--- drivers/ieee1394/dv1394.c | 4 ++-- 4 files changed, 14 insertions(+), 16 deletions(-) create mode 100644 Documentation/ABI/obsolete/dv1394 (limited to 'Documentation') diff --git a/Documentation/ABI/obsolete/dv1394 b/Documentation/ABI/obsolete/dv1394 new file mode 100644 index 000000000000..2ee36864ca10 --- /dev/null +++ b/Documentation/ABI/obsolete/dv1394 @@ -0,0 +1,9 @@ +What: dv1394 (a.k.a. "OHCI-DV I/O support" for FireWire) +Contact: linux1394-devel@lists.sourceforge.net +Description: + New application development should use raw1394 + userspace libraries + instead, notably libiec61883 which is functionally equivalent. + +Users: + ffmpeg/libavformat (used by a variety of media players) + dvgrab v1.x (replaced by dvgrab2 on top of raw1394 and resp. libraries) diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt index 0bc8b0b2e103..19b4c96b2a49 100644 --- a/Documentation/feature-removal-schedule.txt +++ b/Documentation/feature-removal-schedule.txt @@ -39,17 +39,6 @@ Who: Dan Dennedy , Stefan Richter --------------------------- -What: dv1394 driver (CONFIG_IEEE1394_DV1394) -When: June 2007 -Why: Replaced by raw1394 + userspace libraries, notably libiec61883. This - shift of application support has been indicated on www.linux1394.org - and developers' mailinglists for quite some time. Major applications - have been converted, with the exception of ffmpeg and hence xine. - Piped output of dvgrab2 is a partial equivalent to dv1394. -Who: Dan Dennedy , Stefan Richter - ---------------------------- - What: Video4Linux API 1 ioctls and video_decoder.h from Video devices. When: December 2006 Why: V4L1 AP1 was replaced by V4L2 API. during migration from 2.4 to 2.6 diff --git a/drivers/ieee1394/Kconfig b/drivers/ieee1394/Kconfig index b8a47342cd2c..cd84a55ecf20 100644 --- a/drivers/ieee1394/Kconfig +++ b/drivers/ieee1394/Kconfig @@ -138,9 +138,9 @@ config IEEE1394_DV1394 tristate "OHCI-DV I/O support (deprecated)" depends on IEEE1394 && IEEE1394_OHCI1394 help - The dv1394 driver will be removed from Linux in a future release. - Its functionality is now provided by raw1394 together with libraries - such as libiec61883. + The dv1394 driver is unsupported and may be removed from Linux in a + future release. Its functionality is now provided by raw1394 together + with libraries such as libiec61883. config IEEE1394_RAWIO tristate "Raw IEEE1394 I/O support" diff --git a/drivers/ieee1394/dv1394.c b/drivers/ieee1394/dv1394.c index dee9529aa8e7..026e38face5c 100644 --- a/drivers/ieee1394/dv1394.c +++ b/drivers/ieee1394/dv1394.c @@ -2564,8 +2564,8 @@ static int __init dv1394_init_module(void) int ret; printk(KERN_WARNING - "WARNING: The dv1394 driver is unsupported and will be removed " - "from Linux soon. Use raw1394 instead.\n"); + "NOTE: The dv1394 driver is unsupported and may be removed in a " + "future Linux release. Use raw1394 instead.\n"); cdev_init(&dv1394_cdev, &dv1394_fops); dv1394_cdev.owner = THIS_MODULE; -- cgit v1.2.3 From 3d2b8a9f2c26bc0fe03b3545d07245798b1b81b9 Mon Sep 17 00:00:00 2001 From: "malattia@linux.it" Date: Mon, 9 Apr 2007 19:31:16 +0200 Subject: sony-laptop: update documentation and Kconfig help Signed-off-by: Mattia Dongili Signed-off-by: Len Brown --- Documentation/sony-laptop.txt | 25 ++++++++++++++++++------- drivers/misc/Kconfig | 9 ++++----- 2 files changed, 22 insertions(+), 12 deletions(-) (limited to 'Documentation') diff --git a/Documentation/sony-laptop.txt b/Documentation/sony-laptop.txt index dfd26df056f4..7a5c1a81905c 100644 --- a/Documentation/sony-laptop.txt +++ b/Documentation/sony-laptop.txt @@ -3,12 +3,18 @@ Sony Notebook Control Driver (SNC) Readme Copyright (C) 2004- 2005 Stelian Pop Copyright (C) 2007 Mattia Dongili -This mini-driver drives the SNC device present in the ACPI BIOS of -the Sony Vaio laptops. +This mini-driver drives the SNC and SPIC device present in the ACPI BIOS of the +Sony Vaio laptops. This driver mixes both devices functions under the same +(hopefully consistent) interface. This also means that the sonypi driver is +obsoleted by sony-laptop now. -It gives access to some extra laptop functionalities. In its current -form, this driver let the user set or query the screen brightness -through the backlight subsystem and remove/apply power to some devices. +Fn keys (hotkeys): +------------------ +Some models report hotkeys through the SNC or SPIC devices, such events are +reported both through the ACPI subsystem as acpi events and through the INPUT +subsystem. See the logs of acpid or /proc/acpi/event and +/proc/bus/input/devices to find out what those events are and which input +devices are created by the driver. Backlight control: ------------------ @@ -39,6 +45,8 @@ The files are: audiopower power on/off the internal sound card lanpower power on/off the internal ethernet card (only in debug mode) + bluetoothpower power on/off the internal bluetooth device + fanspeed get/set the fan speed Note that some files may be missing if they are not supported by your particular laptop model. @@ -76,9 +84,9 @@ The sony-laptop driver creates, for some of those methods (the most current ones found on several Vaio models), an entry under /sys/devices/platform/sony-laptop, just like the 'cdpower' one. You can create other entries corresponding to your own laptop methods by -further editing the source (see the 'sony_acpi_values' table, and add a new +further editing the source (see the 'sony_nc_values' table, and add a new entry to this table with your get/set method names using the -HANDLE_NAMES macro). +SNC_HANDLE_NAMES macro). Your mission, should you accept it, is to try finding out what those entries are for, by reading/writing random values from/to those @@ -87,6 +95,9 @@ files and find out what is the impact on your laptop. Should you find anything interesting, please report it back to me, I will not disavow all knowledge of your actions :) +See also http://www.linux.it/~malattia/wiki/index.php/Sony_drivers for other +useful info. + Bugs/Limitations: ----------------- diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 80b199fa0aa9..4fb951b693ad 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -112,12 +112,11 @@ config SONY_LAPTOP depends on X86 && ACPI select BACKLIGHT_CLASS_DEVICE ---help--- - This mini-driver drives the SNC device present in the ACPI BIOS of - the Sony Vaio laptops. + This mini-driver drives the SNC and SPIC devices present in the ACPI + BIOS of the Sony Vaio laptops. - It gives access to some extra laptop functionalities. In its current - form, this driver let the user set or query the screen brightness - through the backlight subsystem and remove/apply power to some + It gives access to some extra laptop functionalities like Bluetooth, + screen brightness control, Fn keys and allows powering on/off some devices. Read for more information. -- cgit v1.2.3 From 1668be71ccae5a9610fc8a224bd80fbe852f93ae Mon Sep 17 00:00:00 2001 From: David Brownell Date: Wed, 11 Apr 2007 23:28:42 -0700 Subject: [PATCH] doc: gpio.txt describes open-drain emulation Update the GPIO docs to describe the idiom whereby open drain signals are emulated by toggling the GPIO direction. Signed-off-by: David Brownell Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/gpio.txt | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/gpio.txt b/Documentation/gpio.txt index 989f1130f4f3..f8528db967fa 100644 --- a/Documentation/gpio.txt +++ b/Documentation/gpio.txt @@ -27,7 +27,7 @@ The exact capabilities of GPIOs vary between systems. Common options: - Output values are writable (high=1, low=0). Some chips also have options about how that value is driven, so that for example only one value might be driven ... supporting "wire-OR" and similar schemes - for the other value. + for the other value (notably, "open drain" signaling). - Input values are likewise readable (1, 0). Some chips support readback of pins configured as "output", which is very useful in such "wire-OR" @@ -247,6 +247,35 @@ with gpio_get_value(), for example to initialize or update driver state when the IRQ is edge-triggered. +Emulating Open Drain Signals +---------------------------- +Sometimes shared signals need to use "open drain" signaling, where only the +low signal level is actually driven. (That term applies to CMOS transistors; +"open collector" is used for TTL.) A pullup resistor causes the high signal +level. This is sometimes called a "wire-AND"; or more practically, from the +negative logic (low=true) perspective this is a "wire-OR". + +One common example of an open drain signal is a shared active-low IRQ line. +Also, bidirectional data bus signals sometimes use open drain signals. + +Some GPIO controllers directly support open drain outputs; many don't. When +you need open drain signaling but your hardware doesn't directly support it, +there's a common idiom you can use to emulate it with any GPIO pin that can +be used as either an input or an output: + + LOW: gpio_direction_output(gpio, 0) ... this drives the signal + and overrides the pullup. + + HIGH: gpio_direction_input(gpio) ... this turns off the output, + so the pullup (or some other device) controls the signal. + +If you are "driving" the signal high but gpio_get_value(gpio) reports a low +value (after the appropriate rise time passes), you know some other component +is driving the shared signal low. That's not necessarily an error. As one +common example, that's how I2C clocks are stretched: a slave that needs a +slower clock delays the rising edge of SCK, and the I2C master adjusts its +signaling rate accordingly. + What do these conventions omit? =============================== -- cgit v1.2.3 From 132ce09123755ec5e3d3a8ae22f4f753c3baac97 Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Sat, 21 Apr 2007 11:08:30 -0300 Subject: ACPI: thinkpad-acpi: add debug mode Add a debug mode parameter and verbose debug mode Kconfig option. Signed-off-by: Henrique de Moraes Holschuh Signed-off-by: Len Brown --- Documentation/thinkpad-acpi.txt | 13 +++++++++++++ drivers/misc/Kconfig | 10 ++++++++++ drivers/misc/thinkpad_acpi.c | 3 +++ drivers/misc/thinkpad_acpi.h | 13 +++++++++++++ 4 files changed, 39 insertions(+) (limited to 'Documentation') diff --git a/Documentation/thinkpad-acpi.txt b/Documentation/thinkpad-acpi.txt index af18d294cf1a..82fd8228fd41 100644 --- a/Documentation/thinkpad-acpi.txt +++ b/Documentation/thinkpad-acpi.txt @@ -699,3 +699,16 @@ for example: modprobe thinkpad_acpi hotkey=enable,0xffff video=auto_disable +Enabling debugging output +------------------------- + +The module takes a debug paramater which can be used to selectively +enable various classes of debugging output, for example: + + modprobe ibm_acpi debug=0xffff + +will enable all debugging output classes. It takes a bitmask, so +to enable more than one output class, just add their values. + +There is also a kernel build option to enable more debugging +information, which may be necessary to debug driver problems. diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 2cd96a3dff54..44e4c8fb7a74 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -137,6 +137,16 @@ config THINKPAD_ACPI If you have an IBM or Lenovo ThinkPad laptop, say Y or M here. +config THINKPAD_ACPI_DEBUG + bool "Verbose debug mode" + depends on THINKPAD_ACPI + default n + ---help--- + Enables extra debugging information, at the expense of a slightly + increase in driver size. + + If you are not sure, say N here. + config THINKPAD_ACPI_DOCK bool "Legacy Docking Station Support" depends on THINKPAD_ACPI diff --git a/drivers/misc/thinkpad_acpi.c b/drivers/misc/thinkpad_acpi.c index 4131a7875ad7..7fa906fd45c0 100644 --- a/drivers/misc/thinkpad_acpi.c +++ b/drivers/misc/thinkpad_acpi.c @@ -2684,6 +2684,9 @@ static int __init set_ibm_param(const char *val, struct kernel_param *kp) static int experimental; module_param(experimental, int, 0); +static u32 dbg_level; +module_param_named(debug, dbg_level, uint, 0); + #define IBM_PARAM(feature) \ module_param_call(feature, set_ibm_param, NULL, NULL, 0) diff --git a/drivers/misc/thinkpad_acpi.h b/drivers/misc/thinkpad_acpi.h index 02a297e0525f..b2348d7a07c4 100644 --- a/drivers/misc/thinkpad_acpi.h +++ b/drivers/misc/thinkpad_acpi.h @@ -74,6 +74,18 @@ #define enabled(status,bit) ((status) & (1 << (bit)) ? "enabled" : "disabled") #define strlencmp(a,b) (strncmp((a), (b), strlen(b))) +/* Debugging */ +#define TPACPI_DBG_ALL 0xffff +#define dbg_printk(a_dbg_level, format, arg...) \ + do { if (dbg_level & a_dbg_level) \ + printk(IBM_DEBUG "%s: " format, __func__ , ## arg); } while (0) +#ifdef CONFIG_THINKPAD_ACPI_DEBUG +#define vdbg_printk(a_dbg_level, format, arg...) \ + dbg_printk(a_dbg_level, format, ## arg) +#else +#define vdbg_printk(a_dbg_level, format, arg...) +#endif + /* ACPI HIDs */ #define IBM_HKEY_HID "IBM0068" #define IBM_PCI_HID "PNP0A03" @@ -112,6 +124,7 @@ static char *next_cmd(char **cmds); /* Module */ static int experimental; +static u32 dbg_level; static char *ibm_thinkpad_ec_found; static char* check_dmi_for_ec(void); -- cgit v1.2.3 From fe08bc4b4fd1371fad111675a564e4d2ebbf39ea Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Sat, 21 Apr 2007 11:08:32 -0300 Subject: ACPI: thinkpad-acpi: add subdriver debug statements Add debug messages to the subdriver initialization and exit code. Signed-off-by: Henrique de Moraes Holschuh Signed-off-by: Len Brown --- Documentation/thinkpad-acpi.txt | 4 ++ drivers/misc/thinkpad_acpi.c | 111 ++++++++++++++++++++++++++++++++++++++++ drivers/misc/thinkpad_acpi.h | 4 ++ 3 files changed, 119 insertions(+) (limited to 'Documentation') diff --git a/Documentation/thinkpad-acpi.txt b/Documentation/thinkpad-acpi.txt index 82fd8228fd41..20d5ec309cbd 100644 --- a/Documentation/thinkpad-acpi.txt +++ b/Documentation/thinkpad-acpi.txt @@ -710,5 +710,9 @@ enable various classes of debugging output, for example: will enable all debugging output classes. It takes a bitmask, so to enable more than one output class, just add their values. + Debug bitmask Description + 0x0001 Initialization and probing + 0x0002 Removal + There is also a kernel build option to enable more debugging information, which may be necessary to debug driver problems. diff --git a/drivers/misc/thinkpad_acpi.c b/drivers/misc/thinkpad_acpi.c index eeab39418c71..e8fc8da35669 100644 --- a/drivers/misc/thinkpad_acpi.c +++ b/drivers/misc/thinkpad_acpi.c @@ -313,6 +313,9 @@ static int __init setup_notify(struct ibm_struct *ibm) if (!*ibm->handle) return 0; + dbg_printk(TPACPI_DBG_INIT, + "setting up ACPI notify for %s\n", ibm->name); + ret = acpi_bus_get_device(*ibm->handle, &ibm->device); if (ret < 0) { printk(IBM_ERR "%s device not present\n", ibm->name); @@ -349,6 +352,9 @@ static int __init register_tpacpi_subdriver(struct ibm_struct *ibm) { int ret; + dbg_printk(TPACPI_DBG_INIT, + "registering %s as an ACPI driver\n", ibm->name); + ibm->driver = kzalloc(sizeof(struct acpi_driver), GFP_KERNEL); if (!ibm->driver) { printk(IBM_ERR "kzalloc(ibm->driver) failed\n"); @@ -497,17 +503,25 @@ static int hotkey_orig_mask; static int hotkey_init(void) { + vdbg_printk(TPACPI_DBG_INIT, "initializing hotkey subdriver\n"); + IBM_HANDLE_INIT(hkey); /* hotkey not supported on 570 */ hotkey_supported = hkey_handle != NULL; + vdbg_printk(TPACPI_DBG_INIT, "hotkeys are %s\n", + str_supported(hotkey_supported)); + if (hotkey_supported) { /* mask not supported on 570, 600e/x, 770e, 770x, A21e, A2xm/p, A30, R30, R31, T20-22, X20-21, X22-24 */ hotkey_mask_supported = acpi_evalf(hkey_handle, NULL, "DHKN", "qv"); + vdbg_printk(TPACPI_DBG_INIT, "hotkey masks are %s\n", + str_supported(hotkey_mask_supported)); + if (!hotkey_get(&hotkey_orig_status, &hotkey_orig_mask)) return -ENODEV; } @@ -518,6 +532,7 @@ static int hotkey_init(void) static void hotkey_exit(void) { if (hotkey_supported) { + dbg_printk(TPACPI_DBG_EXIT, "restoring original hotkey mask\n"); hotkey_set(hotkey_orig_status, hotkey_orig_mask); } } @@ -633,6 +648,8 @@ static int bluetooth_supported; static int bluetooth_init(void) { + vdbg_printk(TPACPI_DBG_INIT, "initializing bluetooth subdriver\n"); + IBM_HANDLE_INIT(hkey); /* bluetooth not supported on 570, 600e/x, 770e, 770x, A21e, A2xm/p, @@ -640,6 +657,9 @@ static int bluetooth_init(void) bluetooth_supported = hkey_handle && acpi_evalf(hkey_handle, NULL, "GBDC", "qv"); + vdbg_printk(TPACPI_DBG_INIT, "bluetooth is %s\n", + str_supported(bluetooth_supported)); + return (bluetooth_supported)? 0 : 1; } @@ -704,11 +724,16 @@ static int wan_supported; static int wan_init(void) { + vdbg_printk(TPACPI_DBG_INIT, "initializing wan subdriver\n"); + IBM_HANDLE_INIT(hkey); wan_supported = hkey_handle && acpi_evalf(hkey_handle, NULL, "GWAN", "qv"); + vdbg_printk(TPACPI_DBG_INIT, "wan is %s\n", + str_supported(wan_supported)); + return (wan_supported)? 0 : 1; } @@ -784,6 +809,8 @@ static int video_init(void) { int ivga; + vdbg_printk(TPACPI_DBG_INIT, "initializing video subdriver\n"); + IBM_HANDLE_INIT(vid); IBM_HANDLE_INIT(vid2); @@ -804,11 +831,16 @@ static int video_init(void) /* all others */ video_supported = TPACPI_VIDEO_NEW; + vdbg_printk(TPACPI_DBG_INIT, "video is %s, mode %d\n", + str_supported(video_supported != TPACPI_VIDEO_NONE), + video_supported); + return (video_supported != TPACPI_VIDEO_NONE)? 0 : 1; } static void video_exit(void) { + dbg_printk(TPACPI_DBG_EXIT, "restoring original video autoswitch mode\n"); acpi_evalf(vid_handle, NULL, "_DOS", "vd", video_orig_autosw); } @@ -991,6 +1023,8 @@ IBM_HANDLE(ledb, ec, "LEDB"); /* G4x */ static int light_init(void) { + vdbg_printk(TPACPI_DBG_INIT, "initializing light subdriver\n"); + IBM_HANDLE_INIT(ledb); IBM_HANDLE_INIT(lght); IBM_HANDLE_INIT(cmos); @@ -1004,6 +1038,9 @@ static int light_init(void) light_status_supported = acpi_evalf(ec_handle, NULL, "KBLT", "qv"); + vdbg_printk(TPACPI_DBG_INIT, "light is %s\n", + str_supported(light_supported)); + return (light_supported)? 0 : 1; } @@ -1075,9 +1112,14 @@ IBM_HANDLE(pci, root, "\\_SB.PCI"); /* 570 */ static int dock_init(void) { + vdbg_printk(TPACPI_DBG_INIT, "initializing dock subdriver\n"); + IBM_HANDLE_INIT(dock); IBM_HANDLE_INIT(pci); + vdbg_printk(TPACPI_DBG_INIT, "dock is %s\n", + str_supported(dock_handle != NULL)); + return (dock_handle)? 0 : 1; } @@ -1171,6 +1213,8 @@ IBM_HANDLE(bay2_ej, bay2, "_EJ3", /* 600e/x, 770e, A3x */ static int bay_init(void) { + vdbg_printk(TPACPI_DBG_INIT, "initializing bay subdriver\n"); + IBM_HANDLE_INIT(bay); if (bay_handle) IBM_HANDLE_INIT(bay_ej); @@ -1188,6 +1232,13 @@ static int bay_init(void) bay_eject2_supported = bay2_handle && bay2_ej_handle && (strlencmp(bay2_ej_path, "_EJ0") == 0 || experimental); + vdbg_printk(TPACPI_DBG_INIT, + "bay 1: status %s, eject %s; bay 2: status %s, eject %s\n", + str_supported(bay_status_supported), + str_supported(bay_eject_supported), + str_supported(bay_status2_supported), + str_supported(bay_eject2_supported)); + return (bay_status_supported || bay_eject_supported || bay_status2_supported || bay_eject2_supported)? 0 : 1; } @@ -1255,8 +1306,13 @@ static int bay_write(char *buf) static int cmos_init(void) { + vdbg_printk(TPACPI_DBG_INIT, + "initializing cmos commands subdriver\n"); + IBM_HANDLE_INIT(cmos); + vdbg_printk(TPACPI_DBG_INIT, "cmos commands are %s\n", + str_supported(cmos_handle != NULL)); return (cmos_handle)? 0 : 1; } @@ -1320,6 +1376,8 @@ IBM_HANDLE(led, ec, "SLED", /* 570 */ static int led_init(void) { + vdbg_printk(TPACPI_DBG_INIT, "initializing LED subdriver\n"); + IBM_HANDLE_INIT(led); if (!led_handle) @@ -1335,6 +1393,9 @@ static int led_init(void) /* all others */ led_supported = TPACPI_LED_NEW; + vdbg_printk(TPACPI_DBG_INIT, "LED commands are %s, mode %d\n", + str_supported(led_supported), led_supported); + return (led_supported != TPACPI_LED_NONE)? 0 : 1; } @@ -1434,8 +1495,13 @@ IBM_HANDLE(beep, ec, "BEEP"); /* all except R30, R31 */ static int beep_init(void) { + vdbg_printk(TPACPI_DBG_INIT, "initializing beep subdriver\n"); + IBM_HANDLE_INIT(beep); + vdbg_printk(TPACPI_DBG_INIT, "beep is %s\n", + str_supported(beep_handle != NULL)); + return (beep_handle)? 0 : 1; } @@ -1486,6 +1552,8 @@ static int thermal_init(void) int i; int acpi_tmp7; + vdbg_printk(TPACPI_DBG_INIT, "initializing thermal subdriver\n"); + acpi_tmp7 = acpi_evalf(ec_handle, NULL, "TMP7", "qv"); if (ibm_thinkpad_ec_found && experimental) { @@ -1542,6 +1610,10 @@ static int thermal_init(void) thermal_read_mode = TPACPI_THERMAL_NONE; } + vdbg_printk(TPACPI_DBG_INIT, "thermal is %s, mode %d\n", + str_supported(thermal_read_mode != TPACPI_THERMAL_NONE), + thermal_read_mode); + return (thermal_read_mode != TPACPI_THERMAL_NONE)? 0 : 1; } @@ -1698,6 +1770,8 @@ static int brightness_init(void) { int b; + vdbg_printk(TPACPI_DBG_INIT, "initializing brightness subdriver\n"); + b = brightness_get(NULL); if (b < 0) return b; @@ -1708,6 +1782,7 @@ static int brightness_init(void) printk(IBM_ERR "Could not register backlight device\n"); return PTR_ERR(ibm_backlight_device); } + vdbg_printk(TPACPI_DBG_INIT, "brightness is supported\n"); ibm_backlight_device->props.max_brightness = 7; ibm_backlight_device->props.brightness = b; @@ -1719,6 +1794,8 @@ static int brightness_init(void) static void brightness_exit(void) { if (ibm_backlight_device) { + vdbg_printk(TPACPI_DBG_EXIT, + "calling backlight_device_unregister()\n"); backlight_device_unregister(ibm_backlight_device); ibm_backlight_device = NULL; } @@ -2017,6 +2094,8 @@ IBM_HANDLE(sfan, ec, "SFAN", /* 570 */ static int fan_init(void) { + vdbg_printk(TPACPI_DBG_INIT, "initializing fan subdriver\n"); + fan_status_access_mode = TPACPI_FAN_NONE; fan_control_access_mode = TPACPI_FAN_WR_NONE; fan_control_commands = 0; @@ -2095,6 +2174,11 @@ static int fan_init(void) } } + vdbg_printk(TPACPI_DBG_INIT, "fan is %s, modes %d, %d\n", + str_supported(fan_status_access_mode != TPACPI_FAN_NONE || + fan_control_access_mode != TPACPI_FAN_WR_NONE), + fan_status_access_mode, fan_control_access_mode); + return (fan_status_access_mode != TPACPI_FAN_NONE || fan_control_access_mode != TPACPI_FAN_WR_NONE)? 0 : 1; @@ -2138,6 +2222,7 @@ static int fan_get_status(u8 *status) static void fan_exit(void) { + vdbg_printk(TPACPI_DBG_EXIT, "cancelling any pending watchdogs\n"); cancel_delayed_work(&fan_watchdog_task); flush_scheduled_work(); } @@ -2622,6 +2707,15 @@ static struct ibm_struct ibms[] = { * Module and infrastructure proble, init and exit handling */ +#ifdef CONFIG_THINKPAD_ACPI_DEBUG +static const char * str_supported(int is_supported) +{ + static const char * const text_unsupported = "not supported"; + + return (is_supported)? text_unsupported + 4 : text_unsupported; +} +#endif /* CONFIG_THINKPAD_ACPI_DEBUG */ + static int __init ibm_init(struct ibm_struct *ibm) { int ret; @@ -2630,6 +2724,9 @@ static int __init ibm_init(struct ibm_struct *ibm) if (ibm->experimental && !experimental) return 0; + dbg_printk(TPACPI_DBG_INIT, + "probing for %s\n", ibm->name); + if (ibm->init) { ret = ibm->init(); if (ret > 0) @@ -2657,6 +2754,9 @@ static int __init ibm_init(struct ibm_struct *ibm) goto err_out; } + dbg_printk(TPACPI_DBG_INIT, + "%s installed\n", ibm->name); + if (ibm->read) { entry = create_proc_entry(ibm->name, S_IFREG | S_IRUGO | S_IWUSR, @@ -2678,24 +2778,35 @@ static int __init ibm_init(struct ibm_struct *ibm) return 0; err_out: + dbg_printk(TPACPI_DBG_INIT, + "%s: at error exit path with result %d\n", + ibm->name, ret); + ibm_exit(ibm); return (ret < 0)? ret : 0; } static void ibm_exit(struct ibm_struct *ibm) { + dbg_printk(TPACPI_DBG_EXIT, "removing %s\n", ibm->name); if (ibm->notify_installed) { + dbg_printk(TPACPI_DBG_EXIT, + "%s: acpi_remove_notify_handler\n", ibm->name); acpi_remove_notify_handler(*ibm->handle, ibm->type, dispatch_notify); ibm->notify_installed = 0; } if (ibm->proc_created) { + dbg_printk(TPACPI_DBG_EXIT, + "%s: remove_proc_entry\n", ibm->name); remove_proc_entry(ibm->name, proc_dir); ibm->proc_created = 0; } if (ibm->driver_registered) { + dbg_printk(TPACPI_DBG_EXIT, + "%s: acpi_bus_unregister_driver\n", ibm->name); acpi_bus_unregister_driver(ibm->driver); kfree(ibm->driver); ibm->driver = NULL; diff --git a/drivers/misc/thinkpad_acpi.h b/drivers/misc/thinkpad_acpi.h index 06d4c3839afd..beb1447a7f3f 100644 --- a/drivers/misc/thinkpad_acpi.h +++ b/drivers/misc/thinkpad_acpi.h @@ -76,12 +76,16 @@ /* Debugging */ #define TPACPI_DBG_ALL 0xffff +#define TPACPI_DBG_ALL 0xffff +#define TPACPI_DBG_INIT 0x0001 +#define TPACPI_DBG_EXIT 0x0002 #define dbg_printk(a_dbg_level, format, arg...) \ do { if (dbg_level & a_dbg_level) \ printk(IBM_DEBUG "%s: " format, __func__ , ## arg); } while (0) #ifdef CONFIG_THINKPAD_ACPI_DEBUG #define vdbg_printk(a_dbg_level, format, arg...) \ dbg_printk(a_dbg_level, format, ## arg) +static const char *str_supported(int is_supported); #else #define vdbg_printk(a_dbg_level, format, arg...) #endif -- cgit v1.2.3 From 0dcef77c5b889338811d35e786b42046259fe433 Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Sat, 21 Apr 2007 11:08:34 -0300 Subject: ACPI: thinkpad-acpi: improve thinkpad detection Improve the detection of ThinkPads, so as to reduce the chances of false positives. Since this could potentially add false negatives on the very old models, add a module parameter to force the detection of a thinkpad. Signed-off-by: Henrique de Moraes Holschuh Signed-off-by: Len Brown --- Documentation/thinkpad-acpi.txt | 7 +++++++ drivers/misc/thinkpad_acpi.c | 13 +++++++++++++ drivers/misc/thinkpad_acpi.h | 1 + 3 files changed, 21 insertions(+) (limited to 'Documentation') diff --git a/Documentation/thinkpad-acpi.txt b/Documentation/thinkpad-acpi.txt index 20d5ec309cbd..1a42b77e2ece 100644 --- a/Documentation/thinkpad-acpi.txt +++ b/Documentation/thinkpad-acpi.txt @@ -716,3 +716,10 @@ to enable more than one output class, just add their values. There is also a kernel build option to enable more debugging information, which may be necessary to debug driver problems. + +Force loading of module +----------------------- + +If thinkpad-acpi refuses to detect your ThinkPad, you can try to specify +the module parameter force_load=1. Regardless of whether this works or +not, please contact ibm-acpi-devel@lists.sourceforge.net with a report. diff --git a/drivers/misc/thinkpad_acpi.c b/drivers/misc/thinkpad_acpi.c index 56112684967b..cddf81bb2d97 100644 --- a/drivers/misc/thinkpad_acpi.c +++ b/drivers/misc/thinkpad_acpi.c @@ -2881,6 +2881,16 @@ static int __init probe_for_thinkpad(void) return -ENODEV; } + /* + * Risks a regression on very old machines, but reduces potential + * false positives a damn great deal + */ + if (!is_thinkpad) + is_thinkpad = dmi_name_in_vendors("IBM"); + + if (!is_thinkpad && !force_load) + return -ENODEV; + return 0; } @@ -2986,6 +2996,9 @@ module_param(experimental, int, 0); static u32 dbg_level; module_param_named(debug, dbg_level, uint, 0); +static int force_load; +module_param(force_load, int, 0); + #define IBM_PARAM(feature) \ module_param_call(feature, set_ibm_param, NULL, NULL, 0) diff --git a/drivers/misc/thinkpad_acpi.h b/drivers/misc/thinkpad_acpi.h index 97467b71b727..20203981cb7a 100644 --- a/drivers/misc/thinkpad_acpi.h +++ b/drivers/misc/thinkpad_acpi.h @@ -128,6 +128,7 @@ static char *next_cmd(char **cmds); /* Module */ static int experimental; static u32 dbg_level; +static int force_load; static char *ibm_thinkpad_ec_found; static char* check_dmi_for_ec(void); -- cgit v1.2.3 From 9ce883becb83190061369940de9c415595836c9b Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Tue, 24 Apr 2007 13:05:37 +0200 Subject: [PATCH] x86: Remove noreplacement option noreplacement is dangerous on modern systems because it will not replace the context switch FNSAVE with SSE aware FXSAVE. But other places in the kernel still assume SSE and do FXSAVE and the CPU will then access FXSAVE information with FNSAVE and cause corruption. Easiest way to avoid this is to remove the option. It was mostly for paranoia reasons anyways and alternative()s have been stable for some time. Thanks to Jeremy F. for reporting and helping debug it. Signed-off-by: Andi Kleen --- Documentation/x86_64/boot-options.txt | 4 ---- arch/i386/kernel/alternative.c | 21 ++------------------- 2 files changed, 2 insertions(+), 23 deletions(-) (limited to 'Documentation') diff --git a/Documentation/x86_64/boot-options.txt b/Documentation/x86_64/boot-options.txt index 625a21db0c2a..85f51e5a749f 100644 --- a/Documentation/x86_64/boot-options.txt +++ b/Documentation/x86_64/boot-options.txt @@ -293,7 +293,3 @@ Debugging stuck (default) Miscellaneous - - noreplacement Don't replace instructions with more appropriate ones - for the CPU. This may be useful on asymmetric MP systems - where some CPUs have less capabilities than others. diff --git a/arch/i386/kernel/alternative.c b/arch/i386/kernel/alternative.c index 9eca21b49f6b..426f59b0106b 100644 --- a/arch/i386/kernel/alternative.c +++ b/arch/i386/kernel/alternative.c @@ -5,15 +5,9 @@ #include #include -static int no_replacement = 0; static int smp_alt_once = 0; static int debug_alternative = 0; -static int __init noreplacement_setup(char *s) -{ - no_replacement = 1; - return 1; -} static int __init bootonly(char *str) { smp_alt_once = 1; @@ -25,7 +19,6 @@ static int __init debug_alt(char *str) return 1; } -__setup("noreplacement", noreplacement_setup); __setup("smp-alt-boot", bootonly); __setup("debug-alternative", debug_alt); @@ -252,9 +245,6 @@ void alternatives_smp_module_add(struct module *mod, char *name, struct smp_alt_module *smp; unsigned long flags; - if (no_replacement) - return; - if (smp_alt_once) { if (boot_cpu_has(X86_FEATURE_UP)) alternatives_smp_unlock(locks, locks_end, @@ -289,7 +279,7 @@ void alternatives_smp_module_del(struct module *mod) struct smp_alt_module *item; unsigned long flags; - if (no_replacement || smp_alt_once) + if (smp_alt_once) return; spin_lock_irqsave(&smp_alt, flags); @@ -320,7 +310,7 @@ void alternatives_smp_switch(int smp) return; #endif - if (no_replacement || smp_alt_once) + if (smp_alt_once) return; BUG_ON(!smp && (num_online_cpus() > 1)); @@ -386,13 +376,6 @@ extern struct paravirt_patch __start_parainstructions[], void __init alternative_instructions(void) { unsigned long flags; - if (no_replacement) { - printk(KERN_INFO "(SMP-)alternatives turned off\n"); - free_init_pages("SMP alternatives", - (unsigned long)__smp_alt_begin, - (unsigned long)__smp_alt_end); - return; - } local_irq_save(flags); apply_alternatives(__alt_instructions, __alt_instructions_end); -- cgit v1.2.3 From 0bcbc92629044b5403719f77fb015e9005b1f504 Mon Sep 17 00:00:00 2001 From: YOSHIFUJI Hideaki Date: Tue, 24 Apr 2007 14:58:30 -0700 Subject: [IPV6]: Disallow RH0 by default. A security issue is emerging. Disallow Routing Header Type 0 by default as we have been doing for IPv4. Note: We allow RH2 by default because it is harmless. Signed-off-by: YOSHIFUJI Hideaki Signed-off-by: David S. Miller --- Documentation/networking/ip-sysctl.txt | 9 ++++++++ include/linux/ipv6.h | 3 +++ include/linux/sysctl.h | 1 + net/ipv6/addrconf.c | 11 ++++++++++ net/ipv6/exthdrs.c | 40 +++++++++++++++++++++++++++++----- 5 files changed, 58 insertions(+), 6 deletions(-) (limited to 'Documentation') diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt index d3aae1f9b4c1..702d1d8dd04a 100644 --- a/Documentation/networking/ip-sysctl.txt +++ b/Documentation/networking/ip-sysctl.txt @@ -851,6 +851,15 @@ accept_redirects - BOOLEAN Functional default: enabled if local forwarding is disabled. disabled if local forwarding is enabled. +accept_source_route - INTEGER + Accept source routing (routing extension header). + + > 0: Accept routing header. + = 0: Accept only routing header type 2. + < 0: Do not accept routing header. + + Default: 0 + autoconf - BOOLEAN Autoconfigure addresses using Prefix Information in Router Advertisements. diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h index f8241130f5ea..713eb5eaa81f 100644 --- a/include/linux/ipv6.h +++ b/include/linux/ipv6.h @@ -177,6 +177,7 @@ struct ipv6_devconf { #endif #endif __s32 proxy_ndp; + __s32 accept_source_route; void *sysctl; }; @@ -205,6 +206,8 @@ enum { DEVCONF_RTR_PROBE_INTERVAL, DEVCONF_ACCEPT_RA_RT_INFO_MAX_PLEN, DEVCONF_PROXY_NDP, + __DEVCONF_OPTIMISTIC_DAD, + DEVCONF_ACCEPT_SOURCE_ROUTE, DEVCONF_MAX }; diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h index 2c5fb38d9392..9a8970bf99a6 100644 --- a/include/linux/sysctl.h +++ b/include/linux/sysctl.h @@ -580,6 +580,7 @@ enum { NET_IPV6_RTR_PROBE_INTERVAL=21, NET_IPV6_ACCEPT_RA_RT_INFO_MAX_PLEN=22, NET_IPV6_PROXY_NDP=23, + NET_IPV6_ACCEPT_SOURCE_ROUTE=25, __NET_IPV6_MAX }; diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 7552663aa125..452a82ce4796 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -172,6 +172,7 @@ struct ipv6_devconf ipv6_devconf __read_mostly = { #endif #endif .proxy_ndp = 0, + .accept_source_route = 0, /* we do not accept RH0 by default. */ }; static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = { @@ -203,6 +204,7 @@ static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = { #endif #endif .proxy_ndp = 0, + .accept_source_route = 0, /* we do not accept RH0 by default. */ }; /* IPv6 Wildcard Address and Loopback Address defined by RFC2553 */ @@ -3356,6 +3358,7 @@ static inline void ipv6_store_devconf(struct ipv6_devconf *cnf, #endif #endif array[DEVCONF_PROXY_NDP] = cnf->proxy_ndp; + array[DEVCONF_ACCEPT_SOURCE_ROUTE] = cnf->accept_source_route; } static inline size_t inet6_if_nlmsg_size(void) @@ -3883,6 +3886,14 @@ static struct addrconf_sysctl_table .mode = 0644, .proc_handler = &proc_dointvec, }, + { + .ctl_name = NET_IPV6_ACCEPT_SOURCE_ROUTE, + .procname = "accept_source_route", + .data = &ipv6_devconf.accept_source_route, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = &proc_dointvec, + }, { .ctl_name = 0, /* sentinel */ } diff --git a/net/ipv6/exthdrs.c b/net/ipv6/exthdrs.c index 28e0c6568272..6ed6a8cd6a68 100644 --- a/net/ipv6/exthdrs.c +++ b/net/ipv6/exthdrs.c @@ -362,10 +362,27 @@ static int ipv6_rthdr_rcv(struct sk_buff **skbp) struct inet6_skb_parm *opt = IP6CB(skb); struct in6_addr *addr = NULL; struct in6_addr daddr; + struct inet6_dev *idev; int n, i; - struct ipv6_rt_hdr *hdr; struct rt0_hdr *rthdr; + int accept_source_route = ipv6_devconf.accept_source_route; + + if (accept_source_route < 0 || + ((idev = in6_dev_get(skb->dev)) == NULL)) { + kfree_skb(skb); + return -1; + } + if (idev->cnf.accept_source_route < 0) { + in6_dev_put(idev); + kfree_skb(skb); + return -1; + } + + if (accept_source_route > idev->cnf.accept_source_route) + accept_source_route = idev->cnf.accept_source_route; + + in6_dev_put(idev); if (!pskb_may_pull(skb, (skb->h.raw-skb->data)+8) || !pskb_may_pull(skb, (skb->h.raw-skb->data)+((skb->h.raw[1]+1)<<3))) { @@ -377,6 +394,22 @@ static int ipv6_rthdr_rcv(struct sk_buff **skbp) hdr = (struct ipv6_rt_hdr *) skb->h.raw; + switch (hdr->type) { +#ifdef CONFIG_IPV6_MIP6 + break; +#endif + case IPV6_SRCRT_TYPE_0: + if (accept_source_route <= 0) + break; + kfree_skb(skb); + return -1; + default: + IP6_INC_STATS_BH(ip6_dst_idev(skb->dst), + IPSTATS_MIB_INHDRERRORS); + icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, (&hdr->type) - skb->nh.raw); + return -1; + } + if (ipv6_addr_is_multicast(&skb->nh.ipv6h->daddr) || skb->pkt_type != PACKET_HOST) { IP6_INC_STATS_BH(ip6_dst_idev(skb->dst), @@ -434,11 +467,6 @@ looped_back: } break; #endif - default: - IP6_INC_STATS_BH(ip6_dst_idev(skb->dst), - IPSTATS_MIB_INHDRERRORS); - icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, (&hdr->type) - skb->nh.raw); - return -1; } /* -- cgit v1.2.3 From bd8031b49a9b05933fb1ec1c36620ed4e1e67793 Mon Sep 17 00:00:00 2001 From: Hal Rosenstock Date: Tue, 24 Apr 2007 21:30:38 -0700 Subject: IB/umad: Clarify documentation of transaction ID Signed-off-by: Hal Rosenstock Signed-off-by: Roland Dreier --- Documentation/infiniband/user_mad.txt | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'Documentation') diff --git a/Documentation/infiniband/user_mad.txt b/Documentation/infiniband/user_mad.txt index 750fe5e80ebc..8ec54b974b67 100644 --- a/Documentation/infiniband/user_mad.txt +++ b/Documentation/infiniband/user_mad.txt @@ -91,6 +91,14 @@ Sending MADs if (ret != sizeof *mad + mad_length) perror("write"); +Transaction IDs + + Users of the umad devices can use the lower 32 bits of the + transaction ID field (that is, the least significant half of the + field in network byte order) in MADs being sent to match + request/response pairs. The upper 32 bits are reserved for use by + the kernel and will be overwritten before a MAD is sent. + Setting IsSM Capability Bit To set the IsSM capability bit for a port, simply open the -- cgit v1.2.3 From f989106cac719f8fe91da7734e73b3ca09146ecc Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Tue, 24 Apr 2007 13:53:22 +0800 Subject: ACPI: Improve acpi debug documentation Now we use acpi.debug_level and acpi.debug_layer as kernel boot parameters instead of acpi_dbg_level and acpi_dbg_layer. Thanks to Andi Kleen for pointing it out. Signed-off-by: Zhang Rui Signed-off-by: Len Brown --- Documentation/kernel-parameters.txt | 32 +++++++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-) (limited to 'Documentation') diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 12533a958c51..e3394eb42cb7 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -181,19 +181,41 @@ and is between 256 and 4096 characters. It is defined in the file that require a timer override, but don't have HPET - acpi_dbg_layer= [HW,ACPI] + acpi.debug_layer= [HW,ACPI] Format: Each bit of the indicates an ACPI debug layer, 1: enable, 0: disable. It is useful for boot time debugging. After system has booted up, it can be set - via /proc/acpi/debug_layer. - - acpi_dbg_level= [HW,ACPI] + via /sys/module/acpi/parameters/debug_layer. + CONFIG_ACPI_DEBUG must be enabled for this to produce any output. + Available bits (add the numbers together) to enable debug output + for specific parts of the ACPI subsystem: + 0x01 utilities 0x02 hardware 0x04 events 0x08 tables + 0x10 namespace 0x20 parser 0x40 dispatcher + 0x80 executer 0x100 resources 0x200 acpica debugger + 0x400 os services 0x800 acpica disassembler. + The number can be in decimal or prefixed with 0x in hex. + Warning: Many of these options can produce a lot of + output and make your system unusable. Be very careful. + + acpi.debug_level= [HW,ACPI] Format: Each bit of the indicates an ACPI debug level, 1: enable, 0: disable. It is useful for boot time debugging. After system has booted up, it can be set - via /proc/acpi/debug_level. + via /sys/module/acpi/parameters/debug_level. + CONFIG_ACPI_DEBUG must be enabled for this to produce any output. + Available bits (add the numbers together) to enable different + debug output levels of the ACPI subsystem: + 0x01 error 0x02 warn 0x04 init 0x08 debug object + 0x10 info 0x20 init names 0x40 parse 0x80 load + 0x100 dispatch 0x200 execute 0x400 names 0x800 operation region + 0x1000 bfield 0x2000 tables 0x4000 values 0x8000 objects + 0x10000 resources 0x20000 user requests 0x40000 package. + The number can be in decimal or prefixed with 0x in hex. + Warning: Many of these options can produce a lot of + output and make your system unusable. Be very careful. + acpi_fake_ecdt [HW,ACPI] Workaround failure due to BIOS lacking ECDT -- cgit v1.2.3 From 54ae15014c306b3d7ad32c996fea9a5ac8560b60 Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Tue, 24 Apr 2007 11:48:12 -0300 Subject: ACPI: thinkpad-acpi: register with the device model Register thinkpad-acpi platform driver and platform device for the device model. Also register the platform device with the hwmon class. Signed-off-by: Henrique de Moraes Holschuh Signed-off-by: Len Brown --- Documentation/thinkpad-acpi.txt | 40 +++++++++++++++++++++++++----- drivers/misc/Kconfig | 1 + drivers/misc/thinkpad_acpi.c | 54 +++++++++++++++++++++++++++++++++++++++++ drivers/misc/thinkpad_acpi.h | 8 ++++++ 4 files changed, 97 insertions(+), 6 deletions(-) (limited to 'Documentation') diff --git a/Documentation/thinkpad-acpi.txt b/Documentation/thinkpad-acpi.txt index 1a42b77e2ece..0e4e053cface 100644 --- a/Documentation/thinkpad-acpi.txt +++ b/Documentation/thinkpad-acpi.txt @@ -1,7 +1,7 @@ ThinkPad ACPI Extras Driver Version 0.14 - March 26th, 2007 + April 21st, 2007 Borislav Deianov Henrique de Moraes Holschuh @@ -67,11 +67,39 @@ thinkpad-specific bay functionality. Features -------- -The driver creates the /proc/acpi/ibm directory. There is a file under -that directory for each feature described below. Note that while the -driver is still in the alpha stage, the exact proc file format and -commands supported by the various features is guaranteed to change -frequently. +The driver exports two different interfaces to userspace, which can be +used to access the features it provides. One is a legacy procfs-based +interface, which will be removed at some time in the distant future. +The other is a new sysfs-based interface which is not complete yet. + +The procfs interface creates the /proc/acpi/ibm directory. There is a +file under that directory for each feature it supports. The procfs +interface is mostly frozen, and will change very little if at all: it +will not be extended to add any new functionality in the driver, instead +all new functionality will be implemented on the sysfs interface. + +The sysfs interface tries to blend in the generic Linux sysfs subsystems +and classes as much as possible. Since some of these subsystems are not +yet ready or stabilized, it is expected that this interface will change, +and any and all userspace programs must deal with it. + + +Notes about the sysfs interface: + +Unlike what was done with the procfs interface, correctness when talking +to the sysfs interfaces will be enforced, as will correctness in the +thinkpad-acpi's implementation of sysfs interfaces. + +Also, any bugs in the thinkpad-acpi sysfs driver code or in the +thinkpad-acpi's implementation of the sysfs interfaces will be fixed for +maximum correctness, even if that means changing an interface in +non-compatible ways. As these interfaces mature both in the kernel and +in thinkpad-acpi, such changes should become quite rare. + +Applications interfacing to the thinkpad-acpi sysfs interfaces must +follow all sysfs guidelines and correctly process all errors (the sysfs +interface makes extensive use of errors). File descriptors and open / +close operations to the sysfs inodes must also be properly implemented. Driver version -- /proc/acpi/ibm/driver --------------------------------------- diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 44e4c8fb7a74..445c4b10c41e 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -126,6 +126,7 @@ config THINKPAD_ACPI tristate "ThinkPad ACPI Laptop Extras" depends on X86 && ACPI select BACKLIGHT_CLASS_DEVICE + select HWMON ---help--- This is a driver for the IBM and Lenovo ThinkPad laptops. It adds support for Fn-Fx key combinations, Bluetooth control, video diff --git a/drivers/misc/thinkpad_acpi.c b/drivers/misc/thinkpad_acpi.c index 9b4eea4c8ff7..e47eaf72763d 100644 --- a/drivers/misc/thinkpad_acpi.c +++ b/drivers/misc/thinkpad_acpi.c @@ -474,6 +474,25 @@ static char *next_cmd(char **cmds) } +/**************************************************************************** + **************************************************************************** + * + * Device model: hwmon and platform + * + **************************************************************************** + ****************************************************************************/ + +static struct platform_device *tpacpi_pdev = NULL; +static struct class_device *tpacpi_hwmon = NULL; + +static struct platform_driver tpacpi_pdriver = { + .driver = { + .name = IBM_DRVR_NAME, + .owner = THIS_MODULE, + }, +}; + + /**************************************************************************** **************************************************************************** * @@ -3225,10 +3244,12 @@ static int __init thinkpad_acpi_module_init(void) { int ret, i; + /* Driver-level probe */ ret = probe_for_thinkpad(); if (ret) return ret; + /* Driver initialization */ ibm_thinkpad_ec_found = check_dmi_for_ec(); IBM_ACPIHANDLE_INIT(ecrd); IBM_ACPIHANDLE_INIT(ecwr); @@ -3241,6 +3262,31 @@ static int __init thinkpad_acpi_module_init(void) } proc_dir->owner = THIS_MODULE; + ret = platform_driver_register(&tpacpi_pdriver); + if (ret) { + printk(IBM_ERR "unable to register platform driver\n"); + thinkpad_acpi_module_exit(); + return ret; + } + + /* Device initialization */ + tpacpi_pdev = platform_device_register_simple(IBM_DRVR_NAME, -1, + NULL, 0); + if (IS_ERR(tpacpi_pdev)) { + ret = PTR_ERR(tpacpi_pdev); + tpacpi_pdev = NULL; + printk(IBM_ERR "unable to register platform device\n"); + thinkpad_acpi_module_exit(); + return ret; + } + tpacpi_hwmon = hwmon_device_register(&tpacpi_pdev->dev); + if (IS_ERR(tpacpi_hwmon)) { + ret = PTR_ERR(tpacpi_hwmon); + tpacpi_hwmon = NULL; + printk(IBM_ERR "unable to register hwmon device\n"); + thinkpad_acpi_module_exit(); + return ret; + } for (i = 0; i < ARRAY_SIZE(ibms_init); i++) { ret = ibm_init(&ibms_init[i]); if (ret >= 0 && *ibms_init[i].param) @@ -3266,6 +3312,14 @@ static void thinkpad_acpi_module_exit(void) dbg_printk(TPACPI_DBG_INIT, "finished subdriver exit path...\n"); + if (tpacpi_hwmon) + hwmon_device_unregister(tpacpi_hwmon); + + if (tpacpi_pdev) + platform_device_unregister(tpacpi_pdev); + + platform_driver_unregister(&tpacpi_pdriver); + if (proc_dir) remove_proc_entry(IBM_PROC_DIR, acpi_root_dir); diff --git a/drivers/misc/thinkpad_acpi.h b/drivers/misc/thinkpad_acpi.h index 6432b28339af..fea580999e94 100644 --- a/drivers/misc/thinkpad_acpi.h +++ b/drivers/misc/thinkpad_acpi.h @@ -34,6 +34,8 @@ #include #include #include +#include +#include #include #include @@ -56,6 +58,7 @@ #define IBM_PROC_DIR "ibm" #define IBM_ACPI_EVENT_PREFIX "ibm" +#define IBM_DRVR_NAME IBM_FILE #define IBM_LOG IBM_FILE ": " #define IBM_ERR KERN_ERR IBM_LOG @@ -130,6 +133,11 @@ static int dispatch_procfs_write(struct file *file, unsigned long count, void *data); static char *next_cmd(char **cmds); +/* Device model */ +static struct platform_device *tpacpi_pdev; +static struct class_device *tpacpi_hwmon; +static struct platform_driver tpacpi_pdriver; + /* Module */ static int experimental; static u32 dbg_level; -- cgit v1.2.3 From 176750d68801bfa4a88d1cf54174aa0347d7e5d8 Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Tue, 24 Apr 2007 11:48:13 -0300 Subject: ACPI: thinkpad-acpi: driver sysfs conversion Add the sysfs attributes for the platform driver. Signed-off-by: Henrique de Moraes Holschuh Signed-off-by: Len Brown --- Documentation/thinkpad-acpi.txt | 42 ++++++++++++++++++- drivers/misc/thinkpad_acpi.c | 90 +++++++++++++++++++++++++++++++++++++++++ drivers/misc/thinkpad_acpi.h | 3 ++ 3 files changed, 133 insertions(+), 2 deletions(-) (limited to 'Documentation') diff --git a/Documentation/thinkpad-acpi.txt b/Documentation/thinkpad-acpi.txt index 0e4e053cface..cc079afaf66b 100644 --- a/Documentation/thinkpad-acpi.txt +++ b/Documentation/thinkpad-acpi.txt @@ -101,11 +101,39 @@ follow all sysfs guidelines and correctly process all errors (the sysfs interface makes extensive use of errors). File descriptors and open / close operations to the sysfs inodes must also be properly implemented. -Driver version -- /proc/acpi/ibm/driver ---------------------------------------- +The version of thinkpad-acpi's sysfs interface is exported by the driver +as a driver attribute (see below). + +Sysfs driver attributes are on the driver's sysfs attribute space, +for 2.6.20 this is /sys/bus/platform/drivers/thinkpad-acpi/. + +Sysfs device attributes are on the driver's sysfs attribute space, +for 2.6.20 this is /sys/devices/platform/thinkpad-acpi/. + +Driver version +-------------- + +procfs: /proc/acpi/ibm/driver +sysfs driver attribute: version The driver name and version. No commands can be written to this file. +Sysfs interface version +----------------------- + +sysfs driver attribute: interface_version + +Version of the thinkpad-acpi sysfs interface, as an unsigned long +(output in hex format: 0xAAAABBCC), where: + AAAA - major revision + BB - minor revision + CC - bugfix revision + +The sysfs interface version changelog for the driver can be found at the +end of this document. Changes to the sysfs interface done by the kernel +subsystems are not documented here, nor are they tracked by this +attribute. + Hot keys -- /proc/acpi/ibm/hotkey --------------------------------- @@ -745,9 +773,19 @@ to enable more than one output class, just add their values. There is also a kernel build option to enable more debugging information, which may be necessary to debug driver problems. +The level of debugging information output by the driver can be changed +at runtime through sysfs, using the driver attribute debug_level. The +attribute takes the same bitmask as the debug module parameter above. + Force loading of module ----------------------- If thinkpad-acpi refuses to detect your ThinkPad, you can try to specify the module parameter force_load=1. Regardless of whether this works or not, please contact ibm-acpi-devel@lists.sourceforge.net with a report. + + +Sysfs interface changelog: + +0x000100: Initial sysfs support, as a single platform driver and + device. diff --git a/drivers/misc/thinkpad_acpi.c b/drivers/misc/thinkpad_acpi.c index e47eaf72763d..a31d00d570cb 100644 --- a/drivers/misc/thinkpad_acpi.c +++ b/drivers/misc/thinkpad_acpi.c @@ -22,6 +22,7 @@ */ #define IBM_VERSION "0.14" +#define TPACPI_SYSFS_VERSION 0x000100 /* * Changelog: @@ -493,6 +494,87 @@ static struct platform_driver tpacpi_pdriver = { }; +/************************************************************************* + * thinkpad-acpi driver attributes + */ + +/* interface_version --------------------------------------------------- */ +static ssize_t tpacpi_driver_interface_version_show( + struct device_driver *drv, + char *buf) +{ + return snprintf(buf, PAGE_SIZE, "0x%08x\n", TPACPI_SYSFS_VERSION); +} + +static DRIVER_ATTR(interface_version, S_IRUGO, + tpacpi_driver_interface_version_show, NULL); + +/* debug_level --------------------------------------------------------- */ +static ssize_t tpacpi_driver_debug_show(struct device_driver *drv, + char *buf) +{ + return snprintf(buf, PAGE_SIZE, "0x%04x\n", dbg_level); +} + +static ssize_t tpacpi_driver_debug_store(struct device_driver *drv, + const char *buf, size_t count) +{ + unsigned long t; + char *endp; + + t = simple_strtoul(buf, &endp, 0); + while (*endp && isspace(*endp)) + endp++; + if (*endp) + return -EINVAL; + + dbg_level = t; + + return count; +} + +static DRIVER_ATTR(debug_level, S_IWUSR | S_IRUGO, + tpacpi_driver_debug_show, tpacpi_driver_debug_store); + +/* version ------------------------------------------------------------- */ +static ssize_t tpacpi_driver_version_show(struct device_driver *drv, + char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%s v%s\n", IBM_DESC, IBM_VERSION); +} + +static DRIVER_ATTR(version, S_IRUGO, + tpacpi_driver_version_show, NULL); + +/* --------------------------------------------------------------------- */ + +static struct driver_attribute* tpacpi_driver_attributes[] = { + &driver_attr_debug_level, &driver_attr_version, + &driver_attr_interface_version, +}; + +static int __init tpacpi_create_driver_attributes(struct device_driver *drv) +{ + int i, res; + + i = 0; + res = 0; + while (!res && i < ARRAY_SIZE(tpacpi_driver_attributes)) { + res = driver_create_file(drv, tpacpi_driver_attributes[i]); + i++; + } + + return res; +} + +static void tpacpi_remove_driver_attributes(struct device_driver *drv) +{ + int i; + + for(i = 0; i < ARRAY_SIZE(tpacpi_driver_attributes); i++) + driver_remove_file(drv, tpacpi_driver_attributes[i]); +} + /**************************************************************************** **************************************************************************** * @@ -3268,6 +3350,13 @@ static int __init thinkpad_acpi_module_init(void) thinkpad_acpi_module_exit(); return ret; } + ret = tpacpi_create_driver_attributes(&tpacpi_pdriver.driver); + if (ret) { + printk(IBM_ERR "unable to create sysfs driver attributes\n"); + thinkpad_acpi_module_exit(); + return ret; + } + /* Device initialization */ tpacpi_pdev = platform_device_register_simple(IBM_DRVR_NAME, -1, @@ -3318,6 +3407,7 @@ static void thinkpad_acpi_module_exit(void) if (tpacpi_pdev) platform_device_unregister(tpacpi_pdev); + tpacpi_remove_driver_attributes(&tpacpi_pdriver.driver); platform_driver_unregister(&tpacpi_pdriver); if (proc_dir) diff --git a/drivers/misc/thinkpad_acpi.h b/drivers/misc/thinkpad_acpi.h index fea580999e94..37860582956f 100644 --- a/drivers/misc/thinkpad_acpi.h +++ b/drivers/misc/thinkpad_acpi.h @@ -32,6 +32,7 @@ #include #include +#include #include #include #include @@ -137,6 +138,8 @@ static char *next_cmd(char **cmds); static struct platform_device *tpacpi_pdev; static struct class_device *tpacpi_hwmon; static struct platform_driver tpacpi_pdriver; +static int tpacpi_create_driver_attributes(struct device_driver *drv); +static void tpacpi_remove_driver_attributes(struct device_driver *drv); /* Module */ static int experimental; -- cgit v1.2.3 From 2c37aa4e22dd55070c608290c5031f2ee93e69ce Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Tue, 24 Apr 2007 11:48:16 -0300 Subject: ACPI: thinkpad-acpi: add sysfs support to the thermal subdriver Export thinkpad thermal sensors to sysfs, following the hwmon specification for thermal monitoring sensors. ThinkPad thermal monitoring is done by the EC. Sensors can show up or disappear at runtime when they are inside hotswappable hardware, such as batteries. Sensors that are not available return -ENXIO when accessed. Up to 16 thermal sensors are supported on new firmware (but nobody has reported a ThinkPad with more than 12 sensors so far), and 8 sensors are supported on older firmware. Thermal sensor mapping is model-specific. Precision varies, it is 1 degree Celcius on new ThinkPads, but higher on some older models. Signed-off-by: Henrique de Moraes Holschuh Signed-off-by: Len Brown --- Documentation/thinkpad-acpi.txt | 26 +++++++-- drivers/misc/thinkpad_acpi.c | 122 +++++++++++++++++++++++++++++++++++++++- drivers/misc/thinkpad_acpi.h | 2 + 3 files changed, 143 insertions(+), 7 deletions(-) (limited to 'Documentation') diff --git a/Documentation/thinkpad-acpi.txt b/Documentation/thinkpad-acpi.txt index cc079afaf66b..80c0bf28e392 100644 --- a/Documentation/thinkpad-acpi.txt +++ b/Documentation/thinkpad-acpi.txt @@ -458,17 +458,17 @@ X40: 16 - one medium-pitched beep repeating constantly, stop with 17 17 - stop 16 -Temperature sensors -- /proc/acpi/ibm/thermal ---------------------------------------------- +Temperature sensors +------------------- + +procfs: /proc/acpi/ibm/thermal +sysfs device attributes: (hwmon) temp*_input Most ThinkPads include six or more separate temperature sensors but only expose the CPU temperature through the standard ACPI methods. This feature shows readings from up to eight different sensors on older ThinkPads, and it has experimental support for up to sixteen different -sensors on newer ThinkPads. Readings from sensors that are not available -return -128. - -No commands can be written to this file. +sensors on newer ThinkPads. EXPERIMENTAL: The 16-sensors feature is marked EXPERIMENTAL because the implementation directly accesses hardware registers and may not work as @@ -525,6 +525,20 @@ The A31 has a very atypical layout for the thermal sensors 8: Bay Battery: secondary sensor +Procfs notes: + Readings from sensors that are not available return -128. + No commands can be written to this file. + +Sysfs notes: + Sensors that are not available return the ENXIO error. This + status may change at runtime, as there are hotplug thermal + sensors, like those inside the batteries and docks. + + thinkpad-acpi thermal sensors are reported through the hwmon + subsystem, and follow all of the hwmon guidelines at + Documentation/hwmon. + + EXPERIMENTAL: Embedded controller register dump -- /proc/acpi/ibm/ecdump ------------------------------------------------------------------------ diff --git a/drivers/misc/thinkpad_acpi.c b/drivers/misc/thinkpad_acpi.c index aa69ff0c1c91..d5526e882ddd 100644 --- a/drivers/misc/thinkpad_acpi.c +++ b/drivers/misc/thinkpad_acpi.c @@ -1992,11 +1992,91 @@ static struct ibm_struct beep_driver_data = { static enum thermal_access_mode thermal_read_mode; +/* sysfs temp##_input -------------------------------------------------- */ + +static ssize_t thermal_temp_input_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct sensor_device_attribute *sensor_attr = + to_sensor_dev_attr(attr); + int idx = sensor_attr->index; + s32 value; + int res; + + res = thermal_get_sensor(idx, &value); + if (res) + return res; + if (value == TP_EC_THERMAL_TMP_NA * 1000) + return -ENXIO; + + return snprintf(buf, PAGE_SIZE, "%d\n", value); +} + +#define THERMAL_SENSOR_ATTR_TEMP(_idxA, _idxB) \ + SENSOR_ATTR(temp##_idxA##_input, S_IRUGO, thermal_temp_input_show, NULL, _idxB) + +static struct sensor_device_attribute sensor_dev_attr_thermal_temp_input[] = { + THERMAL_SENSOR_ATTR_TEMP(1, 0), + THERMAL_SENSOR_ATTR_TEMP(2, 1), + THERMAL_SENSOR_ATTR_TEMP(3, 2), + THERMAL_SENSOR_ATTR_TEMP(4, 3), + THERMAL_SENSOR_ATTR_TEMP(5, 4), + THERMAL_SENSOR_ATTR_TEMP(6, 5), + THERMAL_SENSOR_ATTR_TEMP(7, 6), + THERMAL_SENSOR_ATTR_TEMP(8, 7), + THERMAL_SENSOR_ATTR_TEMP(9, 8), + THERMAL_SENSOR_ATTR_TEMP(10, 9), + THERMAL_SENSOR_ATTR_TEMP(11, 10), + THERMAL_SENSOR_ATTR_TEMP(12, 11), + THERMAL_SENSOR_ATTR_TEMP(13, 12), + THERMAL_SENSOR_ATTR_TEMP(14, 13), + THERMAL_SENSOR_ATTR_TEMP(15, 14), + THERMAL_SENSOR_ATTR_TEMP(16, 15), +}; + +#define THERMAL_ATTRS(X) \ + &sensor_dev_attr_thermal_temp_input[X].dev_attr.attr + +static struct attribute *thermal_temp_input_attr[] = { + THERMAL_ATTRS(8), + THERMAL_ATTRS(9), + THERMAL_ATTRS(10), + THERMAL_ATTRS(11), + THERMAL_ATTRS(12), + THERMAL_ATTRS(13), + THERMAL_ATTRS(14), + THERMAL_ATTRS(15), + THERMAL_ATTRS(0), + THERMAL_ATTRS(1), + THERMAL_ATTRS(2), + THERMAL_ATTRS(3), + THERMAL_ATTRS(4), + THERMAL_ATTRS(5), + THERMAL_ATTRS(6), + THERMAL_ATTRS(7), + NULL +}; + +static const struct attribute_group thermal_temp_input16_group = { + .attrs = thermal_temp_input_attr +}; + +static const struct attribute_group thermal_temp_input8_group = { + .attrs = &thermal_temp_input_attr[8] +}; + +#undef THERMAL_SENSOR_ATTR_TEMP +#undef THERMAL_ATTRS + +/* --------------------------------------------------------------------- */ + static int __init thermal_init(struct ibm_init_struct *iibm) { u8 t, ta1, ta2; int i; int acpi_tmp7; + int res; vdbg_printk(TPACPI_DBG_INIT, "initializing thermal subdriver\n"); @@ -2060,7 +2140,46 @@ static int __init thermal_init(struct ibm_init_struct *iibm) str_supported(thermal_read_mode != TPACPI_THERMAL_NONE), thermal_read_mode); - return (thermal_read_mode != TPACPI_THERMAL_NONE)? 0 : 1; + switch(thermal_read_mode) { + case TPACPI_THERMAL_TPEC_16: + res = sysfs_create_group(&tpacpi_pdev->dev.kobj, + &thermal_temp_input16_group); + if (res) + return res; + break; + case TPACPI_THERMAL_TPEC_8: + case TPACPI_THERMAL_ACPI_TMP07: + case TPACPI_THERMAL_ACPI_UPDT: + res = sysfs_create_group(&tpacpi_pdev->dev.kobj, + &thermal_temp_input8_group); + if (res) + return res; + break; + case TPACPI_THERMAL_NONE: + default: + return 1; + } + + return 0; +} + +static void thermal_exit(void) +{ + switch(thermal_read_mode) { + case TPACPI_THERMAL_TPEC_16: + sysfs_remove_group(&tpacpi_pdev->dev.kobj, + &thermal_temp_input16_group); + break; + case TPACPI_THERMAL_TPEC_8: + case TPACPI_THERMAL_ACPI_TMP07: + case TPACPI_THERMAL_ACPI_UPDT: + sysfs_remove_group(&tpacpi_pdev->dev.kobj, + &thermal_temp_input16_group); + break; + case TPACPI_THERMAL_NONE: + default: + break; + } } /* idx is zero-based */ @@ -2168,6 +2287,7 @@ static int thermal_read(char *p) static struct ibm_struct thermal_driver_data = { .name = "thermal", .read = thermal_read, + .exit = thermal_exit, }; /************************************************************************* diff --git a/drivers/misc/thinkpad_acpi.h b/drivers/misc/thinkpad_acpi.h index a9feb53c6d3c..e833ff3caf39 100644 --- a/drivers/misc/thinkpad_acpi.h +++ b/drivers/misc/thinkpad_acpi.h @@ -38,6 +38,7 @@ #include #include #include +#include #include #include @@ -467,6 +468,7 @@ enum thermal_access_mode { enum { /* TPACPI_THERMAL_TPEC_* */ TP_EC_THERMAL_TMP0 = 0x78, /* ACPI EC regs TMP 0..7 */ TP_EC_THERMAL_TMP8 = 0xC0, /* ACPI EC regs TMP 8..15 */ + TP_EC_THERMAL_TMP_NA = -128, /* ACPI EC sensor not available */ }; #define TPACPI_MAX_THERMAL_SENSORS 16 /* Max thermal sensors supported */ -- cgit v1.2.3 From fe98a52ce7540fb3a19d57488a08864110cf4d5c Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Tue, 24 Apr 2007 11:48:17 -0300 Subject: ACPI: thinkpad-acpi: add sysfs support to fan subdriver Export sysfs attributes to monitor and control the internal thinkpad fan (some thinkpads have more than one fan, but thinkpad-acpi doesn't support the second fan yet). The sysfs interface follows the hwmon design guide for fan devices. Also, fix some stray "thermal" files in the fan procfs description that have been there forever, and officially support "full-speed" as the name for the PWM-disabled state of the fan controller to keep it in line with the hwmon interface. It is much better a name for that mode than the unobvious "disengaged" anyway. Change the procfs interface to also accept full-speed as a fan level, but still report it as disengaged for backwards compatibility. Signed-off-by: Henrique de Moraes Holschuh Signed-off-by: Len Brown --- Documentation/thinkpad-acpi.txt | 157 ++++++++++++------- drivers/misc/thinkpad_acpi.c | 326 +++++++++++++++++++++++++++++++++++++--- drivers/misc/thinkpad_acpi.h | 6 + 3 files changed, 415 insertions(+), 74 deletions(-) (limited to 'Documentation') diff --git a/Documentation/thinkpad-acpi.txt b/Documentation/thinkpad-acpi.txt index 80c0bf28e392..339ce21e59df 100644 --- a/Documentation/thinkpad-acpi.txt +++ b/Documentation/thinkpad-acpi.txt @@ -642,8 +642,11 @@ distinct. The unmute the volume after the mute command, use either the up or down command (the level command will not unmute the volume). The current volume level and mute state is shown in the file. -EXPERIMENTAL: fan speed, fan enable/disable -- /proc/acpi/ibm/fan ------------------------------------------------------------------ +EXPERIMENTAL: fan speed, fan enable/disable +------------------------------------------- + +procfs: /proc/acpi/ibm/fan +sysfs device attributes: (hwmon) fan_input, pwm1, pwm1_enable This feature is marked EXPERIMENTAL because the implementation directly accesses hardware registers and may not work as expected. USE @@ -656,27 +659,26 @@ from the hardware registers of the embedded controller. This is known to work on later R, T and X series ThinkPads but may show a bogus value on other models. -Most ThinkPad fans work in "levels". Level 0 stops the fan. The higher -the level, the higher the fan speed, although adjacent levels often map -to the same fan speed. 7 is the highest level, where the fan reaches -the maximum recommended speed. Level "auto" means the EC changes the -fan level according to some internal algorithm, usually based on -readings from the thermal sensors. Level "disengaged" means the EC -disables the speed-locked closed-loop fan control, and drives the fan as -fast as it can go, which might exceed hardware limits, so use this level -with caution. +Fan levels: -The fan usually ramps up or down slowly from one speed to another, -and it is normal for the EC to take several seconds to react to fan -commands. +Most ThinkPad fans work in "levels" at the firmware interface. Level 0 +stops the fan. The higher the level, the higher the fan speed, although +adjacent levels often map to the same fan speed. 7 is the highest +level, where the fan reaches the maximum recommended speed. -The fan may be enabled or disabled with the following commands: +Level "auto" means the EC changes the fan level according to some +internal algorithm, usually based on readings from the thermal sensors. - echo enable >/proc/acpi/ibm/fan - echo disable >/proc/acpi/ibm/fan +There is also a "full-speed" level, also known as "disengaged" level. +In this level, the EC disables the speed-locked closed-loop fan control, +and drives the fan as fast as it can go, which might exceed hardware +limits, so use this level with caution. -Placing a fan on level 0 is the same as disabling it. Enabling a fan -will try to place it in a safe level if it is too slow or disabled. +The fan usually ramps up or down slowly from one speed to another, and +it is normal for the EC to take several seconds to react to fan +commands. The full-speed level may take up to two minutes to ramp up to +maximum speed, and in some ThinkPads, the tachometer readings go stale +while the EC is transitioning to the full-speed level. WARNING WARNING WARNING: do not leave the fan disabled unless you are monitoring all of the temperature sensor readings and you are ready to @@ -694,48 +696,101 @@ fan is turned off when the CPU temperature drops to 49 degrees and the HDD temperature drops to 41 degrees. These thresholds cannot currently be controlled. +The ThinkPad's ACPI DSDT code will reprogram the fan on its own when +certain conditions are met. It will override any fan programming done +through thinkpad-acpi. + +The thinkpad-acpi kernel driver can be programmed to revert the fan +level to a safe setting if userspace does not issue one of the procfs +fan commands: "enable", "disable", "level" or "watchdog", or if there +are no writes to pwm1_enable (or to pwm1 *if and only if* pwm1_enable is +set to 1, manual mode) within a configurable amount of time of up to +120 seconds. This functionality is called fan safety watchdog. + +Note that the watchdog timer stops after it enables the fan. It will be +rearmed again automatically (using the same interval) when one of the +above mentioned fan commands is received. The fan watchdog is, +therefore, not suitable to protect against fan mode changes made through +means other than the "enable", "disable", and "level" procfs fan +commands, or the hwmon fan control sysfs interface. + +Procfs notes: + +The fan may be enabled or disabled with the following commands: + + echo enable >/proc/acpi/ibm/fan + echo disable >/proc/acpi/ibm/fan + +Placing a fan on level 0 is the same as disabling it. Enabling a fan +will try to place it in a safe level if it is too slow or disabled. + The fan level can be controlled with the command: - echo 'level ' > /proc/acpi/ibm/thermal + echo 'level ' > /proc/acpi/ibm/fan -Where is an integer from 0 to 7, or one of the words "auto" -or "disengaged" (without the quotes). Not all ThinkPads support the -"auto" and "disengaged" levels. +Where is an integer from 0 to 7, or one of the words "auto" or +"full-speed" (without the quotes). Not all ThinkPads support the "auto" +and "full-speed" levels. The driver accepts "disengaged" as an alias for +"full-speed", and reports it as "disengaged" for backwards +compatibility. On the X31 and X40 (and ONLY on those models), the fan speed can be -controlled to a certain degree. Once the fan is running, it can be +controlled to a certain degree. Once the fan is running, it can be forced to run faster or slower with the following command: - echo 'speed ' > /proc/acpi/ibm/thermal + echo 'speed ' > /proc/acpi/ibm/fan -The sustainable range of fan speeds on the X40 appears to be from -about 3700 to about 7350. Values outside this range either do not have -any effect or the fan speed eventually settles somewhere in that -range. The fan cannot be stopped or started with this command. +The sustainable range of fan speeds on the X40 appears to be from about +3700 to about 7350. Values outside this range either do not have any +effect or the fan speed eventually settles somewhere in that range. The +fan cannot be stopped or started with this command. This functionality +is incomplete, and not available through the sysfs interface. -The ThinkPad's ACPI DSDT code will reprogram the fan on its own when -certain conditions are met. It will override any fan programming done -through thinkpad-acpi. +To program the safety watchdog, use the "watchdog" command. + + echo 'watchdog ' > /proc/acpi/ibm/fan + +If you want to disable the watchdog, use 0 as the interval. + +Sysfs notes: + +The sysfs interface follows the hwmon subsystem guidelines for the most +part, and the exception is the fan safety watchdog. + +hwmon device attribute pwm1_enable: + 0: PWM offline (fan is set to full-speed mode) + 1: Manual PWM control (use pwm1 to set fan level) + 2: Hardware PWM control (EC "auto" mode) + 3: reserved (Software PWM control, not implemented yet) + + Modes 0 and 2 are not supported by all ThinkPads, and the driver + is not always able to detect this. If it does know a mode is + unsupported, it will return -EINVAL. + +hwmon device attribute pwm1: + Fan level, scaled from the firmware values of 0-7 to the hwmon + scale of 0-255. 0 means fan stopped, 255 means highest normal + speed (level 7). + + This attribute only commands the fan if pmw1_enable is set to 1 + (manual PWM control). + +hwmon device attribute fan1_input: + Fan tachometer reading, in RPM. May go stale on certain + ThinkPads while the EC transitions the PWM to offline mode, + which can take up to two minutes. May return rubbish on older + ThinkPads. + +driver attribute fan_watchdog: + Fan safety watchdog timer interval, in seconds. Minimum is + 1 second, maximum is 120 seconds. 0 disables the watchdog. + +To stop the fan: set pwm1 to zero, and pwm1_enable to 1. + +To start the fan in a safe mode: set pwm1_enable to 2. If that fails +with ENOTSUP, set it to 1 and set pwm1 to at least 128 (255 would be the +safest choice, though). -The thinkpad-acpi kernel driver can be programmed to revert the fan -level to a safe setting if userspace does not issue one of the fan -commands: "enable", "disable", "level" or "watchdog" within a -configurable ammount of time. To do this, use the "watchdog" command. - - echo 'watchdog ' > /proc/acpi/ibm/fan - -Interval is the ammount of time in seconds to wait for one of the -above mentioned fan commands before reseting the fan level to a safe -one. If set to zero, the watchdog is disabled (default). When the -watchdog timer runs out, it does the exact equivalent of the "enable" -fan command. - -Note that the watchdog timer stops after it enables the fan. It will -be rearmed again automatically (using the same interval) when one of -the above mentioned fan commands is received. The fan watchdog is, -therefore, not suitable to protect against fan mode changes made -through means other than the "enable", "disable", and "level" fan -commands. EXPERIMENTAL: WAN -- /proc/acpi/ibm/wan --------------------------------------- diff --git a/drivers/misc/thinkpad_acpi.c b/drivers/misc/thinkpad_acpi.c index d5526e882ddd..a4d7ee472396 100644 --- a/drivers/misc/thinkpad_acpi.c +++ b/drivers/misc/thinkpad_acpi.c @@ -2695,6 +2695,7 @@ static enum fan_control_access_mode fan_control_access_mode; static enum fan_control_commands fan_control_commands; static u8 fan_control_initial_status; +static u8 fan_control_desired_level; static void fan_watchdog_fire(struct work_struct *ignored); static int fan_watchdog_maxinterval; @@ -2708,8 +2709,222 @@ IBM_HANDLE(sfan, ec, "SFAN", /* 570 */ "JFNS", /* 770x-JL */ ); /* all others */ +/* + * SYSFS fan layout: hwmon compatible (device) + * + * pwm*_enable: + * 0: "disengaged" mode + * 1: manual mode + * 2: native EC "auto" mode (recommended, hardware default) + * + * pwm*: set speed in manual mode, ignored otherwise. + * 0 is level 0; 255 is level 7. Intermediate points done with linear + * interpolation. + * + * fan*_input: tachometer reading, RPM + * + * + * SYSFS fan layout: extensions + * + * fan_watchdog (driver): + * fan watchdog interval in seconds, 0 disables (default), max 120 + */ + +/* sysfs fan pwm1_enable ----------------------------------------------- */ +static ssize_t fan_pwm1_enable_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int res, mode; + u8 status; + + res = fan_get_status_safe(&status); + if (res) + return res; + + if (unlikely(tp_features.fan_ctrl_status_undef)) { + if (status != fan_control_initial_status) { + tp_features.fan_ctrl_status_undef = 0; + } else { + /* Return most likely status. In fact, it + * might be the only possible status */ + status = TP_EC_FAN_AUTO; + } + } + + if (status & TP_EC_FAN_FULLSPEED) { + mode = 0; + } else if (status & TP_EC_FAN_AUTO) { + mode = 2; + } else + mode = 1; + + return snprintf(buf, PAGE_SIZE, "%d\n", mode); +} + +static ssize_t fan_pwm1_enable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long t; + int res, level; + + if (parse_strtoul(buf, 2, &t)) + return -EINVAL; + + switch (t) { + case 0: + level = TP_EC_FAN_FULLSPEED; + break; + case 1: + level = TPACPI_FAN_LAST_LEVEL; + break; + case 2: + level = TP_EC_FAN_AUTO; + break; + case 3: + /* reserved for software-controlled auto mode */ + return -ENOSYS; + default: + return -EINVAL; + } + + res = fan_set_level_safe(level); + if (res < 0) + return res; + + fan_watchdog_reset(); + + return count; +} + +static struct device_attribute dev_attr_fan_pwm1_enable = + __ATTR(pwm1_enable, S_IWUSR | S_IRUGO, + fan_pwm1_enable_show, fan_pwm1_enable_store); + +/* sysfs fan pwm1 ------------------------------------------------------ */ +static ssize_t fan_pwm1_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int res; + u8 status; + + res = fan_get_status_safe(&status); + if (res) + return res; + + if (unlikely(tp_features.fan_ctrl_status_undef)) { + if (status != fan_control_initial_status) { + tp_features.fan_ctrl_status_undef = 0; + } else { + status = TP_EC_FAN_AUTO; + } + } + + if ((status & + (TP_EC_FAN_AUTO | TP_EC_FAN_FULLSPEED)) != 0) + status = fan_control_desired_level; + + if (status > 7) + status = 7; + + return snprintf(buf, PAGE_SIZE, "%u\n", (status * 255) / 7); +} + +static ssize_t fan_pwm1_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long s; + int rc; + u8 status, newlevel; + + if (parse_strtoul(buf, 255, &s)) + return -EINVAL; + + /* scale down from 0-255 to 0-7 */ + newlevel = (s >> 5) & 0x07; + + rc = mutex_lock_interruptible(&fan_mutex); + if (rc < 0) + return rc; + + rc = fan_get_status(&status); + if (!rc && (status & + (TP_EC_FAN_AUTO | TP_EC_FAN_FULLSPEED)) == 0) { + rc = fan_set_level(newlevel); + if (!rc) + fan_update_desired_level(newlevel); + fan_watchdog_reset(); + } + + mutex_unlock(&fan_mutex); + return (rc)? rc : count; +} + +static struct device_attribute dev_attr_fan_pwm1 = + __ATTR(pwm1, S_IWUSR | S_IRUGO, + fan_pwm1_show, fan_pwm1_store); + +/* sysfs fan fan1_input ------------------------------------------------ */ +static ssize_t fan_fan1_input_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int res; + unsigned int speed; + + res = fan_get_speed(&speed); + if (res < 0) + return res; + + return snprintf(buf, PAGE_SIZE, "%u\n", speed); +} + +static struct device_attribute dev_attr_fan_fan1_input = + __ATTR(fan1_input, S_IRUGO, + fan_fan1_input_show, NULL); + +/* sysfs fan fan_watchdog (driver) ------------------------------------- */ +static ssize_t fan_fan_watchdog_show(struct device_driver *drv, + char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%u\n", fan_watchdog_maxinterval); +} + +static ssize_t fan_fan_watchdog_store(struct device_driver *drv, + const char *buf, size_t count) +{ + unsigned long t; + + if (parse_strtoul(buf, 120, &t)) + return -EINVAL; + + fan_watchdog_maxinterval = t; + fan_watchdog_reset(); + + return count; +} + +static DRIVER_ATTR(fan_watchdog, S_IWUSR | S_IRUGO, + fan_fan_watchdog_show, fan_fan_watchdog_store); + +/* --------------------------------------------------------------------- */ +static struct attribute *fan_attributes[] = { + &dev_attr_fan_pwm1_enable.attr, &dev_attr_fan_pwm1.attr, + &dev_attr_fan_fan1_input.attr, + NULL +}; + +static const struct attribute_group fan_attr_group = { + .attrs = fan_attributes, +}; + static int __init fan_init(struct ibm_init_struct *iibm) { + int rc; + vdbg_printk(TPACPI_DBG_INIT, "initializing fan subdriver\n"); mutex_init(&fan_mutex); @@ -2718,6 +2933,7 @@ static int __init fan_init(struct ibm_init_struct *iibm) fan_control_commands = 0; fan_watchdog_maxinterval = 0; tp_features.fan_ctrl_status_undef = 0; + fan_control_desired_level = 7; IBM_ACPIHANDLE_INIT(fans); IBM_ACPIHANDLE_INIT(gfan); @@ -2796,9 +3012,36 @@ static int __init fan_init(struct ibm_init_struct *iibm) fan_control_access_mode != TPACPI_FAN_WR_NONE), fan_status_access_mode, fan_control_access_mode); - return (fan_status_access_mode != TPACPI_FAN_NONE || - fan_control_access_mode != TPACPI_FAN_WR_NONE)? - 0 : 1; + /* update fan_control_desired_level */ + if (fan_status_access_mode != TPACPI_FAN_NONE) + fan_get_status_safe(NULL); + + if (fan_status_access_mode != TPACPI_FAN_NONE || + fan_control_access_mode != TPACPI_FAN_WR_NONE) { + rc = sysfs_create_group(&tpacpi_pdev->dev.kobj, + &fan_attr_group); + if (!(rc < 0)) + rc = driver_create_file(&tpacpi_pdriver.driver, + &driver_attr_fan_watchdog); + if (rc < 0) + return rc; + return 0; + } else + return 1; +} + +/* + * Call with fan_mutex held + */ +static void fan_update_desired_level(u8 status) +{ + if ((status & + (TP_EC_FAN_AUTO | TP_EC_FAN_FULLSPEED)) == 0) { + if (status > 7) + fan_control_desired_level = 7; + else + fan_control_desired_level = status; + } } static int fan_get_status(u8 *status) @@ -2837,9 +3080,33 @@ static int fan_get_status(u8 *status) return 0; } +static int fan_get_status_safe(u8 *status) +{ + int rc; + u8 s; + + rc = mutex_lock_interruptible(&fan_mutex); + if (rc < 0) + return rc; + rc = fan_get_status(&s); + if (!rc) + fan_update_desired_level(s); + mutex_unlock(&fan_mutex); + + if (status) + *status = s; + + return rc; +} + static void fan_exit(void) { vdbg_printk(TPACPI_DBG_EXIT, "cancelling any pending fan watchdog tasks\n"); + + /* FIXME: can we really do this unconditionally? */ + sysfs_remove_group(&tpacpi_pdev->dev.kobj, &fan_attr_group); + driver_remove_file(&tpacpi_pdriver.driver, &driver_attr_fan_watchdog); + cancel_delayed_work(&fan_watchdog_task); flush_scheduled_work(); } @@ -2902,17 +3169,10 @@ static void fan_watchdog_reset(void) static int fan_set_level(int level) { - int res; - switch (fan_control_access_mode) { case TPACPI_FAN_WR_ACPI_SFAN: if (level >= 0 && level <= 7) { - res = mutex_lock_interruptible(&fan_mutex); - if (res < 0) - return res; - res = acpi_evalf(sfan_handle, NULL, NULL, "vd", level); - mutex_unlock(&fan_mutex); - if (!res) + if (!acpi_evalf(sfan_handle, NULL, NULL, "vd", level)) return -EIO; } else return -EINVAL; @@ -2925,12 +3185,7 @@ static int fan_set_level(int level) ((level < 0) || (level > 7))) return -EINVAL; - res = mutex_lock_interruptible(&fan_mutex); - if (res < 0) - return res; - res = acpi_ec_write(fan_status_offset, level); - mutex_unlock(&fan_mutex); - if (!res) + if (!acpi_ec_write(fan_status_offset, level)) return -EIO; else tp_features.fan_ctrl_status_undef = 0; @@ -2942,6 +3197,25 @@ static int fan_set_level(int level) return 0; } +static int fan_set_level_safe(int level) +{ + int rc; + + rc = mutex_lock_interruptible(&fan_mutex); + if (rc < 0) + return rc; + + if (level == TPACPI_FAN_LAST_LEVEL) + level = fan_control_desired_level; + + rc = fan_set_level(level); + if (!rc) + fan_update_desired_level(level); + + mutex_unlock(&fan_mutex); + return rc; +} + static int fan_set_enable(void) { u8 s; @@ -3009,19 +3283,24 @@ static int fan_set_disable(void) case TPACPI_FAN_WR_TPEC: if (!acpi_ec_write(fan_status_offset, 0x00)) rc = -EIO; - else + else { + fan_control_desired_level = 0; tp_features.fan_ctrl_status_undef = 0; + } break; case TPACPI_FAN_WR_ACPI_SFAN: if (!acpi_evalf(sfan_handle, NULL, NULL, "vd", 0x00)) rc = -EIO; + else + fan_control_desired_level = 0; break; default: rc = -ENXIO; } + mutex_unlock(&fan_mutex); return rc; } @@ -3063,7 +3342,7 @@ static int fan_read(char *p) switch (fan_status_access_mode) { case TPACPI_FAN_RD_ACPI_GFAN: /* 570, 600e/x, 770e, 770x */ - if ((rc = fan_get_status(&status)) < 0) + if ((rc = fan_get_status_safe(&status)) < 0) return rc; len += sprintf(p + len, "status:\t\t%s\n" @@ -3073,7 +3352,7 @@ static int fan_read(char *p) case TPACPI_FAN_RD_TPEC: /* all except 570, 600e/x, 770e, 770x */ - if ((rc = fan_get_status(&status)) < 0) + if ((rc = fan_get_status_safe(&status)) < 0) return rc; if (unlikely(tp_features.fan_ctrl_status_undef)) { @@ -3117,7 +3396,7 @@ static int fan_read(char *p) default: len += sprintf(p + len, " ( is 0-7, " - "auto, disengaged)\n"); + "auto, disengaged, full-speed)\n"); break; } } @@ -3140,12 +3419,13 @@ static int fan_write_cmd_level(const char *cmd, int *rc) if (strlencmp(cmd, "level auto") == 0) level = TP_EC_FAN_AUTO; - else if (strlencmp(cmd, "level disengaged") == 0) + else if ((strlencmp(cmd, "level disengaged") == 0) | + (strlencmp(cmd, "level full-speed") == 0)) level = TP_EC_FAN_FULLSPEED; else if (sscanf(cmd, "level %d", &level) != 1) return 0; - if ((*rc = fan_set_level(level)) == -ENXIO) + if ((*rc = fan_set_level_safe(level)) == -ENXIO) printk(IBM_ERR "level command accepted for unsupported " "access mode %d", fan_control_access_mode); diff --git a/drivers/misc/thinkpad_acpi.h b/drivers/misc/thinkpad_acpi.h index e833ff3caf39..2fe4d61cc27f 100644 --- a/drivers/misc/thinkpad_acpi.h +++ b/drivers/misc/thinkpad_acpi.h @@ -349,6 +349,8 @@ enum { /* Fan control constants */ TP_EC_FAN_FULLSPEED = 0x40, /* EC fan mode: full speed */ TP_EC_FAN_AUTO = 0x80, /* EC fan mode: auto fan control */ + + TPACPI_FAN_LAST_LEVEL = 0x100, /* Use cached last-seen fan level */ }; enum fan_status_access_mode { @@ -375,6 +377,7 @@ static enum fan_status_access_mode fan_status_access_mode; static enum fan_control_access_mode fan_control_access_mode; static enum fan_control_commands fan_control_commands; static u8 fan_control_initial_status; +static u8 fan_control_desired_level; static int fan_watchdog_maxinterval; struct mutex fan_mutex; @@ -384,10 +387,13 @@ static acpi_handle fans_handle, gfan_handle, sfan_handle; static int fan_init(struct ibm_init_struct *iibm); static void fan_exit(void); static int fan_get_status(u8 *status); +static int fan_get_status_safe(u8 *status); static int fan_get_speed(unsigned int *speed); +static void fan_update_desired_level(u8 status); static void fan_watchdog_fire(struct work_struct *ignored); static void fan_watchdog_reset(void); static int fan_set_level(int level); +static int fan_set_level_safe(int level); static int fan_set_enable(void); static int fan_set_disable(void); static int fan_set_speed(int speed); -- cgit v1.2.3 From b616004c70dd7f60a1477c3e9d6fddd00ee1fa37 Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Tue, 24 Apr 2007 11:48:19 -0300 Subject: ACPI: thinkpad-acpi: add sysfs support to the cmos command subdriver Add sysfs attributes to send ThinkPad CMOS commands. Signed-off-by: Henrique de Moraes Holschuh Signed-off-by: Len Brown --- Documentation/thinkpad-acpi.txt | 23 +++++++++++------------ drivers/misc/thinkpad_acpi.c | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 12 deletions(-) (limited to 'Documentation') diff --git a/Documentation/thinkpad-acpi.txt b/Documentation/thinkpad-acpi.txt index 339ce21e59df..352e8aee63fe 100644 --- a/Documentation/thinkpad-acpi.txt +++ b/Documentation/thinkpad-acpi.txt @@ -378,23 +378,19 @@ supported. Use "eject2" instead of "eject" for the second bay. Note: the UltraBay eject support on the 600e/x, A22p and A3x is EXPERIMENTAL and may not work as expected. USE WITH CAUTION! -CMOS control -- /proc/acpi/ibm/cmos ------------------------------------ +CMOS control +------------ + +procfs: /proc/acpi/ibm/cmos +sysfs device attribute: cmos_command This feature is used internally by the ACPI firmware to control the ThinkLight on most newer ThinkPad models. It may also control LCD brightness, sounds volume and more, but only on some models. -The commands are non-negative integer numbers: - - echo 0 >/proc/acpi/ibm/cmos - echo 1 >/proc/acpi/ibm/cmos - echo 2 >/proc/acpi/ibm/cmos - ... - -The range of valid numbers is 0 to 21, but not all have an effect and -the behavior varies from model to model. Here is the behavior on the -X40 (tpb is the ThinkPad Buttons utility): +The range of valid cmos command numbers is 0 to 21, but not all have an +effect and the behavior varies from model to model. Here is the behavior +on the X40 (tpb is the ThinkPad Buttons utility): 0 - no effect but tpb reports "Volume down" 1 - no effect but tpb reports "Volume up" @@ -407,6 +403,9 @@ X40 (tpb is the ThinkPad Buttons utility): 13 - ThinkLight off 14 - no effect but tpb reports ThinkLight status change +The cmos command interface is prone to firmware split-brain problems, as +in newer ThinkPads it is just a compatibility layer. + LED control -- /proc/acpi/ibm/led --------------------------------- diff --git a/drivers/misc/thinkpad_acpi.c b/drivers/misc/thinkpad_acpi.c index 79abc6841e30..ba749df189ab 100644 --- a/drivers/misc/thinkpad_acpi.c +++ b/drivers/misc/thinkpad_acpi.c @@ -1743,8 +1743,30 @@ static struct ibm_struct bay_driver_data = { * CMOS subdriver */ +/* sysfs cmos_command -------------------------------------------------- */ +static ssize_t cmos_command_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long cmos_cmd; + int res; + + if (parse_strtoul(buf, 21, &cmos_cmd)) + return -EINVAL; + + res = issue_thinkpad_cmos_command(cmos_cmd); + return (res)? res : count; +} + +static struct device_attribute dev_attr_cmos_command = + __ATTR(cmos_command, S_IWUSR, NULL, cmos_command_store); + +/* --------------------------------------------------------------------- */ + static int __init cmos_init(struct ibm_init_struct *iibm) { + int res; + vdbg_printk(TPACPI_DBG_INIT, "initializing cmos commands subdriver\n"); @@ -1752,9 +1774,19 @@ static int __init cmos_init(struct ibm_init_struct *iibm) vdbg_printk(TPACPI_DBG_INIT, "cmos commands are %s\n", str_supported(cmos_handle != NULL)); + + res = device_create_file(&tpacpi_pdev->dev, &dev_attr_cmos_command); + if (res) + return res; + return (cmos_handle)? 0 : 1; } +static void cmos_exit(void) +{ + device_remove_file(&tpacpi_pdev->dev, &dev_attr_cmos_command); +} + static int cmos_read(char *p) { int len = 0; @@ -1795,6 +1827,7 @@ static struct ibm_struct cmos_driver_data = { .name = "cmos", .read = cmos_read, .write = cmos_write, + .exit = cmos_exit, }; /************************************************************************* -- cgit v1.2.3 From 7d5a015eece8be9186d3613d595643a520555e33 Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Tue, 24 Apr 2007 11:48:20 -0300 Subject: ACPI: thinkpad-acpi: update brightness sysfs interface support Update the brightness sysfs interface (done through the backlight class) to be in line with the rest of the thinkpad-acpi driver. This renames the incorrect, un-obvious, and clash-prone name of "ibm" for the backlight device to a much more fitting and descriptive "thinkpad_screen". This is something I wanted to do for quite a while... Signed-off-by: Henrique de Moraes Holschuh Signed-off-by: Len Brown --- Documentation/thinkpad-acpi.txt | 52 ++++++++++++++++++++++++++++++++++++----- drivers/misc/thinkpad_acpi.c | 5 ++-- drivers/misc/thinkpad_acpi.h | 2 ++ 3 files changed, 51 insertions(+), 8 deletions(-) (limited to 'Documentation') diff --git a/Documentation/thinkpad-acpi.txt b/Documentation/thinkpad-acpi.txt index 352e8aee63fe..eab4997efc0f 100644 --- a/Documentation/thinkpad-acpi.txt +++ b/Documentation/thinkpad-acpi.txt @@ -611,19 +611,59 @@ registers contain the current battery capacity, etc. If you experiment with this, do send me your results (including some complete dumps with a description of the conditions when they were taken.) -LCD brightness control -- /proc/acpi/ibm/brightness ---------------------------------------------------- +LCD brightness control +---------------------- + +procfs: /proc/acpi/ibm/brightness +sysfs backlight device "thinkpad_screen" This feature allows software control of the LCD brightness on ThinkPad -models which don't have a hardware brightness slider. The available -commands are: +models which don't have a hardware brightness slider. + +It has some limitations: the LCD backlight cannot be actually turned on or off +by this interface, and in many ThinkPad models, the "dim while on battery" +functionality will be enabled by the BIOS when this interface is used, and +cannot be controlled. + +The backlight control has eight levels, ranging from 0 to 7. Some of the +levels may not be distinct. + +Procfs notes: + + The available commands are: echo up >/proc/acpi/ibm/brightness echo down >/proc/acpi/ibm/brightness echo 'level ' >/proc/acpi/ibm/brightness -The number range is 0 to 7, although not all of them may be -distinct. The current brightness level is shown in the file. +Sysfs notes: + +The interface is implemented through the backlight sysfs class, which is poorly +documented at this time. + +Locate the thinkpad_screen device under /sys/class/backlight, and inside it +there will be the following attributes: + + max_brightness: + Reads the maximum brightness the hardware can be set to. + The minimum is always zero. + + actual_brightness: + Reads what brightness the screen is set to at this instant. + + brightness: + Writes request the driver to change brightness to the given + value. Reads will tell you what brightness the driver is trying + to set the display to when "power" is set to zero and the display + has not been dimmed by a kernel power management event. + + power: + power management mode, where 0 is "display on", and 1 to 3 will + dim the display backlight to brightness level 0 because + thinkpad-acpi cannot really turn the backlight off. Kernel + power management events can temporarily increase the current + power management level, i.e. they can dim the display. + Volume control -- /proc/acpi/ibm/volume --------------------------------------- diff --git a/drivers/misc/thinkpad_acpi.c b/drivers/misc/thinkpad_acpi.c index ba749df189ab..c0a023cc5ded 100644 --- a/drivers/misc/thinkpad_acpi.c +++ b/drivers/misc/thinkpad_acpi.c @@ -2414,8 +2414,9 @@ static int __init brightness_init(struct ibm_init_struct *iibm) if (b < 0) return b; - ibm_backlight_device = backlight_device_register("ibm", NULL, NULL, - &ibm_backlight_data); + ibm_backlight_device = backlight_device_register( + TPACPI_BACKLIGHT_DEV_NAME, NULL, NULL, + &ibm_backlight_data); if (IS_ERR(ibm_backlight_device)) { printk(IBM_ERR "Could not register backlight device\n"); return PTR_ERR(ibm_backlight_device); diff --git a/drivers/misc/thinkpad_acpi.h b/drivers/misc/thinkpad_acpi.h index 2fe4d61cc27f..8348fc653009 100644 --- a/drivers/misc/thinkpad_acpi.h +++ b/drivers/misc/thinkpad_acpi.h @@ -296,6 +296,8 @@ static int bluetooth_write(char *buf); * Brightness (backlight) subdriver */ +#define TPACPI_BACKLIGHT_DEV_NAME "thinkpad_screen" + static struct backlight_device *ibm_backlight_device; static int brightness_offset = 0x31; -- cgit v1.2.3 From 127af0c44fc916908abd145914d65b9fe598bcd7 Mon Sep 17 00:00:00 2001 From: Ilpo Järvinen Date: Wed, 21 Feb 2007 23:16:38 -0800 Subject: [TCP] FRTO: Sysctl documentation for SACK enhanced version MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The description is overly verbose to avoid ambiguity between "SACK enabled" and "SACK enhanced FRTO" Signed-off-by: Ilpo Järvinen Signed-off-by: David S. Miller --- Documentation/networking/ip-sysctl.txt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt index 702d1d8dd04a..719b42907314 100644 --- a/Documentation/networking/ip-sysctl.txt +++ b/Documentation/networking/ip-sysctl.txt @@ -183,7 +183,10 @@ tcp_frto - BOOLEAN Enables F-RTO, an enhanced recovery algorithm for TCP retransmission timeouts. It is particularly beneficial in wireless environments where packet loss is typically due to random radio interference - rather than intermediate router congestion. + rather than intermediate router congestion. If set to 1, basic + version is enabled. 2 enables SACK enhanced FRTO, which is + EXPERIMENTAL. The basic version can be used also when SACK is + enabled for a flow through tcp_sack sysctl. tcp_keepalive_time - INTEGER How often TCP sends out keepalive messages when keepalive is enabled. -- cgit v1.2.3 From 89808060b7a71376cc2ba8092d43b2010da465b6 Mon Sep 17 00:00:00 2001 From: Ilpo Järvinen Date: Tue, 27 Feb 2007 10:10:55 -0800 Subject: [TCP] Sysctl documentation: tcp_frto_response MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In addition, fixed minor things in tcp_frto sysctl. Signed-off-by: Ilpo Järvinen Signed-off-by: David S. Miller --- Documentation/networking/ip-sysctl.txt | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) (limited to 'Documentation') diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt index 719b42907314..054c515bd726 100644 --- a/Documentation/networking/ip-sysctl.txt +++ b/Documentation/networking/ip-sysctl.txt @@ -179,15 +179,32 @@ tcp_fin_timeout - INTEGER because they eat maximum 1.5K of memory, but they tend to live longer. Cf. tcp_max_orphans. -tcp_frto - BOOLEAN +tcp_frto - INTEGER Enables F-RTO, an enhanced recovery algorithm for TCP retransmission timeouts. It is particularly beneficial in wireless environments where packet loss is typically due to random radio interference rather than intermediate router congestion. If set to 1, basic - version is enabled. 2 enables SACK enhanced FRTO, which is + version is enabled. 2 enables SACK enhanced F-RTO, which is EXPERIMENTAL. The basic version can be used also when SACK is enabled for a flow through tcp_sack sysctl. +tcp_frto_response - INTEGER + When F-RTO has detected that a TCP retransmission timeout was + spurious (i.e, the timeout would have been avoided had TCP set a + longer retransmission timeout), TCP has several options what to do + next. Possible values are: + 0 Rate halving based; a smooth and conservative response, + results in halved cwnd and ssthresh after one RTT + 1 Very conservative response; not recommended because even + though being valid, it interacts poorly with the rest of + Linux TCP, halves cwnd and ssthresh immediately + 2 Aggressive response; undoes congestion control measures + that are now known to be unnecessary (ignoring the + possibility of a lost retransmission that would require + TCP to be more cautious), cwnd and ssthresh are restored + to the values prior timeout + Default: 0 (rate halving based) + tcp_keepalive_time - INTEGER How often TCP sends out keepalive messages when keepalive is enabled. Default: 2hours. -- cgit v1.2.3 From a2a316fd068c455c609ecc155dcfaa7e208d29fe Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Thu, 8 Mar 2007 20:41:08 -0800 Subject: [NET]: Replace CONFIG_NET_DEBUG with sysctl. Covert network warning messages from a compile time to runtime choice. Removes kernel config option and replaces it with new /proc/sys/net/core/warnings. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- Documentation/filesystems/proc.txt | 9 +++++++++ include/linux/sysctl.h | 1 + include/net/sock.h | 12 +++++------- net/Kconfig | 7 ------- net/core/sysctl_net_core.c | 8 ++++++++ net/core/utils.c | 2 ++ 6 files changed, 25 insertions(+), 14 deletions(-) (limited to 'Documentation') diff --git a/Documentation/filesystems/proc.txt b/Documentation/filesystems/proc.txt index 5484ab5efd4f..7aaf09b86a55 100644 --- a/Documentation/filesystems/proc.txt +++ b/Documentation/filesystems/proc.txt @@ -1421,6 +1421,15 @@ fewer messages that will be written. Message_burst controls when messages will be dropped. The default settings limit warning messages to one every five seconds. +warnings +-------- + +This controls console messages from the networking stack that can occur because +of problems on the network like duplicate address or bad checksums. Normally, +this should be enabled, but if the problem persists the messages can be +disabled. + + netdev_max_backlog ------------------ diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h index c9ccb550206f..df2d9ed20a4e 100644 --- a/include/linux/sysctl.h +++ b/include/linux/sysctl.h @@ -290,6 +290,7 @@ enum NET_CORE_BUDGET=19, NET_CORE_AEVENT_ETIME=20, NET_CORE_AEVENT_RSEQTH=21, + NET_CORE_WARNINGS=22, }; /* /proc/sys/net/ethernet */ diff --git a/include/net/sock.h b/include/net/sock.h index d093e49fdc85..51246579592e 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -1334,14 +1334,12 @@ extern int sock_get_timestampns(struct sock *, struct timespec __user *); /* * Enable debug/info messages */ +extern int net_msg_warn; +#define NETDEBUG(fmt, args...) \ + do { if (net_msg_warn) printk(fmt,##args); } while (0) -#ifdef CONFIG_NETDEBUG -#define NETDEBUG(fmt, args...) printk(fmt,##args) -#define LIMIT_NETDEBUG(fmt, args...) do { if (net_ratelimit()) printk(fmt,##args); } while(0) -#else -#define NETDEBUG(fmt, args...) do { } while (0) -#define LIMIT_NETDEBUG(fmt, args...) do { } while(0) -#endif +#define LIMIT_NETDEBUG(fmt, args...) \ + do { if (net_msg_warn && net_ratelimit()) printk(fmt,##args); } while(0) /* * Macros for sleeping on a socket. Use them like this: diff --git a/net/Kconfig b/net/Kconfig index 915657832d94..e2d9b3b9cda4 100644 --- a/net/Kconfig +++ b/net/Kconfig @@ -27,13 +27,6 @@ if NET menu "Networking options" -config NETDEBUG - bool "Network packet debugging" - help - You can say Y here if you want to get additional messages useful in - debugging bad packets, but can overwhelm logs under denial of service - attacks. - source "net/packet/Kconfig" source "net/unix/Kconfig" source "net/xfrm/Kconfig" diff --git a/net/core/sysctl_net_core.c b/net/core/sysctl_net_core.c index 1e75b1585460..b29712033dd4 100644 --- a/net/core/sysctl_net_core.c +++ b/net/core/sysctl_net_core.c @@ -136,6 +136,14 @@ ctl_table core_table[] = { .mode = 0644, .proc_handler = &proc_dointvec }, + { + .ctl_name = NET_CORE_WARNINGS, + .procname = "warnings", + .data = &net_msg_warn, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = &proc_dointvec + }, { .ctl_name = 0 } }; diff --git a/net/core/utils.c b/net/core/utils.c index 07236c17fab9..34f08107b98e 100644 --- a/net/core/utils.c +++ b/net/core/utils.c @@ -32,6 +32,8 @@ int net_msg_cost = 5*HZ; int net_msg_burst = 10; +int net_msg_warn = 1; +EXPORT_SYMBOL(net_msg_warn); /* * All net warning printk()s should be guarded by this function. -- cgit v1.2.3 From 587aa64163bb14f70098f450abab9410787fce9d Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Wed, 14 Mar 2007 16:37:25 -0700 Subject: [NETFILTER]: Remove IPv4 only connection tracking/NAT Remove the obsolete IPv4 only connection tracking/NAT as scheduled in feature-removal-schedule. Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller --- Documentation/feature-removal-schedule.txt | 9 - include/linux/netfilter_ipv4/Kbuild | 14 - include/linux/netfilter_ipv4/ip_conntrack.h | 402 ----- include/linux/netfilter_ipv4/ip_conntrack_amanda.h | 11 - include/linux/netfilter_ipv4/ip_conntrack_core.h | 61 - include/linux/netfilter_ipv4/ip_conntrack_ftp.h | 44 - include/linux/netfilter_ipv4/ip_conntrack_h323.h | 89 - include/linux/netfilter_ipv4/ip_conntrack_helper.h | 46 - include/linux/netfilter_ipv4/ip_conntrack_icmp.h | 6 - include/linux/netfilter_ipv4/ip_conntrack_irc.h | 32 - include/linux/netfilter_ipv4/ip_conntrack_pptp.h | 326 ---- .../linux/netfilter_ipv4/ip_conntrack_proto_gre.h | 114 -- .../linux/netfilter_ipv4/ip_conntrack_protocol.h | 98 -- include/linux/netfilter_ipv4/ip_conntrack_sctp.h | 6 - include/linux/netfilter_ipv4/ip_conntrack_sip.h | 40 - include/linux/netfilter_ipv4/ip_conntrack_tcp.h | 6 - include/linux/netfilter_ipv4/ip_conntrack_tftp.h | 20 - include/linux/netfilter_ipv4/ip_conntrack_tuple.h | 146 -- include/linux/netfilter_ipv4/ip_nat.h | 79 - include/linux/netfilter_ipv4/ip_nat_core.h | 18 - include/linux/netfilter_ipv4/ip_nat_helper.h | 33 - include/linux/netfilter_ipv4/ip_nat_pptp.h | 11 - include/linux/netfilter_ipv4/ip_nat_protocol.h | 74 - include/linux/netfilter_ipv4/ip_nat_rule.h | 28 - include/linux/netfilter_ipv4/ipt_SAME.h | 2 +- include/net/netfilter/nf_conntrack.h | 5 + include/net/netfilter/nf_conntrack_compat.h | 145 -- include/net/netfilter/nf_nat_rule.h | 10 - net/ipv4/netfilter/Kconfig | 267 +-- net/ipv4/netfilter/Makefile | 45 +- net/ipv4/netfilter/ip_conntrack_amanda.c | 229 --- net/ipv4/netfilter/ip_conntrack_core.c | 1549 ---------------- net/ipv4/netfilter/ip_conntrack_ftp.c | 520 ------ net/ipv4/netfilter/ip_conntrack_helper_h323.c | 1840 -------------------- net/ipv4/netfilter/ip_conntrack_helper_pptp.c | 684 -------- net/ipv4/netfilter/ip_conntrack_irc.c | 314 ---- net/ipv4/netfilter/ip_conntrack_netbios_ns.c | 143 -- net/ipv4/netfilter/ip_conntrack_netlink.c | 1577 ----------------- net/ipv4/netfilter/ip_conntrack_proto_generic.c | 74 - net/ipv4/netfilter/ip_conntrack_proto_gre.c | 328 ---- net/ipv4/netfilter/ip_conntrack_proto_icmp.c | 315 ---- net/ipv4/netfilter/ip_conntrack_proto_sctp.c | 659 ------- net/ipv4/netfilter/ip_conntrack_proto_tcp.c | 1163 ------------- net/ipv4/netfilter/ip_conntrack_proto_udp.c | 148 -- net/ipv4/netfilter/ip_conntrack_sip.c | 520 ------ net/ipv4/netfilter/ip_conntrack_standalone.c | 962 ---------- net/ipv4/netfilter/ip_conntrack_tftp.c | 161 -- net/ipv4/netfilter/ip_nat_amanda.c | 85 - net/ipv4/netfilter/ip_nat_core.c | 633 ------- net/ipv4/netfilter/ip_nat_ftp.c | 180 -- net/ipv4/netfilter/ip_nat_helper.c | 436 ----- net/ipv4/netfilter/ip_nat_helper_h323.c | 611 ------- net/ipv4/netfilter/ip_nat_helper_pptp.c | 350 ---- net/ipv4/netfilter/ip_nat_irc.c | 122 -- net/ipv4/netfilter/ip_nat_proto_gre.c | 174 -- net/ipv4/netfilter/ip_nat_proto_icmp.c | 87 - net/ipv4/netfilter/ip_nat_proto_tcp.c | 154 -- net/ipv4/netfilter/ip_nat_proto_udp.c | 144 -- net/ipv4/netfilter/ip_nat_proto_unknown.c | 55 - net/ipv4/netfilter/ip_nat_rule.c | 314 ---- net/ipv4/netfilter/ip_nat_sip.c | 282 --- net/ipv4/netfilter/ip_nat_snmp_basic.c | 1333 -------------- net/ipv4/netfilter/ip_nat_standalone.c | 387 ---- net/ipv4/netfilter/ip_nat_tftp.c | 70 - net/ipv4/netfilter/ipt_CLUSTERIP.c | 18 +- net/ipv4/netfilter/ipt_MASQUERADE.c | 57 +- net/ipv4/netfilter/ipt_NETMAP.c | 22 +- net/ipv4/netfilter/ipt_REDIRECT.c | 24 +- net/ipv4/netfilter/ipt_SAME.c | 25 +- net/ipv4/netfilter/nf_nat_h323.c | 4 +- net/ipv4/netfilter/nf_nat_pptp.c | 2 +- net/netfilter/Kconfig | 63 +- net/netfilter/xt_CONNMARK.c | 32 +- net/netfilter/xt_CONNSECMARK.c | 18 +- net/netfilter/xt_NOTRACK.c | 4 +- net/netfilter/xt_connbytes.c | 10 +- net/netfilter/xt_connmark.c | 17 +- net/netfilter/xt_conntrack.c | 110 +- net/netfilter/xt_helper.c | 57 - net/netfilter/xt_state.c | 4 +- 80 files changed, 122 insertions(+), 19135 deletions(-) delete mode 100644 include/linux/netfilter_ipv4/ip_conntrack.h delete mode 100644 include/linux/netfilter_ipv4/ip_conntrack_amanda.h delete mode 100644 include/linux/netfilter_ipv4/ip_conntrack_core.h delete mode 100644 include/linux/netfilter_ipv4/ip_conntrack_ftp.h delete mode 100644 include/linux/netfilter_ipv4/ip_conntrack_h323.h delete mode 100644 include/linux/netfilter_ipv4/ip_conntrack_helper.h delete mode 100644 include/linux/netfilter_ipv4/ip_conntrack_icmp.h delete mode 100644 include/linux/netfilter_ipv4/ip_conntrack_irc.h delete mode 100644 include/linux/netfilter_ipv4/ip_conntrack_pptp.h delete mode 100644 include/linux/netfilter_ipv4/ip_conntrack_proto_gre.h delete mode 100644 include/linux/netfilter_ipv4/ip_conntrack_protocol.h delete mode 100644 include/linux/netfilter_ipv4/ip_conntrack_sctp.h delete mode 100644 include/linux/netfilter_ipv4/ip_conntrack_sip.h delete mode 100644 include/linux/netfilter_ipv4/ip_conntrack_tcp.h delete mode 100644 include/linux/netfilter_ipv4/ip_conntrack_tftp.h delete mode 100644 include/linux/netfilter_ipv4/ip_conntrack_tuple.h delete mode 100644 include/linux/netfilter_ipv4/ip_nat.h delete mode 100644 include/linux/netfilter_ipv4/ip_nat_core.h delete mode 100644 include/linux/netfilter_ipv4/ip_nat_helper.h delete mode 100644 include/linux/netfilter_ipv4/ip_nat_pptp.h delete mode 100644 include/linux/netfilter_ipv4/ip_nat_protocol.h delete mode 100644 include/linux/netfilter_ipv4/ip_nat_rule.h delete mode 100644 include/net/netfilter/nf_conntrack_compat.h delete mode 100644 net/ipv4/netfilter/ip_conntrack_amanda.c delete mode 100644 net/ipv4/netfilter/ip_conntrack_core.c delete mode 100644 net/ipv4/netfilter/ip_conntrack_ftp.c delete mode 100644 net/ipv4/netfilter/ip_conntrack_helper_h323.c delete mode 100644 net/ipv4/netfilter/ip_conntrack_helper_pptp.c delete mode 100644 net/ipv4/netfilter/ip_conntrack_irc.c delete mode 100644 net/ipv4/netfilter/ip_conntrack_netbios_ns.c delete mode 100644 net/ipv4/netfilter/ip_conntrack_netlink.c delete mode 100644 net/ipv4/netfilter/ip_conntrack_proto_generic.c delete mode 100644 net/ipv4/netfilter/ip_conntrack_proto_gre.c delete mode 100644 net/ipv4/netfilter/ip_conntrack_proto_icmp.c delete mode 100644 net/ipv4/netfilter/ip_conntrack_proto_sctp.c delete mode 100644 net/ipv4/netfilter/ip_conntrack_proto_tcp.c delete mode 100644 net/ipv4/netfilter/ip_conntrack_proto_udp.c delete mode 100644 net/ipv4/netfilter/ip_conntrack_sip.c delete mode 100644 net/ipv4/netfilter/ip_conntrack_standalone.c delete mode 100644 net/ipv4/netfilter/ip_conntrack_tftp.c delete mode 100644 net/ipv4/netfilter/ip_nat_amanda.c delete mode 100644 net/ipv4/netfilter/ip_nat_core.c delete mode 100644 net/ipv4/netfilter/ip_nat_ftp.c delete mode 100644 net/ipv4/netfilter/ip_nat_helper.c delete mode 100644 net/ipv4/netfilter/ip_nat_helper_h323.c delete mode 100644 net/ipv4/netfilter/ip_nat_helper_pptp.c delete mode 100644 net/ipv4/netfilter/ip_nat_irc.c delete mode 100644 net/ipv4/netfilter/ip_nat_proto_gre.c delete mode 100644 net/ipv4/netfilter/ip_nat_proto_icmp.c delete mode 100644 net/ipv4/netfilter/ip_nat_proto_tcp.c delete mode 100644 net/ipv4/netfilter/ip_nat_proto_udp.c delete mode 100644 net/ipv4/netfilter/ip_nat_proto_unknown.c delete mode 100644 net/ipv4/netfilter/ip_nat_rule.c delete mode 100644 net/ipv4/netfilter/ip_nat_sip.c delete mode 100644 net/ipv4/netfilter/ip_nat_snmp_basic.c delete mode 100644 net/ipv4/netfilter/ip_nat_standalone.c delete mode 100644 net/ipv4/netfilter/ip_nat_tftp.c (limited to 'Documentation') diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt index 19b4c96b2a49..9817b60e70a3 100644 --- a/Documentation/feature-removal-schedule.txt +++ b/Documentation/feature-removal-schedule.txt @@ -211,15 +211,6 @@ Who: Adrian Bunk --------------------------- -What: IPv4 only connection tracking/NAT/helpers -When: 2.6.22 -Why: The new layer 3 independant connection tracking replaces the old - IPv4 only version. After some stabilization of the new code the - old one will be removed. -Who: Patrick McHardy - ---------------------------- - What: ACPI hooks (X86_SPEEDSTEP_CENTRINO_ACPI) in speedstep-centrino driver When: December 2006 Why: Speedstep-centrino driver with ACPI hooks and acpi-cpufreq driver are diff --git a/include/linux/netfilter_ipv4/Kbuild b/include/linux/netfilter_ipv4/Kbuild index 180337801a86..7185792b900f 100644 --- a/include/linux/netfilter_ipv4/Kbuild +++ b/include/linux/netfilter_ipv4/Kbuild @@ -1,9 +1,3 @@ -header-y += ip_conntrack_helper.h -header-y += ip_conntrack_protocol.h -header-y += ip_conntrack_sctp.h -header-y += ip_conntrack_tcp.h -header-y += ip_conntrack_tftp.h -header-y += ip_nat_pptp.h header-y += ipt_addrtype.h header-y += ipt_ah.h header-y += ipt_CLASSIFY.h @@ -49,13 +43,5 @@ header-y += ipt_ttl.h header-y += ipt_TTL.h header-y += ipt_ULOG.h -unifdef-y += ip_conntrack.h -unifdef-y += ip_conntrack_h323.h -unifdef-y += ip_conntrack_irc.h -unifdef-y += ip_conntrack_pptp.h -unifdef-y += ip_conntrack_proto_gre.h -unifdef-y += ip_conntrack_tuple.h -unifdef-y += ip_nat.h -unifdef-y += ip_nat_rule.h unifdef-y += ip_queue.h unifdef-y += ip_tables.h diff --git a/include/linux/netfilter_ipv4/ip_conntrack.h b/include/linux/netfilter_ipv4/ip_conntrack.h deleted file mode 100644 index da9274e6bf12..000000000000 --- a/include/linux/netfilter_ipv4/ip_conntrack.h +++ /dev/null @@ -1,402 +0,0 @@ -#ifndef _IP_CONNTRACK_H -#define _IP_CONNTRACK_H - -#include - -#ifdef __KERNEL__ -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -/* per conntrack: protocol private data */ -union ip_conntrack_proto { - /* insert conntrack proto private data here */ - struct ip_ct_gre gre; - struct ip_ct_sctp sctp; - struct ip_ct_tcp tcp; - struct ip_ct_icmp icmp; -}; - -union ip_conntrack_expect_proto { - /* insert expect proto private data here */ -}; - -/* Add protocol helper include file here */ -#include -#include -#include -#include -#include - -/* per conntrack: application helper private data */ -union ip_conntrack_help { - /* insert conntrack helper private data (master) here */ - struct ip_ct_h323_master ct_h323_info; - struct ip_ct_pptp_master ct_pptp_info; - struct ip_ct_ftp_master ct_ftp_info; - struct ip_ct_irc_master ct_irc_info; -}; - -#ifdef CONFIG_IP_NF_NAT_NEEDED -#include -#include - -/* per conntrack: nat application helper private data */ -union ip_conntrack_nat_help { - /* insert nat helper private data here */ - struct ip_nat_pptp nat_pptp_info; -}; -#endif - -#include -#include - -#ifdef CONFIG_NETFILTER_DEBUG -#define IP_NF_ASSERT(x) \ -do { \ - if (!(x)) \ - /* Wooah! I'm tripping my conntrack in a frenzy of \ - netplay... */ \ - printk("NF_IP_ASSERT: %s:%i(%s)\n", \ - __FILE__, __LINE__, __FUNCTION__); \ -} while(0) -#else -#define IP_NF_ASSERT(x) -#endif - -struct ip_conntrack_helper; - -struct ip_conntrack -{ - /* Usage count in here is 1 for hash table/destruct timer, 1 per skb, - plus 1 for any connection(s) we are `master' for */ - struct nf_conntrack ct_general; - - /* Have we seen traffic both ways yet? (bitset) */ - unsigned long status; - - /* Timer function; drops refcnt when it goes off. */ - struct timer_list timeout; - -#ifdef CONFIG_IP_NF_CT_ACCT - /* Accounting Information (same cache line as other written members) */ - struct ip_conntrack_counter counters[IP_CT_DIR_MAX]; -#endif - /* If we were expected by an expectation, this will be it */ - struct ip_conntrack *master; - - /* Current number of expected connections */ - unsigned int expecting; - - /* Unique ID that identifies this conntrack*/ - unsigned int id; - - /* Helper, if any. */ - struct ip_conntrack_helper *helper; - - /* Storage reserved for other modules: */ - union ip_conntrack_proto proto; - - union ip_conntrack_help help; - -#ifdef CONFIG_IP_NF_NAT_NEEDED - struct { - struct ip_nat_info info; - union ip_conntrack_nat_help help; -#if defined(CONFIG_IP_NF_TARGET_MASQUERADE) || \ - defined(CONFIG_IP_NF_TARGET_MASQUERADE_MODULE) - int masq_index; -#endif - } nat; -#endif /* CONFIG_IP_NF_NAT_NEEDED */ - -#if defined(CONFIG_IP_NF_CONNTRACK_MARK) - u_int32_t mark; -#endif - -#ifdef CONFIG_IP_NF_CONNTRACK_SECMARK - u_int32_t secmark; -#endif - - /* Traversed often, so hopefully in different cacheline to top */ - /* These are my tuples; original and reply */ - struct ip_conntrack_tuple_hash tuplehash[IP_CT_DIR_MAX]; -}; - -struct ip_conntrack_expect -{ - /* Internal linked list (global expectation list) */ - struct list_head list; - - /* We expect this tuple, with the following mask */ - struct ip_conntrack_tuple tuple, mask; - - /* Function to call after setup and insertion */ - void (*expectfn)(struct ip_conntrack *new, - struct ip_conntrack_expect *this); - - /* The conntrack of the master connection */ - struct ip_conntrack *master; - - /* Timer function; deletes the expectation. */ - struct timer_list timeout; - - /* Usage count. */ - atomic_t use; - - /* Unique ID */ - unsigned int id; - - /* Flags */ - unsigned int flags; - -#ifdef CONFIG_IP_NF_NAT_NEEDED - __be32 saved_ip; - /* This is the original per-proto part, used to map the - * expected connection the way the recipient expects. */ - union ip_conntrack_manip_proto saved_proto; - /* Direction relative to the master connection. */ - enum ip_conntrack_dir dir; -#endif -}; - -#define IP_CT_EXPECT_PERMANENT 0x1 - -static inline struct ip_conntrack * -tuplehash_to_ctrack(const struct ip_conntrack_tuple_hash *hash) -{ - return container_of(hash, struct ip_conntrack, - tuplehash[hash->tuple.dst.dir]); -} - -/* get master conntrack via master expectation */ -#define master_ct(conntr) (conntr->master) - -/* Alter reply tuple (maybe alter helper). */ -extern void -ip_conntrack_alter_reply(struct ip_conntrack *conntrack, - const struct ip_conntrack_tuple *newreply); - -/* Is this tuple taken? (ignoring any belonging to the given - conntrack). */ -extern int -ip_conntrack_tuple_taken(const struct ip_conntrack_tuple *tuple, - const struct ip_conntrack *ignored_conntrack); - -/* Return conntrack_info and tuple hash for given skb. */ -static inline struct ip_conntrack * -ip_conntrack_get(const struct sk_buff *skb, enum ip_conntrack_info *ctinfo) -{ - *ctinfo = skb->nfctinfo; - return (struct ip_conntrack *)skb->nfct; -} - -/* decrement reference count on a conntrack */ -static inline void -ip_conntrack_put(struct ip_conntrack *ct) -{ - IP_NF_ASSERT(ct); - nf_conntrack_put(&ct->ct_general); -} - -extern int invert_tuplepr(struct ip_conntrack_tuple *inverse, - const struct ip_conntrack_tuple *orig); - -extern void __ip_ct_refresh_acct(struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo, - const struct sk_buff *skb, - unsigned long extra_jiffies, - int do_acct); - -/* Refresh conntrack for this many jiffies and do accounting */ -static inline void ip_ct_refresh_acct(struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo, - const struct sk_buff *skb, - unsigned long extra_jiffies) -{ - __ip_ct_refresh_acct(ct, ctinfo, skb, extra_jiffies, 1); -} - -/* Refresh conntrack for this many jiffies */ -static inline void ip_ct_refresh(struct ip_conntrack *ct, - const struct sk_buff *skb, - unsigned long extra_jiffies) -{ - __ip_ct_refresh_acct(ct, 0, skb, extra_jiffies, 0); -} - -/* These are for NAT. Icky. */ -/* Update TCP window tracking data when NAT mangles the packet */ -extern void ip_conntrack_tcp_update(struct sk_buff *skb, - struct ip_conntrack *conntrack, - enum ip_conntrack_dir dir); - -/* Call me when a conntrack is destroyed. */ -extern void (*ip_conntrack_destroyed)(struct ip_conntrack *conntrack); - -/* Fake conntrack entry for untracked connections */ -extern struct ip_conntrack ip_conntrack_untracked; - -/* Returns new sk_buff, or NULL */ -struct sk_buff * -ip_ct_gather_frags(struct sk_buff *skb, u_int32_t user); - -/* Iterate over all conntracks: if iter returns true, it's deleted. */ -extern void -ip_ct_iterate_cleanup(int (*iter)(struct ip_conntrack *i, void *data), - void *data); - -extern struct ip_conntrack_helper * -__ip_conntrack_helper_find_byname(const char *); -extern struct ip_conntrack_helper * -ip_conntrack_helper_find_get(const struct ip_conntrack_tuple *tuple); -extern void ip_conntrack_helper_put(struct ip_conntrack_helper *helper); - -extern struct ip_conntrack_protocol * -__ip_conntrack_proto_find(u_int8_t protocol); -extern struct ip_conntrack_protocol * -ip_conntrack_proto_find_get(u_int8_t protocol); -extern void ip_conntrack_proto_put(struct ip_conntrack_protocol *proto); - -extern void ip_ct_remove_expectations(struct ip_conntrack *ct); - -extern struct ip_conntrack *ip_conntrack_alloc(struct ip_conntrack_tuple *, - struct ip_conntrack_tuple *); - -extern void ip_conntrack_free(struct ip_conntrack *ct); - -extern void ip_conntrack_hash_insert(struct ip_conntrack *ct); - -extern struct ip_conntrack_expect * -__ip_conntrack_expect_find(const struct ip_conntrack_tuple *tuple); - -extern struct ip_conntrack_expect * -ip_conntrack_expect_find_get(const struct ip_conntrack_tuple *tuple); - -extern struct ip_conntrack_tuple_hash * -__ip_conntrack_find(const struct ip_conntrack_tuple *tuple, - const struct ip_conntrack *ignored_conntrack); - -extern void ip_conntrack_flush(void); - -/* It's confirmed if it is, or has been in the hash table. */ -static inline int is_confirmed(struct ip_conntrack *ct) -{ - return test_bit(IPS_CONFIRMED_BIT, &ct->status); -} - -static inline int is_dying(struct ip_conntrack *ct) -{ - return test_bit(IPS_DYING_BIT, &ct->status); -} - -extern unsigned int ip_conntrack_htable_size; -extern int ip_conntrack_checksum; - -#define CONNTRACK_STAT_INC(count) (__get_cpu_var(ip_conntrack_stat).count++) -#define CONNTRACK_STAT_INC_ATOMIC(count) \ -do { \ - local_bh_disable(); \ - __get_cpu_var(ip_conntrack_stat).count++; \ - local_bh_enable(); \ -} while (0) - -#ifdef CONFIG_IP_NF_CONNTRACK_EVENTS -#include -#include - -struct ip_conntrack_ecache { - struct ip_conntrack *ct; - unsigned int events; -}; -DECLARE_PER_CPU(struct ip_conntrack_ecache, ip_conntrack_ecache); - -#define CONNTRACK_ECACHE(x) (__get_cpu_var(ip_conntrack_ecache).x) - -extern struct atomic_notifier_head ip_conntrack_chain; -extern struct atomic_notifier_head ip_conntrack_expect_chain; - -static inline int ip_conntrack_register_notifier(struct notifier_block *nb) -{ - return atomic_notifier_chain_register(&ip_conntrack_chain, nb); -} - -static inline int ip_conntrack_unregister_notifier(struct notifier_block *nb) -{ - return atomic_notifier_chain_unregister(&ip_conntrack_chain, nb); -} - -static inline int -ip_conntrack_expect_register_notifier(struct notifier_block *nb) -{ - return atomic_notifier_chain_register(&ip_conntrack_expect_chain, nb); -} - -static inline int -ip_conntrack_expect_unregister_notifier(struct notifier_block *nb) -{ - return atomic_notifier_chain_unregister(&ip_conntrack_expect_chain, - nb); -} - -extern void ip_ct_deliver_cached_events(const struct ip_conntrack *ct); -extern void __ip_ct_event_cache_init(struct ip_conntrack *ct); - -static inline void -ip_conntrack_event_cache(enum ip_conntrack_events event, - const struct sk_buff *skb) -{ - struct ip_conntrack *ct = (struct ip_conntrack *)skb->nfct; - struct ip_conntrack_ecache *ecache; - - local_bh_disable(); - ecache = &__get_cpu_var(ip_conntrack_ecache); - if (ct != ecache->ct) - __ip_ct_event_cache_init(ct); - ecache->events |= event; - local_bh_enable(); -} - -static inline void ip_conntrack_event(enum ip_conntrack_events event, - struct ip_conntrack *ct) -{ - if (is_confirmed(ct) && !is_dying(ct)) - atomic_notifier_call_chain(&ip_conntrack_chain, event, ct); -} - -static inline void -ip_conntrack_expect_event(enum ip_conntrack_expect_events event, - struct ip_conntrack_expect *exp) -{ - atomic_notifier_call_chain(&ip_conntrack_expect_chain, event, exp); -} -#else /* CONFIG_IP_NF_CONNTRACK_EVENTS */ -static inline void ip_conntrack_event_cache(enum ip_conntrack_events event, - const struct sk_buff *skb) {} -static inline void ip_conntrack_event(enum ip_conntrack_events event, - struct ip_conntrack *ct) {} -static inline void ip_ct_deliver_cached_events(const struct ip_conntrack *ct) {} -static inline void -ip_conntrack_expect_event(enum ip_conntrack_expect_events event, - struct ip_conntrack_expect *exp) {} -#endif /* CONFIG_IP_NF_CONNTRACK_EVENTS */ - -#ifdef CONFIG_IP_NF_NAT_NEEDED -static inline int ip_nat_initialized(struct ip_conntrack *conntrack, - enum ip_nat_manip_type manip) -{ - if (manip == IP_NAT_MANIP_SRC) - return test_bit(IPS_SRC_NAT_DONE_BIT, &conntrack->status); - return test_bit(IPS_DST_NAT_DONE_BIT, &conntrack->status); -} -#endif /* CONFIG_IP_NF_NAT_NEEDED */ - -#endif /* __KERNEL__ */ -#endif /* _IP_CONNTRACK_H */ diff --git a/include/linux/netfilter_ipv4/ip_conntrack_amanda.h b/include/linux/netfilter_ipv4/ip_conntrack_amanda.h deleted file mode 100644 index de3e41f51aec..000000000000 --- a/include/linux/netfilter_ipv4/ip_conntrack_amanda.h +++ /dev/null @@ -1,11 +0,0 @@ -#ifndef _IP_CONNTRACK_AMANDA_H -#define _IP_CONNTRACK_AMANDA_H -/* AMANDA tracking. */ - -struct ip_conntrack_expect; -extern unsigned int (*ip_nat_amanda_hook)(struct sk_buff **pskb, - enum ip_conntrack_info ctinfo, - unsigned int matchoff, - unsigned int matchlen, - struct ip_conntrack_expect *exp); -#endif /* _IP_CONNTRACK_AMANDA_H */ diff --git a/include/linux/netfilter_ipv4/ip_conntrack_core.h b/include/linux/netfilter_ipv4/ip_conntrack_core.h deleted file mode 100644 index e3a6df07aa4b..000000000000 --- a/include/linux/netfilter_ipv4/ip_conntrack_core.h +++ /dev/null @@ -1,61 +0,0 @@ -#ifndef _IP_CONNTRACK_CORE_H -#define _IP_CONNTRACK_CORE_H -#include - -#define MAX_IP_CT_PROTO 256 -extern struct ip_conntrack_protocol *ip_ct_protos[MAX_IP_CT_PROTO]; - -/* This header is used to share core functionality between the - standalone connection tracking module, and the compatibility layer's use - of connection tracking. */ -extern unsigned int ip_conntrack_in(unsigned int hooknum, - struct sk_buff **pskb, - const struct net_device *in, - const struct net_device *out, - int (*okfn)(struct sk_buff *)); - -extern int ip_conntrack_init(void); -extern void ip_conntrack_cleanup(void); - -struct ip_conntrack_protocol; - -extern int -ip_ct_get_tuple(const struct iphdr *iph, - const struct sk_buff *skb, - unsigned int dataoff, - struct ip_conntrack_tuple *tuple, - const struct ip_conntrack_protocol *protocol); - -extern int -ip_ct_invert_tuple(struct ip_conntrack_tuple *inverse, - const struct ip_conntrack_tuple *orig, - const struct ip_conntrack_protocol *protocol); - -/* Find a connection corresponding to a tuple. */ -struct ip_conntrack_tuple_hash * -ip_conntrack_find_get(const struct ip_conntrack_tuple *tuple, - const struct ip_conntrack *ignored_conntrack); - -extern int __ip_conntrack_confirm(struct sk_buff **pskb); - -/* Confirm a connection: returns NF_DROP if packet must be dropped. */ -static inline int ip_conntrack_confirm(struct sk_buff **pskb) -{ - struct ip_conntrack *ct = (struct ip_conntrack *)(*pskb)->nfct; - int ret = NF_ACCEPT; - - if (ct) { - if (!is_confirmed(ct) && !is_dying(ct)) - ret = __ip_conntrack_confirm(pskb); - ip_ct_deliver_cached_events(ct); - } - return ret; -} - -extern void ip_ct_unlink_expect(struct ip_conntrack_expect *exp); - -extern struct list_head *ip_conntrack_hash; -extern struct list_head ip_conntrack_expect_list; -extern rwlock_t ip_conntrack_lock; -#endif /* _IP_CONNTRACK_CORE_H */ - diff --git a/include/linux/netfilter_ipv4/ip_conntrack_ftp.h b/include/linux/netfilter_ipv4/ip_conntrack_ftp.h deleted file mode 100644 index 2129fc3972ac..000000000000 --- a/include/linux/netfilter_ipv4/ip_conntrack_ftp.h +++ /dev/null @@ -1,44 +0,0 @@ -#ifndef _IP_CONNTRACK_FTP_H -#define _IP_CONNTRACK_FTP_H -/* FTP tracking. */ - -/* This enum is exposed to userspace */ -enum ip_ct_ftp_type -{ - /* PORT command from client */ - IP_CT_FTP_PORT, - /* PASV response from server */ - IP_CT_FTP_PASV, - /* EPRT command from client */ - IP_CT_FTP_EPRT, - /* EPSV response from server */ - IP_CT_FTP_EPSV, -}; - -#ifdef __KERNEL__ - -#define FTP_PORT 21 - -#define NUM_SEQ_TO_REMEMBER 2 -/* This structure exists only once per master */ -struct ip_ct_ftp_master { - /* Valid seq positions for cmd matching after newline */ - u_int32_t seq_aft_nl[IP_CT_DIR_MAX][NUM_SEQ_TO_REMEMBER]; - /* 0 means seq_match_aft_nl not set */ - int seq_aft_nl_num[IP_CT_DIR_MAX]; -}; - -struct ip_conntrack_expect; - -/* For NAT to hook in when we find a packet which describes what other - * connection we should expect. */ -extern unsigned int (*ip_nat_ftp_hook)(struct sk_buff **pskb, - enum ip_conntrack_info ctinfo, - enum ip_ct_ftp_type type, - unsigned int matchoff, - unsigned int matchlen, - struct ip_conntrack_expect *exp, - u32 *seq); -#endif /* __KERNEL__ */ - -#endif /* _IP_CONNTRACK_FTP_H */ diff --git a/include/linux/netfilter_ipv4/ip_conntrack_h323.h b/include/linux/netfilter_ipv4/ip_conntrack_h323.h deleted file mode 100644 index 18f769818f4e..000000000000 --- a/include/linux/netfilter_ipv4/ip_conntrack_h323.h +++ /dev/null @@ -1,89 +0,0 @@ -#ifndef _IP_CONNTRACK_H323_H -#define _IP_CONNTRACK_H323_H - -#ifdef __KERNEL__ - -#include - -#define RAS_PORT 1719 -#define Q931_PORT 1720 -#define H323_RTP_CHANNEL_MAX 4 /* Audio, video, FAX and other */ - -/* This structure exists only once per master */ -struct ip_ct_h323_master { - - /* Original and NATed Q.931 or H.245 signal ports */ - u_int16_t sig_port[IP_CT_DIR_MAX]; - - /* Original and NATed RTP ports */ - u_int16_t rtp_port[H323_RTP_CHANNEL_MAX][IP_CT_DIR_MAX]; - - union { - /* RAS connection timeout */ - u_int32_t timeout; - - /* Next TPKT length (for separate TPKT header and data) */ - u_int16_t tpkt_len[IP_CT_DIR_MAX]; - }; -}; - -struct ip_conntrack_expect; - -extern int get_h225_addr(unsigned char *data, TransportAddress * addr, - __be32 * ip, u_int16_t * port); -extern void ip_conntrack_h245_expect(struct ip_conntrack *new, - struct ip_conntrack_expect *this); -extern void ip_conntrack_q931_expect(struct ip_conntrack *new, - struct ip_conntrack_expect *this); -extern int (*set_h245_addr_hook) (struct sk_buff ** pskb, - unsigned char **data, int dataoff, - H245_TransportAddress * addr, - __be32 ip, u_int16_t port); -extern int (*set_h225_addr_hook) (struct sk_buff ** pskb, - unsigned char **data, int dataoff, - TransportAddress * addr, - __be32 ip, u_int16_t port); -extern int (*set_sig_addr_hook) (struct sk_buff ** pskb, - struct ip_conntrack * ct, - enum ip_conntrack_info ctinfo, - unsigned char **data, - TransportAddress * addr, int count); -extern int (*set_ras_addr_hook) (struct sk_buff ** pskb, - struct ip_conntrack * ct, - enum ip_conntrack_info ctinfo, - unsigned char **data, - TransportAddress * addr, int count); -extern int (*nat_rtp_rtcp_hook) (struct sk_buff ** pskb, - struct ip_conntrack * ct, - enum ip_conntrack_info ctinfo, - unsigned char **data, int dataoff, - H245_TransportAddress * addr, - u_int16_t port, u_int16_t rtp_port, - struct ip_conntrack_expect * rtp_exp, - struct ip_conntrack_expect * rtcp_exp); -extern int (*nat_t120_hook) (struct sk_buff ** pskb, struct ip_conntrack * ct, - enum ip_conntrack_info ctinfo, - unsigned char **data, int dataoff, - H245_TransportAddress * addr, u_int16_t port, - struct ip_conntrack_expect * exp); -extern int (*nat_h245_hook) (struct sk_buff ** pskb, struct ip_conntrack * ct, - enum ip_conntrack_info ctinfo, - unsigned char **data, int dataoff, - TransportAddress * addr, u_int16_t port, - struct ip_conntrack_expect * exp); -extern int (*nat_callforwarding_hook) (struct sk_buff ** pskb, - struct ip_conntrack * ct, - enum ip_conntrack_info ctinfo, - unsigned char **data, int dataoff, - TransportAddress * addr, - u_int16_t port, - struct ip_conntrack_expect * exp); -extern int (*nat_q931_hook) (struct sk_buff ** pskb, struct ip_conntrack * ct, - enum ip_conntrack_info ctinfo, - unsigned char **data, TransportAddress * addr, - int idx, u_int16_t port, - struct ip_conntrack_expect * exp); - -#endif - -#endif diff --git a/include/linux/netfilter_ipv4/ip_conntrack_helper.h b/include/linux/netfilter_ipv4/ip_conntrack_helper.h deleted file mode 100644 index 77fe868d36ff..000000000000 --- a/include/linux/netfilter_ipv4/ip_conntrack_helper.h +++ /dev/null @@ -1,46 +0,0 @@ -/* IP connection tracking helpers. */ -#ifndef _IP_CONNTRACK_HELPER_H -#define _IP_CONNTRACK_HELPER_H -#include - -struct module; - -struct ip_conntrack_helper -{ - struct list_head list; /* Internal use. */ - - const char *name; /* name of the module */ - struct module *me; /* pointer to self */ - unsigned int max_expected; /* Maximum number of concurrent - * expected connections */ - unsigned int timeout; /* timeout for expecteds */ - - /* Mask of things we will help (compared against server response) */ - struct ip_conntrack_tuple tuple; - struct ip_conntrack_tuple mask; - - /* Function to call when data passes; return verdict, or -1 to - invalidate. */ - int (*help)(struct sk_buff **pskb, - struct ip_conntrack *ct, - enum ip_conntrack_info conntrackinfo); - - void (*destroy)(struct ip_conntrack *ct); - - int (*to_nfattr)(struct sk_buff *skb, const struct ip_conntrack *ct); -}; - -extern int ip_conntrack_helper_register(struct ip_conntrack_helper *); -extern void ip_conntrack_helper_unregister(struct ip_conntrack_helper *); - -/* Allocate space for an expectation: this is mandatory before calling - ip_conntrack_expect_related. You will have to call put afterwards. */ -extern struct ip_conntrack_expect * -ip_conntrack_expect_alloc(struct ip_conntrack *master); -extern void ip_conntrack_expect_put(struct ip_conntrack_expect *exp); - -/* Add an expected connection: can have more than one per connection */ -extern int ip_conntrack_expect_related(struct ip_conntrack_expect *exp); -extern void ip_conntrack_unexpect_related(struct ip_conntrack_expect *exp); - -#endif /*_IP_CONNTRACK_HELPER_H*/ diff --git a/include/linux/netfilter_ipv4/ip_conntrack_icmp.h b/include/linux/netfilter_ipv4/ip_conntrack_icmp.h deleted file mode 100644 index eed5ee3e4744..000000000000 --- a/include/linux/netfilter_ipv4/ip_conntrack_icmp.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef _IP_CONNTRACK_ICMP_H -#define _IP_CONNTRACK_ICMP_H - -#include - -#endif /* _IP_CONNTRACK_ICMP_H */ diff --git a/include/linux/netfilter_ipv4/ip_conntrack_irc.h b/include/linux/netfilter_ipv4/ip_conntrack_irc.h deleted file mode 100644 index 16601e0d5626..000000000000 --- a/include/linux/netfilter_ipv4/ip_conntrack_irc.h +++ /dev/null @@ -1,32 +0,0 @@ -/* IRC extension for IP connection tracking. - * (C) 2000 by Harald Welte - * based on RR's ip_conntrack_ftp.h - * - * ip_conntrack_irc.h,v 1.6 2000/11/07 18:26:42 laforge Exp - * - * 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. - * - * - */ -#ifndef _IP_CONNTRACK_IRC_H -#define _IP_CONNTRACK_IRC_H - -/* This structure exists only once per master */ -struct ip_ct_irc_master { -}; - -#ifdef __KERNEL__ -extern unsigned int (*ip_nat_irc_hook)(struct sk_buff **pskb, - enum ip_conntrack_info ctinfo, - unsigned int matchoff, - unsigned int matchlen, - struct ip_conntrack_expect *exp); - -#define IRC_PORT 6667 - -#endif /* __KERNEL__ */ - -#endif /* _IP_CONNTRACK_IRC_H */ diff --git a/include/linux/netfilter_ipv4/ip_conntrack_pptp.h b/include/linux/netfilter_ipv4/ip_conntrack_pptp.h deleted file mode 100644 index 2644b1faddd6..000000000000 --- a/include/linux/netfilter_ipv4/ip_conntrack_pptp.h +++ /dev/null @@ -1,326 +0,0 @@ -/* PPTP constants and structs */ -#ifndef _CONNTRACK_PPTP_H -#define _CONNTRACK_PPTP_H - -/* state of the control session */ -enum pptp_ctrlsess_state { - PPTP_SESSION_NONE, /* no session present */ - PPTP_SESSION_ERROR, /* some session error */ - PPTP_SESSION_STOPREQ, /* stop_sess request seen */ - PPTP_SESSION_REQUESTED, /* start_sess request seen */ - PPTP_SESSION_CONFIRMED, /* session established */ -}; - -/* state of the call inside the control session */ -enum pptp_ctrlcall_state { - PPTP_CALL_NONE, - PPTP_CALL_ERROR, - PPTP_CALL_OUT_REQ, - PPTP_CALL_OUT_CONF, - PPTP_CALL_IN_REQ, - PPTP_CALL_IN_REP, - PPTP_CALL_IN_CONF, - PPTP_CALL_CLEAR_REQ, -}; - - -/* conntrack private data */ -struct ip_ct_pptp_master { - enum pptp_ctrlsess_state sstate; /* session state */ - - /* everything below is going to be per-expectation in newnat, - * since there could be more than one call within one session */ - enum pptp_ctrlcall_state cstate; /* call state */ - __be16 pac_call_id; /* call id of PAC, host byte order */ - __be16 pns_call_id; /* call id of PNS, host byte order */ - - /* in pre-2.6.11 this used to be per-expect. Now it is per-conntrack - * and therefore imposes a fixed limit on the number of maps */ - struct ip_ct_gre_keymap *keymap_orig, *keymap_reply; -}; - -/* conntrack_expect private member */ -struct ip_ct_pptp_expect { - enum pptp_ctrlcall_state cstate; /* call state */ - __be16 pac_call_id; /* call id of PAC */ - __be16 pns_call_id; /* call id of PNS */ -}; - - -#ifdef __KERNEL__ - -#define IP_CONNTR_PPTP PPTP_CONTROL_PORT - -#define PPTP_CONTROL_PORT 1723 - -#define PPTP_PACKET_CONTROL 1 -#define PPTP_PACKET_MGMT 2 - -#define PPTP_MAGIC_COOKIE 0x1a2b3c4d - -struct pptp_pkt_hdr { - __u16 packetLength; - __be16 packetType; - __be32 magicCookie; -}; - -/* PptpControlMessageType values */ -#define PPTP_START_SESSION_REQUEST 1 -#define PPTP_START_SESSION_REPLY 2 -#define PPTP_STOP_SESSION_REQUEST 3 -#define PPTP_STOP_SESSION_REPLY 4 -#define PPTP_ECHO_REQUEST 5 -#define PPTP_ECHO_REPLY 6 -#define PPTP_OUT_CALL_REQUEST 7 -#define PPTP_OUT_CALL_REPLY 8 -#define PPTP_IN_CALL_REQUEST 9 -#define PPTP_IN_CALL_REPLY 10 -#define PPTP_IN_CALL_CONNECT 11 -#define PPTP_CALL_CLEAR_REQUEST 12 -#define PPTP_CALL_DISCONNECT_NOTIFY 13 -#define PPTP_WAN_ERROR_NOTIFY 14 -#define PPTP_SET_LINK_INFO 15 - -#define PPTP_MSG_MAX 15 - -/* PptpGeneralError values */ -#define PPTP_ERROR_CODE_NONE 0 -#define PPTP_NOT_CONNECTED 1 -#define PPTP_BAD_FORMAT 2 -#define PPTP_BAD_VALUE 3 -#define PPTP_NO_RESOURCE 4 -#define PPTP_BAD_CALLID 5 -#define PPTP_REMOVE_DEVICE_ERROR 6 - -struct PptpControlHeader { - __be16 messageType; - __u16 reserved; -}; - -/* FramingCapability Bitmap Values */ -#define PPTP_FRAME_CAP_ASYNC 0x1 -#define PPTP_FRAME_CAP_SYNC 0x2 - -/* BearerCapability Bitmap Values */ -#define PPTP_BEARER_CAP_ANALOG 0x1 -#define PPTP_BEARER_CAP_DIGITAL 0x2 - -struct PptpStartSessionRequest { - __be16 protocolVersion; - __u16 reserved1; - __be32 framingCapability; - __be32 bearerCapability; - __be16 maxChannels; - __be16 firmwareRevision; - __u8 hostName[64]; - __u8 vendorString[64]; -}; - -/* PptpStartSessionResultCode Values */ -#define PPTP_START_OK 1 -#define PPTP_START_GENERAL_ERROR 2 -#define PPTP_START_ALREADY_CONNECTED 3 -#define PPTP_START_NOT_AUTHORIZED 4 -#define PPTP_START_UNKNOWN_PROTOCOL 5 - -struct PptpStartSessionReply { - __be16 protocolVersion; - __u8 resultCode; - __u8 generalErrorCode; - __be32 framingCapability; - __be32 bearerCapability; - __be16 maxChannels; - __be16 firmwareRevision; - __u8 hostName[64]; - __u8 vendorString[64]; -}; - -/* PptpStopReasons */ -#define PPTP_STOP_NONE 1 -#define PPTP_STOP_PROTOCOL 2 -#define PPTP_STOP_LOCAL_SHUTDOWN 3 - -struct PptpStopSessionRequest { - __u8 reason; - __u8 reserved1; - __u16 reserved2; -}; - -/* PptpStopSessionResultCode */ -#define PPTP_STOP_OK 1 -#define PPTP_STOP_GENERAL_ERROR 2 - -struct PptpStopSessionReply { - __u8 resultCode; - __u8 generalErrorCode; - __u16 reserved1; -}; - -struct PptpEchoRequest { - __be32 identNumber; -}; - -/* PptpEchoReplyResultCode */ -#define PPTP_ECHO_OK 1 -#define PPTP_ECHO_GENERAL_ERROR 2 - -struct PptpEchoReply { - __be32 identNumber; - __u8 resultCode; - __u8 generalErrorCode; - __u16 reserved; -}; - -/* PptpFramingType */ -#define PPTP_ASYNC_FRAMING 1 -#define PPTP_SYNC_FRAMING 2 -#define PPTP_DONT_CARE_FRAMING 3 - -/* PptpCallBearerType */ -#define PPTP_ANALOG_TYPE 1 -#define PPTP_DIGITAL_TYPE 2 -#define PPTP_DONT_CARE_BEARER_TYPE 3 - -struct PptpOutCallRequest { - __be16 callID; - __be16 callSerialNumber; - __be32 minBPS; - __be32 maxBPS; - __be32 bearerType; - __be32 framingType; - __be16 packetWindow; - __be16 packetProcDelay; - __be16 phoneNumberLength; - __u16 reserved1; - __u8 phoneNumber[64]; - __u8 subAddress[64]; -}; - -/* PptpCallResultCode */ -#define PPTP_OUTCALL_CONNECT 1 -#define PPTP_OUTCALL_GENERAL_ERROR 2 -#define PPTP_OUTCALL_NO_CARRIER 3 -#define PPTP_OUTCALL_BUSY 4 -#define PPTP_OUTCALL_NO_DIAL_TONE 5 -#define PPTP_OUTCALL_TIMEOUT 6 -#define PPTP_OUTCALL_DONT_ACCEPT 7 - -struct PptpOutCallReply { - __be16 callID; - __be16 peersCallID; - __u8 resultCode; - __u8 generalErrorCode; - __be16 causeCode; - __be32 connectSpeed; - __be16 packetWindow; - __be16 packetProcDelay; - __be32 physChannelID; -}; - -struct PptpInCallRequest { - __be16 callID; - __be16 callSerialNumber; - __be32 callBearerType; - __be32 physChannelID; - __be16 dialedNumberLength; - __be16 dialingNumberLength; - __u8 dialedNumber[64]; - __u8 dialingNumber[64]; - __u8 subAddress[64]; -}; - -/* PptpInCallResultCode */ -#define PPTP_INCALL_ACCEPT 1 -#define PPTP_INCALL_GENERAL_ERROR 2 -#define PPTP_INCALL_DONT_ACCEPT 3 - -struct PptpInCallReply { - __be16 callID; - __be16 peersCallID; - __u8 resultCode; - __u8 generalErrorCode; - __be16 packetWindow; - __be16 packetProcDelay; - __u16 reserved; -}; - -struct PptpInCallConnected { - __be16 peersCallID; - __u16 reserved; - __be32 connectSpeed; - __be16 packetWindow; - __be16 packetProcDelay; - __be32 callFramingType; -}; - -struct PptpClearCallRequest { - __be16 callID; - __u16 reserved; -}; - -struct PptpCallDisconnectNotify { - __be16 callID; - __u8 resultCode; - __u8 generalErrorCode; - __be16 causeCode; - __u16 reserved; - __u8 callStatistics[128]; -}; - -struct PptpWanErrorNotify { - __be16 peersCallID; - __u16 reserved; - __be32 crcErrors; - __be32 framingErrors; - __be32 hardwareOverRuns; - __be32 bufferOverRuns; - __be32 timeoutErrors; - __be32 alignmentErrors; -}; - -struct PptpSetLinkInfo { - __be16 peersCallID; - __u16 reserved; - __be32 sendAccm; - __be32 recvAccm; -}; - -union pptp_ctrl_union { - struct PptpStartSessionRequest sreq; - struct PptpStartSessionReply srep; - struct PptpStopSessionRequest streq; - struct PptpStopSessionReply strep; - struct PptpOutCallRequest ocreq; - struct PptpOutCallReply ocack; - struct PptpInCallRequest icreq; - struct PptpInCallReply icack; - struct PptpInCallConnected iccon; - struct PptpClearCallRequest clrreq; - struct PptpCallDisconnectNotify disc; - struct PptpWanErrorNotify wanerr; - struct PptpSetLinkInfo setlink; -}; - -extern int -(*ip_nat_pptp_hook_outbound)(struct sk_buff **pskb, - struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo, - struct PptpControlHeader *ctlh, - union pptp_ctrl_union *pptpReq); - -extern int -(*ip_nat_pptp_hook_inbound)(struct sk_buff **pskb, - struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo, - struct PptpControlHeader *ctlh, - union pptp_ctrl_union *pptpReq); - -extern void -(*ip_nat_pptp_hook_exp_gre)(struct ip_conntrack_expect *exp_orig, - struct ip_conntrack_expect *exp_reply); - -extern void -(*ip_nat_pptp_hook_expectfn)(struct ip_conntrack *ct, - struct ip_conntrack_expect *exp); -#endif /* __KERNEL__ */ -#endif /* _CONNTRACK_PPTP_H */ diff --git a/include/linux/netfilter_ipv4/ip_conntrack_proto_gre.h b/include/linux/netfilter_ipv4/ip_conntrack_proto_gre.h deleted file mode 100644 index e371e0fc1672..000000000000 --- a/include/linux/netfilter_ipv4/ip_conntrack_proto_gre.h +++ /dev/null @@ -1,114 +0,0 @@ -#ifndef _CONNTRACK_PROTO_GRE_H -#define _CONNTRACK_PROTO_GRE_H -#include - -/* GRE PROTOCOL HEADER */ - -/* GRE Version field */ -#define GRE_VERSION_1701 0x0 -#define GRE_VERSION_PPTP 0x1 - -/* GRE Protocol field */ -#define GRE_PROTOCOL_PPTP 0x880B - -/* GRE Flags */ -#define GRE_FLAG_C 0x80 -#define GRE_FLAG_R 0x40 -#define GRE_FLAG_K 0x20 -#define GRE_FLAG_S 0x10 -#define GRE_FLAG_A 0x80 - -#define GRE_IS_C(f) ((f)&GRE_FLAG_C) -#define GRE_IS_R(f) ((f)&GRE_FLAG_R) -#define GRE_IS_K(f) ((f)&GRE_FLAG_K) -#define GRE_IS_S(f) ((f)&GRE_FLAG_S) -#define GRE_IS_A(f) ((f)&GRE_FLAG_A) - -/* GRE is a mess: Four different standards */ -struct gre_hdr { -#if defined(__LITTLE_ENDIAN_BITFIELD) - __u16 rec:3, - srr:1, - seq:1, - key:1, - routing:1, - csum:1, - version:3, - reserved:4, - ack:1; -#elif defined(__BIG_ENDIAN_BITFIELD) - __u16 csum:1, - routing:1, - key:1, - seq:1, - srr:1, - rec:3, - ack:1, - reserved:4, - version:3; -#else -#error "Adjust your defines" -#endif - __be16 protocol; -}; - -/* modified GRE header for PPTP */ -struct gre_hdr_pptp { - __u8 flags; /* bitfield */ - __u8 version; /* should be GRE_VERSION_PPTP */ - __be16 protocol; /* should be GRE_PROTOCOL_PPTP */ - __be16 payload_len; /* size of ppp payload, not inc. gre header */ - __be16 call_id; /* peer's call_id for this session */ - __be32 seq; /* sequence number. Present if S==1 */ - __be32 ack; /* seq number of highest packet recieved by */ - /* sender in this session */ -}; - - -/* this is part of ip_conntrack */ -struct ip_ct_gre { - unsigned int stream_timeout; - unsigned int timeout; -}; - -#ifdef __KERNEL__ -struct ip_conntrack_expect; -struct ip_conntrack; - -/* structure for original <-> reply keymap */ -struct ip_ct_gre_keymap { - struct list_head list; - - struct ip_conntrack_tuple tuple; -}; - -/* add new tuple->key_reply pair to keymap */ -int ip_ct_gre_keymap_add(struct ip_conntrack *ct, - struct ip_conntrack_tuple *t, - int reply); - -/* delete keymap entries */ -void ip_ct_gre_keymap_destroy(struct ip_conntrack *ct); - - -/* get pointer to gre key, if present */ -static inline __be32 *gre_key(struct gre_hdr *greh) -{ - if (!greh->key) - return NULL; - if (greh->csum || greh->routing) - return (__be32 *) (greh+sizeof(*greh)+4); - return (__be32 *) (greh+sizeof(*greh)); -} - -/* get pointer ot gre csum, if present */ -static inline __sum16 *gre_csum(struct gre_hdr *greh) -{ - if (!greh->csum) - return NULL; - return (__sum16 *) (greh+sizeof(*greh)); -} - -#endif /* __KERNEL__ */ - -#endif /* _CONNTRACK_PROTO_GRE_H */ diff --git a/include/linux/netfilter_ipv4/ip_conntrack_protocol.h b/include/linux/netfilter_ipv4/ip_conntrack_protocol.h deleted file mode 100644 index 2c76b879e3dc..000000000000 --- a/include/linux/netfilter_ipv4/ip_conntrack_protocol.h +++ /dev/null @@ -1,98 +0,0 @@ -/* Header for use in defining a given protocol for connection tracking. */ -#ifndef _IP_CONNTRACK_PROTOCOL_H -#define _IP_CONNTRACK_PROTOCOL_H -#include -#include - -struct seq_file; - -struct ip_conntrack_protocol -{ - /* Protocol number. */ - u_int8_t proto; - - /* Protocol name */ - const char *name; - - /* Try to fill in the third arg: dataoff is offset past IP - hdr. Return true if possible. */ - int (*pkt_to_tuple)(const struct sk_buff *skb, - unsigned int dataoff, - struct ip_conntrack_tuple *tuple); - - /* Invert the per-proto part of the tuple: ie. turn xmit into reply. - * Some packets can't be inverted: return 0 in that case. - */ - int (*invert_tuple)(struct ip_conntrack_tuple *inverse, - const struct ip_conntrack_tuple *orig); - - /* Print out the per-protocol part of the tuple. Return like seq_* */ - int (*print_tuple)(struct seq_file *, - const struct ip_conntrack_tuple *); - - /* Print out the private part of the conntrack. */ - int (*print_conntrack)(struct seq_file *, const struct ip_conntrack *); - - /* Returns verdict for packet, or -1 for invalid. */ - int (*packet)(struct ip_conntrack *conntrack, - const struct sk_buff *skb, - enum ip_conntrack_info ctinfo); - - /* Called when a new connection for this protocol found; - * returns TRUE if it's OK. If so, packet() called next. */ - int (*new)(struct ip_conntrack *conntrack, const struct sk_buff *skb); - - /* Called when a conntrack entry is destroyed */ - void (*destroy)(struct ip_conntrack *conntrack); - - int (*error)(struct sk_buff *skb, enum ip_conntrack_info *ctinfo, - unsigned int hooknum); - - /* convert protoinfo to nfnetink attributes */ - int (*to_nfattr)(struct sk_buff *skb, struct nfattr *nfa, - const struct ip_conntrack *ct); - - /* convert nfnetlink attributes to protoinfo */ - int (*from_nfattr)(struct nfattr *tb[], struct ip_conntrack *ct); - - int (*tuple_to_nfattr)(struct sk_buff *skb, - const struct ip_conntrack_tuple *t); - int (*nfattr_to_tuple)(struct nfattr *tb[], - struct ip_conntrack_tuple *t); - - /* Module (if any) which this is connected to. */ - struct module *me; -}; - -/* Protocol registration. */ -extern int ip_conntrack_protocol_register(struct ip_conntrack_protocol *proto); -extern void ip_conntrack_protocol_unregister(struct ip_conntrack_protocol *proto); -/* Existing built-in protocols */ -extern struct ip_conntrack_protocol ip_conntrack_protocol_tcp; -extern struct ip_conntrack_protocol ip_conntrack_protocol_udp; -extern struct ip_conntrack_protocol ip_conntrack_protocol_icmp; -extern struct ip_conntrack_protocol ip_conntrack_generic_protocol; -extern int ip_conntrack_protocol_tcp_init(void); - -/* Log invalid packets */ -extern unsigned int ip_ct_log_invalid; - -extern int ip_ct_port_tuple_to_nfattr(struct sk_buff *, - const struct ip_conntrack_tuple *); -extern int ip_ct_port_nfattr_to_tuple(struct nfattr *tb[], - struct ip_conntrack_tuple *); - -#ifdef CONFIG_SYSCTL -#ifdef DEBUG_INVALID_PACKETS -#define LOG_INVALID(proto) \ - (ip_ct_log_invalid == (proto) || ip_ct_log_invalid == IPPROTO_RAW) -#else -#define LOG_INVALID(proto) \ - ((ip_ct_log_invalid == (proto) || ip_ct_log_invalid == IPPROTO_RAW) \ - && net_ratelimit()) -#endif -#else -#define LOG_INVALID(proto) 0 -#endif /* CONFIG_SYSCTL */ - -#endif /*_IP_CONNTRACK_PROTOCOL_H*/ diff --git a/include/linux/netfilter_ipv4/ip_conntrack_sctp.h b/include/linux/netfilter_ipv4/ip_conntrack_sctp.h deleted file mode 100644 index 4099a041a32a..000000000000 --- a/include/linux/netfilter_ipv4/ip_conntrack_sctp.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef _IP_CONNTRACK_SCTP_H -#define _IP_CONNTRACK_SCTP_H - -#include - -#endif /* _IP_CONNTRACK_SCTP_H */ diff --git a/include/linux/netfilter_ipv4/ip_conntrack_sip.h b/include/linux/netfilter_ipv4/ip_conntrack_sip.h deleted file mode 100644 index bef6c646defa..000000000000 --- a/include/linux/netfilter_ipv4/ip_conntrack_sip.h +++ /dev/null @@ -1,40 +0,0 @@ -#ifndef __IP_CONNTRACK_SIP_H__ -#define __IP_CONNTRACK_SIP_H__ -#ifdef __KERNEL__ - -#define SIP_PORT 5060 -#define SIP_TIMEOUT 3600 - -enum sip_header_pos { - POS_REG_REQ_URI, - POS_REQ_URI, - POS_FROM, - POS_TO, - POS_VIA, - POS_CONTACT, - POS_CONTENT, - POS_MEDIA, - POS_OWNER, - POS_CONNECTION, - POS_SDP_HEADER, -}; - -extern unsigned int (*ip_nat_sip_hook)(struct sk_buff **pskb, - enum ip_conntrack_info ctinfo, - struct ip_conntrack *ct, - const char **dptr); -extern unsigned int (*ip_nat_sdp_hook)(struct sk_buff **pskb, - enum ip_conntrack_info ctinfo, - struct ip_conntrack_expect *exp, - const char *dptr); - -extern int ct_sip_get_info(const char *dptr, size_t dlen, - unsigned int *matchoff, - unsigned int *matchlen, - enum sip_header_pos pos); -extern int ct_sip_lnlen(const char *line, const char *limit); -extern const char *ct_sip_search(const char *needle, const char *haystack, - size_t needle_len, size_t haystack_len, - int case_sensitive); -#endif /* __KERNEL__ */ -#endif /* __IP_CONNTRACK_SIP_H__ */ diff --git a/include/linux/netfilter_ipv4/ip_conntrack_tcp.h b/include/linux/netfilter_ipv4/ip_conntrack_tcp.h deleted file mode 100644 index 876b8fb17e68..000000000000 --- a/include/linux/netfilter_ipv4/ip_conntrack_tcp.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef _IP_CONNTRACK_TCP_H -#define _IP_CONNTRACK_TCP_H - -#include - -#endif /* _IP_CONNTRACK_TCP_H */ diff --git a/include/linux/netfilter_ipv4/ip_conntrack_tftp.h b/include/linux/netfilter_ipv4/ip_conntrack_tftp.h deleted file mode 100644 index a404fc0abf0e..000000000000 --- a/include/linux/netfilter_ipv4/ip_conntrack_tftp.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef _IP_CT_TFTP -#define _IP_CT_TFTP - -#define TFTP_PORT 69 - -struct tftphdr { - __be16 opcode; -}; - -#define TFTP_OPCODE_READ 1 -#define TFTP_OPCODE_WRITE 2 -#define TFTP_OPCODE_DATA 3 -#define TFTP_OPCODE_ACK 4 -#define TFTP_OPCODE_ERROR 5 - -extern unsigned int (*ip_nat_tftp_hook)(struct sk_buff **pskb, - enum ip_conntrack_info ctinfo, - struct ip_conntrack_expect *exp); - -#endif /* _IP_CT_TFTP */ diff --git a/include/linux/netfilter_ipv4/ip_conntrack_tuple.h b/include/linux/netfilter_ipv4/ip_conntrack_tuple.h deleted file mode 100644 index c228bde74c33..000000000000 --- a/include/linux/netfilter_ipv4/ip_conntrack_tuple.h +++ /dev/null @@ -1,146 +0,0 @@ -#ifndef _IP_CONNTRACK_TUPLE_H -#define _IP_CONNTRACK_TUPLE_H - -#include -#include - -/* A `tuple' is a structure containing the information to uniquely - identify a connection. ie. if two packets have the same tuple, they - are in the same connection; if not, they are not. - - We divide the structure along "manipulatable" and - "non-manipulatable" lines, for the benefit of the NAT code. -*/ - -/* The protocol-specific manipulable parts of the tuple: always in - network order! */ -union ip_conntrack_manip_proto -{ - /* Add other protocols here. */ - u_int16_t all; - - struct { - __be16 port; - } tcp; - struct { - __be16 port; - } udp; - struct { - __be16 id; - } icmp; - struct { - __be16 port; - } sctp; - struct { - __be16 key; /* key is 32bit, pptp only uses 16 */ - } gre; -}; - -/* The manipulable part of the tuple. */ -struct ip_conntrack_manip -{ - __be32 ip; - union ip_conntrack_manip_proto u; -}; - -/* This contains the information to distinguish a connection. */ -struct ip_conntrack_tuple -{ - struct ip_conntrack_manip src; - - /* These are the parts of the tuple which are fixed. */ - struct { - __be32 ip; - union { - /* Add other protocols here. */ - u_int16_t all; - - struct { - __be16 port; - } tcp; - struct { - __be16 port; - } udp; - struct { - u_int8_t type, code; - } icmp; - struct { - __be16 port; - } sctp; - struct { - __be16 key; /* key is 32bit, - * pptp only uses 16 */ - } gre; - } u; - - /* The protocol. */ - u_int8_t protonum; - - /* The direction (for tuplehash) */ - u_int8_t dir; - } dst; -}; - -/* This is optimized opposed to a memset of the whole structure. Everything we - * really care about is the source/destination unions */ -#define IP_CT_TUPLE_U_BLANK(tuple) \ - do { \ - (tuple)->src.u.all = 0; \ - (tuple)->dst.u.all = 0; \ - } while (0) - -#ifdef __KERNEL__ - -#define DUMP_TUPLE(tp) \ -DEBUGP("tuple %p: %u %u.%u.%u.%u:%hu -> %u.%u.%u.%u:%hu\n", \ - (tp), (tp)->dst.protonum, \ - NIPQUAD((tp)->src.ip), ntohs((tp)->src.u.all), \ - NIPQUAD((tp)->dst.ip), ntohs((tp)->dst.u.all)) - -/* If we're the first tuple, it's the original dir. */ -#define DIRECTION(h) ((enum ip_conntrack_dir)(h)->tuple.dst.dir) - -/* Connections have two entries in the hash table: one for each way */ -struct ip_conntrack_tuple_hash -{ - struct list_head list; - - struct ip_conntrack_tuple tuple; -}; - -#endif /* __KERNEL__ */ - -static inline int ip_ct_tuple_src_equal(const struct ip_conntrack_tuple *t1, - const struct ip_conntrack_tuple *t2) -{ - return t1->src.ip == t2->src.ip - && t1->src.u.all == t2->src.u.all; -} - -static inline int ip_ct_tuple_dst_equal(const struct ip_conntrack_tuple *t1, - const struct ip_conntrack_tuple *t2) -{ - return t1->dst.ip == t2->dst.ip - && t1->dst.u.all == t2->dst.u.all - && t1->dst.protonum == t2->dst.protonum; -} - -static inline int ip_ct_tuple_equal(const struct ip_conntrack_tuple *t1, - const struct ip_conntrack_tuple *t2) -{ - return ip_ct_tuple_src_equal(t1, t2) && ip_ct_tuple_dst_equal(t1, t2); -} - -static inline int ip_ct_tuple_mask_cmp(const struct ip_conntrack_tuple *t, - const struct ip_conntrack_tuple *tuple, - const struct ip_conntrack_tuple *mask) -{ - return !(((t->src.ip ^ tuple->src.ip) & mask->src.ip) - || ((t->dst.ip ^ tuple->dst.ip) & mask->dst.ip) - || ((t->src.u.all ^ tuple->src.u.all) & mask->src.u.all) - || ((t->dst.u.all ^ tuple->dst.u.all) & mask->dst.u.all) - || ((t->dst.protonum ^ tuple->dst.protonum) - & mask->dst.protonum)); -} - -#endif /* _IP_CONNTRACK_TUPLE_H */ diff --git a/include/linux/netfilter_ipv4/ip_nat.h b/include/linux/netfilter_ipv4/ip_nat.h deleted file mode 100644 index bbca89aab813..000000000000 --- a/include/linux/netfilter_ipv4/ip_nat.h +++ /dev/null @@ -1,79 +0,0 @@ -#ifndef _IP_NAT_H -#define _IP_NAT_H -#include -#include - -#define IP_NAT_MAPPING_TYPE_MAX_NAMELEN 16 - -enum ip_nat_manip_type -{ - IP_NAT_MANIP_SRC, - IP_NAT_MANIP_DST -}; - -/* SRC manip occurs POST_ROUTING or LOCAL_IN */ -#define HOOK2MANIP(hooknum) ((hooknum) != NF_IP_POST_ROUTING && (hooknum) != NF_IP_LOCAL_IN) - -#define IP_NAT_RANGE_MAP_IPS 1 -#define IP_NAT_RANGE_PROTO_SPECIFIED 2 -#define IP_NAT_RANGE_PROTO_RANDOM 4 /* add randomness to "port" selection */ - -/* NAT sequence number modifications */ -struct ip_nat_seq { - /* position of the last TCP sequence number - * modification (if any) */ - u_int32_t correction_pos; - /* sequence number offset before and after last modification */ - int16_t offset_before, offset_after; -}; - -/* Single range specification. */ -struct ip_nat_range -{ - /* Set to OR of flags above. */ - unsigned int flags; - - /* Inclusive: network order. */ - __be32 min_ip, max_ip; - - /* Inclusive: network order */ - union ip_conntrack_manip_proto min, max; -}; - -/* For backwards compat: don't use in modern code. */ -struct ip_nat_multi_range_compat -{ - unsigned int rangesize; /* Must be 1. */ - - /* hangs off end. */ - struct ip_nat_range range[1]; -}; - -#ifdef __KERNEL__ -#include - -/* Protects NAT hash tables, and NAT-private part of conntracks. */ -extern rwlock_t ip_nat_lock; - -/* The structure embedded in the conntrack structure. */ -struct ip_nat_info -{ - struct list_head bysource; - struct ip_nat_seq seq[IP_CT_DIR_MAX]; -}; - -struct ip_conntrack; - -/* Set up the info structure to map into this range. */ -extern unsigned int ip_nat_setup_info(struct ip_conntrack *conntrack, - const struct ip_nat_range *range, - unsigned int hooknum); - -/* Is this tuple already taken? (not by us)*/ -extern int ip_nat_used_tuple(const struct ip_conntrack_tuple *tuple, - const struct ip_conntrack *ignored_conntrack); - -#else /* !__KERNEL__: iptables wants this to compile. */ -#define ip_nat_multi_range ip_nat_multi_range_compat -#endif /*__KERNEL__*/ -#endif diff --git a/include/linux/netfilter_ipv4/ip_nat_core.h b/include/linux/netfilter_ipv4/ip_nat_core.h deleted file mode 100644 index 60566f9fd7b3..000000000000 --- a/include/linux/netfilter_ipv4/ip_nat_core.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef _IP_NAT_CORE_H -#define _IP_NAT_CORE_H -#include -#include - -/* This header used to share core functionality between the standalone - NAT module, and the compatibility layer's use of NAT for masquerading. */ - -extern unsigned int ip_nat_packet(struct ip_conntrack *ct, - enum ip_conntrack_info conntrackinfo, - unsigned int hooknum, - struct sk_buff **pskb); - -extern int ip_nat_icmp_reply_translation(struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo, - unsigned int hooknum, - struct sk_buff **pskb); -#endif /* _IP_NAT_CORE_H */ diff --git a/include/linux/netfilter_ipv4/ip_nat_helper.h b/include/linux/netfilter_ipv4/ip_nat_helper.h deleted file mode 100644 index bf9cb105c885..000000000000 --- a/include/linux/netfilter_ipv4/ip_nat_helper.h +++ /dev/null @@ -1,33 +0,0 @@ -#ifndef _IP_NAT_HELPER_H -#define _IP_NAT_HELPER_H -/* NAT protocol helper routines. */ - -#include -#include - -struct sk_buff; - -/* These return true or false. */ -extern int ip_nat_mangle_tcp_packet(struct sk_buff **skb, - struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo, - unsigned int match_offset, - unsigned int match_len, - const char *rep_buffer, - unsigned int rep_len); -extern int ip_nat_mangle_udp_packet(struct sk_buff **skb, - struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo, - unsigned int match_offset, - unsigned int match_len, - const char *rep_buffer, - unsigned int rep_len); -extern int ip_nat_seq_adjust(struct sk_buff **pskb, - struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo); - -/* Setup NAT on this expected conntrack so it follows master, but goes - * to port ct->master->saved_proto. */ -extern void ip_nat_follow_master(struct ip_conntrack *ct, - struct ip_conntrack_expect *this); -#endif diff --git a/include/linux/netfilter_ipv4/ip_nat_pptp.h b/include/linux/netfilter_ipv4/ip_nat_pptp.h deleted file mode 100644 index 36668bf0f373..000000000000 --- a/include/linux/netfilter_ipv4/ip_nat_pptp.h +++ /dev/null @@ -1,11 +0,0 @@ -/* PPTP constants and structs */ -#ifndef _NAT_PPTP_H -#define _NAT_PPTP_H - -/* conntrack private data */ -struct ip_nat_pptp { - __be16 pns_call_id; /* NAT'ed PNS call id */ - __be16 pac_call_id; /* NAT'ed PAC call id */ -}; - -#endif /* _NAT_PPTP_H */ diff --git a/include/linux/netfilter_ipv4/ip_nat_protocol.h b/include/linux/netfilter_ipv4/ip_nat_protocol.h deleted file mode 100644 index 612a43614e7b..000000000000 --- a/include/linux/netfilter_ipv4/ip_nat_protocol.h +++ /dev/null @@ -1,74 +0,0 @@ -/* Header for use in defining a given protocol. */ -#ifndef _IP_NAT_PROTOCOL_H -#define _IP_NAT_PROTOCOL_H -#include -#include - -#include -#include - -struct iphdr; -struct ip_nat_range; - -struct ip_nat_protocol -{ - /* Protocol name */ - const char *name; - - /* Protocol number. */ - unsigned int protonum; - - struct module *me; - - /* Translate a packet to the target according to manip type. - Return true if succeeded. */ - int (*manip_pkt)(struct sk_buff **pskb, - unsigned int iphdroff, - const struct ip_conntrack_tuple *tuple, - enum ip_nat_manip_type maniptype); - - /* Is the manipable part of the tuple between min and max incl? */ - int (*in_range)(const struct ip_conntrack_tuple *tuple, - enum ip_nat_manip_type maniptype, - const union ip_conntrack_manip_proto *min, - const union ip_conntrack_manip_proto *max); - - /* Alter the per-proto part of the tuple (depending on - maniptype), to give a unique tuple in the given range if - possible; return false if not. Per-protocol part of tuple - is initialized to the incoming packet. */ - int (*unique_tuple)(struct ip_conntrack_tuple *tuple, - const struct ip_nat_range *range, - enum ip_nat_manip_type maniptype, - const struct ip_conntrack *conntrack); - - int (*range_to_nfattr)(struct sk_buff *skb, - const struct ip_nat_range *range); - - int (*nfattr_to_range)(struct nfattr *tb[], - struct ip_nat_range *range); -}; - -/* Protocol registration. */ -extern int ip_nat_protocol_register(struct ip_nat_protocol *proto); -extern void ip_nat_protocol_unregister(struct ip_nat_protocol *proto); - -extern struct ip_nat_protocol *ip_nat_proto_find_get(u_int8_t protocol); -extern void ip_nat_proto_put(struct ip_nat_protocol *proto); - -/* Built-in protocols. */ -extern struct ip_nat_protocol ip_nat_protocol_tcp; -extern struct ip_nat_protocol ip_nat_protocol_udp; -extern struct ip_nat_protocol ip_nat_protocol_icmp; -extern struct ip_nat_protocol ip_nat_unknown_protocol; - -extern int init_protocols(void) __init; -extern void cleanup_protocols(void); -extern struct ip_nat_protocol *find_nat_proto(u_int16_t protonum); - -extern int ip_nat_port_range_to_nfattr(struct sk_buff *skb, - const struct ip_nat_range *range); -extern int ip_nat_port_nfattr_to_range(struct nfattr *tb[], - struct ip_nat_range *range); - -#endif /*_IP_NAT_PROTO_H*/ diff --git a/include/linux/netfilter_ipv4/ip_nat_rule.h b/include/linux/netfilter_ipv4/ip_nat_rule.h deleted file mode 100644 index 73b9552e6a89..000000000000 --- a/include/linux/netfilter_ipv4/ip_nat_rule.h +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef _IP_NAT_RULE_H -#define _IP_NAT_RULE_H -#include -#include -#include - -#ifdef __KERNEL__ - -extern int ip_nat_rule_init(void) __init; -extern void ip_nat_rule_cleanup(void); -extern int ip_nat_rule_find(struct sk_buff **pskb, - unsigned int hooknum, - const struct net_device *in, - const struct net_device *out, - struct ip_conntrack *ct, - struct ip_nat_info *info); - -extern unsigned int -alloc_null_binding(struct ip_conntrack *conntrack, - struct ip_nat_info *info, - unsigned int hooknum); - -extern unsigned int -alloc_null_binding_confirmed(struct ip_conntrack *conntrack, - struct ip_nat_info *info, - unsigned int hooknum); -#endif -#endif /* _IP_NAT_RULE_H */ diff --git a/include/linux/netfilter_ipv4/ipt_SAME.h b/include/linux/netfilter_ipv4/ipt_SAME.h index cc4c0b2269af..be6e682a85ec 100644 --- a/include/linux/netfilter_ipv4/ipt_SAME.h +++ b/include/linux/netfilter_ipv4/ipt_SAME.h @@ -13,7 +13,7 @@ struct ipt_same_info u_int32_t *iparray; /* hangs off end. */ - struct ip_nat_range range[IPT_SAME_MAX_RANGE]; + struct nf_nat_range range[IPT_SAME_MAX_RANGE]; }; #endif /*_IPT_SAME_H*/ diff --git a/include/net/netfilter/nf_conntrack.h b/include/net/netfilter/nf_conntrack.h index 0e690e34c00b..1c6b8bd09b9a 100644 --- a/include/net/netfilter/nf_conntrack.h +++ b/include/net/netfilter/nf_conntrack.h @@ -250,6 +250,11 @@ static inline int nf_ct_is_dying(struct nf_conn *ct) return test_bit(IPS_DYING_BIT, &ct->status); } +static inline int nf_ct_is_untracked(const struct sk_buff *skb) +{ + return (skb->nfct == &nf_conntrack_untracked.ct_general); +} + extern unsigned int nf_conntrack_htable_size; extern int nf_conntrack_checksum; extern atomic_t nf_conntrack_count; diff --git a/include/net/netfilter/nf_conntrack_compat.h b/include/net/netfilter/nf_conntrack_compat.h deleted file mode 100644 index 6f84c1f7fcd4..000000000000 --- a/include/net/netfilter/nf_conntrack_compat.h +++ /dev/null @@ -1,145 +0,0 @@ -#ifndef _NF_CONNTRACK_COMPAT_H -#define _NF_CONNTRACK_COMPAT_H - -#ifdef __KERNEL__ - -#if defined(CONFIG_IP_NF_CONNTRACK) || defined(CONFIG_IP_NF_CONNTRACK_MODULE) - -#include -#include - -#ifdef CONFIG_IP_NF_CONNTRACK_MARK -static inline u_int32_t *nf_ct_get_mark(const struct sk_buff *skb, - u_int32_t *ctinfo) -{ - struct ip_conntrack *ct = ip_conntrack_get(skb, ctinfo); - - if (ct) - return &ct->mark; - else - return NULL; -} -#endif /* CONFIG_IP_NF_CONNTRACK_MARK */ - -#ifdef CONFIG_IP_NF_CONNTRACK_SECMARK -static inline u_int32_t *nf_ct_get_secmark(const struct sk_buff *skb, - u_int32_t *ctinfo) -{ - struct ip_conntrack *ct = ip_conntrack_get(skb, ctinfo); - - if (ct) - return &ct->secmark; - else - return NULL; -} -#endif /* CONFIG_IP_NF_CONNTRACK_SECMARK */ - -#ifdef CONFIG_IP_NF_CT_ACCT -static inline struct ip_conntrack_counter * -nf_ct_get_counters(const struct sk_buff *skb) -{ - enum ip_conntrack_info ctinfo; - struct ip_conntrack *ct = ip_conntrack_get(skb, &ctinfo); - - if (ct) - return ct->counters; - else - return NULL; -} -#endif /* CONFIG_IP_NF_CT_ACCT */ - -static inline int nf_ct_is_untracked(const struct sk_buff *skb) -{ - return (skb->nfct == &ip_conntrack_untracked.ct_general); -} - -static inline void nf_ct_untrack(struct sk_buff *skb) -{ - skb->nfct = &ip_conntrack_untracked.ct_general; -} - -static inline int nf_ct_get_ctinfo(const struct sk_buff *skb, - enum ip_conntrack_info *ctinfo) -{ - struct ip_conntrack *ct = ip_conntrack_get(skb, ctinfo); - return (ct != NULL); -} - -static inline int nf_ct_l3proto_try_module_get(unsigned short l3proto) -{ - need_conntrack(); - return l3proto == PF_INET ? 0 : -1; -} - -static inline void nf_ct_l3proto_module_put(unsigned short l3proto) -{ -} - -#else /* CONFIG_IP_NF_CONNTRACK */ - -#include -#include - -#ifdef CONFIG_NF_CONNTRACK_MARK - -static inline u_int32_t *nf_ct_get_mark(const struct sk_buff *skb, - u_int32_t *ctinfo) -{ - struct nf_conn *ct = nf_ct_get(skb, ctinfo); - - if (ct) - return &ct->mark; - else - return NULL; -} -#endif /* CONFIG_NF_CONNTRACK_MARK */ - -#ifdef CONFIG_NF_CONNTRACK_SECMARK -static inline u_int32_t *nf_ct_get_secmark(const struct sk_buff *skb, - u_int32_t *ctinfo) -{ - struct nf_conn *ct = nf_ct_get(skb, ctinfo); - - if (ct) - return &ct->secmark; - else - return NULL; -} -#endif /* CONFIG_NF_CONNTRACK_MARK */ - -#ifdef CONFIG_NF_CT_ACCT -static inline struct ip_conntrack_counter * -nf_ct_get_counters(const struct sk_buff *skb) -{ - enum ip_conntrack_info ctinfo; - struct nf_conn *ct = nf_ct_get(skb, &ctinfo); - - if (ct) - return ct->counters; - else - return NULL; -} -#endif /* CONFIG_NF_CT_ACCT */ - -static inline int nf_ct_is_untracked(const struct sk_buff *skb) -{ - return (skb->nfct == &nf_conntrack_untracked.ct_general); -} - -static inline void nf_ct_untrack(struct sk_buff *skb) -{ - skb->nfct = &nf_conntrack_untracked.ct_general; -} - -static inline int nf_ct_get_ctinfo(const struct sk_buff *skb, - enum ip_conntrack_info *ctinfo) -{ - struct nf_conn *ct = nf_ct_get(skb, ctinfo); - return (ct != NULL); -} - -#endif /* CONFIG_IP_NF_CONNTRACK */ - -#endif /* __KERNEL__ */ - -#endif /* _NF_CONNTRACK_COMPAT_H */ diff --git a/include/net/netfilter/nf_nat_rule.h b/include/net/netfilter/nf_nat_rule.h index f191c672bcc6..e76565459ad9 100644 --- a/include/net/netfilter/nf_nat_rule.h +++ b/include/net/netfilter/nf_nat_rule.h @@ -4,16 +4,6 @@ #include #include -/* Compatibility definitions for ipt_FOO modules */ -#define ip_nat_range nf_nat_range -#define ip_conntrack_tuple nf_conntrack_tuple -#define ip_conntrack_get nf_ct_get -#define ip_conntrack nf_conn -#define ip_nat_setup_info nf_nat_setup_info -#define ip_nat_multi_range_compat nf_nat_multi_range_compat -#define ip_ct_iterate_cleanup nf_ct_iterate_cleanup -#define IP_NF_ASSERT NF_CT_ASSERT - extern int nf_nat_rule_init(void) __init; extern void nf_nat_rule_cleanup(void); extern int nf_nat_rule_find(struct sk_buff **pskb, diff --git a/net/ipv4/netfilter/Kconfig b/net/ipv4/netfilter/Kconfig index 601808c796ec..46509fae9fd8 100644 --- a/net/ipv4/netfilter/Kconfig +++ b/net/ipv4/netfilter/Kconfig @@ -30,188 +30,6 @@ config NF_CONNTRACK_PROC_COMPAT If unsure, say Y. -# connection tracking, helpers and protocols -config IP_NF_CT_ACCT - bool "Connection tracking flow accounting" - depends on IP_NF_CONNTRACK - help - If this option is enabled, the connection tracking code will - keep per-flow packet and byte counters. - - Those counters can be used for flow-based accounting or the - `connbytes' match. - - If unsure, say `N'. - -config IP_NF_CONNTRACK_MARK - bool 'Connection mark tracking support' - depends on IP_NF_CONNTRACK - help - This option enables support for connection marks, used by the - `CONNMARK' target and `connmark' match. Similar to the mark value - of packets, but this mark value is kept in the conntrack session - instead of the individual packets. - -config IP_NF_CONNTRACK_SECMARK - bool 'Connection tracking security mark support' - depends on IP_NF_CONNTRACK && NETWORK_SECMARK - help - This option enables security markings to be applied to - connections. Typically they are copied to connections from - packets using the CONNSECMARK target and copied back from - connections to packets with the same target, with the packets - being originally labeled via SECMARK. - - If unsure, say 'N'. - -config IP_NF_CONNTRACK_EVENTS - bool "Connection tracking events (EXPERIMENTAL)" - depends on EXPERIMENTAL && IP_NF_CONNTRACK - help - If this option is enabled, the connection tracking code will - provide a notifier chain that can be used by other kernel code - to get notified about changes in the connection tracking state. - - IF unsure, say `N'. - -config IP_NF_CONNTRACK_NETLINK - tristate 'Connection tracking netlink interface (EXPERIMENTAL)' - depends on EXPERIMENTAL && IP_NF_CONNTRACK && NETFILTER_NETLINK - depends on IP_NF_CONNTRACK!=y || NETFILTER_NETLINK!=m - depends on IP_NF_NAT=n || IP_NF_NAT - help - This option enables support for a netlink-based userspace interface - - -config IP_NF_CT_PROTO_SCTP - tristate 'SCTP protocol connection tracking support (EXPERIMENTAL)' - depends on IP_NF_CONNTRACK && EXPERIMENTAL - help - With this option enabled, the connection tracking code will - be able to do state tracking on SCTP connections. - - If you want to compile it as a module, say M here and read - . If unsure, say `N'. - -config IP_NF_FTP - tristate "FTP protocol support" - depends on IP_NF_CONNTRACK - help - Tracking FTP connections is problematic: special helpers are - required for tracking them, and doing masquerading and other forms - of Network Address Translation on them. - - To compile it as a module, choose M here. If unsure, say Y. - -config IP_NF_IRC - tristate "IRC protocol support" - depends on IP_NF_CONNTRACK - ---help--- - There is a commonly-used extension to IRC called - Direct Client-to-Client Protocol (DCC). This enables users to send - files to each other, and also chat to each other without the need - of a server. DCC Sending is used anywhere you send files over IRC, - and DCC Chat is most commonly used by Eggdrop bots. If you are - using NAT, this extension will enable you to send files and initiate - chats. Note that you do NOT need this extension to get files or - have others initiate chats, or everything else in IRC. - - To compile it as a module, choose M here. If unsure, say Y. - -config IP_NF_NETBIOS_NS - tristate "NetBIOS name service protocol support (EXPERIMENTAL)" - depends on IP_NF_CONNTRACK && EXPERIMENTAL - help - NetBIOS name service requests are sent as broadcast messages from an - unprivileged port and responded to with unicast messages to the - same port. This make them hard to firewall properly because connection - tracking doesn't deal with broadcasts. This helper tracks locally - originating NetBIOS name service requests and the corresponding - responses. It relies on correct IP address configuration, specifically - netmask and broadcast address. When properly configured, the output - of "ip address show" should look similar to this: - - $ ip -4 address show eth0 - 4: eth0: mtu 1500 qdisc pfifo_fast qlen 1000 - inet 172.16.2.252/24 brd 172.16.2.255 scope global eth0 - - To compile it as a module, choose M here. If unsure, say N. - -config IP_NF_TFTP - tristate "TFTP protocol support" - depends on IP_NF_CONNTRACK - help - TFTP connection tracking helper, this is required depending - on how restrictive your ruleset is. - If you are using a tftp client behind -j SNAT or -j MASQUERADING - you will need this. - - To compile it as a module, choose M here. If unsure, say Y. - -config IP_NF_AMANDA - tristate "Amanda backup protocol support" - depends on IP_NF_CONNTRACK - select TEXTSEARCH - select TEXTSEARCH_KMP - help - If you are running the Amanda backup package - on this machine or machines that will be MASQUERADED through this - machine, then you may want to enable this feature. This allows the - connection tracking and natting code to allow the sub-channels that - Amanda requires for communication of the backup data, messages and - index. - - To compile it as a module, choose M here. If unsure, say Y. - -config IP_NF_PPTP - tristate 'PPTP protocol support' - depends on IP_NF_CONNTRACK - help - This module adds support for PPTP (Point to Point Tunnelling - Protocol, RFC2637) connection tracking and NAT. - - If you are running PPTP sessions over a stateful firewall or NAT - box, you may want to enable this feature. - - Please note that not all PPTP modes of operation are supported yet. - For more info, read top of the file - net/ipv4/netfilter/ip_conntrack_pptp.c - - If you want to compile it as a module, say M here and read - Documentation/modules.txt. If unsure, say `N'. - -config IP_NF_H323 - tristate 'H.323 protocol support (EXPERIMENTAL)' - depends on IP_NF_CONNTRACK && EXPERIMENTAL - help - H.323 is a VoIP signalling protocol from ITU-T. As one of the most - important VoIP protocols, it is widely used by voice hardware and - software including voice gateways, IP phones, Netmeeting, OpenPhone, - Gnomemeeting, etc. - - With this module you can support H.323 on a connection tracking/NAT - firewall. - - This module supports RAS, Fast Start, H.245 Tunnelling, Call - Forwarding, RTP/RTCP and T.120 based audio, video, fax, chat, - whiteboard, file transfer, etc. For more information, please - visit http://nath323.sourceforge.net/. - - If you want to compile it as a module, say 'M' here and read - Documentation/modules.txt. If unsure, say 'N'. - -config IP_NF_SIP - tristate "SIP protocol support (EXPERIMENTAL)" - depends on IP_NF_CONNTRACK && EXPERIMENTAL - help - SIP is an application-layer control protocol that can establish, - modify, and terminate multimedia sessions (conferences) such as - Internet telephony calls. With the ip_conntrack_sip and - the ip_nat_sip modules you can support the protocol on a connection - tracking/NATing firewall. - - To compile it as a module, choose M here. If unsure, say Y. - config IP_NF_QUEUE tristate "IP Userspace queueing via NETLINK (OBSOLETE)" help @@ -361,17 +179,6 @@ config IP_NF_TARGET_ULOG To compile it as a module, choose M here. If unsure, say N. -# NAT + specific targets: ip_conntrack -config IP_NF_NAT - tristate "Full NAT" - depends on IP_NF_IPTABLES && IP_NF_CONNTRACK - help - The Full NAT option allows masquerading, port forwarding and other - forms of full Network Address Port Translation. It is controlled by - the `nat' table in iptables: see the man page for iptables(8). - - To compile it as a module, choose M here. If unsure, say N. - # NAT + specific targets: nf_conntrack config NF_NAT tristate "Full NAT" @@ -383,11 +190,6 @@ config NF_NAT To compile it as a module, choose M here. If unsure, say N. -config IP_NF_NAT_NEEDED - bool - depends on IP_NF_NAT - default y - config NF_NAT_NEEDED bool depends on NF_NAT @@ -395,7 +197,7 @@ config NF_NAT_NEEDED config IP_NF_TARGET_MASQUERADE tristate "MASQUERADE target support" - depends on (NF_NAT || IP_NF_NAT) + depends on NF_NAT help Masquerading is a special case of NAT: all outgoing connections are changed to seem to come from a particular interface's address, and @@ -407,7 +209,7 @@ config IP_NF_TARGET_MASQUERADE config IP_NF_TARGET_REDIRECT tristate "REDIRECT target support" - depends on (NF_NAT || IP_NF_NAT) + depends on NF_NAT help REDIRECT is a special case of NAT: all incoming connections are mapped onto the incoming interface's address, causing the packets to @@ -418,7 +220,7 @@ config IP_NF_TARGET_REDIRECT config IP_NF_TARGET_NETMAP tristate "NETMAP target support" - depends on (NF_NAT || IP_NF_NAT) + depends on NF_NAT help NETMAP is an implementation of static 1:1 NAT mapping of network addresses. It maps the network address part, while keeping the host @@ -429,28 +231,13 @@ config IP_NF_TARGET_NETMAP config IP_NF_TARGET_SAME tristate "SAME target support" - depends on (NF_NAT || IP_NF_NAT) + depends on NF_NAT help This option adds a `SAME' target, which works like the standard SNAT target, but attempts to give clients the same IP for all connections. To compile it as a module, choose M here. If unsure, say N. -config IP_NF_NAT_SNMP_BASIC - tristate "Basic SNMP-ALG support (EXPERIMENTAL)" - depends on EXPERIMENTAL && IP_NF_NAT - ---help--- - - This module implements an Application Layer Gateway (ALG) for - SNMP payloads. In conjunction with NAT, it allows a network - management system to access multiple private networks with - conflicting addresses. It works by modifying IP addresses - inside SNMP payloads to match IP-layer NAT mapping. - - This is the "basic" form of SNMP-ALG, as described in RFC 2962 - - To compile it as a module, choose M here. If unsure, say N. - config NF_NAT_SNMP_BASIC tristate "Basic SNMP-ALG support (EXPERIMENTAL)" depends on EXPERIMENTAL && NF_NAT @@ -477,78 +264,37 @@ config NF_NAT_PROTO_GRE tristate depends on NF_NAT && NF_CT_PROTO_GRE -config IP_NF_NAT_FTP - tristate - depends on IP_NF_IPTABLES && IP_NF_CONNTRACK && IP_NF_NAT - default IP_NF_NAT && IP_NF_FTP - config NF_NAT_FTP tristate depends on IP_NF_IPTABLES && NF_CONNTRACK && NF_NAT default NF_NAT && NF_CONNTRACK_FTP -config IP_NF_NAT_IRC - tristate - depends on IP_NF_IPTABLES!=n && IP_NF_CONNTRACK!=n && IP_NF_NAT!=n - default IP_NF_NAT if IP_NF_IRC=y - default m if IP_NF_IRC=m - config NF_NAT_IRC tristate depends on IP_NF_IPTABLES && NF_CONNTRACK && NF_NAT default NF_NAT && NF_CONNTRACK_IRC -config IP_NF_NAT_TFTP - tristate - depends on IP_NF_IPTABLES!=n && IP_NF_CONNTRACK!=n && IP_NF_NAT!=n - default IP_NF_NAT if IP_NF_TFTP=y - default m if IP_NF_TFTP=m - config NF_NAT_TFTP tristate depends on IP_NF_IPTABLES && NF_CONNTRACK && NF_NAT default NF_NAT && NF_CONNTRACK_TFTP -config IP_NF_NAT_AMANDA - tristate - depends on IP_NF_IPTABLES!=n && IP_NF_CONNTRACK!=n && IP_NF_NAT!=n - default IP_NF_NAT if IP_NF_AMANDA=y - default m if IP_NF_AMANDA=m - config NF_NAT_AMANDA tristate depends on IP_NF_IPTABLES && NF_CONNTRACK && NF_NAT default NF_NAT && NF_CONNTRACK_AMANDA -config IP_NF_NAT_PPTP - tristate - depends on IP_NF_NAT!=n && IP_NF_PPTP!=n - default IP_NF_NAT if IP_NF_PPTP=y - default m if IP_NF_PPTP=m - config NF_NAT_PPTP tristate depends on IP_NF_IPTABLES && NF_CONNTRACK && NF_NAT default NF_NAT && NF_CONNTRACK_PPTP select NF_NAT_PROTO_GRE -config IP_NF_NAT_H323 - tristate - depends on IP_NF_IPTABLES!=n && IP_NF_CONNTRACK!=n && IP_NF_NAT!=n - default IP_NF_NAT if IP_NF_H323=y - default m if IP_NF_H323=m - config NF_NAT_H323 tristate depends on IP_NF_IPTABLES && NF_CONNTRACK && NF_NAT default NF_NAT && NF_CONNTRACK_H323 -config IP_NF_NAT_SIP - tristate - depends on IP_NF_IPTABLES!=n && IP_NF_CONNTRACK!=n && IP_NF_NAT!=n - default IP_NF_NAT if IP_NF_SIP=y - default m if IP_NF_SIP=m - config NF_NAT_SIP tristate depends on IP_NF_IPTABLES && NF_CONNTRACK && NF_NAT @@ -606,9 +352,8 @@ config IP_NF_TARGET_TTL config IP_NF_TARGET_CLUSTERIP tristate "CLUSTERIP target support (EXPERIMENTAL)" depends on IP_NF_MANGLE && EXPERIMENTAL - depends on IP_NF_CONNTRACK || NF_CONNTRACK_IPV4 - select IP_NF_CONNTRACK_MARK if IP_NF_CONNTRACK - select NF_CONNTRACK_MARK if NF_CONNTRACK_IPV4 + depends on NF_CONNTRACK_IPV4 + select NF_CONNTRACK_MARK help The CLUSTERIP target allows you to build load-balancing clusters of network servers without having a dedicated load-balancing diff --git a/net/ipv4/netfilter/Makefile b/net/ipv4/netfilter/Makefile index 6625ec68180c..409d273f6f82 100644 --- a/net/ipv4/netfilter/Makefile +++ b/net/ipv4/netfilter/Makefile @@ -2,8 +2,6 @@ # Makefile for the netfilter modules on top of IPv4. # -# objects for the standalone - connection tracking / NAT -ip_conntrack-objs := ip_conntrack_standalone.o ip_conntrack_core.o ip_conntrack_proto_generic.o ip_conntrack_proto_tcp.o ip_conntrack_proto_udp.o ip_conntrack_proto_icmp.o # objects for l3 independent conntrack nf_conntrack_ipv4-objs := nf_conntrack_l3proto_ipv4.o nf_conntrack_proto_icmp.o ifeq ($(CONFIG_NF_CONNTRACK_PROC_COMPAT),y) @@ -12,53 +10,14 @@ nf_conntrack_ipv4-objs += nf_conntrack_l3proto_ipv4_compat.o endif endif -ip_nat-objs := ip_nat_core.o ip_nat_helper.o ip_nat_proto_unknown.o ip_nat_proto_tcp.o ip_nat_proto_udp.o ip_nat_proto_icmp.o -nf_nat-objs := nf_nat_core.o nf_nat_helper.o nf_nat_proto_unknown.o nf_nat_proto_tcp.o nf_nat_proto_udp.o nf_nat_proto_icmp.o -ifneq ($(CONFIG_NF_NAT),) +nf_nat-objs := nf_nat_core.o nf_nat_helper.o nf_nat_proto_unknown.o nf_nat_proto_tcp.o nf_nat_proto_udp.o nf_nat_proto_icmp.o iptable_nat-objs := nf_nat_rule.o nf_nat_standalone.o -else -iptable_nat-objs := ip_nat_rule.o ip_nat_standalone.o -endif - -ip_conntrack_pptp-objs := ip_conntrack_helper_pptp.o ip_conntrack_proto_gre.o -ip_nat_pptp-objs := ip_nat_helper_pptp.o ip_nat_proto_gre.o - -ip_conntrack_h323-objs := ip_conntrack_helper_h323.o ../../netfilter/nf_conntrack_h323_asn1.o -ip_nat_h323-objs := ip_nat_helper_h323.o # connection tracking -obj-$(CONFIG_IP_NF_CONNTRACK) += ip_conntrack.o obj-$(CONFIG_NF_CONNTRACK_IPV4) += nf_conntrack_ipv4.o -obj-$(CONFIG_IP_NF_NAT) += ip_nat.o obj-$(CONFIG_NF_NAT) += nf_nat.o -# conntrack netlink interface -obj-$(CONFIG_IP_NF_CONNTRACK_NETLINK) += ip_conntrack_netlink.o - - -# SCTP protocol connection tracking -obj-$(CONFIG_IP_NF_CT_PROTO_SCTP) += ip_conntrack_proto_sctp.o - -# connection tracking helpers -obj-$(CONFIG_IP_NF_H323) += ip_conntrack_h323.o -obj-$(CONFIG_IP_NF_PPTP) += ip_conntrack_pptp.o -obj-$(CONFIG_IP_NF_AMANDA) += ip_conntrack_amanda.o -obj-$(CONFIG_IP_NF_TFTP) += ip_conntrack_tftp.o -obj-$(CONFIG_IP_NF_FTP) += ip_conntrack_ftp.o -obj-$(CONFIG_IP_NF_IRC) += ip_conntrack_irc.o -obj-$(CONFIG_IP_NF_SIP) += ip_conntrack_sip.o -obj-$(CONFIG_IP_NF_NETBIOS_NS) += ip_conntrack_netbios_ns.o - -# NAT helpers (ip_conntrack) -obj-$(CONFIG_IP_NF_NAT_H323) += ip_nat_h323.o -obj-$(CONFIG_IP_NF_NAT_PPTP) += ip_nat_pptp.o -obj-$(CONFIG_IP_NF_NAT_AMANDA) += ip_nat_amanda.o -obj-$(CONFIG_IP_NF_NAT_TFTP) += ip_nat_tftp.o -obj-$(CONFIG_IP_NF_NAT_FTP) += ip_nat_ftp.o -obj-$(CONFIG_IP_NF_NAT_IRC) += ip_nat_irc.o -obj-$(CONFIG_IP_NF_NAT_SIP) += ip_nat_sip.o - # NAT helpers (nf_conntrack) obj-$(CONFIG_NF_NAT_AMANDA) += nf_nat_amanda.o obj-$(CONFIG_NF_NAT_FTP) += nf_nat_ftp.o @@ -78,7 +37,6 @@ obj-$(CONFIG_IP_NF_IPTABLES) += ip_tables.o # the three instances of ip_tables obj-$(CONFIG_IP_NF_FILTER) += iptable_filter.o obj-$(CONFIG_IP_NF_MANGLE) += iptable_mangle.o -obj-$(CONFIG_IP_NF_NAT) += iptable_nat.o obj-$(CONFIG_NF_NAT) += iptable_nat.o obj-$(CONFIG_IP_NF_RAW) += iptable_raw.o @@ -100,7 +58,6 @@ obj-$(CONFIG_IP_NF_TARGET_MASQUERADE) += ipt_MASQUERADE.o obj-$(CONFIG_IP_NF_TARGET_REDIRECT) += ipt_REDIRECT.o obj-$(CONFIG_IP_NF_TARGET_NETMAP) += ipt_NETMAP.o obj-$(CONFIG_IP_NF_TARGET_SAME) += ipt_SAME.o -obj-$(CONFIG_IP_NF_NAT_SNMP_BASIC) += ip_nat_snmp_basic.o obj-$(CONFIG_IP_NF_TARGET_LOG) += ipt_LOG.o obj-$(CONFIG_IP_NF_TARGET_ULOG) += ipt_ULOG.o obj-$(CONFIG_IP_NF_TARGET_CLUSTERIP) += ipt_CLUSTERIP.o diff --git a/net/ipv4/netfilter/ip_conntrack_amanda.c b/net/ipv4/netfilter/ip_conntrack_amanda.c deleted file mode 100644 index c40762c67d0e..000000000000 --- a/net/ipv4/netfilter/ip_conntrack_amanda.c +++ /dev/null @@ -1,229 +0,0 @@ -/* Amanda extension for IP connection tracking, Version 0.2 - * (C) 2002 by Brian J. Murrell - * based on HW's ip_conntrack_irc.c as well as other modules - * - * 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. - * - * Module load syntax: - * insmod ip_conntrack_amanda.o [master_timeout=n] - * - * Where master_timeout is the timeout (in seconds) of the master - * connection (port 10080). This defaults to 5 minutes but if - * your clients take longer than 5 minutes to do their work - * before getting back to the Amanda server, you can increase - * this value. - * - */ -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -static unsigned int master_timeout = 300; -static char *ts_algo = "kmp"; - -MODULE_AUTHOR("Brian J. Murrell "); -MODULE_DESCRIPTION("Amanda connection tracking module"); -MODULE_LICENSE("GPL"); -module_param(master_timeout, uint, 0600); -MODULE_PARM_DESC(master_timeout, "timeout for the master connection"); -module_param(ts_algo, charp, 0400); -MODULE_PARM_DESC(ts_algo, "textsearch algorithm to use (default kmp)"); - -unsigned int (*ip_nat_amanda_hook)(struct sk_buff **pskb, - enum ip_conntrack_info ctinfo, - unsigned int matchoff, - unsigned int matchlen, - struct ip_conntrack_expect *exp); -EXPORT_SYMBOL_GPL(ip_nat_amanda_hook); - -enum amanda_strings { - SEARCH_CONNECT, - SEARCH_NEWLINE, - SEARCH_DATA, - SEARCH_MESG, - SEARCH_INDEX, -}; - -static struct { - char *string; - size_t len; - struct ts_config *ts; -} search[] = { - [SEARCH_CONNECT] = { - .string = "CONNECT ", - .len = 8, - }, - [SEARCH_NEWLINE] = { - .string = "\n", - .len = 1, - }, - [SEARCH_DATA] = { - .string = "DATA ", - .len = 5, - }, - [SEARCH_MESG] = { - .string = "MESG ", - .len = 5, - }, - [SEARCH_INDEX] = { - .string = "INDEX ", - .len = 6, - }, -}; - -static int help(struct sk_buff **pskb, - struct ip_conntrack *ct, enum ip_conntrack_info ctinfo) -{ - struct ts_state ts; - struct ip_conntrack_expect *exp; - unsigned int dataoff, start, stop, off, i; - char pbuf[sizeof("65535")], *tmp; - u_int16_t port, len; - int ret = NF_ACCEPT; - typeof(ip_nat_amanda_hook) ip_nat_amanda; - - /* Only look at packets from the Amanda server */ - if (CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL) - return NF_ACCEPT; - - /* increase the UDP timeout of the master connection as replies from - * Amanda clients to the server can be quite delayed */ - ip_ct_refresh(ct, *pskb, master_timeout * HZ); - - /* No data? */ - dataoff = ip_hdrlen(*pskb) + sizeof(struct udphdr); - if (dataoff >= (*pskb)->len) { - if (net_ratelimit()) - printk("amanda_help: skblen = %u\n", (*pskb)->len); - return NF_ACCEPT; - } - - memset(&ts, 0, sizeof(ts)); - start = skb_find_text(*pskb, dataoff, (*pskb)->len, - search[SEARCH_CONNECT].ts, &ts); - if (start == UINT_MAX) - goto out; - start += dataoff + search[SEARCH_CONNECT].len; - - memset(&ts, 0, sizeof(ts)); - stop = skb_find_text(*pskb, start, (*pskb)->len, - search[SEARCH_NEWLINE].ts, &ts); - if (stop == UINT_MAX) - goto out; - stop += start; - - for (i = SEARCH_DATA; i <= SEARCH_INDEX; i++) { - memset(&ts, 0, sizeof(ts)); - off = skb_find_text(*pskb, start, stop, search[i].ts, &ts); - if (off == UINT_MAX) - continue; - off += start + search[i].len; - - len = min_t(unsigned int, sizeof(pbuf) - 1, stop - off); - if (skb_copy_bits(*pskb, off, pbuf, len)) - break; - pbuf[len] = '\0'; - - port = simple_strtoul(pbuf, &tmp, 10); - len = tmp - pbuf; - if (port == 0 || len > 5) - break; - - exp = ip_conntrack_expect_alloc(ct); - if (exp == NULL) { - ret = NF_DROP; - goto out; - } - - exp->expectfn = NULL; - exp->flags = 0; - - exp->tuple.src.ip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip; - exp->tuple.src.u.tcp.port = 0; - exp->tuple.dst.ip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip; - exp->tuple.dst.protonum = IPPROTO_TCP; - exp->tuple.dst.u.tcp.port = htons(port); - - exp->mask.src.ip = htonl(0xFFFFFFFF); - exp->mask.src.u.tcp.port = 0; - exp->mask.dst.ip = htonl(0xFFFFFFFF); - exp->mask.dst.protonum = 0xFF; - exp->mask.dst.u.tcp.port = htons(0xFFFF); - - /* RCU read locked by nf_hook_slow */ - ip_nat_amanda = rcu_dereference(ip_nat_amanda_hook); - if (ip_nat_amanda) - ret = ip_nat_amanda(pskb, ctinfo, off - dataoff, - len, exp); - else if (ip_conntrack_expect_related(exp) != 0) - ret = NF_DROP; - ip_conntrack_expect_put(exp); - } - -out: - return ret; -} - -static struct ip_conntrack_helper amanda_helper = { - .max_expected = 3, - .timeout = 180, - .me = THIS_MODULE, - .help = help, - .name = "amanda", - - .tuple = { .src = { .u = { .udp = {.port = __constant_htons(10080) } } }, - .dst = { .protonum = IPPROTO_UDP }, - }, - .mask = { .src = { .u = { 0xFFFF } }, - .dst = { .protonum = 0xFF }, - }, -}; - -static void __exit ip_conntrack_amanda_fini(void) -{ - int i; - - ip_conntrack_helper_unregister(&amanda_helper); - for (i = 0; i < ARRAY_SIZE(search); i++) - textsearch_destroy(search[i].ts); -} - -static int __init ip_conntrack_amanda_init(void) -{ - int ret, i; - - ret = -ENOMEM; - for (i = 0; i < ARRAY_SIZE(search); i++) { - search[i].ts = textsearch_prepare(ts_algo, search[i].string, - search[i].len, - GFP_KERNEL, TS_AUTOLOAD); - if (search[i].ts == NULL) - goto err; - } - ret = ip_conntrack_helper_register(&amanda_helper); - if (ret < 0) - goto err; - return 0; - -err: - for (; i >= 0; i--) { - if (search[i].ts) - textsearch_destroy(search[i].ts); - } - return ret; -} - -module_init(ip_conntrack_amanda_init); -module_exit(ip_conntrack_amanda_fini); diff --git a/net/ipv4/netfilter/ip_conntrack_core.c b/net/ipv4/netfilter/ip_conntrack_core.c deleted file mode 100644 index 986c0c81294f..000000000000 --- a/net/ipv4/netfilter/ip_conntrack_core.c +++ /dev/null @@ -1,1549 +0,0 @@ -/* Connection state tracking for netfilter. This is separated from, - but required by, the NAT layer; it can also be used by an iptables - extension. */ - -/* (C) 1999-2001 Paul `Rusty' Russell - * (C) 2002-2004 Netfilter Core Team - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * 23 Apr 2001: Harald Welte - * - new API and handling of conntrack/nat helpers - * - now capable of multiple expectations for one master - * 16 Jul 2002: Harald Welte - * - add usage/reference counts to ip_conntrack_expect - * - export ip_conntrack[_expect]_{find_get,put} functions - * */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* ip_conntrack_lock protects the main hash table, protocol/helper/expected - registrations, conntrack timers*/ -#include -#include -#include -#include - -#define IP_CONNTRACK_VERSION "2.4" - -#if 0 -#define DEBUGP printk -#else -#define DEBUGP(format, args...) -#endif - -DEFINE_RWLOCK(ip_conntrack_lock); - -/* ip_conntrack_standalone needs this */ -atomic_t ip_conntrack_count = ATOMIC_INIT(0); - -void (*ip_conntrack_destroyed)(struct ip_conntrack *conntrack) = NULL; -LIST_HEAD(ip_conntrack_expect_list); -struct ip_conntrack_protocol *ip_ct_protos[MAX_IP_CT_PROTO] __read_mostly; -static LIST_HEAD(helpers); -unsigned int ip_conntrack_htable_size __read_mostly = 0; -int ip_conntrack_max __read_mostly; -struct list_head *ip_conntrack_hash __read_mostly; -static struct kmem_cache *ip_conntrack_cachep __read_mostly; -static struct kmem_cache *ip_conntrack_expect_cachep __read_mostly; -struct ip_conntrack ip_conntrack_untracked; -unsigned int ip_ct_log_invalid __read_mostly; -static LIST_HEAD(unconfirmed); -static int ip_conntrack_vmalloc __read_mostly; - -static unsigned int ip_conntrack_next_id; -static unsigned int ip_conntrack_expect_next_id; -#ifdef CONFIG_IP_NF_CONNTRACK_EVENTS -ATOMIC_NOTIFIER_HEAD(ip_conntrack_chain); -ATOMIC_NOTIFIER_HEAD(ip_conntrack_expect_chain); - -DEFINE_PER_CPU(struct ip_conntrack_ecache, ip_conntrack_ecache); - -/* deliver cached events and clear cache entry - must be called with locally - * disabled softirqs */ -static inline void -__ip_ct_deliver_cached_events(struct ip_conntrack_ecache *ecache) -{ - DEBUGP("ecache: delivering events for %p\n", ecache->ct); - if (is_confirmed(ecache->ct) && !is_dying(ecache->ct) && ecache->events) - atomic_notifier_call_chain(&ip_conntrack_chain, ecache->events, - ecache->ct); - ecache->events = 0; - ip_conntrack_put(ecache->ct); - ecache->ct = NULL; -} - -/* Deliver all cached events for a particular conntrack. This is called - * by code prior to async packet handling or freeing the skb */ -void ip_ct_deliver_cached_events(const struct ip_conntrack *ct) -{ - struct ip_conntrack_ecache *ecache; - - local_bh_disable(); - ecache = &__get_cpu_var(ip_conntrack_ecache); - if (ecache->ct == ct) - __ip_ct_deliver_cached_events(ecache); - local_bh_enable(); -} - -void __ip_ct_event_cache_init(struct ip_conntrack *ct) -{ - struct ip_conntrack_ecache *ecache; - - /* take care of delivering potentially old events */ - ecache = &__get_cpu_var(ip_conntrack_ecache); - BUG_ON(ecache->ct == ct); - if (ecache->ct) - __ip_ct_deliver_cached_events(ecache); - /* initialize for this conntrack/packet */ - ecache->ct = ct; - nf_conntrack_get(&ct->ct_general); -} - -/* flush the event cache - touches other CPU's data and must not be called while - * packets are still passing through the code */ -static void ip_ct_event_cache_flush(void) -{ - struct ip_conntrack_ecache *ecache; - int cpu; - - for_each_possible_cpu(cpu) { - ecache = &per_cpu(ip_conntrack_ecache, cpu); - if (ecache->ct) - ip_conntrack_put(ecache->ct); - } -} -#else -static inline void ip_ct_event_cache_flush(void) {} -#endif /* CONFIG_IP_NF_CONNTRACK_EVENTS */ - -DEFINE_PER_CPU(struct ip_conntrack_stat, ip_conntrack_stat); - -static int ip_conntrack_hash_rnd_initted; -static unsigned int ip_conntrack_hash_rnd; - -static u_int32_t __hash_conntrack(const struct ip_conntrack_tuple *tuple, - unsigned int size, unsigned int rnd) -{ - return (jhash_3words((__force u32)tuple->src.ip, - ((__force u32)tuple->dst.ip ^ tuple->dst.protonum), - (tuple->src.u.all | (tuple->dst.u.all << 16)), - rnd) % size); -} - -static u_int32_t -hash_conntrack(const struct ip_conntrack_tuple *tuple) -{ - return __hash_conntrack(tuple, ip_conntrack_htable_size, - ip_conntrack_hash_rnd); -} - -int -ip_ct_get_tuple(const struct iphdr *iph, - const struct sk_buff *skb, - unsigned int dataoff, - struct ip_conntrack_tuple *tuple, - const struct ip_conntrack_protocol *protocol) -{ - /* Never happen */ - if (iph->frag_off & htons(IP_OFFSET)) { - printk("ip_conntrack_core: Frag of proto %u.\n", - iph->protocol); - return 0; - } - - tuple->src.ip = iph->saddr; - tuple->dst.ip = iph->daddr; - tuple->dst.protonum = iph->protocol; - tuple->dst.dir = IP_CT_DIR_ORIGINAL; - - return protocol->pkt_to_tuple(skb, dataoff, tuple); -} - -int -ip_ct_invert_tuple(struct ip_conntrack_tuple *inverse, - const struct ip_conntrack_tuple *orig, - const struct ip_conntrack_protocol *protocol) -{ - inverse->src.ip = orig->dst.ip; - inverse->dst.ip = orig->src.ip; - inverse->dst.protonum = orig->dst.protonum; - inverse->dst.dir = !orig->dst.dir; - - return protocol->invert_tuple(inverse, orig); -} - - -/* ip_conntrack_expect helper functions */ -void ip_ct_unlink_expect(struct ip_conntrack_expect *exp) -{ - IP_NF_ASSERT(!timer_pending(&exp->timeout)); - list_del(&exp->list); - CONNTRACK_STAT_INC(expect_delete); - exp->master->expecting--; - ip_conntrack_expect_put(exp); -} - -static void expectation_timed_out(unsigned long ul_expect) -{ - struct ip_conntrack_expect *exp = (void *)ul_expect; - - write_lock_bh(&ip_conntrack_lock); - ip_ct_unlink_expect(exp); - write_unlock_bh(&ip_conntrack_lock); - ip_conntrack_expect_put(exp); -} - -struct ip_conntrack_expect * -__ip_conntrack_expect_find(const struct ip_conntrack_tuple *tuple) -{ - struct ip_conntrack_expect *i; - - list_for_each_entry(i, &ip_conntrack_expect_list, list) { - if (ip_ct_tuple_mask_cmp(tuple, &i->tuple, &i->mask)) - return i; - } - return NULL; -} - -/* Just find a expectation corresponding to a tuple. */ -struct ip_conntrack_expect * -ip_conntrack_expect_find_get(const struct ip_conntrack_tuple *tuple) -{ - struct ip_conntrack_expect *i; - - read_lock_bh(&ip_conntrack_lock); - i = __ip_conntrack_expect_find(tuple); - if (i) - atomic_inc(&i->use); - read_unlock_bh(&ip_conntrack_lock); - - return i; -} - -/* If an expectation for this connection is found, it gets delete from - * global list then returned. */ -static struct ip_conntrack_expect * -find_expectation(const struct ip_conntrack_tuple *tuple) -{ - struct ip_conntrack_expect *i; - - list_for_each_entry(i, &ip_conntrack_expect_list, list) { - /* If master is not in hash table yet (ie. packet hasn't left - this machine yet), how can other end know about expected? - Hence these are not the droids you are looking for (if - master ct never got confirmed, we'd hold a reference to it - and weird things would happen to future packets). */ - if (ip_ct_tuple_mask_cmp(tuple, &i->tuple, &i->mask) - && is_confirmed(i->master)) { - if (i->flags & IP_CT_EXPECT_PERMANENT) { - atomic_inc(&i->use); - return i; - } else if (del_timer(&i->timeout)) { - ip_ct_unlink_expect(i); - return i; - } - } - } - return NULL; -} - -/* delete all expectations for this conntrack */ -void ip_ct_remove_expectations(struct ip_conntrack *ct) -{ - struct ip_conntrack_expect *i, *tmp; - - /* Optimization: most connection never expect any others. */ - if (ct->expecting == 0) - return; - - list_for_each_entry_safe(i, tmp, &ip_conntrack_expect_list, list) { - if (i->master == ct && del_timer(&i->timeout)) { - ip_ct_unlink_expect(i); - ip_conntrack_expect_put(i); - } - } -} - -static void -clean_from_lists(struct ip_conntrack *ct) -{ - DEBUGP("clean_from_lists(%p)\n", ct); - list_del(&ct->tuplehash[IP_CT_DIR_ORIGINAL].list); - list_del(&ct->tuplehash[IP_CT_DIR_REPLY].list); - - /* Destroy all pending expectations */ - ip_ct_remove_expectations(ct); -} - -static void -destroy_conntrack(struct nf_conntrack *nfct) -{ - struct ip_conntrack *ct = (struct ip_conntrack *)nfct; - struct ip_conntrack_protocol *proto; - struct ip_conntrack_helper *helper; - typeof(ip_conntrack_destroyed) destroyed; - - DEBUGP("destroy_conntrack(%p)\n", ct); - IP_NF_ASSERT(atomic_read(&nfct->use) == 0); - IP_NF_ASSERT(!timer_pending(&ct->timeout)); - - ip_conntrack_event(IPCT_DESTROY, ct); - set_bit(IPS_DYING_BIT, &ct->status); - - helper = ct->helper; - if (helper && helper->destroy) - helper->destroy(ct); - - /* To make sure we don't get any weird locking issues here: - * destroy_conntrack() MUST NOT be called with a write lock - * to ip_conntrack_lock!!! -HW */ - rcu_read_lock(); - proto = __ip_conntrack_proto_find(ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.protonum); - if (proto && proto->destroy) - proto->destroy(ct); - - destroyed = rcu_dereference(ip_conntrack_destroyed); - if (destroyed) - destroyed(ct); - - rcu_read_unlock(); - - write_lock_bh(&ip_conntrack_lock); - /* Expectations will have been removed in clean_from_lists, - * except TFTP can create an expectation on the first packet, - * before connection is in the list, so we need to clean here, - * too. */ - ip_ct_remove_expectations(ct); - - /* We overload first tuple to link into unconfirmed list. */ - if (!is_confirmed(ct)) { - BUG_ON(list_empty(&ct->tuplehash[IP_CT_DIR_ORIGINAL].list)); - list_del(&ct->tuplehash[IP_CT_DIR_ORIGINAL].list); - } - - CONNTRACK_STAT_INC(delete); - write_unlock_bh(&ip_conntrack_lock); - - if (ct->master) - ip_conntrack_put(ct->master); - - DEBUGP("destroy_conntrack: returning ct=%p to slab\n", ct); - ip_conntrack_free(ct); -} - -static void death_by_timeout(unsigned long ul_conntrack) -{ - struct ip_conntrack *ct = (void *)ul_conntrack; - - write_lock_bh(&ip_conntrack_lock); - /* Inside lock so preempt is disabled on module removal path. - * Otherwise we can get spurious warnings. */ - CONNTRACK_STAT_INC(delete_list); - clean_from_lists(ct); - write_unlock_bh(&ip_conntrack_lock); - ip_conntrack_put(ct); -} - -struct ip_conntrack_tuple_hash * -__ip_conntrack_find(const struct ip_conntrack_tuple *tuple, - const struct ip_conntrack *ignored_conntrack) -{ - struct ip_conntrack_tuple_hash *h; - unsigned int hash = hash_conntrack(tuple); - - list_for_each_entry(h, &ip_conntrack_hash[hash], list) { - if (tuplehash_to_ctrack(h) != ignored_conntrack && - ip_ct_tuple_equal(tuple, &h->tuple)) { - CONNTRACK_STAT_INC(found); - return h; - } - CONNTRACK_STAT_INC(searched); - } - - return NULL; -} - -/* Find a connection corresponding to a tuple. */ -struct ip_conntrack_tuple_hash * -ip_conntrack_find_get(const struct ip_conntrack_tuple *tuple, - const struct ip_conntrack *ignored_conntrack) -{ - struct ip_conntrack_tuple_hash *h; - - read_lock_bh(&ip_conntrack_lock); - h = __ip_conntrack_find(tuple, ignored_conntrack); - if (h) - atomic_inc(&tuplehash_to_ctrack(h)->ct_general.use); - read_unlock_bh(&ip_conntrack_lock); - - return h; -} - -static void __ip_conntrack_hash_insert(struct ip_conntrack *ct, - unsigned int hash, - unsigned int repl_hash) -{ - ct->id = ++ip_conntrack_next_id; - list_add(&ct->tuplehash[IP_CT_DIR_ORIGINAL].list, - &ip_conntrack_hash[hash]); - list_add(&ct->tuplehash[IP_CT_DIR_REPLY].list, - &ip_conntrack_hash[repl_hash]); -} - -void ip_conntrack_hash_insert(struct ip_conntrack *ct) -{ - unsigned int hash, repl_hash; - - hash = hash_conntrack(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple); - repl_hash = hash_conntrack(&ct->tuplehash[IP_CT_DIR_REPLY].tuple); - - write_lock_bh(&ip_conntrack_lock); - __ip_conntrack_hash_insert(ct, hash, repl_hash); - write_unlock_bh(&ip_conntrack_lock); -} - -/* Confirm a connection given skb; places it in hash table */ -int -__ip_conntrack_confirm(struct sk_buff **pskb) -{ - unsigned int hash, repl_hash; - struct ip_conntrack_tuple_hash *h; - struct ip_conntrack *ct; - enum ip_conntrack_info ctinfo; - - ct = ip_conntrack_get(*pskb, &ctinfo); - - /* ipt_REJECT uses ip_conntrack_attach to attach related - ICMP/TCP RST packets in other direction. Actual packet - which created connection will be IP_CT_NEW or for an - expected connection, IP_CT_RELATED. */ - if (CTINFO2DIR(ctinfo) != IP_CT_DIR_ORIGINAL) - return NF_ACCEPT; - - hash = hash_conntrack(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple); - repl_hash = hash_conntrack(&ct->tuplehash[IP_CT_DIR_REPLY].tuple); - - /* We're not in hash table, and we refuse to set up related - connections for unconfirmed conns. But packet copies and - REJECT will give spurious warnings here. */ - /* IP_NF_ASSERT(atomic_read(&ct->ct_general.use) == 1); */ - - /* No external references means noone else could have - confirmed us. */ - IP_NF_ASSERT(!is_confirmed(ct)); - DEBUGP("Confirming conntrack %p\n", ct); - - write_lock_bh(&ip_conntrack_lock); - - /* See if there's one in the list already, including reverse: - NAT could have grabbed it without realizing, since we're - not in the hash. If there is, we lost race. */ - list_for_each_entry(h, &ip_conntrack_hash[hash], list) - if (ip_ct_tuple_equal(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple, - &h->tuple)) - goto out; - list_for_each_entry(h, &ip_conntrack_hash[repl_hash], list) - if (ip_ct_tuple_equal(&ct->tuplehash[IP_CT_DIR_REPLY].tuple, - &h->tuple)) - goto out; - - /* Remove from unconfirmed list */ - list_del(&ct->tuplehash[IP_CT_DIR_ORIGINAL].list); - - __ip_conntrack_hash_insert(ct, hash, repl_hash); - /* Timer relative to confirmation time, not original - setting time, otherwise we'd get timer wrap in - weird delay cases. */ - ct->timeout.expires += jiffies; - add_timer(&ct->timeout); - atomic_inc(&ct->ct_general.use); - set_bit(IPS_CONFIRMED_BIT, &ct->status); - CONNTRACK_STAT_INC(insert); - write_unlock_bh(&ip_conntrack_lock); - if (ct->helper) - ip_conntrack_event_cache(IPCT_HELPER, *pskb); -#ifdef CONFIG_IP_NF_NAT_NEEDED - if (test_bit(IPS_SRC_NAT_DONE_BIT, &ct->status) || - test_bit(IPS_DST_NAT_DONE_BIT, &ct->status)) - ip_conntrack_event_cache(IPCT_NATINFO, *pskb); -#endif - ip_conntrack_event_cache(master_ct(ct) ? - IPCT_RELATED : IPCT_NEW, *pskb); - - return NF_ACCEPT; - -out: - CONNTRACK_STAT_INC(insert_failed); - write_unlock_bh(&ip_conntrack_lock); - return NF_DROP; -} - -/* Returns true if a connection correspondings to the tuple (required - for NAT). */ -int -ip_conntrack_tuple_taken(const struct ip_conntrack_tuple *tuple, - const struct ip_conntrack *ignored_conntrack) -{ - struct ip_conntrack_tuple_hash *h; - - read_lock_bh(&ip_conntrack_lock); - h = __ip_conntrack_find(tuple, ignored_conntrack); - read_unlock_bh(&ip_conntrack_lock); - - return h != NULL; -} - -/* There's a small race here where we may free a just-assured - connection. Too bad: we're in trouble anyway. */ -static int early_drop(struct list_head *chain) -{ - /* Traverse backwards: gives us oldest, which is roughly LRU */ - struct ip_conntrack_tuple_hash *h; - struct ip_conntrack *ct = NULL, *tmp; - int dropped = 0; - - read_lock_bh(&ip_conntrack_lock); - list_for_each_entry_reverse(h, chain, list) { - tmp = tuplehash_to_ctrack(h); - if (!test_bit(IPS_ASSURED_BIT, &tmp->status)) { - ct = tmp; - atomic_inc(&ct->ct_general.use); - break; - } - } - read_unlock_bh(&ip_conntrack_lock); - - if (!ct) - return dropped; - - if (del_timer(&ct->timeout)) { - death_by_timeout((unsigned long)ct); - dropped = 1; - CONNTRACK_STAT_INC_ATOMIC(early_drop); - } - ip_conntrack_put(ct); - return dropped; -} - -static struct ip_conntrack_helper * -__ip_conntrack_helper_find( const struct ip_conntrack_tuple *tuple) -{ - struct ip_conntrack_helper *h; - - list_for_each_entry(h, &helpers, list) { - if (ip_ct_tuple_mask_cmp(tuple, &h->tuple, &h->mask)) - return h; - } - return NULL; -} - -struct ip_conntrack_helper * -ip_conntrack_helper_find_get( const struct ip_conntrack_tuple *tuple) -{ - struct ip_conntrack_helper *helper; - - /* need ip_conntrack_lock to assure that helper exists until - * try_module_get() is called */ - read_lock_bh(&ip_conntrack_lock); - - helper = __ip_conntrack_helper_find(tuple); - if (helper) { - /* need to increase module usage count to assure helper will - * not go away while the caller is e.g. busy putting a - * conntrack in the hash that uses the helper */ - if (!try_module_get(helper->me)) - helper = NULL; - } - - read_unlock_bh(&ip_conntrack_lock); - - return helper; -} - -void ip_conntrack_helper_put(struct ip_conntrack_helper *helper) -{ - module_put(helper->me); -} - -struct ip_conntrack_protocol * -__ip_conntrack_proto_find(u_int8_t protocol) -{ - return ip_ct_protos[protocol]; -} - -/* this is guaranteed to always return a valid protocol helper, since - * it falls back to generic_protocol */ -struct ip_conntrack_protocol * -ip_conntrack_proto_find_get(u_int8_t protocol) -{ - struct ip_conntrack_protocol *p; - - rcu_read_lock(); - p = __ip_conntrack_proto_find(protocol); - if (p) { - if (!try_module_get(p->me)) - p = &ip_conntrack_generic_protocol; - } - rcu_read_unlock(); - - return p; -} - -void ip_conntrack_proto_put(struct ip_conntrack_protocol *p) -{ - module_put(p->me); -} - -struct ip_conntrack *ip_conntrack_alloc(struct ip_conntrack_tuple *orig, - struct ip_conntrack_tuple *repl) -{ - struct ip_conntrack *conntrack; - - if (!ip_conntrack_hash_rnd_initted) { - get_random_bytes(&ip_conntrack_hash_rnd, 4); - ip_conntrack_hash_rnd_initted = 1; - } - - /* We don't want any race condition at early drop stage */ - atomic_inc(&ip_conntrack_count); - - if (ip_conntrack_max - && atomic_read(&ip_conntrack_count) > ip_conntrack_max) { - unsigned int hash = hash_conntrack(orig); - /* Try dropping from this hash chain. */ - if (!early_drop(&ip_conntrack_hash[hash])) { - atomic_dec(&ip_conntrack_count); - if (net_ratelimit()) - printk(KERN_WARNING - "ip_conntrack: table full, dropping" - " packet.\n"); - return ERR_PTR(-ENOMEM); - } - } - - conntrack = kmem_cache_zalloc(ip_conntrack_cachep, GFP_ATOMIC); - if (!conntrack) { - DEBUGP("Can't allocate conntrack.\n"); - atomic_dec(&ip_conntrack_count); - return ERR_PTR(-ENOMEM); - } - - atomic_set(&conntrack->ct_general.use, 1); - conntrack->ct_general.destroy = destroy_conntrack; - conntrack->tuplehash[IP_CT_DIR_ORIGINAL].tuple = *orig; - conntrack->tuplehash[IP_CT_DIR_REPLY].tuple = *repl; - /* Don't set timer yet: wait for confirmation */ - init_timer(&conntrack->timeout); - conntrack->timeout.data = (unsigned long)conntrack; - conntrack->timeout.function = death_by_timeout; - - return conntrack; -} - -void -ip_conntrack_free(struct ip_conntrack *conntrack) -{ - atomic_dec(&ip_conntrack_count); - kmem_cache_free(ip_conntrack_cachep, conntrack); -} - -/* Allocate a new conntrack: we return -ENOMEM if classification - * failed due to stress. Otherwise it really is unclassifiable */ -static struct ip_conntrack_tuple_hash * -init_conntrack(struct ip_conntrack_tuple *tuple, - struct ip_conntrack_protocol *protocol, - struct sk_buff *skb) -{ - struct ip_conntrack *conntrack; - struct ip_conntrack_tuple repl_tuple; - struct ip_conntrack_expect *exp; - - if (!ip_ct_invert_tuple(&repl_tuple, tuple, protocol)) { - DEBUGP("Can't invert tuple.\n"); - return NULL; - } - - conntrack = ip_conntrack_alloc(tuple, &repl_tuple); - if (conntrack == NULL || IS_ERR(conntrack)) - return (struct ip_conntrack_tuple_hash *)conntrack; - - if (!protocol->new(conntrack, skb)) { - ip_conntrack_free(conntrack); - return NULL; - } - - write_lock_bh(&ip_conntrack_lock); - exp = find_expectation(tuple); - - if (exp) { - DEBUGP("conntrack: expectation arrives ct=%p exp=%p\n", - conntrack, exp); - /* Welcome, Mr. Bond. We've been expecting you... */ - __set_bit(IPS_EXPECTED_BIT, &conntrack->status); - conntrack->master = exp->master; -#ifdef CONFIG_IP_NF_CONNTRACK_MARK - conntrack->mark = exp->master->mark; -#endif -#if defined(CONFIG_IP_NF_TARGET_MASQUERADE) || \ - defined(CONFIG_IP_NF_TARGET_MASQUERADE_MODULE) - /* this is ugly, but there is no other place where to put it */ - conntrack->nat.masq_index = exp->master->nat.masq_index; -#endif -#ifdef CONFIG_IP_NF_CONNTRACK_SECMARK - conntrack->secmark = exp->master->secmark; -#endif - nf_conntrack_get(&conntrack->master->ct_general); - CONNTRACK_STAT_INC(expect_new); - } else { - conntrack->helper = __ip_conntrack_helper_find(&repl_tuple); - - CONNTRACK_STAT_INC(new); - } - - /* Overload tuple linked list to put us in unconfirmed list. */ - list_add(&conntrack->tuplehash[IP_CT_DIR_ORIGINAL].list, &unconfirmed); - - write_unlock_bh(&ip_conntrack_lock); - - if (exp) { - if (exp->expectfn) - exp->expectfn(conntrack, exp); - ip_conntrack_expect_put(exp); - } - - return &conntrack->tuplehash[IP_CT_DIR_ORIGINAL]; -} - -/* On success, returns conntrack ptr, sets skb->nfct and ctinfo */ -static inline struct ip_conntrack * -resolve_normal_ct(struct sk_buff *skb, - struct ip_conntrack_protocol *proto, - int *set_reply, - unsigned int hooknum, - enum ip_conntrack_info *ctinfo) -{ - struct ip_conntrack_tuple tuple; - struct ip_conntrack_tuple_hash *h; - struct ip_conntrack *ct; - - IP_NF_ASSERT((ip_hdr(skb)->frag_off & htons(IP_OFFSET)) == 0); - - if (!ip_ct_get_tuple(ip_hdr(skb), skb, ip_hdrlen(skb), &tuple,proto)) - return NULL; - - /* look for tuple match */ - h = ip_conntrack_find_get(&tuple, NULL); - if (!h) { - h = init_conntrack(&tuple, proto, skb); - if (!h) - return NULL; - if (IS_ERR(h)) - return (void *)h; - } - ct = tuplehash_to_ctrack(h); - - /* It exists; we have (non-exclusive) reference. */ - if (DIRECTION(h) == IP_CT_DIR_REPLY) { - *ctinfo = IP_CT_ESTABLISHED + IP_CT_IS_REPLY; - /* Please set reply bit if this packet OK */ - *set_reply = 1; - } else { - /* Once we've had two way comms, always ESTABLISHED. */ - if (test_bit(IPS_SEEN_REPLY_BIT, &ct->status)) { - DEBUGP("ip_conntrack_in: normal packet for %p\n", - ct); - *ctinfo = IP_CT_ESTABLISHED; - } else if (test_bit(IPS_EXPECTED_BIT, &ct->status)) { - DEBUGP("ip_conntrack_in: related packet for %p\n", - ct); - *ctinfo = IP_CT_RELATED; - } else { - DEBUGP("ip_conntrack_in: new packet for %p\n", - ct); - *ctinfo = IP_CT_NEW; - } - *set_reply = 0; - } - skb->nfct = &ct->ct_general; - skb->nfctinfo = *ctinfo; - return ct; -} - -/* Netfilter hook itself. */ -unsigned int ip_conntrack_in(unsigned int hooknum, - struct sk_buff **pskb, - const struct net_device *in, - const struct net_device *out, - int (*okfn)(struct sk_buff *)) -{ - struct ip_conntrack *ct; - enum ip_conntrack_info ctinfo; - struct ip_conntrack_protocol *proto; - int set_reply = 0; - int ret; - - /* Previously seen (loopback or untracked)? Ignore. */ - if ((*pskb)->nfct) { - CONNTRACK_STAT_INC_ATOMIC(ignore); - return NF_ACCEPT; - } - - /* Never happen */ - if (ip_hdr(*pskb)->frag_off & htons(IP_OFFSET)) { - if (net_ratelimit()) { - printk(KERN_ERR "ip_conntrack_in: Frag of proto %u (hook=%u)\n", - ip_hdr(*pskb)->protocol, hooknum); - } - return NF_DROP; - } - -/* Doesn't cover locally-generated broadcast, so not worth it. */ -#if 0 - /* Ignore broadcast: no `connection'. */ - if ((*pskb)->pkt_type == PACKET_BROADCAST) { - printk("Broadcast packet!\n"); - return NF_ACCEPT; - } else if ((ip_hdr(*pskb)->daddr & htonl(0x000000FF)) - == htonl(0x000000FF)) { - printk("Should bcast: %u.%u.%u.%u->%u.%u.%u.%u (sk=%p, ptype=%u)\n", - NIPQUAD(ip_hdr(*pskb)->saddr), - NIPQUAD(ip_hdr(*pskb)->daddr), - (*pskb)->sk, (*pskb)->pkt_type); - } -#endif - - /* rcu_read_lock()ed by nf_hook_slow */ - proto = __ip_conntrack_proto_find(ip_hdr(*pskb)->protocol); - - /* It may be an special packet, error, unclean... - * inverse of the return code tells to the netfilter - * core what to do with the packet. */ - if (proto->error != NULL - && (ret = proto->error(*pskb, &ctinfo, hooknum)) <= 0) { - CONNTRACK_STAT_INC_ATOMIC(error); - CONNTRACK_STAT_INC_ATOMIC(invalid); - return -ret; - } - - if (!(ct = resolve_normal_ct(*pskb, proto,&set_reply,hooknum,&ctinfo))) { - /* Not valid part of a connection */ - CONNTRACK_STAT_INC_ATOMIC(invalid); - return NF_ACCEPT; - } - - if (IS_ERR(ct)) { - /* Too stressed to deal. */ - CONNTRACK_STAT_INC_ATOMIC(drop); - return NF_DROP; - } - - IP_NF_ASSERT((*pskb)->nfct); - - ret = proto->packet(ct, *pskb, ctinfo); - if (ret < 0) { - /* Invalid: inverse of the return code tells - * the netfilter core what to do*/ - nf_conntrack_put((*pskb)->nfct); - (*pskb)->nfct = NULL; - CONNTRACK_STAT_INC_ATOMIC(invalid); - return -ret; - } - - if (set_reply && !test_and_set_bit(IPS_SEEN_REPLY_BIT, &ct->status)) - ip_conntrack_event_cache(IPCT_STATUS, *pskb); - - return ret; -} - -int invert_tuplepr(struct ip_conntrack_tuple *inverse, - const struct ip_conntrack_tuple *orig) -{ - struct ip_conntrack_protocol *proto; - int ret; - - rcu_read_lock(); - proto = __ip_conntrack_proto_find(orig->dst.protonum); - ret = ip_ct_invert_tuple(inverse, orig, proto); - rcu_read_unlock(); - - return ret; -} - -/* Would two expected things clash? */ -static inline int expect_clash(const struct ip_conntrack_expect *a, - const struct ip_conntrack_expect *b) -{ - /* Part covered by intersection of masks must be unequal, - otherwise they clash */ - struct ip_conntrack_tuple intersect_mask - = { { a->mask.src.ip & b->mask.src.ip, - { a->mask.src.u.all & b->mask.src.u.all } }, - { a->mask.dst.ip & b->mask.dst.ip, - { a->mask.dst.u.all & b->mask.dst.u.all }, - a->mask.dst.protonum & b->mask.dst.protonum } }; - - return ip_ct_tuple_mask_cmp(&a->tuple, &b->tuple, &intersect_mask); -} - -static inline int expect_matches(const struct ip_conntrack_expect *a, - const struct ip_conntrack_expect *b) -{ - return a->master == b->master - && ip_ct_tuple_equal(&a->tuple, &b->tuple) - && ip_ct_tuple_equal(&a->mask, &b->mask); -} - -/* Generally a bad idea to call this: could have matched already. */ -void ip_conntrack_unexpect_related(struct ip_conntrack_expect *exp) -{ - struct ip_conntrack_expect *i; - - write_lock_bh(&ip_conntrack_lock); - /* choose the the oldest expectation to evict */ - list_for_each_entry_reverse(i, &ip_conntrack_expect_list, list) { - if (expect_matches(i, exp) && del_timer(&i->timeout)) { - ip_ct_unlink_expect(i); - write_unlock_bh(&ip_conntrack_lock); - ip_conntrack_expect_put(i); - return; - } - } - write_unlock_bh(&ip_conntrack_lock); -} - -/* We don't increase the master conntrack refcount for non-fulfilled - * conntracks. During the conntrack destruction, the expectations are - * always killed before the conntrack itself */ -struct ip_conntrack_expect *ip_conntrack_expect_alloc(struct ip_conntrack *me) -{ - struct ip_conntrack_expect *new; - - new = kmem_cache_alloc(ip_conntrack_expect_cachep, GFP_ATOMIC); - if (!new) { - DEBUGP("expect_related: OOM allocating expect\n"); - return NULL; - } - new->master = me; - atomic_set(&new->use, 1); - return new; -} - -void ip_conntrack_expect_put(struct ip_conntrack_expect *exp) -{ - if (atomic_dec_and_test(&exp->use)) - kmem_cache_free(ip_conntrack_expect_cachep, exp); -} - -static void ip_conntrack_expect_insert(struct ip_conntrack_expect *exp) -{ - atomic_inc(&exp->use); - exp->master->expecting++; - list_add(&exp->list, &ip_conntrack_expect_list); - - init_timer(&exp->timeout); - exp->timeout.data = (unsigned long)exp; - exp->timeout.function = expectation_timed_out; - exp->timeout.expires = jiffies + exp->master->helper->timeout * HZ; - add_timer(&exp->timeout); - - exp->id = ++ip_conntrack_expect_next_id; - atomic_inc(&exp->use); - CONNTRACK_STAT_INC(expect_create); -} - -/* Race with expectations being used means we could have none to find; OK. */ -static void evict_oldest_expect(struct ip_conntrack *master) -{ - struct ip_conntrack_expect *i; - - list_for_each_entry_reverse(i, &ip_conntrack_expect_list, list) { - if (i->master == master) { - if (del_timer(&i->timeout)) { - ip_ct_unlink_expect(i); - ip_conntrack_expect_put(i); - } - break; - } - } -} - -static inline int refresh_timer(struct ip_conntrack_expect *i) -{ - if (!del_timer(&i->timeout)) - return 0; - - i->timeout.expires = jiffies + i->master->helper->timeout*HZ; - add_timer(&i->timeout); - return 1; -} - -int ip_conntrack_expect_related(struct ip_conntrack_expect *expect) -{ - struct ip_conntrack_expect *i; - int ret; - - DEBUGP("ip_conntrack_expect_related %p\n", related_to); - DEBUGP("tuple: "); DUMP_TUPLE(&expect->tuple); - DEBUGP("mask: "); DUMP_TUPLE(&expect->mask); - - write_lock_bh(&ip_conntrack_lock); - list_for_each_entry(i, &ip_conntrack_expect_list, list) { - if (expect_matches(i, expect)) { - /* Refresh timer: if it's dying, ignore.. */ - if (refresh_timer(i)) { - ret = 0; - goto out; - } - } else if (expect_clash(i, expect)) { - ret = -EBUSY; - goto out; - } - } - - /* Will be over limit? */ - if (expect->master->helper->max_expected && - expect->master->expecting >= expect->master->helper->max_expected) - evict_oldest_expect(expect->master); - - ip_conntrack_expect_insert(expect); - ip_conntrack_expect_event(IPEXP_NEW, expect); - ret = 0; -out: - write_unlock_bh(&ip_conntrack_lock); - return ret; -} - -/* Alter reply tuple (maybe alter helper). This is for NAT, and is - implicitly racy: see __ip_conntrack_confirm */ -void ip_conntrack_alter_reply(struct ip_conntrack *conntrack, - const struct ip_conntrack_tuple *newreply) -{ - write_lock_bh(&ip_conntrack_lock); - /* Should be unconfirmed, so not in hash table yet */ - IP_NF_ASSERT(!is_confirmed(conntrack)); - - DEBUGP("Altering reply tuple of %p to ", conntrack); - DUMP_TUPLE(newreply); - - conntrack->tuplehash[IP_CT_DIR_REPLY].tuple = *newreply; - if (!conntrack->master && conntrack->expecting == 0) - conntrack->helper = __ip_conntrack_helper_find(newreply); - write_unlock_bh(&ip_conntrack_lock); -} - -int ip_conntrack_helper_register(struct ip_conntrack_helper *me) -{ - BUG_ON(me->timeout == 0); - write_lock_bh(&ip_conntrack_lock); - list_add(&me->list, &helpers); - write_unlock_bh(&ip_conntrack_lock); - - return 0; -} - -struct ip_conntrack_helper * -__ip_conntrack_helper_find_byname(const char *name) -{ - struct ip_conntrack_helper *h; - - list_for_each_entry(h, &helpers, list) { - if (!strcmp(h->name, name)) - return h; - } - - return NULL; -} - -static inline void unhelp(struct ip_conntrack_tuple_hash *i, - const struct ip_conntrack_helper *me) -{ - if (tuplehash_to_ctrack(i)->helper == me) { - ip_conntrack_event(IPCT_HELPER, tuplehash_to_ctrack(i)); - tuplehash_to_ctrack(i)->helper = NULL; - } -} - -void ip_conntrack_helper_unregister(struct ip_conntrack_helper *me) -{ - unsigned int i; - struct ip_conntrack_tuple_hash *h; - struct ip_conntrack_expect *exp, *tmp; - - /* Need write lock here, to delete helper. */ - write_lock_bh(&ip_conntrack_lock); - list_del(&me->list); - - /* Get rid of expectations */ - list_for_each_entry_safe(exp, tmp, &ip_conntrack_expect_list, list) { - if (exp->master->helper == me && del_timer(&exp->timeout)) { - ip_ct_unlink_expect(exp); - ip_conntrack_expect_put(exp); - } - } - /* Get rid of expecteds, set helpers to NULL. */ - list_for_each_entry(h, &unconfirmed, list) - unhelp(h, me); - for (i = 0; i < ip_conntrack_htable_size; i++) { - list_for_each_entry(h, &ip_conntrack_hash[i], list) - unhelp(h, me); - } - write_unlock_bh(&ip_conntrack_lock); - - /* Someone could be still looking at the helper in a bh. */ - synchronize_net(); -} - -/* Refresh conntrack for this many jiffies and do accounting if do_acct is 1 */ -void __ip_ct_refresh_acct(struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo, - const struct sk_buff *skb, - unsigned long extra_jiffies, - int do_acct) -{ - int event = 0; - - IP_NF_ASSERT(ct->timeout.data == (unsigned long)ct); - IP_NF_ASSERT(skb); - - write_lock_bh(&ip_conntrack_lock); - - /* Only update if this is not a fixed timeout */ - if (test_bit(IPS_FIXED_TIMEOUT_BIT, &ct->status)) { - write_unlock_bh(&ip_conntrack_lock); - return; - } - - /* If not in hash table, timer will not be active yet */ - if (!is_confirmed(ct)) { - ct->timeout.expires = extra_jiffies; - event = IPCT_REFRESH; - } else { - /* Need del_timer for race avoidance (may already be dying). */ - if (del_timer(&ct->timeout)) { - ct->timeout.expires = jiffies + extra_jiffies; - add_timer(&ct->timeout); - event = IPCT_REFRESH; - } - } - -#ifdef CONFIG_IP_NF_CT_ACCT - if (do_acct) { - ct->counters[CTINFO2DIR(ctinfo)].packets++; - ct->counters[CTINFO2DIR(ctinfo)].bytes += - ntohs(ip_hdr(skb)->tot_len); - if ((ct->counters[CTINFO2DIR(ctinfo)].packets & 0x80000000) - || (ct->counters[CTINFO2DIR(ctinfo)].bytes & 0x80000000)) - event |= IPCT_COUNTER_FILLING; - } -#endif - - write_unlock_bh(&ip_conntrack_lock); - - /* must be unlocked when calling event cache */ - if (event) - ip_conntrack_event_cache(event, skb); -} - -#if defined(CONFIG_IP_NF_CONNTRACK_NETLINK) || \ - defined(CONFIG_IP_NF_CONNTRACK_NETLINK_MODULE) -/* Generic function for tcp/udp/sctp/dccp and alike. This needs to be - * in ip_conntrack_core, since we don't want the protocols to autoload - * or depend on ctnetlink */ -int ip_ct_port_tuple_to_nfattr(struct sk_buff *skb, - const struct ip_conntrack_tuple *tuple) -{ - NFA_PUT(skb, CTA_PROTO_SRC_PORT, sizeof(__be16), - &tuple->src.u.tcp.port); - NFA_PUT(skb, CTA_PROTO_DST_PORT, sizeof(__be16), - &tuple->dst.u.tcp.port); - return 0; - -nfattr_failure: - return -1; -} - -int ip_ct_port_nfattr_to_tuple(struct nfattr *tb[], - struct ip_conntrack_tuple *t) -{ - if (!tb[CTA_PROTO_SRC_PORT-1] || !tb[CTA_PROTO_DST_PORT-1]) - return -EINVAL; - - t->src.u.tcp.port = - *(__be16 *)NFA_DATA(tb[CTA_PROTO_SRC_PORT-1]); - t->dst.u.tcp.port = - *(__be16 *)NFA_DATA(tb[CTA_PROTO_DST_PORT-1]); - - return 0; -} -#endif - -/* Returns new sk_buff, or NULL */ -struct sk_buff * -ip_ct_gather_frags(struct sk_buff *skb, u_int32_t user) -{ - skb_orphan(skb); - - local_bh_disable(); - skb = ip_defrag(skb, user); - local_bh_enable(); - - if (skb) - ip_send_check(ip_hdr(skb)); - return skb; -} - -/* Used by ipt_REJECT. */ -static void ip_conntrack_attach(struct sk_buff *nskb, struct sk_buff *skb) -{ - struct ip_conntrack *ct; - enum ip_conntrack_info ctinfo; - - /* This ICMP is in reverse direction to the packet which caused it */ - ct = ip_conntrack_get(skb, &ctinfo); - - if (CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL) - ctinfo = IP_CT_RELATED + IP_CT_IS_REPLY; - else - ctinfo = IP_CT_RELATED; - - /* Attach to new skbuff, and increment count */ - nskb->nfct = &ct->ct_general; - nskb->nfctinfo = ctinfo; - nf_conntrack_get(nskb->nfct); -} - -/* Bring out ya dead! */ -static struct ip_conntrack * -get_next_corpse(int (*iter)(struct ip_conntrack *i, void *data), - void *data, unsigned int *bucket) -{ - struct ip_conntrack_tuple_hash *h; - struct ip_conntrack *ct; - - write_lock_bh(&ip_conntrack_lock); - for (; *bucket < ip_conntrack_htable_size; (*bucket)++) { - list_for_each_entry(h, &ip_conntrack_hash[*bucket], list) { - ct = tuplehash_to_ctrack(h); - if (iter(ct, data)) - goto found; - } - } - list_for_each_entry(h, &unconfirmed, list) { - ct = tuplehash_to_ctrack(h); - if (iter(ct, data)) - set_bit(IPS_DYING_BIT, &ct->status); - } - write_unlock_bh(&ip_conntrack_lock); - return NULL; - -found: - atomic_inc(&ct->ct_general.use); - write_unlock_bh(&ip_conntrack_lock); - return ct; -} - -void -ip_ct_iterate_cleanup(int (*iter)(struct ip_conntrack *i, void *), void *data) -{ - struct ip_conntrack *ct; - unsigned int bucket = 0; - - while ((ct = get_next_corpse(iter, data, &bucket)) != NULL) { - /* Time to push up daises... */ - if (del_timer(&ct->timeout)) - death_by_timeout((unsigned long)ct); - /* ... else the timer will get him soon. */ - - ip_conntrack_put(ct); - } -} - -/* Fast function for those who don't want to parse /proc (and I don't - blame them). */ -/* Reversing the socket's dst/src point of view gives us the reply - mapping. */ -static int -getorigdst(struct sock *sk, int optval, void __user *user, int *len) -{ - struct inet_sock *inet = inet_sk(sk); - struct ip_conntrack_tuple_hash *h; - struct ip_conntrack_tuple tuple; - - IP_CT_TUPLE_U_BLANK(&tuple); - tuple.src.ip = inet->rcv_saddr; - tuple.src.u.tcp.port = inet->sport; - tuple.dst.ip = inet->daddr; - tuple.dst.u.tcp.port = inet->dport; - tuple.dst.protonum = IPPROTO_TCP; - - /* We only do TCP at the moment: is there a better way? */ - if (strcmp(sk->sk_prot->name, "TCP")) { - DEBUGP("SO_ORIGINAL_DST: Not a TCP socket\n"); - return -ENOPROTOOPT; - } - - if ((unsigned int) *len < sizeof(struct sockaddr_in)) { - DEBUGP("SO_ORIGINAL_DST: len %u not %u\n", - *len, sizeof(struct sockaddr_in)); - return -EINVAL; - } - - h = ip_conntrack_find_get(&tuple, NULL); - if (h) { - struct sockaddr_in sin; - struct ip_conntrack *ct = tuplehash_to_ctrack(h); - - sin.sin_family = AF_INET; - sin.sin_port = ct->tuplehash[IP_CT_DIR_ORIGINAL] - .tuple.dst.u.tcp.port; - sin.sin_addr.s_addr = ct->tuplehash[IP_CT_DIR_ORIGINAL] - .tuple.dst.ip; - memset(sin.sin_zero, 0, sizeof(sin.sin_zero)); - - DEBUGP("SO_ORIGINAL_DST: %u.%u.%u.%u %u\n", - NIPQUAD(sin.sin_addr.s_addr), ntohs(sin.sin_port)); - ip_conntrack_put(ct); - if (copy_to_user(user, &sin, sizeof(sin)) != 0) - return -EFAULT; - else - return 0; - } - DEBUGP("SO_ORIGINAL_DST: Can't find %u.%u.%u.%u/%u-%u.%u.%u.%u/%u.\n", - NIPQUAD(tuple.src.ip), ntohs(tuple.src.u.tcp.port), - NIPQUAD(tuple.dst.ip), ntohs(tuple.dst.u.tcp.port)); - return -ENOENT; -} - -static struct nf_sockopt_ops so_getorigdst = { - .pf = PF_INET, - .get_optmin = SO_ORIGINAL_DST, - .get_optmax = SO_ORIGINAL_DST+1, - .get = &getorigdst, -}; - -static int kill_all(struct ip_conntrack *i, void *data) -{ - return 1; -} - -void ip_conntrack_flush(void) -{ - ip_ct_iterate_cleanup(kill_all, NULL); -} - -static void free_conntrack_hash(struct list_head *hash, int vmalloced,int size) -{ - if (vmalloced) - vfree(hash); - else - free_pages((unsigned long)hash, - get_order(sizeof(struct list_head) * size)); -} - -/* Mishearing the voices in his head, our hero wonders how he's - supposed to kill the mall. */ -void ip_conntrack_cleanup(void) -{ - rcu_assign_pointer(ip_ct_attach, NULL); - - /* This makes sure all current packets have passed through - netfilter framework. Roll on, two-stage module - delete... */ - synchronize_net(); - - ip_ct_event_cache_flush(); - i_see_dead_people: - ip_conntrack_flush(); - if (atomic_read(&ip_conntrack_count) != 0) { - schedule(); - goto i_see_dead_people; - } - /* wait until all references to ip_conntrack_untracked are dropped */ - while (atomic_read(&ip_conntrack_untracked.ct_general.use) > 1) - schedule(); - - kmem_cache_destroy(ip_conntrack_cachep); - kmem_cache_destroy(ip_conntrack_expect_cachep); - free_conntrack_hash(ip_conntrack_hash, ip_conntrack_vmalloc, - ip_conntrack_htable_size); - nf_unregister_sockopt(&so_getorigdst); -} - -static struct list_head *alloc_hashtable(int size, int *vmalloced) -{ - struct list_head *hash; - unsigned int i; - - *vmalloced = 0; - hash = (void*)__get_free_pages(GFP_KERNEL, - get_order(sizeof(struct list_head) - * size)); - if (!hash) { - *vmalloced = 1; - printk(KERN_WARNING"ip_conntrack: falling back to vmalloc.\n"); - hash = vmalloc(sizeof(struct list_head) * size); - } - - if (hash) - for (i = 0; i < size; i++) - INIT_LIST_HEAD(&hash[i]); - - return hash; -} - -static int set_hashsize(const char *val, struct kernel_param *kp) -{ - int i, bucket, hashsize, vmalloced; - int old_vmalloced, old_size; - int rnd; - struct list_head *hash, *old_hash; - struct ip_conntrack_tuple_hash *h; - - /* On boot, we can set this without any fancy locking. */ - if (!ip_conntrack_htable_size) - return param_set_int(val, kp); - - hashsize = simple_strtol(val, NULL, 0); - if (!hashsize) - return -EINVAL; - - hash = alloc_hashtable(hashsize, &vmalloced); - if (!hash) - return -ENOMEM; - - /* We have to rehash for the new table anyway, so we also can - * use a new random seed */ - get_random_bytes(&rnd, 4); - - write_lock_bh(&ip_conntrack_lock); - for (i = 0; i < ip_conntrack_htable_size; i++) { - while (!list_empty(&ip_conntrack_hash[i])) { - h = list_entry(ip_conntrack_hash[i].next, - struct ip_conntrack_tuple_hash, list); - list_del(&h->list); - bucket = __hash_conntrack(&h->tuple, hashsize, rnd); - list_add_tail(&h->list, &hash[bucket]); - } - } - old_size = ip_conntrack_htable_size; - old_vmalloced = ip_conntrack_vmalloc; - old_hash = ip_conntrack_hash; - - ip_conntrack_htable_size = hashsize; - ip_conntrack_vmalloc = vmalloced; - ip_conntrack_hash = hash; - ip_conntrack_hash_rnd = rnd; - write_unlock_bh(&ip_conntrack_lock); - - free_conntrack_hash(old_hash, old_vmalloced, old_size); - return 0; -} - -module_param_call(hashsize, set_hashsize, param_get_uint, - &ip_conntrack_htable_size, 0600); - -int __init ip_conntrack_init(void) -{ - unsigned int i; - int ret; - - /* Idea from tcp.c: use 1/16384 of memory. On i386: 32MB - * machine has 256 buckets. >= 1GB machines have 8192 buckets. */ - if (!ip_conntrack_htable_size) { - ip_conntrack_htable_size - = (((num_physpages << PAGE_SHIFT) / 16384) - / sizeof(struct list_head)); - if (num_physpages > (1024 * 1024 * 1024 / PAGE_SIZE)) - ip_conntrack_htable_size = 8192; - if (ip_conntrack_htable_size < 16) - ip_conntrack_htable_size = 16; - } - ip_conntrack_max = 8 * ip_conntrack_htable_size; - - printk("ip_conntrack version %s (%u buckets, %d max)" - " - %Zd bytes per conntrack\n", IP_CONNTRACK_VERSION, - ip_conntrack_htable_size, ip_conntrack_max, - sizeof(struct ip_conntrack)); - - ret = nf_register_sockopt(&so_getorigdst); - if (ret != 0) { - printk(KERN_ERR "Unable to register netfilter socket option\n"); - return ret; - } - - ip_conntrack_hash = alloc_hashtable(ip_conntrack_htable_size, - &ip_conntrack_vmalloc); - if (!ip_conntrack_hash) { - printk(KERN_ERR "Unable to create ip_conntrack_hash\n"); - goto err_unreg_sockopt; - } - - ip_conntrack_cachep = kmem_cache_create("ip_conntrack", - sizeof(struct ip_conntrack), 0, - 0, NULL, NULL); - if (!ip_conntrack_cachep) { - printk(KERN_ERR "Unable to create ip_conntrack slab cache\n"); - goto err_free_hash; - } - - ip_conntrack_expect_cachep = kmem_cache_create("ip_conntrack_expect", - sizeof(struct ip_conntrack_expect), - 0, 0, NULL, NULL); - if (!ip_conntrack_expect_cachep) { - printk(KERN_ERR "Unable to create ip_expect slab cache\n"); - goto err_free_conntrack_slab; - } - - /* Don't NEED lock here, but good form anyway. */ - write_lock_bh(&ip_conntrack_lock); - for (i = 0; i < MAX_IP_CT_PROTO; i++) - rcu_assign_pointer(ip_ct_protos[i], &ip_conntrack_generic_protocol); - /* Sew in builtin protocols. */ - rcu_assign_pointer(ip_ct_protos[IPPROTO_TCP], &ip_conntrack_protocol_tcp); - rcu_assign_pointer(ip_ct_protos[IPPROTO_UDP], &ip_conntrack_protocol_udp); - rcu_assign_pointer(ip_ct_protos[IPPROTO_ICMP], &ip_conntrack_protocol_icmp); - write_unlock_bh(&ip_conntrack_lock); - - /* For use by ipt_REJECT */ - rcu_assign_pointer(ip_ct_attach, ip_conntrack_attach); - - /* Set up fake conntrack: - - to never be deleted, not in any hashes */ - atomic_set(&ip_conntrack_untracked.ct_general.use, 1); - /* - and look it like as a confirmed connection */ - set_bit(IPS_CONFIRMED_BIT, &ip_conntrack_untracked.status); - - return ret; - -err_free_conntrack_slab: - kmem_cache_destroy(ip_conntrack_cachep); -err_free_hash: - free_conntrack_hash(ip_conntrack_hash, ip_conntrack_vmalloc, - ip_conntrack_htable_size); -err_unreg_sockopt: - nf_unregister_sockopt(&so_getorigdst); - - return -ENOMEM; -} diff --git a/net/ipv4/netfilter/ip_conntrack_ftp.c b/net/ipv4/netfilter/ip_conntrack_ftp.c deleted file mode 100644 index 92389987e789..000000000000 --- a/net/ipv4/netfilter/ip_conntrack_ftp.c +++ /dev/null @@ -1,520 +0,0 @@ -/* FTP extension for IP connection tracking. */ - -/* (C) 1999-2001 Paul `Rusty' Russell - * (C) 2002-2004 Netfilter Core Team - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Rusty Russell "); -MODULE_DESCRIPTION("ftp connection tracking helper"); - -/* This is slow, but it's simple. --RR */ -static char *ftp_buffer; -static DEFINE_SPINLOCK(ip_ftp_lock); - -#define MAX_PORTS 8 -static unsigned short ports[MAX_PORTS]; -static int ports_c; -module_param_array(ports, ushort, &ports_c, 0400); - -static int loose; -module_param(loose, bool, 0600); - -unsigned int (*ip_nat_ftp_hook)(struct sk_buff **pskb, - enum ip_conntrack_info ctinfo, - enum ip_ct_ftp_type type, - unsigned int matchoff, - unsigned int matchlen, - struct ip_conntrack_expect *exp, - u32 *seq); -EXPORT_SYMBOL_GPL(ip_nat_ftp_hook); - -#if 0 -#define DEBUGP printk -#else -#define DEBUGP(format, args...) -#endif - -static int try_rfc959(const char *, size_t, u_int32_t [], char); -static int try_eprt(const char *, size_t, u_int32_t [], char); -static int try_epsv_response(const char *, size_t, u_int32_t [], char); - -static const struct ftp_search { - const char *pattern; - size_t plen; - char skip; - char term; - enum ip_ct_ftp_type ftptype; - int (*getnum)(const char *, size_t, u_int32_t[], char); -} search[IP_CT_DIR_MAX][2] = { - [IP_CT_DIR_ORIGINAL] = { - { - .pattern = "PORT", - .plen = sizeof("PORT") - 1, - .skip = ' ', - .term = '\r', - .ftptype = IP_CT_FTP_PORT, - .getnum = try_rfc959, - }, - { - .pattern = "EPRT", - .plen = sizeof("EPRT") - 1, - .skip = ' ', - .term = '\r', - .ftptype = IP_CT_FTP_EPRT, - .getnum = try_eprt, - }, - }, - [IP_CT_DIR_REPLY] = { - { - .pattern = "227 ", - .plen = sizeof("227 ") - 1, - .skip = '(', - .term = ')', - .ftptype = IP_CT_FTP_PASV, - .getnum = try_rfc959, - }, - { - .pattern = "229 ", - .plen = sizeof("229 ") - 1, - .skip = '(', - .term = ')', - .ftptype = IP_CT_FTP_EPSV, - .getnum = try_epsv_response, - }, - }, -}; - -static int try_number(const char *data, size_t dlen, u_int32_t array[], - int array_size, char sep, char term) -{ - u_int32_t i, len; - - memset(array, 0, sizeof(array[0])*array_size); - - /* Keep data pointing at next char. */ - for (i = 0, len = 0; len < dlen && i < array_size; len++, data++) { - if (*data >= '0' && *data <= '9') { - array[i] = array[i]*10 + *data - '0'; - } - else if (*data == sep) - i++; - else { - /* Unexpected character; true if it's the - terminator and we're finished. */ - if (*data == term && i == array_size - 1) - return len; - - DEBUGP("Char %u (got %u nums) `%u' unexpected\n", - len, i, *data); - return 0; - } - } - DEBUGP("Failed to fill %u numbers separated by %c\n", array_size, sep); - - return 0; -} - -/* Returns 0, or length of numbers: 192,168,1,1,5,6 */ -static int try_rfc959(const char *data, size_t dlen, u_int32_t array[6], - char term) -{ - return try_number(data, dlen, array, 6, ',', term); -} - -/* Grab port: number up to delimiter */ -static int get_port(const char *data, int start, size_t dlen, char delim, - u_int32_t array[2]) -{ - u_int16_t port = 0; - int i; - - for (i = start; i < dlen; i++) { - /* Finished? */ - if (data[i] == delim) { - if (port == 0) - break; - array[0] = port >> 8; - array[1] = port; - return i + 1; - } - else if (data[i] >= '0' && data[i] <= '9') - port = port*10 + data[i] - '0'; - else /* Some other crap */ - break; - } - return 0; -} - -/* Returns 0, or length of numbers: |1|132.235.1.2|6275| */ -static int try_eprt(const char *data, size_t dlen, u_int32_t array[6], - char term) -{ - char delim; - int length; - - /* First character is delimiter, then "1" for IPv4, then - delimiter again. */ - if (dlen <= 3) return 0; - delim = data[0]; - if (isdigit(delim) || delim < 33 || delim > 126 - || data[1] != '1' || data[2] != delim) - return 0; - - DEBUGP("EPRT: Got |1|!\n"); - /* Now we have IP address. */ - length = try_number(data + 3, dlen - 3, array, 4, '.', delim); - if (length == 0) - return 0; - - DEBUGP("EPRT: Got IP address!\n"); - /* Start offset includes initial "|1|", and trailing delimiter */ - return get_port(data, 3 + length + 1, dlen, delim, array+4); -} - -/* Returns 0, or length of numbers: |||6446| */ -static int try_epsv_response(const char *data, size_t dlen, u_int32_t array[6], - char term) -{ - char delim; - - /* Three delimiters. */ - if (dlen <= 3) return 0; - delim = data[0]; - if (isdigit(delim) || delim < 33 || delim > 126 - || data[1] != delim || data[2] != delim) - return 0; - - return get_port(data, 3, dlen, delim, array+4); -} - -/* Return 1 for match, 0 for accept, -1 for partial. */ -static int find_pattern(const char *data, size_t dlen, - const char *pattern, size_t plen, - char skip, char term, - unsigned int *numoff, - unsigned int *numlen, - u_int32_t array[6], - int (*getnum)(const char *, size_t, u_int32_t[], char)) -{ - size_t i; - - DEBUGP("find_pattern `%s': dlen = %u\n", pattern, dlen); - if (dlen == 0) - return 0; - - if (dlen <= plen) { - /* Short packet: try for partial? */ - if (strnicmp(data, pattern, dlen) == 0) - return -1; - else return 0; - } - - if (strnicmp(data, pattern, plen) != 0) { -#if 0 - size_t i; - - DEBUGP("ftp: string mismatch\n"); - for (i = 0; i < plen; i++) { - DEBUGP("ftp:char %u `%c'(%u) vs `%c'(%u)\n", - i, data[i], data[i], - pattern[i], pattern[i]); - } -#endif - return 0; - } - - DEBUGP("Pattern matches!\n"); - /* Now we've found the constant string, try to skip - to the 'skip' character */ - for (i = plen; data[i] != skip; i++) - if (i == dlen - 1) return -1; - - /* Skip over the last character */ - i++; - - DEBUGP("Skipped up to `%c'!\n", skip); - - *numoff = i; - *numlen = getnum(data + i, dlen - i, array, term); - if (!*numlen) - return -1; - - DEBUGP("Match succeeded!\n"); - return 1; -} - -/* Look up to see if we're just after a \n. */ -static int find_nl_seq(u32 seq, const struct ip_ct_ftp_master *info, int dir) -{ - unsigned int i; - - for (i = 0; i < info->seq_aft_nl_num[dir]; i++) - if (info->seq_aft_nl[dir][i] == seq) - return 1; - return 0; -} - -/* We don't update if it's older than what we have. */ -static void update_nl_seq(u32 nl_seq, struct ip_ct_ftp_master *info, int dir, - struct sk_buff *skb) -{ - unsigned int i, oldest = NUM_SEQ_TO_REMEMBER; - - /* Look for oldest: if we find exact match, we're done. */ - for (i = 0; i < info->seq_aft_nl_num[dir]; i++) { - if (info->seq_aft_nl[dir][i] == nl_seq) - return; - - if (oldest == info->seq_aft_nl_num[dir] - || before(info->seq_aft_nl[dir][i], oldest)) - oldest = i; - } - - if (info->seq_aft_nl_num[dir] < NUM_SEQ_TO_REMEMBER) { - info->seq_aft_nl[dir][info->seq_aft_nl_num[dir]++] = nl_seq; - ip_conntrack_event_cache(IPCT_HELPINFO_VOLATILE, skb); - } else if (oldest != NUM_SEQ_TO_REMEMBER) { - info->seq_aft_nl[dir][oldest] = nl_seq; - ip_conntrack_event_cache(IPCT_HELPINFO_VOLATILE, skb); - } -} - -static int help(struct sk_buff **pskb, - struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo) -{ - unsigned int dataoff, datalen; - struct tcphdr _tcph, *th; - char *fb_ptr; - int ret; - u32 seq, array[6] = { 0 }; - int dir = CTINFO2DIR(ctinfo); - unsigned int matchlen, matchoff; - struct ip_ct_ftp_master *ct_ftp_info = &ct->help.ct_ftp_info; - struct ip_conntrack_expect *exp; - unsigned int i; - int found = 0, ends_in_nl; - typeof(ip_nat_ftp_hook) ip_nat_ftp; - - /* Until there's been traffic both ways, don't look in packets. */ - if (ctinfo != IP_CT_ESTABLISHED - && ctinfo != IP_CT_ESTABLISHED+IP_CT_IS_REPLY) { - DEBUGP("ftp: Conntrackinfo = %u\n", ctinfo); - return NF_ACCEPT; - } - - th = skb_header_pointer(*pskb, ip_hdrlen(*pskb), - sizeof(_tcph), &_tcph); - if (th == NULL) - return NF_ACCEPT; - - dataoff = ip_hdrlen(*pskb) + th->doff * 4; - /* No data? */ - if (dataoff >= (*pskb)->len) { - DEBUGP("ftp: pskblen = %u\n", (*pskb)->len); - return NF_ACCEPT; - } - datalen = (*pskb)->len - dataoff; - - spin_lock_bh(&ip_ftp_lock); - fb_ptr = skb_header_pointer(*pskb, dataoff, - (*pskb)->len - dataoff, ftp_buffer); - BUG_ON(fb_ptr == NULL); - - ends_in_nl = (fb_ptr[datalen - 1] == '\n'); - seq = ntohl(th->seq) + datalen; - - /* Look up to see if we're just after a \n. */ - if (!find_nl_seq(ntohl(th->seq), ct_ftp_info, dir)) { - /* Now if this ends in \n, update ftp info. */ - DEBUGP("ip_conntrack_ftp_help: wrong seq pos %s(%u) or %s(%u)\n", - ct_ftp_info->seq_aft_nl[0][dir] - old_seq_aft_nl_set ? "":"(UNSET) ", old_seq_aft_nl); - ret = NF_ACCEPT; - goto out_update_nl; - } - - /* Initialize IP array to expected address (it's not mentioned - in EPSV responses) */ - array[0] = (ntohl(ct->tuplehash[dir].tuple.src.ip) >> 24) & 0xFF; - array[1] = (ntohl(ct->tuplehash[dir].tuple.src.ip) >> 16) & 0xFF; - array[2] = (ntohl(ct->tuplehash[dir].tuple.src.ip) >> 8) & 0xFF; - array[3] = ntohl(ct->tuplehash[dir].tuple.src.ip) & 0xFF; - - for (i = 0; i < ARRAY_SIZE(search[dir]); i++) { - found = find_pattern(fb_ptr, (*pskb)->len - dataoff, - search[dir][i].pattern, - search[dir][i].plen, - search[dir][i].skip, - search[dir][i].term, - &matchoff, &matchlen, - array, - search[dir][i].getnum); - if (found) break; - } - if (found == -1) { - /* We don't usually drop packets. After all, this is - connection tracking, not packet filtering. - However, it is necessary for accurate tracking in - this case. */ - if (net_ratelimit()) - printk("conntrack_ftp: partial %s %u+%u\n", - search[dir][i].pattern, - ntohl(th->seq), datalen); - ret = NF_DROP; - goto out; - } else if (found == 0) { /* No match */ - ret = NF_ACCEPT; - goto out_update_nl; - } - - DEBUGP("conntrack_ftp: match `%s' (%u bytes at %u)\n", - fb_ptr + matchoff, matchlen, ntohl(th->seq) + matchoff); - - /* Allocate expectation which will be inserted */ - exp = ip_conntrack_expect_alloc(ct); - if (exp == NULL) { - ret = NF_DROP; - goto out; - } - - /* We refer to the reverse direction ("!dir") tuples here, - * because we're expecting something in the other direction. - * Doesn't matter unless NAT is happening. */ - exp->tuple.dst.ip = ct->tuplehash[!dir].tuple.dst.ip; - - if (htonl((array[0] << 24) | (array[1] << 16) | (array[2] << 8) | array[3]) - != ct->tuplehash[dir].tuple.src.ip) { - /* Enrico Scholz's passive FTP to partially RNAT'd ftp - server: it really wants us to connect to a - different IP address. Simply don't record it for - NAT. */ - DEBUGP("conntrack_ftp: NOT RECORDING: %u,%u,%u,%u != %u.%u.%u.%u\n", - array[0], array[1], array[2], array[3], - NIPQUAD(ct->tuplehash[dir].tuple.src.ip)); - - /* Thanks to Cristiano Lincoln Mattos - for reporting this potential - problem (DMZ machines opening holes to internal - networks, or the packet filter itself). */ - if (!loose) { - ret = NF_ACCEPT; - goto out_put_expect; - } - exp->tuple.dst.ip = htonl((array[0] << 24) | (array[1] << 16) - | (array[2] << 8) | array[3]); - } - - exp->tuple.src.ip = ct->tuplehash[!dir].tuple.src.ip; - exp->tuple.dst.u.tcp.port = htons(array[4] << 8 | array[5]); - exp->tuple.src.u.tcp.port = 0; /* Don't care. */ - exp->tuple.dst.protonum = IPPROTO_TCP; - exp->mask = ((struct ip_conntrack_tuple) - { { htonl(0xFFFFFFFF), { 0 } }, - { htonl(0xFFFFFFFF), { .tcp = { htons(0xFFFF) } }, 0xFF }}); - - exp->expectfn = NULL; - exp->flags = 0; - - /* Now, NAT might want to mangle the packet, and register the - * (possibly changed) expectation itself. */ - ip_nat_ftp = rcu_dereference(ip_nat_ftp_hook); - if (ip_nat_ftp) - ret = ip_nat_ftp(pskb, ctinfo, search[dir][i].ftptype, - matchoff, matchlen, exp, &seq); - else { - /* Can't expect this? Best to drop packet now. */ - if (ip_conntrack_expect_related(exp) != 0) - ret = NF_DROP; - else - ret = NF_ACCEPT; - } - -out_put_expect: - ip_conntrack_expect_put(exp); - -out_update_nl: - /* Now if this ends in \n, update ftp info. Seq may have been - * adjusted by NAT code. */ - if (ends_in_nl) - update_nl_seq(seq, ct_ftp_info,dir, *pskb); - out: - spin_unlock_bh(&ip_ftp_lock); - return ret; -} - -static struct ip_conntrack_helper ftp[MAX_PORTS]; -static char ftp_names[MAX_PORTS][sizeof("ftp-65535")]; - -/* Not __exit: called from init() */ -static void ip_conntrack_ftp_fini(void) -{ - int i; - for (i = 0; i < ports_c; i++) { - DEBUGP("ip_ct_ftp: unregistering helper for port %d\n", - ports[i]); - ip_conntrack_helper_unregister(&ftp[i]); - } - - kfree(ftp_buffer); -} - -static int __init ip_conntrack_ftp_init(void) -{ - int i, ret; - char *tmpname; - - ftp_buffer = kmalloc(65536, GFP_KERNEL); - if (!ftp_buffer) - return -ENOMEM; - - if (ports_c == 0) - ports[ports_c++] = FTP_PORT; - - for (i = 0; i < ports_c; i++) { - ftp[i].tuple.src.u.tcp.port = htons(ports[i]); - ftp[i].tuple.dst.protonum = IPPROTO_TCP; - ftp[i].mask.src.u.tcp.port = htons(0xFFFF); - ftp[i].mask.dst.protonum = 0xFF; - ftp[i].max_expected = 1; - ftp[i].timeout = 5 * 60; /* 5 minutes */ - ftp[i].me = THIS_MODULE; - ftp[i].help = help; - - tmpname = &ftp_names[i][0]; - if (ports[i] == FTP_PORT) - sprintf(tmpname, "ftp"); - else - sprintf(tmpname, "ftp-%d", ports[i]); - ftp[i].name = tmpname; - - DEBUGP("ip_ct_ftp: registering helper for port %d\n", - ports[i]); - ret = ip_conntrack_helper_register(&ftp[i]); - - if (ret) { - ip_conntrack_ftp_fini(); - return ret; - } - } - return 0; -} - -module_init(ip_conntrack_ftp_init); -module_exit(ip_conntrack_ftp_fini); diff --git a/net/ipv4/netfilter/ip_conntrack_helper_h323.c b/net/ipv4/netfilter/ip_conntrack_helper_h323.c deleted file mode 100644 index cecb6e0c8ed0..000000000000 --- a/net/ipv4/netfilter/ip_conntrack_helper_h323.c +++ /dev/null @@ -1,1840 +0,0 @@ -/* - * H.323 connection tracking helper - * - * Copyright (c) 2006 Jing Min Zhao - * - * This source code is licensed under General Public License version 2. - * - * Based on the 'brute force' H.323 connection tracking module by - * Jozsef Kadlecsik - * - * For more information, please see http://nath323.sourceforge.net/ - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#if 0 -#define DEBUGP printk -#else -#define DEBUGP(format, args...) -#endif - -/* Parameters */ -static unsigned int default_rrq_ttl = 300; -module_param(default_rrq_ttl, uint, 0600); -MODULE_PARM_DESC(default_rrq_ttl, "use this TTL if it's missing in RRQ"); - -static int gkrouted_only = 1; -module_param(gkrouted_only, int, 0600); -MODULE_PARM_DESC(gkrouted_only, "only accept calls from gatekeeper"); - -static int callforward_filter = 1; -module_param(callforward_filter, bool, 0600); -MODULE_PARM_DESC(callforward_filter, "only create call forwarding expectations " - "if both endpoints are on different sides " - "(determined by routing information)"); - -/* Hooks for NAT */ -int (*set_h245_addr_hook) (struct sk_buff ** pskb, - unsigned char **data, int dataoff, - H245_TransportAddress * addr, - __be32 ip, u_int16_t port); -int (*set_h225_addr_hook) (struct sk_buff ** pskb, - unsigned char **data, int dataoff, - TransportAddress * addr, - __be32 ip, u_int16_t port); -int (*set_sig_addr_hook) (struct sk_buff ** pskb, - struct ip_conntrack * ct, - enum ip_conntrack_info ctinfo, - unsigned char **data, - TransportAddress * addr, int count); -int (*set_ras_addr_hook) (struct sk_buff ** pskb, - struct ip_conntrack * ct, - enum ip_conntrack_info ctinfo, - unsigned char **data, - TransportAddress * addr, int count); -int (*nat_rtp_rtcp_hook) (struct sk_buff ** pskb, - struct ip_conntrack * ct, - enum ip_conntrack_info ctinfo, - unsigned char **data, int dataoff, - H245_TransportAddress * addr, - u_int16_t port, u_int16_t rtp_port, - struct ip_conntrack_expect * rtp_exp, - struct ip_conntrack_expect * rtcp_exp); -int (*nat_t120_hook) (struct sk_buff ** pskb, - struct ip_conntrack * ct, - enum ip_conntrack_info ctinfo, - unsigned char **data, int dataoff, - H245_TransportAddress * addr, u_int16_t port, - struct ip_conntrack_expect * exp); -int (*nat_h245_hook) (struct sk_buff ** pskb, - struct ip_conntrack * ct, - enum ip_conntrack_info ctinfo, - unsigned char **data, int dataoff, - TransportAddress * addr, u_int16_t port, - struct ip_conntrack_expect * exp); -int (*nat_callforwarding_hook) (struct sk_buff ** pskb, - struct ip_conntrack * ct, - enum ip_conntrack_info ctinfo, - unsigned char **data, int dataoff, - TransportAddress * addr, u_int16_t port, - struct ip_conntrack_expect * exp); -int (*nat_q931_hook) (struct sk_buff ** pskb, - struct ip_conntrack * ct, - enum ip_conntrack_info ctinfo, - unsigned char **data, TransportAddress * addr, int idx, - u_int16_t port, struct ip_conntrack_expect * exp); - - -static DEFINE_SPINLOCK(ip_h323_lock); -static char *h323_buffer; - -/****************************************************************************/ -static int get_tpkt_data(struct sk_buff **pskb, struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo, - unsigned char **data, int *datalen, int *dataoff) -{ - struct ip_ct_h323_master *info = &ct->help.ct_h323_info; - int dir = CTINFO2DIR(ctinfo); - struct tcphdr _tcph, *th; - int tcpdatalen; - int tcpdataoff; - unsigned char *tpkt; - int tpktlen; - int tpktoff; - - /* Get TCP header */ - th = skb_header_pointer(*pskb, ip_hdrlen(*pskb), - sizeof(_tcph), &_tcph); - if (th == NULL) - return 0; - - /* Get TCP data offset */ - tcpdataoff = ip_hdrlen(*pskb) + th->doff * 4; - - /* Get TCP data length */ - tcpdatalen = (*pskb)->len - tcpdataoff; - if (tcpdatalen <= 0) /* No TCP data */ - goto clear_out; - - if (*data == NULL) { /* first TPKT */ - /* Get first TPKT pointer */ - tpkt = skb_header_pointer(*pskb, tcpdataoff, tcpdatalen, - h323_buffer); - BUG_ON(tpkt == NULL); - - /* Validate TPKT identifier */ - if (tcpdatalen < 4 || tpkt[0] != 0x03 || tpkt[1] != 0) { - /* Netmeeting sends TPKT header and data separately */ - if (info->tpkt_len[dir] > 0) { - DEBUGP("ip_ct_h323: previous packet " - "indicated separate TPKT data of %hu " - "bytes\n", info->tpkt_len[dir]); - if (info->tpkt_len[dir] <= tcpdatalen) { - /* Yes, there was a TPKT header - * received */ - *data = tpkt; - *datalen = info->tpkt_len[dir]; - *dataoff = 0; - goto out; - } - - /* Fragmented TPKT */ - if (net_ratelimit()) - printk("ip_ct_h323: " - "fragmented TPKT\n"); - goto clear_out; - } - - /* It is not even a TPKT */ - return 0; - } - tpktoff = 0; - } else { /* Next TPKT */ - tpktoff = *dataoff + *datalen; - tcpdatalen -= tpktoff; - if (tcpdatalen <= 4) /* No more TPKT */ - goto clear_out; - tpkt = *data + *datalen; - - /* Validate TPKT identifier */ - if (tpkt[0] != 0x03 || tpkt[1] != 0) - goto clear_out; - } - - /* Validate TPKT length */ - tpktlen = tpkt[2] * 256 + tpkt[3]; - if (tpktlen < 4) - goto clear_out; - if (tpktlen > tcpdatalen) { - if (tcpdatalen == 4) { /* Separate TPKT header */ - /* Netmeeting sends TPKT header and data separately */ - DEBUGP("ip_ct_h323: separate TPKT header indicates " - "there will be TPKT data of %hu bytes\n", - tpktlen - 4); - info->tpkt_len[dir] = tpktlen - 4; - return 0; - } - - if (net_ratelimit()) - printk("ip_ct_h323: incomplete TPKT (fragmented?)\n"); - goto clear_out; - } - - /* This is the encapsulated data */ - *data = tpkt + 4; - *datalen = tpktlen - 4; - *dataoff = tpktoff + 4; - - out: - /* Clear TPKT length */ - info->tpkt_len[dir] = 0; - return 1; - - clear_out: - info->tpkt_len[dir] = 0; - return 0; -} - -/****************************************************************************/ -static int get_h245_addr(unsigned char *data, H245_TransportAddress * addr, - __be32 * ip, u_int16_t * port) -{ - unsigned char *p; - - if (addr->choice != eH245_TransportAddress_unicastAddress || - addr->unicastAddress.choice != eUnicastAddress_iPAddress) - return 0; - - p = data + addr->unicastAddress.iPAddress.network; - *ip = htonl((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | (p[3])); - *port = (p[4] << 8) | (p[5]); - - return 1; -} - -/****************************************************************************/ -static int expect_rtp_rtcp(struct sk_buff **pskb, struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo, - unsigned char **data, int dataoff, - H245_TransportAddress * addr) -{ - int dir = CTINFO2DIR(ctinfo); - int ret = 0; - __be32 ip; - u_int16_t port; - u_int16_t rtp_port; - struct ip_conntrack_expect *rtp_exp; - struct ip_conntrack_expect *rtcp_exp; - typeof(nat_rtp_rtcp_hook) nat_rtp_rtcp; - - /* Read RTP or RTCP address */ - if (!get_h245_addr(*data, addr, &ip, &port) || - ip != ct->tuplehash[dir].tuple.src.ip || port == 0) - return 0; - - /* RTP port is even */ - rtp_port = port & (~1); - - /* Create expect for RTP */ - if ((rtp_exp = ip_conntrack_expect_alloc(ct)) == NULL) - return -1; - rtp_exp->tuple.src.ip = ct->tuplehash[!dir].tuple.src.ip; - rtp_exp->tuple.src.u.udp.port = 0; - rtp_exp->tuple.dst.ip = ct->tuplehash[!dir].tuple.dst.ip; - rtp_exp->tuple.dst.u.udp.port = htons(rtp_port); - rtp_exp->tuple.dst.protonum = IPPROTO_UDP; - rtp_exp->mask.src.ip = htonl(0xFFFFFFFF); - rtp_exp->mask.src.u.udp.port = 0; - rtp_exp->mask.dst.ip = htonl(0xFFFFFFFF); - rtp_exp->mask.dst.u.udp.port = htons(0xFFFF); - rtp_exp->mask.dst.protonum = 0xFF; - rtp_exp->flags = 0; - - /* Create expect for RTCP */ - if ((rtcp_exp = ip_conntrack_expect_alloc(ct)) == NULL) { - ip_conntrack_expect_put(rtp_exp); - return -1; - } - rtcp_exp->tuple.src.ip = ct->tuplehash[!dir].tuple.src.ip; - rtcp_exp->tuple.src.u.udp.port = 0; - rtcp_exp->tuple.dst.ip = ct->tuplehash[!dir].tuple.dst.ip; - rtcp_exp->tuple.dst.u.udp.port = htons(rtp_port + 1); - rtcp_exp->tuple.dst.protonum = IPPROTO_UDP; - rtcp_exp->mask.src.ip = htonl(0xFFFFFFFF); - rtcp_exp->mask.src.u.udp.port = 0; - rtcp_exp->mask.dst.ip = htonl(0xFFFFFFFF); - rtcp_exp->mask.dst.u.udp.port = htons(0xFFFF); - rtcp_exp->mask.dst.protonum = 0xFF; - rtcp_exp->flags = 0; - - if (ct->tuplehash[dir].tuple.src.ip != - ct->tuplehash[!dir].tuple.dst.ip && - (nat_rtp_rtcp = rcu_dereference(nat_rtp_rtcp_hook))) { - /* NAT needed */ - ret = nat_rtp_rtcp(pskb, ct, ctinfo, data, dataoff, - addr, port, rtp_port, rtp_exp, rtcp_exp); - } else { /* Conntrack only */ - rtp_exp->expectfn = NULL; - rtcp_exp->expectfn = NULL; - - if (ip_conntrack_expect_related(rtp_exp) == 0) { - if (ip_conntrack_expect_related(rtcp_exp) == 0) { - DEBUGP("ip_ct_h323: expect RTP " - "%u.%u.%u.%u:%hu->%u.%u.%u.%u:%hu\n", - NIPQUAD(rtp_exp->tuple.src.ip), - ntohs(rtp_exp->tuple.src.u.udp.port), - NIPQUAD(rtp_exp->tuple.dst.ip), - ntohs(rtp_exp->tuple.dst.u.udp.port)); - DEBUGP("ip_ct_h323: expect RTCP " - "%u.%u.%u.%u:%hu->%u.%u.%u.%u:%hu\n", - NIPQUAD(rtcp_exp->tuple.src.ip), - ntohs(rtcp_exp->tuple.src.u.udp.port), - NIPQUAD(rtcp_exp->tuple.dst.ip), - ntohs(rtcp_exp->tuple.dst.u.udp.port)); - } else { - ip_conntrack_unexpect_related(rtp_exp); - ret = -1; - } - } else - ret = -1; - } - - ip_conntrack_expect_put(rtp_exp); - ip_conntrack_expect_put(rtcp_exp); - - return ret; -} - -/****************************************************************************/ -static int expect_t120(struct sk_buff **pskb, - struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo, - unsigned char **data, int dataoff, - H245_TransportAddress * addr) -{ - int dir = CTINFO2DIR(ctinfo); - int ret = 0; - __be32 ip; - u_int16_t port; - struct ip_conntrack_expect *exp = NULL; - typeof(nat_t120_hook) nat_t120; - - /* Read T.120 address */ - if (!get_h245_addr(*data, addr, &ip, &port) || - ip != ct->tuplehash[dir].tuple.src.ip || port == 0) - return 0; - - /* Create expect for T.120 connections */ - if ((exp = ip_conntrack_expect_alloc(ct)) == NULL) - return -1; - exp->tuple.src.ip = ct->tuplehash[!dir].tuple.src.ip; - exp->tuple.src.u.tcp.port = 0; - exp->tuple.dst.ip = ct->tuplehash[!dir].tuple.dst.ip; - exp->tuple.dst.u.tcp.port = htons(port); - exp->tuple.dst.protonum = IPPROTO_TCP; - exp->mask.src.ip = htonl(0xFFFFFFFF); - exp->mask.src.u.tcp.port = 0; - exp->mask.dst.ip = htonl(0xFFFFFFFF); - exp->mask.dst.u.tcp.port = htons(0xFFFF); - exp->mask.dst.protonum = 0xFF; - exp->flags = IP_CT_EXPECT_PERMANENT; /* Accept multiple channels */ - - if (ct->tuplehash[dir].tuple.src.ip != - ct->tuplehash[!dir].tuple.dst.ip && - (nat_t120 = rcu_dereference(nat_t120_hook))) { - /* NAT needed */ - ret = nat_t120(pskb, ct, ctinfo, data, dataoff, addr, - port, exp); - } else { /* Conntrack only */ - exp->expectfn = NULL; - if (ip_conntrack_expect_related(exp) == 0) { - DEBUGP("ip_ct_h323: expect T.120 " - "%u.%u.%u.%u:%hu->%u.%u.%u.%u:%hu\n", - NIPQUAD(exp->tuple.src.ip), - ntohs(exp->tuple.src.u.tcp.port), - NIPQUAD(exp->tuple.dst.ip), - ntohs(exp->tuple.dst.u.tcp.port)); - } else - ret = -1; - } - - ip_conntrack_expect_put(exp); - - return ret; -} - -/****************************************************************************/ -static int process_h245_channel(struct sk_buff **pskb, - struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo, - unsigned char **data, int dataoff, - H2250LogicalChannelParameters * channel) -{ - int ret; - - if (channel->options & eH2250LogicalChannelParameters_mediaChannel) { - /* RTP */ - ret = expect_rtp_rtcp(pskb, ct, ctinfo, data, dataoff, - &channel->mediaChannel); - if (ret < 0) - return -1; - } - - if (channel-> - options & eH2250LogicalChannelParameters_mediaControlChannel) { - /* RTCP */ - ret = expect_rtp_rtcp(pskb, ct, ctinfo, data, dataoff, - &channel->mediaControlChannel); - if (ret < 0) - return -1; - } - - return 0; -} - -/****************************************************************************/ -static int process_olc(struct sk_buff **pskb, struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo, - unsigned char **data, int dataoff, - OpenLogicalChannel * olc) -{ - int ret; - - DEBUGP("ip_ct_h323: OpenLogicalChannel\n"); - - if (olc->forwardLogicalChannelParameters.multiplexParameters.choice == - eOpenLogicalChannel_forwardLogicalChannelParameters_multiplexParameters_h2250LogicalChannelParameters) - { - ret = process_h245_channel(pskb, ct, ctinfo, data, dataoff, - &olc-> - forwardLogicalChannelParameters. - multiplexParameters. - h2250LogicalChannelParameters); - if (ret < 0) - return -1; - } - - if ((olc->options & - eOpenLogicalChannel_reverseLogicalChannelParameters) && - (olc->reverseLogicalChannelParameters.options & - eOpenLogicalChannel_reverseLogicalChannelParameters_multiplexParameters) - && (olc->reverseLogicalChannelParameters.multiplexParameters. - choice == - eOpenLogicalChannel_reverseLogicalChannelParameters_multiplexParameters_h2250LogicalChannelParameters)) - { - ret = - process_h245_channel(pskb, ct, ctinfo, data, dataoff, - &olc-> - reverseLogicalChannelParameters. - multiplexParameters. - h2250LogicalChannelParameters); - if (ret < 0) - return -1; - } - - if ((olc->options & eOpenLogicalChannel_separateStack) && - olc->forwardLogicalChannelParameters.dataType.choice == - eDataType_data && - olc->forwardLogicalChannelParameters.dataType.data.application. - choice == eDataApplicationCapability_application_t120 && - olc->forwardLogicalChannelParameters.dataType.data.application. - t120.choice == eDataProtocolCapability_separateLANStack && - olc->separateStack.networkAddress.choice == - eNetworkAccessParameters_networkAddress_localAreaAddress) { - ret = expect_t120(pskb, ct, ctinfo, data, dataoff, - &olc->separateStack.networkAddress. - localAreaAddress); - if (ret < 0) - return -1; - } - - return 0; -} - -/****************************************************************************/ -static int process_olca(struct sk_buff **pskb, struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo, - unsigned char **data, int dataoff, - OpenLogicalChannelAck * olca) -{ - H2250LogicalChannelAckParameters *ack; - int ret; - - DEBUGP("ip_ct_h323: OpenLogicalChannelAck\n"); - - if ((olca->options & - eOpenLogicalChannelAck_reverseLogicalChannelParameters) && - (olca->reverseLogicalChannelParameters.options & - eOpenLogicalChannelAck_reverseLogicalChannelParameters_multiplexParameters) - && (olca->reverseLogicalChannelParameters.multiplexParameters. - choice == - eOpenLogicalChannelAck_reverseLogicalChannelParameters_multiplexParameters_h2250LogicalChannelParameters)) - { - ret = process_h245_channel(pskb, ct, ctinfo, data, dataoff, - &olca-> - reverseLogicalChannelParameters. - multiplexParameters. - h2250LogicalChannelParameters); - if (ret < 0) - return -1; - } - - if ((olca->options & - eOpenLogicalChannelAck_forwardMultiplexAckParameters) && - (olca->forwardMultiplexAckParameters.choice == - eOpenLogicalChannelAck_forwardMultiplexAckParameters_h2250LogicalChannelAckParameters)) - { - ack = &olca->forwardMultiplexAckParameters. - h2250LogicalChannelAckParameters; - if (ack->options & - eH2250LogicalChannelAckParameters_mediaChannel) { - /* RTP */ - ret = expect_rtp_rtcp(pskb, ct, ctinfo, data, dataoff, - &ack->mediaChannel); - if (ret < 0) - return -1; - } - - if (ack->options & - eH2250LogicalChannelAckParameters_mediaControlChannel) { - /* RTCP */ - ret = expect_rtp_rtcp(pskb, ct, ctinfo, data, dataoff, - &ack->mediaControlChannel); - if (ret < 0) - return -1; - } - } - - return 0; -} - -/****************************************************************************/ -static int process_h245(struct sk_buff **pskb, struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo, - unsigned char **data, int dataoff, - MultimediaSystemControlMessage * mscm) -{ - switch (mscm->choice) { - case eMultimediaSystemControlMessage_request: - if (mscm->request.choice == - eRequestMessage_openLogicalChannel) { - return process_olc(pskb, ct, ctinfo, data, dataoff, - &mscm->request.openLogicalChannel); - } - DEBUGP("ip_ct_h323: H.245 Request %d\n", - mscm->request.choice); - break; - case eMultimediaSystemControlMessage_response: - if (mscm->response.choice == - eResponseMessage_openLogicalChannelAck) { - return process_olca(pskb, ct, ctinfo, data, dataoff, - &mscm->response. - openLogicalChannelAck); - } - DEBUGP("ip_ct_h323: H.245 Response %d\n", - mscm->response.choice); - break; - default: - DEBUGP("ip_ct_h323: H.245 signal %d\n", mscm->choice); - break; - } - - return 0; -} - -/****************************************************************************/ -static int h245_help(struct sk_buff **pskb, struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo) -{ - static MultimediaSystemControlMessage mscm; - unsigned char *data = NULL; - int datalen; - int dataoff; - int ret; - - /* Until there's been traffic both ways, don't look in packets. */ - if (ctinfo != IP_CT_ESTABLISHED - && ctinfo != IP_CT_ESTABLISHED + IP_CT_IS_REPLY) { - return NF_ACCEPT; - } - DEBUGP("ip_ct_h245: skblen = %u\n", (*pskb)->len); - - spin_lock_bh(&ip_h323_lock); - - /* Process each TPKT */ - while (get_tpkt_data(pskb, ct, ctinfo, &data, &datalen, &dataoff)) { - DEBUGP("ip_ct_h245: TPKT %u.%u.%u.%u->%u.%u.%u.%u, len=%d\n", - NIPQUAD(ip_hdr(*pskb)->saddr), - NIPQUAD(ip_hdr(*pskb)->daddr), datalen); - - /* Decode H.245 signal */ - ret = DecodeMultimediaSystemControlMessage(data, datalen, - &mscm); - if (ret < 0) { - if (net_ratelimit()) - printk("ip_ct_h245: decoding error: %s\n", - ret == H323_ERROR_BOUND ? - "out of bound" : "out of range"); - /* We don't drop when decoding error */ - break; - } - - /* Process H.245 signal */ - if (process_h245(pskb, ct, ctinfo, &data, dataoff, &mscm) < 0) - goto drop; - } - - spin_unlock_bh(&ip_h323_lock); - return NF_ACCEPT; - - drop: - spin_unlock_bh(&ip_h323_lock); - if (net_ratelimit()) - printk("ip_ct_h245: packet dropped\n"); - return NF_DROP; -} - -/****************************************************************************/ -static struct ip_conntrack_helper ip_conntrack_helper_h245 = { - .name = "H.245", - .me = THIS_MODULE, - .max_expected = H323_RTP_CHANNEL_MAX * 4 + 2 /* T.120 */ , - .timeout = 240, - .tuple = {.dst = {.protonum = IPPROTO_TCP}}, - .mask = {.src = {.u = {0xFFFF}}, - .dst = {.protonum = 0xFF}}, - .help = h245_help -}; - -/****************************************************************************/ -void ip_conntrack_h245_expect(struct ip_conntrack *new, - struct ip_conntrack_expect *this) -{ - write_lock_bh(&ip_conntrack_lock); - new->helper = &ip_conntrack_helper_h245; - write_unlock_bh(&ip_conntrack_lock); -} - -/****************************************************************************/ -int get_h225_addr(unsigned char *data, TransportAddress * addr, - __be32 * ip, u_int16_t * port) -{ - unsigned char *p; - - if (addr->choice != eTransportAddress_ipAddress) - return 0; - - p = data + addr->ipAddress.ip; - *ip = htonl((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | (p[3])); - *port = (p[4] << 8) | (p[5]); - - return 1; -} - -/****************************************************************************/ -static int expect_h245(struct sk_buff **pskb, struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo, - unsigned char **data, int dataoff, - TransportAddress * addr) -{ - int dir = CTINFO2DIR(ctinfo); - int ret = 0; - __be32 ip; - u_int16_t port; - struct ip_conntrack_expect *exp = NULL; - typeof(nat_h245_hook) nat_h245; - - /* Read h245Address */ - if (!get_h225_addr(*data, addr, &ip, &port) || - ip != ct->tuplehash[dir].tuple.src.ip || port == 0) - return 0; - - /* Create expect for h245 connection */ - if ((exp = ip_conntrack_expect_alloc(ct)) == NULL) - return -1; - exp->tuple.src.ip = ct->tuplehash[!dir].tuple.src.ip; - exp->tuple.src.u.tcp.port = 0; - exp->tuple.dst.ip = ct->tuplehash[!dir].tuple.dst.ip; - exp->tuple.dst.u.tcp.port = htons(port); - exp->tuple.dst.protonum = IPPROTO_TCP; - exp->mask.src.ip = htonl(0xFFFFFFFF); - exp->mask.src.u.tcp.port = 0; - exp->mask.dst.ip = htonl(0xFFFFFFFF); - exp->mask.dst.u.tcp.port = htons(0xFFFF); - exp->mask.dst.protonum = 0xFF; - exp->flags = 0; - - if (ct->tuplehash[dir].tuple.src.ip != - ct->tuplehash[!dir].tuple.dst.ip && - (nat_h245 = rcu_dereference(nat_h245_hook))) { - /* NAT needed */ - ret = nat_h245(pskb, ct, ctinfo, data, dataoff, addr, - port, exp); - } else { /* Conntrack only */ - exp->expectfn = ip_conntrack_h245_expect; - - if (ip_conntrack_expect_related(exp) == 0) { - DEBUGP("ip_ct_q931: expect H.245 " - "%u.%u.%u.%u:%hu->%u.%u.%u.%u:%hu\n", - NIPQUAD(exp->tuple.src.ip), - ntohs(exp->tuple.src.u.tcp.port), - NIPQUAD(exp->tuple.dst.ip), - ntohs(exp->tuple.dst.u.tcp.port)); - } else - ret = -1; - } - - ip_conntrack_expect_put(exp); - - return ret; -} - -/* Forwarding declaration */ -void ip_conntrack_q931_expect(struct ip_conntrack *new, - struct ip_conntrack_expect *this); - -/****************************************************************************/ -static int expect_callforwarding(struct sk_buff **pskb, - struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo, - unsigned char **data, int dataoff, - TransportAddress * addr) -{ - int dir = CTINFO2DIR(ctinfo); - int ret = 0; - __be32 ip; - u_int16_t port; - struct ip_conntrack_expect *exp = NULL; - typeof(nat_callforwarding_hook) nat_callforwarding; - - /* Read alternativeAddress */ - if (!get_h225_addr(*data, addr, &ip, &port) || port == 0) - return 0; - - /* If the calling party is on the same side of the forward-to party, - * we don't need to track the second call */ - if (callforward_filter) { - struct rtable *rt1, *rt2; - struct flowi fl1 = { - .fl4_dst = ip, - }; - struct flowi fl2 = { - .fl4_dst = ct->tuplehash[!dir].tuple.src.ip, - }; - - if (ip_route_output_key(&rt1, &fl1) == 0) { - if (ip_route_output_key(&rt2, &fl2) == 0) { - if (rt1->rt_gateway == rt2->rt_gateway && - rt1->u.dst.dev == rt2->u.dst.dev) - ret = 1; - dst_release(&rt2->u.dst); - } - dst_release(&rt1->u.dst); - } - if (ret) { - DEBUGP("ip_ct_q931: Call Forwarding not tracked\n"); - return 0; - } - } - - /* Create expect for the second call leg */ - if ((exp = ip_conntrack_expect_alloc(ct)) == NULL) - return -1; - exp->tuple.src.ip = ct->tuplehash[!dir].tuple.src.ip; - exp->tuple.src.u.tcp.port = 0; - exp->tuple.dst.ip = ip; - exp->tuple.dst.u.tcp.port = htons(port); - exp->tuple.dst.protonum = IPPROTO_TCP; - exp->mask.src.ip = htonl(0xFFFFFFFF); - exp->mask.src.u.tcp.port = 0; - exp->mask.dst.ip = htonl(0xFFFFFFFF); - exp->mask.dst.u.tcp.port = htons(0xFFFF); - exp->mask.dst.protonum = 0xFF; - exp->flags = 0; - - if (ct->tuplehash[dir].tuple.src.ip != - ct->tuplehash[!dir].tuple.dst.ip && - (nat_callforwarding = rcu_dereference(nat_callforwarding_hook))) { - /* Need NAT */ - ret = nat_callforwarding(pskb, ct, ctinfo, data, dataoff, - addr, port, exp); - } else { /* Conntrack only */ - exp->expectfn = ip_conntrack_q931_expect; - - if (ip_conntrack_expect_related(exp) == 0) { - DEBUGP("ip_ct_q931: expect Call Forwarding " - "%u.%u.%u.%u:%hu->%u.%u.%u.%u:%hu\n", - NIPQUAD(exp->tuple.src.ip), - ntohs(exp->tuple.src.u.tcp.port), - NIPQUAD(exp->tuple.dst.ip), - ntohs(exp->tuple.dst.u.tcp.port)); - } else - ret = -1; - } - - ip_conntrack_expect_put(exp); - - return ret; -} - -/****************************************************************************/ -static int process_setup(struct sk_buff **pskb, struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo, - unsigned char **data, int dataoff, - Setup_UUIE * setup) -{ - int dir = CTINFO2DIR(ctinfo); - int ret; - int i; - __be32 ip; - u_int16_t port; - typeof(set_h225_addr_hook) set_h225_addr; - - DEBUGP("ip_ct_q931: Setup\n"); - - if (setup->options & eSetup_UUIE_h245Address) { - ret = expect_h245(pskb, ct, ctinfo, data, dataoff, - &setup->h245Address); - if (ret < 0) - return -1; - } - - set_h225_addr = rcu_dereference(set_h225_addr_hook); - - if ((setup->options & eSetup_UUIE_destCallSignalAddress) && - (set_h225_addr) && - get_h225_addr(*data, &setup->destCallSignalAddress, &ip, &port) && - ip != ct->tuplehash[!dir].tuple.src.ip) { - DEBUGP("ip_ct_q931: set destCallSignalAddress " - "%u.%u.%u.%u:%hu->%u.%u.%u.%u:%hu\n", - NIPQUAD(ip), port, - NIPQUAD(ct->tuplehash[!dir].tuple.src.ip), - ntohs(ct->tuplehash[!dir].tuple.src.u.tcp.port)); - ret = set_h225_addr(pskb, data, dataoff, - &setup->destCallSignalAddress, - ct->tuplehash[!dir].tuple.src.ip, - ntohs(ct->tuplehash[!dir].tuple.src. - u.tcp.port)); - if (ret < 0) - return -1; - } - - if ((setup->options & eSetup_UUIE_sourceCallSignalAddress) && - (set_h225_addr) && - get_h225_addr(*data, &setup->sourceCallSignalAddress, &ip, &port) - && ip != ct->tuplehash[!dir].tuple.dst.ip) { - DEBUGP("ip_ct_q931: set sourceCallSignalAddress " - "%u.%u.%u.%u:%hu->%u.%u.%u.%u:%hu\n", - NIPQUAD(ip), port, - NIPQUAD(ct->tuplehash[!dir].tuple.dst.ip), - ntohs(ct->tuplehash[!dir].tuple.dst.u.tcp.port)); - ret = set_h225_addr(pskb, data, dataoff, - &setup->sourceCallSignalAddress, - ct->tuplehash[!dir].tuple.dst.ip, - ntohs(ct->tuplehash[!dir].tuple.dst. - u.tcp.port)); - if (ret < 0) - return -1; - } - - if (setup->options & eSetup_UUIE_fastStart) { - for (i = 0; i < setup->fastStart.count; i++) { - ret = process_olc(pskb, ct, ctinfo, data, dataoff, - &setup->fastStart.item[i]); - if (ret < 0) - return -1; - } - } - - return 0; -} - -/****************************************************************************/ -static int process_callproceeding(struct sk_buff **pskb, - struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo, - unsigned char **data, int dataoff, - CallProceeding_UUIE * callproc) -{ - int ret; - int i; - - DEBUGP("ip_ct_q931: CallProceeding\n"); - - if (callproc->options & eCallProceeding_UUIE_h245Address) { - ret = expect_h245(pskb, ct, ctinfo, data, dataoff, - &callproc->h245Address); - if (ret < 0) - return -1; - } - - if (callproc->options & eCallProceeding_UUIE_fastStart) { - for (i = 0; i < callproc->fastStart.count; i++) { - ret = process_olc(pskb, ct, ctinfo, data, dataoff, - &callproc->fastStart.item[i]); - if (ret < 0) - return -1; - } - } - - return 0; -} - -/****************************************************************************/ -static int process_connect(struct sk_buff **pskb, struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo, - unsigned char **data, int dataoff, - Connect_UUIE * connect) -{ - int ret; - int i; - - DEBUGP("ip_ct_q931: Connect\n"); - - if (connect->options & eConnect_UUIE_h245Address) { - ret = expect_h245(pskb, ct, ctinfo, data, dataoff, - &connect->h245Address); - if (ret < 0) - return -1; - } - - if (connect->options & eConnect_UUIE_fastStart) { - for (i = 0; i < connect->fastStart.count; i++) { - ret = process_olc(pskb, ct, ctinfo, data, dataoff, - &connect->fastStart.item[i]); - if (ret < 0) - return -1; - } - } - - return 0; -} - -/****************************************************************************/ -static int process_alerting(struct sk_buff **pskb, struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo, - unsigned char **data, int dataoff, - Alerting_UUIE * alert) -{ - int ret; - int i; - - DEBUGP("ip_ct_q931: Alerting\n"); - - if (alert->options & eAlerting_UUIE_h245Address) { - ret = expect_h245(pskb, ct, ctinfo, data, dataoff, - &alert->h245Address); - if (ret < 0) - return -1; - } - - if (alert->options & eAlerting_UUIE_fastStart) { - for (i = 0; i < alert->fastStart.count; i++) { - ret = process_olc(pskb, ct, ctinfo, data, dataoff, - &alert->fastStart.item[i]); - if (ret < 0) - return -1; - } - } - - return 0; -} - -/****************************************************************************/ -static int process_information(struct sk_buff **pskb, - struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo, - unsigned char **data, int dataoff, - Information_UUIE * info) -{ - int ret; - int i; - - DEBUGP("ip_ct_q931: Information\n"); - - if (info->options & eInformation_UUIE_fastStart) { - for (i = 0; i < info->fastStart.count; i++) { - ret = process_olc(pskb, ct, ctinfo, data, dataoff, - &info->fastStart.item[i]); - if (ret < 0) - return -1; - } - } - - return 0; -} - -/****************************************************************************/ -static int process_facility(struct sk_buff **pskb, struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo, - unsigned char **data, int dataoff, - Facility_UUIE * facility) -{ - int ret; - int i; - - DEBUGP("ip_ct_q931: Facility\n"); - - if (facility->reason.choice == eFacilityReason_callForwarded) { - if (facility->options & eFacility_UUIE_alternativeAddress) - return expect_callforwarding(pskb, ct, ctinfo, data, - dataoff, - &facility-> - alternativeAddress); - return 0; - } - - if (facility->options & eFacility_UUIE_h245Address) { - ret = expect_h245(pskb, ct, ctinfo, data, dataoff, - &facility->h245Address); - if (ret < 0) - return -1; - } - - if (facility->options & eFacility_UUIE_fastStart) { - for (i = 0; i < facility->fastStart.count; i++) { - ret = process_olc(pskb, ct, ctinfo, data, dataoff, - &facility->fastStart.item[i]); - if (ret < 0) - return -1; - } - } - - return 0; -} - -/****************************************************************************/ -static int process_progress(struct sk_buff **pskb, struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo, - unsigned char **data, int dataoff, - Progress_UUIE * progress) -{ - int ret; - int i; - - DEBUGP("ip_ct_q931: Progress\n"); - - if (progress->options & eProgress_UUIE_h245Address) { - ret = expect_h245(pskb, ct, ctinfo, data, dataoff, - &progress->h245Address); - if (ret < 0) - return -1; - } - - if (progress->options & eProgress_UUIE_fastStart) { - for (i = 0; i < progress->fastStart.count; i++) { - ret = process_olc(pskb, ct, ctinfo, data, dataoff, - &progress->fastStart.item[i]); - if (ret < 0) - return -1; - } - } - - return 0; -} - -/****************************************************************************/ -static int process_q931(struct sk_buff **pskb, struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo, - unsigned char **data, int dataoff, Q931 * q931) -{ - H323_UU_PDU *pdu = &q931->UUIE.h323_uu_pdu; - int i; - int ret = 0; - - switch (pdu->h323_message_body.choice) { - case eH323_UU_PDU_h323_message_body_setup: - ret = process_setup(pskb, ct, ctinfo, data, dataoff, - &pdu->h323_message_body.setup); - break; - case eH323_UU_PDU_h323_message_body_callProceeding: - ret = process_callproceeding(pskb, ct, ctinfo, data, dataoff, - &pdu->h323_message_body. - callProceeding); - break; - case eH323_UU_PDU_h323_message_body_connect: - ret = process_connect(pskb, ct, ctinfo, data, dataoff, - &pdu->h323_message_body.connect); - break; - case eH323_UU_PDU_h323_message_body_alerting: - ret = process_alerting(pskb, ct, ctinfo, data, dataoff, - &pdu->h323_message_body.alerting); - break; - case eH323_UU_PDU_h323_message_body_information: - ret = process_information(pskb, ct, ctinfo, data, dataoff, - &pdu->h323_message_body. - information); - break; - case eH323_UU_PDU_h323_message_body_facility: - ret = process_facility(pskb, ct, ctinfo, data, dataoff, - &pdu->h323_message_body.facility); - break; - case eH323_UU_PDU_h323_message_body_progress: - ret = process_progress(pskb, ct, ctinfo, data, dataoff, - &pdu->h323_message_body.progress); - break; - default: - DEBUGP("ip_ct_q931: Q.931 signal %d\n", - pdu->h323_message_body.choice); - break; - } - - if (ret < 0) - return -1; - - if (pdu->options & eH323_UU_PDU_h245Control) { - for (i = 0; i < pdu->h245Control.count; i++) { - ret = process_h245(pskb, ct, ctinfo, data, dataoff, - &pdu->h245Control.item[i]); - if (ret < 0) - return -1; - } - } - - return 0; -} - -/****************************************************************************/ -static int q931_help(struct sk_buff **pskb, struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo) -{ - static Q931 q931; - unsigned char *data = NULL; - int datalen; - int dataoff; - int ret; - - /* Until there's been traffic both ways, don't look in packets. */ - if (ctinfo != IP_CT_ESTABLISHED - && ctinfo != IP_CT_ESTABLISHED + IP_CT_IS_REPLY) { - return NF_ACCEPT; - } - DEBUGP("ip_ct_q931: skblen = %u\n", (*pskb)->len); - - spin_lock_bh(&ip_h323_lock); - - /* Process each TPKT */ - while (get_tpkt_data(pskb, ct, ctinfo, &data, &datalen, &dataoff)) { - DEBUGP("ip_ct_q931: TPKT %u.%u.%u.%u->%u.%u.%u.%u, len=%d\n", - NIPQUAD(ip_hdr(*pskb)->saddr), - NIPQUAD(ip_hdr(*pskb)->daddr), datalen); - - /* Decode Q.931 signal */ - ret = DecodeQ931(data, datalen, &q931); - if (ret < 0) { - if (net_ratelimit()) - printk("ip_ct_q931: decoding error: %s\n", - ret == H323_ERROR_BOUND ? - "out of bound" : "out of range"); - /* We don't drop when decoding error */ - break; - } - - /* Process Q.931 signal */ - if (process_q931(pskb, ct, ctinfo, &data, dataoff, &q931) < 0) - goto drop; - } - - spin_unlock_bh(&ip_h323_lock); - return NF_ACCEPT; - - drop: - spin_unlock_bh(&ip_h323_lock); - if (net_ratelimit()) - printk("ip_ct_q931: packet dropped\n"); - return NF_DROP; -} - -/****************************************************************************/ -static struct ip_conntrack_helper ip_conntrack_helper_q931 = { - .name = "Q.931", - .me = THIS_MODULE, - .max_expected = H323_RTP_CHANNEL_MAX * 4 + 4 /* T.120 and H.245 */ , - .timeout = 240, - .tuple = {.src = {.u = {.tcp = {.port = __constant_htons(Q931_PORT)}}}, - .dst = {.protonum = IPPROTO_TCP}}, - .mask = {.src = {.u = {0xFFFF}}, - .dst = {.protonum = 0xFF}}, - .help = q931_help -}; - -/****************************************************************************/ -void ip_conntrack_q931_expect(struct ip_conntrack *new, - struct ip_conntrack_expect *this) -{ - write_lock_bh(&ip_conntrack_lock); - new->helper = &ip_conntrack_helper_q931; - write_unlock_bh(&ip_conntrack_lock); -} - -/****************************************************************************/ -static unsigned char *get_udp_data(struct sk_buff **pskb, int *datalen) -{ - struct udphdr _uh, *uh; - int dataoff; - - uh = skb_header_pointer(*pskb, ip_hdrlen(*pskb), sizeof(_uh), &_uh); - if (uh == NULL) - return NULL; - dataoff = ip_hdrlen(*pskb) + sizeof(_uh); - if (dataoff >= (*pskb)->len) - return NULL; - *datalen = (*pskb)->len - dataoff; - return skb_header_pointer(*pskb, dataoff, *datalen, h323_buffer); -} - -/****************************************************************************/ -static struct ip_conntrack_expect *find_expect(struct ip_conntrack *ct, - __be32 ip, u_int16_t port) -{ - struct ip_conntrack_expect *exp; - struct ip_conntrack_tuple tuple; - - tuple.src.ip = 0; - tuple.src.u.tcp.port = 0; - tuple.dst.ip = ip; - tuple.dst.u.tcp.port = htons(port); - tuple.dst.protonum = IPPROTO_TCP; - - exp = __ip_conntrack_expect_find(&tuple); - if (exp && exp->master == ct) - return exp; - return NULL; -} - -/****************************************************************************/ -static int set_expect_timeout(struct ip_conntrack_expect *exp, - unsigned timeout) -{ - if (!exp || !del_timer(&exp->timeout)) - return 0; - - exp->timeout.expires = jiffies + timeout * HZ; - add_timer(&exp->timeout); - - return 1; -} - -/****************************************************************************/ -static int expect_q931(struct sk_buff **pskb, struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo, - unsigned char **data, - TransportAddress * addr, int count) -{ - struct ip_ct_h323_master *info = &ct->help.ct_h323_info; - int dir = CTINFO2DIR(ctinfo); - int ret = 0; - int i; - __be32 ip; - u_int16_t port; - struct ip_conntrack_expect *exp; - typeof(nat_q931_hook) nat_q931; - - /* Look for the first related address */ - for (i = 0; i < count; i++) { - if (get_h225_addr(*data, &addr[i], &ip, &port) && - ip == ct->tuplehash[dir].tuple.src.ip && port != 0) - break; - } - - if (i >= count) /* Not found */ - return 0; - - /* Create expect for Q.931 */ - if ((exp = ip_conntrack_expect_alloc(ct)) == NULL) - return -1; - exp->tuple.src.ip = gkrouted_only ? /* only accept calls from GK? */ - ct->tuplehash[!dir].tuple.src.ip : 0; - exp->tuple.src.u.tcp.port = 0; - exp->tuple.dst.ip = ct->tuplehash[!dir].tuple.dst.ip; - exp->tuple.dst.u.tcp.port = htons(port); - exp->tuple.dst.protonum = IPPROTO_TCP; - exp->mask.src.ip = gkrouted_only ? htonl(0xFFFFFFFF) : 0; - exp->mask.src.u.tcp.port = 0; - exp->mask.dst.ip = htonl(0xFFFFFFFF); - exp->mask.dst.u.tcp.port = htons(0xFFFF); - exp->mask.dst.protonum = 0xFF; - exp->flags = IP_CT_EXPECT_PERMANENT; /* Accept multiple calls */ - - nat_q931 = rcu_dereference(nat_q931_hook); - if (nat_q931) { /* Need NAT */ - ret = nat_q931(pskb, ct, ctinfo, data, addr, i, port, exp); - } else { /* Conntrack only */ - exp->expectfn = ip_conntrack_q931_expect; - - if (ip_conntrack_expect_related(exp) == 0) { - DEBUGP("ip_ct_ras: expect Q.931 " - "%u.%u.%u.%u:%hu->%u.%u.%u.%u:%hu\n", - NIPQUAD(exp->tuple.src.ip), - ntohs(exp->tuple.src.u.tcp.port), - NIPQUAD(exp->tuple.dst.ip), - ntohs(exp->tuple.dst.u.tcp.port)); - - /* Save port for looking up expect in processing RCF */ - info->sig_port[dir] = port; - } else - ret = -1; - } - - ip_conntrack_expect_put(exp); - - return ret; -} - -/****************************************************************************/ -static int process_grq(struct sk_buff **pskb, struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo, - unsigned char **data, GatekeeperRequest * grq) -{ - typeof(set_ras_addr_hook) set_ras_addr; - - DEBUGP("ip_ct_ras: GRQ\n"); - - set_ras_addr = rcu_dereference(set_ras_addr_hook); - if (set_ras_addr) /* NATed */ - return set_ras_addr(pskb, ct, ctinfo, data, - &grq->rasAddress, 1); - return 0; -} - -/* Declare before using */ -static void ip_conntrack_ras_expect(struct ip_conntrack *new, - struct ip_conntrack_expect *this); - -/****************************************************************************/ -static int process_gcf(struct sk_buff **pskb, struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo, - unsigned char **data, GatekeeperConfirm * gcf) -{ - int dir = CTINFO2DIR(ctinfo); - int ret = 0; - __be32 ip; - u_int16_t port; - struct ip_conntrack_expect *exp; - - DEBUGP("ip_ct_ras: GCF\n"); - - if (!get_h225_addr(*data, &gcf->rasAddress, &ip, &port)) - return 0; - - /* Registration port is the same as discovery port */ - if (ip == ct->tuplehash[dir].tuple.src.ip && - port == ntohs(ct->tuplehash[dir].tuple.src.u.udp.port)) - return 0; - - /* Avoid RAS expectation loops. A GCF is never expected. */ - if (test_bit(IPS_EXPECTED_BIT, &ct->status)) - return 0; - - /* Need new expect */ - if ((exp = ip_conntrack_expect_alloc(ct)) == NULL) - return -1; - exp->tuple.src.ip = ct->tuplehash[!dir].tuple.src.ip; - exp->tuple.src.u.tcp.port = 0; - exp->tuple.dst.ip = ip; - exp->tuple.dst.u.tcp.port = htons(port); - exp->tuple.dst.protonum = IPPROTO_UDP; - exp->mask.src.ip = htonl(0xFFFFFFFF); - exp->mask.src.u.tcp.port = 0; - exp->mask.dst.ip = htonl(0xFFFFFFFF); - exp->mask.dst.u.tcp.port = htons(0xFFFF); - exp->mask.dst.protonum = 0xFF; - exp->flags = 0; - exp->expectfn = ip_conntrack_ras_expect; - if (ip_conntrack_expect_related(exp) == 0) { - DEBUGP("ip_ct_ras: expect RAS " - "%u.%u.%u.%u:%hu->%u.%u.%u.%u:%hu\n", - NIPQUAD(exp->tuple.src.ip), - ntohs(exp->tuple.src.u.tcp.port), - NIPQUAD(exp->tuple.dst.ip), - ntohs(exp->tuple.dst.u.tcp.port)); - } else - ret = -1; - - ip_conntrack_expect_put(exp); - - return ret; -} - -/****************************************************************************/ -static int process_rrq(struct sk_buff **pskb, struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo, - unsigned char **data, RegistrationRequest * rrq) -{ - struct ip_ct_h323_master *info = &ct->help.ct_h323_info; - int ret; - typeof(set_ras_addr_hook) set_ras_addr; - - DEBUGP("ip_ct_ras: RRQ\n"); - - ret = expect_q931(pskb, ct, ctinfo, data, - rrq->callSignalAddress.item, - rrq->callSignalAddress.count); - if (ret < 0) - return -1; - - set_ras_addr = rcu_dereference(set_ras_addr_hook); - if (set_ras_addr) { - ret = set_ras_addr(pskb, ct, ctinfo, data, - rrq->rasAddress.item, - rrq->rasAddress.count); - if (ret < 0) - return -1; - } - - if (rrq->options & eRegistrationRequest_timeToLive) { - DEBUGP("ip_ct_ras: RRQ TTL = %u seconds\n", rrq->timeToLive); - info->timeout = rrq->timeToLive; - } else - info->timeout = default_rrq_ttl; - - return 0; -} - -/****************************************************************************/ -static int process_rcf(struct sk_buff **pskb, struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo, - unsigned char **data, RegistrationConfirm * rcf) -{ - struct ip_ct_h323_master *info = &ct->help.ct_h323_info; - int dir = CTINFO2DIR(ctinfo); - int ret; - struct ip_conntrack_expect *exp; - typeof(set_sig_addr_hook) set_sig_addr; - - DEBUGP("ip_ct_ras: RCF\n"); - - set_sig_addr = rcu_dereference(set_sig_addr_hook); - if (set_sig_addr) { - ret = set_sig_addr(pskb, ct, ctinfo, data, - rcf->callSignalAddress.item, - rcf->callSignalAddress.count); - if (ret < 0) - return -1; - } - - if (rcf->options & eRegistrationConfirm_timeToLive) { - DEBUGP("ip_ct_ras: RCF TTL = %u seconds\n", rcf->timeToLive); - info->timeout = rcf->timeToLive; - } - - if (info->timeout > 0) { - DEBUGP - ("ip_ct_ras: set RAS connection timeout to %u seconds\n", - info->timeout); - ip_ct_refresh(ct, *pskb, info->timeout * HZ); - - /* Set expect timeout */ - read_lock_bh(&ip_conntrack_lock); - exp = find_expect(ct, ct->tuplehash[dir].tuple.dst.ip, - info->sig_port[!dir]); - if (exp) { - DEBUGP("ip_ct_ras: set Q.931 expect " - "(%u.%u.%u.%u:%hu->%u.%u.%u.%u:%hu) " - "timeout to %u seconds\n", - NIPQUAD(exp->tuple.src.ip), - ntohs(exp->tuple.src.u.tcp.port), - NIPQUAD(exp->tuple.dst.ip), - ntohs(exp->tuple.dst.u.tcp.port), - info->timeout); - set_expect_timeout(exp, info->timeout); - } - read_unlock_bh(&ip_conntrack_lock); - } - - return 0; -} - -/****************************************************************************/ -static int process_urq(struct sk_buff **pskb, struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo, - unsigned char **data, UnregistrationRequest * urq) -{ - struct ip_ct_h323_master *info = &ct->help.ct_h323_info; - int dir = CTINFO2DIR(ctinfo); - int ret; - typeof(set_sig_addr_hook) set_sig_addr; - - DEBUGP("ip_ct_ras: URQ\n"); - - set_sig_addr = rcu_dereference(set_sig_addr_hook); - if (set_sig_addr) { - ret = set_sig_addr(pskb, ct, ctinfo, data, - urq->callSignalAddress.item, - urq->callSignalAddress.count); - if (ret < 0) - return -1; - } - - /* Clear old expect */ - ip_ct_remove_expectations(ct); - info->sig_port[dir] = 0; - info->sig_port[!dir] = 0; - - /* Give it 30 seconds for UCF or URJ */ - ip_ct_refresh(ct, *pskb, 30 * HZ); - - return 0; -} - -/****************************************************************************/ -static int process_arq(struct sk_buff **pskb, struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo, - unsigned char **data, AdmissionRequest * arq) -{ - struct ip_ct_h323_master *info = &ct->help.ct_h323_info; - int dir = CTINFO2DIR(ctinfo); - __be32 ip; - u_int16_t port; - typeof(set_h225_addr_hook) set_h225_addr; - - DEBUGP("ip_ct_ras: ARQ\n"); - - set_h225_addr = rcu_dereference(set_h225_addr_hook); - if ((arq->options & eAdmissionRequest_destCallSignalAddress) && - get_h225_addr(*data, &arq->destCallSignalAddress, &ip, &port) && - ip == ct->tuplehash[dir].tuple.src.ip && - port == info->sig_port[dir] && set_h225_addr) { - /* Answering ARQ */ - return set_h225_addr(pskb, data, 0, - &arq->destCallSignalAddress, - ct->tuplehash[!dir].tuple.dst.ip, - info->sig_port[!dir]); - } - - if ((arq->options & eAdmissionRequest_srcCallSignalAddress) && - get_h225_addr(*data, &arq->srcCallSignalAddress, &ip, &port) && - ip == ct->tuplehash[dir].tuple.src.ip && set_h225_addr) { - /* Calling ARQ */ - return set_h225_addr(pskb, data, 0, - &arq->srcCallSignalAddress, - ct->tuplehash[!dir].tuple.dst.ip, - port); - } - - return 0; -} - -/****************************************************************************/ -static int process_acf(struct sk_buff **pskb, struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo, - unsigned char **data, AdmissionConfirm * acf) -{ - int dir = CTINFO2DIR(ctinfo); - int ret = 0; - __be32 ip; - u_int16_t port; - struct ip_conntrack_expect *exp; - typeof(set_sig_addr_hook) set_sig_addr; - - DEBUGP("ip_ct_ras: ACF\n"); - - if (!get_h225_addr(*data, &acf->destCallSignalAddress, &ip, &port)) - return 0; - - if (ip == ct->tuplehash[dir].tuple.dst.ip) { /* Answering ACF */ - set_sig_addr = rcu_dereference(set_sig_addr_hook); - if (set_sig_addr) - return set_sig_addr(pskb, ct, ctinfo, data, - &acf->destCallSignalAddress, 1); - return 0; - } - - /* Need new expect */ - if ((exp = ip_conntrack_expect_alloc(ct)) == NULL) - return -1; - exp->tuple.src.ip = ct->tuplehash[!dir].tuple.src.ip; - exp->tuple.src.u.tcp.port = 0; - exp->tuple.dst.ip = ip; - exp->tuple.dst.u.tcp.port = htons(port); - exp->tuple.dst.protonum = IPPROTO_TCP; - exp->mask.src.ip = htonl(0xFFFFFFFF); - exp->mask.src.u.tcp.port = 0; - exp->mask.dst.ip = htonl(0xFFFFFFFF); - exp->mask.dst.u.tcp.port = htons(0xFFFF); - exp->mask.dst.protonum = 0xFF; - exp->flags = IP_CT_EXPECT_PERMANENT; - exp->expectfn = ip_conntrack_q931_expect; - - if (ip_conntrack_expect_related(exp) == 0) { - DEBUGP("ip_ct_ras: expect Q.931 " - "%u.%u.%u.%u:%hu->%u.%u.%u.%u:%hu\n", - NIPQUAD(exp->tuple.src.ip), - ntohs(exp->tuple.src.u.tcp.port), - NIPQUAD(exp->tuple.dst.ip), - ntohs(exp->tuple.dst.u.tcp.port)); - } else - ret = -1; - - ip_conntrack_expect_put(exp); - - return ret; -} - -/****************************************************************************/ -static int process_lrq(struct sk_buff **pskb, struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo, - unsigned char **data, LocationRequest * lrq) -{ - typeof(set_ras_addr_hook) set_ras_addr; - - DEBUGP("ip_ct_ras: LRQ\n"); - - set_ras_addr = rcu_dereference(set_ras_addr_hook); - if (set_ras_addr) - return set_ras_addr(pskb, ct, ctinfo, data, - &lrq->replyAddress, 1); - return 0; -} - -/****************************************************************************/ -static int process_lcf(struct sk_buff **pskb, struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo, - unsigned char **data, LocationConfirm * lcf) -{ - int dir = CTINFO2DIR(ctinfo); - int ret = 0; - __be32 ip; - u_int16_t port; - struct ip_conntrack_expect *exp = NULL; - - DEBUGP("ip_ct_ras: LCF\n"); - - if (!get_h225_addr(*data, &lcf->callSignalAddress, &ip, &port)) - return 0; - - /* Need new expect for call signal */ - if ((exp = ip_conntrack_expect_alloc(ct)) == NULL) - return -1; - exp->tuple.src.ip = ct->tuplehash[!dir].tuple.src.ip; - exp->tuple.src.u.tcp.port = 0; - exp->tuple.dst.ip = ip; - exp->tuple.dst.u.tcp.port = htons(port); - exp->tuple.dst.protonum = IPPROTO_TCP; - exp->mask.src.ip = htonl(0xFFFFFFFF); - exp->mask.src.u.tcp.port = 0; - exp->mask.dst.ip = htonl(0xFFFFFFFF); - exp->mask.dst.u.tcp.port = htons(0xFFFF); - exp->mask.dst.protonum = 0xFF; - exp->flags = IP_CT_EXPECT_PERMANENT; - exp->expectfn = ip_conntrack_q931_expect; - - if (ip_conntrack_expect_related(exp) == 0) { - DEBUGP("ip_ct_ras: expect Q.931 " - "%u.%u.%u.%u:%hu->%u.%u.%u.%u:%hu\n", - NIPQUAD(exp->tuple.src.ip), - ntohs(exp->tuple.src.u.tcp.port), - NIPQUAD(exp->tuple.dst.ip), - ntohs(exp->tuple.dst.u.tcp.port)); - } else - ret = -1; - - ip_conntrack_expect_put(exp); - - /* Ignore rasAddress */ - - return ret; -} - -/****************************************************************************/ -static int process_irr(struct sk_buff **pskb, struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo, - unsigned char **data, InfoRequestResponse * irr) -{ - int ret; - typeof(set_ras_addr_hook) set_ras_addr; - typeof(set_sig_addr_hook) set_sig_addr; - - DEBUGP("ip_ct_ras: IRR\n"); - - set_ras_addr = rcu_dereference(set_ras_addr_hook); - if (set_ras_addr) { - ret = set_ras_addr(pskb, ct, ctinfo, data, - &irr->rasAddress, 1); - if (ret < 0) - return -1; - } - - set_sig_addr = rcu_dereference(set_sig_addr_hook); - if (set_sig_addr) { - ret = set_sig_addr(pskb, ct, ctinfo, data, - irr->callSignalAddress.item, - irr->callSignalAddress.count); - if (ret < 0) - return -1; - } - - return 0; -} - -/****************************************************************************/ -static int process_ras(struct sk_buff **pskb, struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo, - unsigned char **data, RasMessage * ras) -{ - switch (ras->choice) { - case eRasMessage_gatekeeperRequest: - return process_grq(pskb, ct, ctinfo, data, - &ras->gatekeeperRequest); - case eRasMessage_gatekeeperConfirm: - return process_gcf(pskb, ct, ctinfo, data, - &ras->gatekeeperConfirm); - case eRasMessage_registrationRequest: - return process_rrq(pskb, ct, ctinfo, data, - &ras->registrationRequest); - case eRasMessage_registrationConfirm: - return process_rcf(pskb, ct, ctinfo, data, - &ras->registrationConfirm); - case eRasMessage_unregistrationRequest: - return process_urq(pskb, ct, ctinfo, data, - &ras->unregistrationRequest); - case eRasMessage_admissionRequest: - return process_arq(pskb, ct, ctinfo, data, - &ras->admissionRequest); - case eRasMessage_admissionConfirm: - return process_acf(pskb, ct, ctinfo, data, - &ras->admissionConfirm); - case eRasMessage_locationRequest: - return process_lrq(pskb, ct, ctinfo, data, - &ras->locationRequest); - case eRasMessage_locationConfirm: - return process_lcf(pskb, ct, ctinfo, data, - &ras->locationConfirm); - case eRasMessage_infoRequestResponse: - return process_irr(pskb, ct, ctinfo, data, - &ras->infoRequestResponse); - default: - DEBUGP("ip_ct_ras: RAS message %d\n", ras->choice); - break; - } - - return 0; -} - -/****************************************************************************/ -static int ras_help(struct sk_buff **pskb, struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo) -{ - static RasMessage ras; - unsigned char *data; - int datalen = 0; - int ret; - - DEBUGP("ip_ct_ras: skblen = %u\n", (*pskb)->len); - - spin_lock_bh(&ip_h323_lock); - - /* Get UDP data */ - data = get_udp_data(pskb, &datalen); - if (data == NULL) - goto accept; - DEBUGP("ip_ct_ras: RAS message %u.%u.%u.%u->%u.%u.%u.%u, len=%d\n", - NIPQUAD(ip_hdr(*pskb)->saddr), - NIPQUAD(ip_hdr(*pskb)->daddr), datalen); - - /* Decode RAS message */ - ret = DecodeRasMessage(data, datalen, &ras); - if (ret < 0) { - if (net_ratelimit()) - printk("ip_ct_ras: decoding error: %s\n", - ret == H323_ERROR_BOUND ? - "out of bound" : "out of range"); - goto accept; - } - - /* Process RAS message */ - if (process_ras(pskb, ct, ctinfo, &data, &ras) < 0) - goto drop; - - accept: - spin_unlock_bh(&ip_h323_lock); - return NF_ACCEPT; - - drop: - spin_unlock_bh(&ip_h323_lock); - if (net_ratelimit()) - printk("ip_ct_ras: packet dropped\n"); - return NF_DROP; -} - -/****************************************************************************/ -static struct ip_conntrack_helper ip_conntrack_helper_ras = { - .name = "RAS", - .me = THIS_MODULE, - .max_expected = 32, - .timeout = 240, - .tuple = {.src = {.u = {.tcp = {.port = __constant_htons(RAS_PORT)}}}, - .dst = {.protonum = IPPROTO_UDP}}, - .mask = {.src = {.u = {0xFFFE}}, - .dst = {.protonum = 0xFF}}, - .help = ras_help, -}; - -/****************************************************************************/ -static void ip_conntrack_ras_expect(struct ip_conntrack *new, - struct ip_conntrack_expect *this) -{ - write_lock_bh(&ip_conntrack_lock); - new->helper = &ip_conntrack_helper_ras; - write_unlock_bh(&ip_conntrack_lock); -} - -/****************************************************************************/ -/* Not __exit - called from init() */ -static void fini(void) -{ - ip_conntrack_helper_unregister(&ip_conntrack_helper_ras); - ip_conntrack_helper_unregister(&ip_conntrack_helper_q931); - kfree(h323_buffer); - DEBUGP("ip_ct_h323: fini\n"); -} - -/****************************************************************************/ -static int __init init(void) -{ - int ret; - - h323_buffer = kmalloc(65536, GFP_KERNEL); - if (!h323_buffer) - return -ENOMEM; - if ((ret = ip_conntrack_helper_register(&ip_conntrack_helper_q931)) || - (ret = ip_conntrack_helper_register(&ip_conntrack_helper_ras))) { - fini(); - return ret; - } - DEBUGP("ip_ct_h323: init success\n"); - return 0; -} - -/****************************************************************************/ -module_init(init); -module_exit(fini); - -EXPORT_SYMBOL_GPL(get_h225_addr); -EXPORT_SYMBOL_GPL(ip_conntrack_h245_expect); -EXPORT_SYMBOL_GPL(ip_conntrack_q931_expect); -EXPORT_SYMBOL_GPL(set_h245_addr_hook); -EXPORT_SYMBOL_GPL(set_h225_addr_hook); -EXPORT_SYMBOL_GPL(set_sig_addr_hook); -EXPORT_SYMBOL_GPL(set_ras_addr_hook); -EXPORT_SYMBOL_GPL(nat_rtp_rtcp_hook); -EXPORT_SYMBOL_GPL(nat_t120_hook); -EXPORT_SYMBOL_GPL(nat_h245_hook); -EXPORT_SYMBOL_GPL(nat_callforwarding_hook); -EXPORT_SYMBOL_GPL(nat_q931_hook); - -MODULE_AUTHOR("Jing Min Zhao "); -MODULE_DESCRIPTION("H.323 connection tracking helper"); -MODULE_LICENSE("GPL"); diff --git a/net/ipv4/netfilter/ip_conntrack_helper_pptp.c b/net/ipv4/netfilter/ip_conntrack_helper_pptp.c deleted file mode 100644 index f5ab8e4b97cb..000000000000 --- a/net/ipv4/netfilter/ip_conntrack_helper_pptp.c +++ /dev/null @@ -1,684 +0,0 @@ -/* - * ip_conntrack_pptp.c - Version 3.0 - * - * Connection tracking support for PPTP (Point to Point Tunneling Protocol). - * PPTP is a a protocol for creating virtual private networks. - * It is a specification defined by Microsoft and some vendors - * working with Microsoft. PPTP is built on top of a modified - * version of the Internet Generic Routing Encapsulation Protocol. - * GRE is defined in RFC 1701 and RFC 1702. Documentation of - * PPTP can be found in RFC 2637 - * - * (C) 2000-2005 by Harald Welte - * - * Development of this code funded by Astaro AG (http://www.astaro.com/) - * - * Limitations: - * - We blindly assume that control connections are always - * established in PNS->PAC direction. This is a violation - * of RFFC2673 - * - We can only support one single call within each session - * - * TODO: - * - testing of incoming PPTP calls - * - * Changes: - * 2002-02-05 - Version 1.3 - * - Call ip_conntrack_unexpect_related() from - * pptp_destroy_siblings() to destroy expectations in case - * CALL_DISCONNECT_NOTIFY or tcp fin packet was seen - * (Philip Craig ) - * - Add Version information at module loadtime - * 2002-02-10 - Version 1.6 - * - move to C99 style initializers - * - remove second expectation if first arrives - * 2004-10-22 - Version 2.0 - * - merge Mandrake's 2.6.x port with recent 2.6.x API changes - * - fix lots of linear skb assumptions from Mandrake's port - * 2005-06-10 - Version 2.1 - * - use ip_conntrack_expect_free() instead of kfree() on the - * expect's (which are from the slab for quite some time) - * 2005-06-10 - Version 3.0 - * - port helper to post-2.6.11 API changes, - * funded by Oxcoda NetBox Blue (http://www.netboxblue.com/) - * 2005-07-30 - Version 3.1 - * - port helper to 2.6.13 API changes - * - */ - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#define IP_CT_PPTP_VERSION "3.1" - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Harald Welte "); -MODULE_DESCRIPTION("Netfilter connection tracking helper module for PPTP"); - -static DEFINE_SPINLOCK(ip_pptp_lock); - -int -(*ip_nat_pptp_hook_outbound)(struct sk_buff **pskb, - struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo, - struct PptpControlHeader *ctlh, - union pptp_ctrl_union *pptpReq); - -int -(*ip_nat_pptp_hook_inbound)(struct sk_buff **pskb, - struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo, - struct PptpControlHeader *ctlh, - union pptp_ctrl_union *pptpReq); - -void -(*ip_nat_pptp_hook_exp_gre)(struct ip_conntrack_expect *expect_orig, - struct ip_conntrack_expect *expect_reply); - -void -(*ip_nat_pptp_hook_expectfn)(struct ip_conntrack *ct, - struct ip_conntrack_expect *exp); - -#if 0 -/* PptpControlMessageType names */ -const char *pptp_msg_name[] = { - "UNKNOWN_MESSAGE", - "START_SESSION_REQUEST", - "START_SESSION_REPLY", - "STOP_SESSION_REQUEST", - "STOP_SESSION_REPLY", - "ECHO_REQUEST", - "ECHO_REPLY", - "OUT_CALL_REQUEST", - "OUT_CALL_REPLY", - "IN_CALL_REQUEST", - "IN_CALL_REPLY", - "IN_CALL_CONNECT", - "CALL_CLEAR_REQUEST", - "CALL_DISCONNECT_NOTIFY", - "WAN_ERROR_NOTIFY", - "SET_LINK_INFO" -}; -EXPORT_SYMBOL(pptp_msg_name); -#define DEBUGP(format, args...) printk(KERN_DEBUG "%s:%s: " format, __FILE__, __FUNCTION__, ## args) -#else -#define DEBUGP(format, args...) -#endif - -#define SECS *HZ -#define MINS * 60 SECS -#define HOURS * 60 MINS - -#define PPTP_GRE_TIMEOUT (10 MINS) -#define PPTP_GRE_STREAM_TIMEOUT (5 HOURS) - -static void pptp_expectfn(struct ip_conntrack *ct, - struct ip_conntrack_expect *exp) -{ - typeof(ip_nat_pptp_hook_expectfn) ip_nat_pptp_expectfn; - - DEBUGP("increasing timeouts\n"); - - /* increase timeout of GRE data channel conntrack entry */ - ct->proto.gre.timeout = PPTP_GRE_TIMEOUT; - ct->proto.gre.stream_timeout = PPTP_GRE_STREAM_TIMEOUT; - - /* Can you see how rusty this code is, compared with the pre-2.6.11 - * one? That's what happened to my shiny newnat of 2002 ;( -HW */ - - rcu_read_lock(); - ip_nat_pptp_expectfn = rcu_dereference(ip_nat_pptp_hook_expectfn); - if (!ip_nat_pptp_expectfn) { - struct ip_conntrack_tuple inv_t; - struct ip_conntrack_expect *exp_other; - - /* obviously this tuple inversion only works until you do NAT */ - invert_tuplepr(&inv_t, &exp->tuple); - DEBUGP("trying to unexpect other dir: "); - DUMP_TUPLE(&inv_t); - - exp_other = ip_conntrack_expect_find_get(&inv_t); - if (exp_other) { - /* delete other expectation. */ - DEBUGP("found\n"); - ip_conntrack_unexpect_related(exp_other); - ip_conntrack_expect_put(exp_other); - } else { - DEBUGP("not found\n"); - } - } else { - /* we need more than simple inversion */ - ip_nat_pptp_expectfn(ct, exp); - } - rcu_read_unlock(); -} - -static int destroy_sibling_or_exp(const struct ip_conntrack_tuple *t) -{ - struct ip_conntrack_tuple_hash *h; - struct ip_conntrack_expect *exp; - - DEBUGP("trying to timeout ct or exp for tuple "); - DUMP_TUPLE(t); - - h = ip_conntrack_find_get(t, NULL); - if (h) { - struct ip_conntrack *sibling = tuplehash_to_ctrack(h); - DEBUGP("setting timeout of conntrack %p to 0\n", sibling); - sibling->proto.gre.timeout = 0; - sibling->proto.gre.stream_timeout = 0; - if (del_timer(&sibling->timeout)) - sibling->timeout.function((unsigned long)sibling); - ip_conntrack_put(sibling); - return 1; - } else { - exp = ip_conntrack_expect_find_get(t); - if (exp) { - DEBUGP("unexpect_related of expect %p\n", exp); - ip_conntrack_unexpect_related(exp); - ip_conntrack_expect_put(exp); - return 1; - } - } - - return 0; -} - - -/* timeout GRE data connections */ -static void pptp_destroy_siblings(struct ip_conntrack *ct) -{ - struct ip_conntrack_tuple t; - - ip_ct_gre_keymap_destroy(ct); - /* Since ct->sibling_list has literally rusted away in 2.6.11, - * we now need another way to find out about our sibling - * contrack and expects... -HW */ - - /* try original (pns->pac) tuple */ - memcpy(&t, &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple, sizeof(t)); - t.dst.protonum = IPPROTO_GRE; - t.src.u.gre.key = ct->help.ct_pptp_info.pns_call_id; - t.dst.u.gre.key = ct->help.ct_pptp_info.pac_call_id; - - if (!destroy_sibling_or_exp(&t)) - DEBUGP("failed to timeout original pns->pac ct/exp\n"); - - /* try reply (pac->pns) tuple */ - memcpy(&t, &ct->tuplehash[IP_CT_DIR_REPLY].tuple, sizeof(t)); - t.dst.protonum = IPPROTO_GRE; - t.src.u.gre.key = ct->help.ct_pptp_info.pac_call_id; - t.dst.u.gre.key = ct->help.ct_pptp_info.pns_call_id; - - if (!destroy_sibling_or_exp(&t)) - DEBUGP("failed to timeout reply pac->pns ct/exp\n"); -} - -/* expect GRE connections (PNS->PAC and PAC->PNS direction) */ -static inline int -exp_gre(struct ip_conntrack *ct, - __be16 callid, - __be16 peer_callid) -{ - struct ip_conntrack_expect *exp_orig, *exp_reply; - int ret = 1; - typeof(ip_nat_pptp_hook_exp_gre) ip_nat_pptp_exp_gre; - - exp_orig = ip_conntrack_expect_alloc(ct); - if (exp_orig == NULL) - goto out; - - exp_reply = ip_conntrack_expect_alloc(ct); - if (exp_reply == NULL) - goto out_put_orig; - - /* original direction, PNS->PAC */ - exp_orig->tuple.src.ip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip; - exp_orig->tuple.src.u.gre.key = peer_callid; - exp_orig->tuple.dst.ip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip; - exp_orig->tuple.dst.u.gre.key = callid; - exp_orig->tuple.dst.protonum = IPPROTO_GRE; - - exp_orig->mask.src.ip = htonl(0xffffffff); - exp_orig->mask.src.u.all = 0; - exp_orig->mask.dst.u.gre.key = htons(0xffff); - exp_orig->mask.dst.ip = htonl(0xffffffff); - exp_orig->mask.dst.protonum = 0xff; - - exp_orig->master = ct; - exp_orig->expectfn = pptp_expectfn; - exp_orig->flags = 0; - - /* both expectations are identical apart from tuple */ - memcpy(exp_reply, exp_orig, sizeof(*exp_reply)); - - /* reply direction, PAC->PNS */ - exp_reply->tuple.src.ip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip; - exp_reply->tuple.src.u.gre.key = callid; - exp_reply->tuple.dst.ip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip; - exp_reply->tuple.dst.u.gre.key = peer_callid; - exp_reply->tuple.dst.protonum = IPPROTO_GRE; - - ip_nat_pptp_exp_gre = rcu_dereference(ip_nat_pptp_hook_exp_gre); - if (ip_nat_pptp_exp_gre) - ip_nat_pptp_exp_gre(exp_orig, exp_reply); - if (ip_conntrack_expect_related(exp_orig) != 0) - goto out_put_both; - if (ip_conntrack_expect_related(exp_reply) != 0) - goto out_unexpect_orig; - - /* Add GRE keymap entries */ - if (ip_ct_gre_keymap_add(ct, &exp_orig->tuple, 0) != 0) - goto out_unexpect_both; - if (ip_ct_gre_keymap_add(ct, &exp_reply->tuple, 1) != 0) { - ip_ct_gre_keymap_destroy(ct); - goto out_unexpect_both; - } - ret = 0; - -out_put_both: - ip_conntrack_expect_put(exp_reply); -out_put_orig: - ip_conntrack_expect_put(exp_orig); -out: - return ret; - -out_unexpect_both: - ip_conntrack_unexpect_related(exp_reply); -out_unexpect_orig: - ip_conntrack_unexpect_related(exp_orig); - goto out_put_both; -} - -static inline int -pptp_inbound_pkt(struct sk_buff **pskb, - struct PptpControlHeader *ctlh, - union pptp_ctrl_union *pptpReq, - unsigned int reqlen, - struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo) -{ - struct ip_ct_pptp_master *info = &ct->help.ct_pptp_info; - u_int16_t msg; - __be16 cid = 0, pcid = 0; - typeof(ip_nat_pptp_hook_inbound) ip_nat_pptp_inbound; - - msg = ntohs(ctlh->messageType); - DEBUGP("inbound control message %s\n", pptp_msg_name[msg]); - - switch (msg) { - case PPTP_START_SESSION_REPLY: - /* server confirms new control session */ - if (info->sstate < PPTP_SESSION_REQUESTED) - goto invalid; - if (pptpReq->srep.resultCode == PPTP_START_OK) - info->sstate = PPTP_SESSION_CONFIRMED; - else - info->sstate = PPTP_SESSION_ERROR; - break; - - case PPTP_STOP_SESSION_REPLY: - /* server confirms end of control session */ - if (info->sstate > PPTP_SESSION_STOPREQ) - goto invalid; - if (pptpReq->strep.resultCode == PPTP_STOP_OK) - info->sstate = PPTP_SESSION_NONE; - else - info->sstate = PPTP_SESSION_ERROR; - break; - - case PPTP_OUT_CALL_REPLY: - /* server accepted call, we now expect GRE frames */ - if (info->sstate != PPTP_SESSION_CONFIRMED) - goto invalid; - if (info->cstate != PPTP_CALL_OUT_REQ && - info->cstate != PPTP_CALL_OUT_CONF) - goto invalid; - - cid = pptpReq->ocack.callID; - pcid = pptpReq->ocack.peersCallID; - if (info->pns_call_id != pcid) - goto invalid; - DEBUGP("%s, CID=%X, PCID=%X\n", pptp_msg_name[msg], - ntohs(cid), ntohs(pcid)); - - if (pptpReq->ocack.resultCode == PPTP_OUTCALL_CONNECT) { - info->cstate = PPTP_CALL_OUT_CONF; - info->pac_call_id = cid; - exp_gre(ct, cid, pcid); - } else - info->cstate = PPTP_CALL_NONE; - break; - - case PPTP_IN_CALL_REQUEST: - /* server tells us about incoming call request */ - if (info->sstate != PPTP_SESSION_CONFIRMED) - goto invalid; - - cid = pptpReq->icreq.callID; - DEBUGP("%s, CID=%X\n", pptp_msg_name[msg], ntohs(cid)); - info->cstate = PPTP_CALL_IN_REQ; - info->pac_call_id = cid; - break; - - case PPTP_IN_CALL_CONNECT: - /* server tells us about incoming call established */ - if (info->sstate != PPTP_SESSION_CONFIRMED) - goto invalid; - if (info->cstate != PPTP_CALL_IN_REP && - info->cstate != PPTP_CALL_IN_CONF) - goto invalid; - - pcid = pptpReq->iccon.peersCallID; - cid = info->pac_call_id; - - if (info->pns_call_id != pcid) - goto invalid; - - DEBUGP("%s, PCID=%X\n", pptp_msg_name[msg], ntohs(pcid)); - info->cstate = PPTP_CALL_IN_CONF; - - /* we expect a GRE connection from PAC to PNS */ - exp_gre(ct, cid, pcid); - break; - - case PPTP_CALL_DISCONNECT_NOTIFY: - /* server confirms disconnect */ - cid = pptpReq->disc.callID; - DEBUGP("%s, CID=%X\n", pptp_msg_name[msg], ntohs(cid)); - info->cstate = PPTP_CALL_NONE; - - /* untrack this call id, unexpect GRE packets */ - pptp_destroy_siblings(ct); - break; - - case PPTP_WAN_ERROR_NOTIFY: - case PPTP_ECHO_REQUEST: - case PPTP_ECHO_REPLY: - /* I don't have to explain these ;) */ - break; - default: - goto invalid; - } - - ip_nat_pptp_inbound = rcu_dereference(ip_nat_pptp_hook_inbound); - if (ip_nat_pptp_inbound) - return ip_nat_pptp_inbound(pskb, ct, ctinfo, ctlh, pptpReq); - return NF_ACCEPT; - -invalid: - DEBUGP("invalid %s: type=%d cid=%u pcid=%u " - "cstate=%d sstate=%d pns_cid=%u pac_cid=%u\n", - msg <= PPTP_MSG_MAX ? pptp_msg_name[msg] : pptp_msg_name[0], - msg, ntohs(cid), ntohs(pcid), info->cstate, info->sstate, - ntohs(info->pns_call_id), ntohs(info->pac_call_id)); - return NF_ACCEPT; -} - -static inline int -pptp_outbound_pkt(struct sk_buff **pskb, - struct PptpControlHeader *ctlh, - union pptp_ctrl_union *pptpReq, - unsigned int reqlen, - struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo) -{ - struct ip_ct_pptp_master *info = &ct->help.ct_pptp_info; - u_int16_t msg; - __be16 cid = 0, pcid = 0; - typeof(ip_nat_pptp_hook_outbound) ip_nat_pptp_outbound; - - msg = ntohs(ctlh->messageType); - DEBUGP("outbound control message %s\n", pptp_msg_name[msg]); - - switch (msg) { - case PPTP_START_SESSION_REQUEST: - /* client requests for new control session */ - if (info->sstate != PPTP_SESSION_NONE) - goto invalid; - info->sstate = PPTP_SESSION_REQUESTED; - break; - case PPTP_STOP_SESSION_REQUEST: - /* client requests end of control session */ - info->sstate = PPTP_SESSION_STOPREQ; - break; - - case PPTP_OUT_CALL_REQUEST: - /* client initiating connection to server */ - if (info->sstate != PPTP_SESSION_CONFIRMED) - goto invalid; - info->cstate = PPTP_CALL_OUT_REQ; - /* track PNS call id */ - cid = pptpReq->ocreq.callID; - DEBUGP("%s, CID=%X\n", pptp_msg_name[msg], ntohs(cid)); - info->pns_call_id = cid; - break; - case PPTP_IN_CALL_REPLY: - /* client answers incoming call */ - if (info->cstate != PPTP_CALL_IN_REQ && - info->cstate != PPTP_CALL_IN_REP) - goto invalid; - - cid = pptpReq->icack.callID; - pcid = pptpReq->icack.peersCallID; - if (info->pac_call_id != pcid) - goto invalid; - DEBUGP("%s, CID=%X PCID=%X\n", pptp_msg_name[msg], - ntohs(cid), ntohs(pcid)); - - if (pptpReq->icack.resultCode == PPTP_INCALL_ACCEPT) { - /* part two of the three-way handshake */ - info->cstate = PPTP_CALL_IN_REP; - info->pns_call_id = cid; - } else - info->cstate = PPTP_CALL_NONE; - break; - - case PPTP_CALL_CLEAR_REQUEST: - /* client requests hangup of call */ - if (info->sstate != PPTP_SESSION_CONFIRMED) - goto invalid; - /* FUTURE: iterate over all calls and check if - * call ID is valid. We don't do this without newnat, - * because we only know about last call */ - info->cstate = PPTP_CALL_CLEAR_REQ; - break; - case PPTP_SET_LINK_INFO: - case PPTP_ECHO_REQUEST: - case PPTP_ECHO_REPLY: - /* I don't have to explain these ;) */ - break; - default: - goto invalid; - } - - ip_nat_pptp_outbound = rcu_dereference(ip_nat_pptp_hook_outbound); - if (ip_nat_pptp_outbound) - return ip_nat_pptp_outbound(pskb, ct, ctinfo, ctlh, pptpReq); - return NF_ACCEPT; - -invalid: - DEBUGP("invalid %s: type=%d cid=%u pcid=%u " - "cstate=%d sstate=%d pns_cid=%u pac_cid=%u\n", - msg <= PPTP_MSG_MAX ? pptp_msg_name[msg] : pptp_msg_name[0], - msg, ntohs(cid), ntohs(pcid), info->cstate, info->sstate, - ntohs(info->pns_call_id), ntohs(info->pac_call_id)); - return NF_ACCEPT; -} - -static const unsigned int pptp_msg_size[] = { - [PPTP_START_SESSION_REQUEST] = sizeof(struct PptpStartSessionRequest), - [PPTP_START_SESSION_REPLY] = sizeof(struct PptpStartSessionReply), - [PPTP_STOP_SESSION_REQUEST] = sizeof(struct PptpStopSessionRequest), - [PPTP_STOP_SESSION_REPLY] = sizeof(struct PptpStopSessionReply), - [PPTP_OUT_CALL_REQUEST] = sizeof(struct PptpOutCallRequest), - [PPTP_OUT_CALL_REPLY] = sizeof(struct PptpOutCallReply), - [PPTP_IN_CALL_REQUEST] = sizeof(struct PptpInCallRequest), - [PPTP_IN_CALL_REPLY] = sizeof(struct PptpInCallReply), - [PPTP_IN_CALL_CONNECT] = sizeof(struct PptpInCallConnected), - [PPTP_CALL_CLEAR_REQUEST] = sizeof(struct PptpClearCallRequest), - [PPTP_CALL_DISCONNECT_NOTIFY] = sizeof(struct PptpCallDisconnectNotify), - [PPTP_WAN_ERROR_NOTIFY] = sizeof(struct PptpWanErrorNotify), - [PPTP_SET_LINK_INFO] = sizeof(struct PptpSetLinkInfo), -}; - -/* track caller id inside control connection, call expect_related */ -static int -conntrack_pptp_help(struct sk_buff **pskb, - struct ip_conntrack *ct, enum ip_conntrack_info ctinfo) - -{ - int dir = CTINFO2DIR(ctinfo); - struct ip_ct_pptp_master *info = &ct->help.ct_pptp_info; - struct tcphdr _tcph, *tcph; - struct pptp_pkt_hdr _pptph, *pptph; - struct PptpControlHeader _ctlh, *ctlh; - union pptp_ctrl_union _pptpReq, *pptpReq; - unsigned int tcplen = (*pskb)->len - ip_hdrlen(*pskb); - unsigned int datalen, reqlen, nexthdr_off; - int oldsstate, oldcstate; - int ret; - u_int16_t msg; - - /* don't do any tracking before tcp handshake complete */ - if (ctinfo != IP_CT_ESTABLISHED - && ctinfo != IP_CT_ESTABLISHED+IP_CT_IS_REPLY) { - DEBUGP("ctinfo = %u, skipping\n", ctinfo); - return NF_ACCEPT; - } - - nexthdr_off = ip_hdrlen(*pskb); - tcph = skb_header_pointer(*pskb, nexthdr_off, sizeof(_tcph), &_tcph); - BUG_ON(!tcph); - nexthdr_off += tcph->doff * 4; - datalen = tcplen - tcph->doff * 4; - - pptph = skb_header_pointer(*pskb, nexthdr_off, sizeof(_pptph), &_pptph); - if (!pptph) { - DEBUGP("no full PPTP header, can't track\n"); - return NF_ACCEPT; - } - nexthdr_off += sizeof(_pptph); - datalen -= sizeof(_pptph); - - /* if it's not a control message we can't do anything with it */ - if (ntohs(pptph->packetType) != PPTP_PACKET_CONTROL || - ntohl(pptph->magicCookie) != PPTP_MAGIC_COOKIE) { - DEBUGP("not a control packet\n"); - return NF_ACCEPT; - } - - ctlh = skb_header_pointer(*pskb, nexthdr_off, sizeof(_ctlh), &_ctlh); - if (!ctlh) - return NF_ACCEPT; - nexthdr_off += sizeof(_ctlh); - datalen -= sizeof(_ctlh); - - reqlen = datalen; - msg = ntohs(ctlh->messageType); - if (msg > 0 && msg <= PPTP_MSG_MAX && reqlen < pptp_msg_size[msg]) - return NF_ACCEPT; - if (reqlen > sizeof(*pptpReq)) - reqlen = sizeof(*pptpReq); - - pptpReq = skb_header_pointer(*pskb, nexthdr_off, reqlen, &_pptpReq); - if (!pptpReq) - return NF_ACCEPT; - - oldsstate = info->sstate; - oldcstate = info->cstate; - - spin_lock_bh(&ip_pptp_lock); - - /* FIXME: We just blindly assume that the control connection is always - * established from PNS->PAC. However, RFC makes no guarantee */ - if (dir == IP_CT_DIR_ORIGINAL) - /* client -> server (PNS -> PAC) */ - ret = pptp_outbound_pkt(pskb, ctlh, pptpReq, reqlen, ct, - ctinfo); - else - /* server -> client (PAC -> PNS) */ - ret = pptp_inbound_pkt(pskb, ctlh, pptpReq, reqlen, ct, - ctinfo); - DEBUGP("sstate: %d->%d, cstate: %d->%d\n", - oldsstate, info->sstate, oldcstate, info->cstate); - spin_unlock_bh(&ip_pptp_lock); - - return ret; -} - -/* control protocol helper */ -static struct ip_conntrack_helper pptp = { - .list = { NULL, NULL }, - .name = "pptp", - .me = THIS_MODULE, - .max_expected = 2, - .timeout = 5 * 60, - .tuple = { .src = { .ip = 0, - .u = { .tcp = { .port = - __constant_htons(PPTP_CONTROL_PORT) } } - }, - .dst = { .ip = 0, - .u = { .all = 0 }, - .protonum = IPPROTO_TCP - } - }, - .mask = { .src = { .ip = 0, - .u = { .tcp = { .port = __constant_htons(0xffff) } } - }, - .dst = { .ip = 0, - .u = { .all = 0 }, - .protonum = 0xff - } - }, - .help = conntrack_pptp_help, - .destroy = pptp_destroy_siblings, -}; - -extern void ip_ct_proto_gre_fini(void); -extern int __init ip_ct_proto_gre_init(void); - -/* ip_conntrack_pptp initialization */ -static int __init ip_conntrack_helper_pptp_init(void) -{ - int retcode; - - retcode = ip_ct_proto_gre_init(); - if (retcode < 0) - return retcode; - - DEBUGP(" registering helper\n"); - if ((retcode = ip_conntrack_helper_register(&pptp))) { - printk(KERN_ERR "Unable to register conntrack application " - "helper for pptp: %d\n", retcode); - ip_ct_proto_gre_fini(); - return retcode; - } - - printk("ip_conntrack_pptp version %s loaded\n", IP_CT_PPTP_VERSION); - return 0; -} - -static void __exit ip_conntrack_helper_pptp_fini(void) -{ - ip_conntrack_helper_unregister(&pptp); - ip_ct_proto_gre_fini(); - printk("ip_conntrack_pptp version %s unloaded\n", IP_CT_PPTP_VERSION); -} - -module_init(ip_conntrack_helper_pptp_init); -module_exit(ip_conntrack_helper_pptp_fini); - -EXPORT_SYMBOL(ip_nat_pptp_hook_outbound); -EXPORT_SYMBOL(ip_nat_pptp_hook_inbound); -EXPORT_SYMBOL(ip_nat_pptp_hook_exp_gre); -EXPORT_SYMBOL(ip_nat_pptp_hook_expectfn); diff --git a/net/ipv4/netfilter/ip_conntrack_irc.c b/net/ipv4/netfilter/ip_conntrack_irc.c deleted file mode 100644 index ee99abe482e3..000000000000 --- a/net/ipv4/netfilter/ip_conntrack_irc.c +++ /dev/null @@ -1,314 +0,0 @@ -/* IRC extension for IP connection tracking, Version 1.21 - * (C) 2000-2002 by Harald Welte - * based on RR's ip_conntrack_ftp.c - * - * ip_conntrack_irc.c,v 1.21 2002/02/05 14:49:26 laforge Exp - * - * 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. - ** - * Module load syntax: - * insmod ip_conntrack_irc.o ports=port1,port2,...port - * max_dcc_channels=n dcc_timeout=secs - * - * please give the ports of all IRC servers You wish to connect to. - * If You don't specify ports, the default will be port 6667. - * With max_dcc_channels you can define the maximum number of not - * yet answered DCC channels per IRC session (default 8). - * With dcc_timeout you can specify how long the system waits for - * an expected DCC channel (default 300 seconds). - * - */ - -#include -#include -#include -#include -#include - -#include -#include -#include - -#define MAX_PORTS 8 -static unsigned short ports[MAX_PORTS]; -static int ports_c; -static unsigned int max_dcc_channels = 8; -static unsigned int dcc_timeout = 300; -/* This is slow, but it's simple. --RR */ -static char *irc_buffer; -static DEFINE_SPINLOCK(irc_buffer_lock); - -unsigned int (*ip_nat_irc_hook)(struct sk_buff **pskb, - enum ip_conntrack_info ctinfo, - unsigned int matchoff, - unsigned int matchlen, - struct ip_conntrack_expect *exp); -EXPORT_SYMBOL_GPL(ip_nat_irc_hook); - -MODULE_AUTHOR("Harald Welte "); -MODULE_DESCRIPTION("IRC (DCC) connection tracking helper"); -MODULE_LICENSE("GPL"); -module_param_array(ports, ushort, &ports_c, 0400); -MODULE_PARM_DESC(ports, "port numbers of IRC servers"); -module_param(max_dcc_channels, uint, 0400); -MODULE_PARM_DESC(max_dcc_channels, "max number of expected DCC channels per IRC session"); -module_param(dcc_timeout, uint, 0400); -MODULE_PARM_DESC(dcc_timeout, "timeout on for unestablished DCC channels"); - -static const char *dccprotos[] = { "SEND ", "CHAT ", "MOVE ", "TSEND ", "SCHAT " }; -#define MINMATCHLEN 5 - -#if 0 -#define DEBUGP(format, args...) printk(KERN_DEBUG "%s:%s:" format, \ - __FILE__, __FUNCTION__ , ## args) -#else -#define DEBUGP(format, args...) -#endif - -static int parse_dcc(char *data, char *data_end, u_int32_t *ip, - u_int16_t *port, char **ad_beg_p, char **ad_end_p) -/* tries to get the ip_addr and port out of a dcc command - return value: -1 on failure, 0 on success - data pointer to first byte of DCC command data - data_end pointer to last byte of dcc command data - ip returns parsed ip of dcc command - port returns parsed port of dcc command - ad_beg_p returns pointer to first byte of addr data - ad_end_p returns pointer to last byte of addr data */ -{ - - /* at least 12: "AAAAAAAA P\1\n" */ - while (*data++ != ' ') - if (data > data_end - 12) - return -1; - - *ad_beg_p = data; - *ip = simple_strtoul(data, &data, 10); - - /* skip blanks between ip and port */ - while (*data == ' ') { - if (data >= data_end) - return -1; - data++; - } - - *port = simple_strtoul(data, &data, 10); - *ad_end_p = data; - - return 0; -} - -static int help(struct sk_buff **pskb, - struct ip_conntrack *ct, enum ip_conntrack_info ctinfo) -{ - unsigned int dataoff; - struct tcphdr _tcph, *th; - char *data, *data_limit, *ib_ptr; - int dir = CTINFO2DIR(ctinfo); - struct ip_conntrack_expect *exp; - u32 seq; - u_int32_t dcc_ip; - u_int16_t dcc_port; - int i, ret = NF_ACCEPT; - char *addr_beg_p, *addr_end_p; - typeof(ip_nat_irc_hook) ip_nat_irc; - - DEBUGP("entered\n"); - - /* If packet is coming from IRC server */ - if (dir == IP_CT_DIR_REPLY) - return NF_ACCEPT; - - /* Until there's been traffic both ways, don't look in packets. */ - if (ctinfo != IP_CT_ESTABLISHED - && ctinfo != IP_CT_ESTABLISHED + IP_CT_IS_REPLY) { - DEBUGP("Conntrackinfo = %u\n", ctinfo); - return NF_ACCEPT; - } - - /* Not a full tcp header? */ - th = skb_header_pointer(*pskb, ip_hdrlen(*pskb), - sizeof(_tcph), &_tcph); - if (th == NULL) - return NF_ACCEPT; - - /* No data? */ - dataoff = ip_hdrlen(*pskb) + th->doff * 4; - if (dataoff >= (*pskb)->len) - return NF_ACCEPT; - - spin_lock_bh(&irc_buffer_lock); - ib_ptr = skb_header_pointer(*pskb, dataoff, - (*pskb)->len - dataoff, irc_buffer); - BUG_ON(ib_ptr == NULL); - - data = ib_ptr; - data_limit = ib_ptr + (*pskb)->len - dataoff; - - /* strlen("\1DCC SENT t AAAAAAAA P\1\n")=24 - * 5+MINMATCHLEN+strlen("t AAAAAAAA P\1\n")=14 */ - while (data < (data_limit - (19 + MINMATCHLEN))) { - if (memcmp(data, "\1DCC ", 5)) { - data++; - continue; - } - - data += 5; - /* we have at least (19+MINMATCHLEN)-5 bytes valid data left */ - - DEBUGP("DCC found in master %u.%u.%u.%u:%u %u.%u.%u.%u:%u...\n", - NIPQUAD(iph->saddr), ntohs(th->source), - NIPQUAD(iph->daddr), ntohs(th->dest)); - - for (i = 0; i < ARRAY_SIZE(dccprotos); i++) { - if (memcmp(data, dccprotos[i], strlen(dccprotos[i]))) { - /* no match */ - continue; - } - - DEBUGP("DCC %s detected\n", dccprotos[i]); - data += strlen(dccprotos[i]); - /* we have at least - * (19+MINMATCHLEN)-5-dccprotos[i].matchlen bytes valid - * data left (== 14/13 bytes) */ - if (parse_dcc((char *)data, data_limit, &dcc_ip, - &dcc_port, &addr_beg_p, &addr_end_p)) { - /* unable to parse */ - DEBUGP("unable to parse dcc command\n"); - continue; - } - DEBUGP("DCC bound ip/port: %u.%u.%u.%u:%u\n", - HIPQUAD(dcc_ip), dcc_port); - - /* dcc_ip can be the internal OR external (NAT'ed) IP - * Tiago Sousa */ - if (ct->tuplehash[dir].tuple.src.ip != htonl(dcc_ip) - && ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip != htonl(dcc_ip)) { - if (net_ratelimit()) - printk(KERN_WARNING - "Forged DCC command from " - "%u.%u.%u.%u: %u.%u.%u.%u:%u\n", - NIPQUAD(ct->tuplehash[dir].tuple.src.ip), - HIPQUAD(dcc_ip), dcc_port); - - continue; - } - - exp = ip_conntrack_expect_alloc(ct); - if (exp == NULL) { - ret = NF_DROP; - goto out; - } - - /* save position of address in dcc string, - * necessary for NAT */ - DEBUGP("tcph->seq = %u\n", th->seq); - seq = ntohl(th->seq) + (addr_beg_p - ib_ptr); - - /* We refer to the reverse direction ("!dir") - * tuples here, because we're expecting - * something in the other * direction. - * Doesn't matter unless NAT is happening. */ - exp->tuple = ((struct ip_conntrack_tuple) - { { 0, { 0 } }, - { ct->tuplehash[!dir].tuple.dst.ip, - { .tcp = { htons(dcc_port) } }, - IPPROTO_TCP }}); - exp->mask = ((struct ip_conntrack_tuple) - { { 0, { 0 } }, - { htonl(0xFFFFFFFF), - { .tcp = { htons(0xFFFF) } }, 0xFF }}); - exp->expectfn = NULL; - exp->flags = 0; - ip_nat_irc = rcu_dereference(ip_nat_irc_hook); - if (ip_nat_irc) - ret = ip_nat_irc(pskb, ctinfo, - addr_beg_p - ib_ptr, - addr_end_p - addr_beg_p, - exp); - else if (ip_conntrack_expect_related(exp) != 0) - ret = NF_DROP; - ip_conntrack_expect_put(exp); - goto out; - } /* for .. NUM_DCCPROTO */ - } /* while data < ... */ - - out: - spin_unlock_bh(&irc_buffer_lock); - return ret; -} - -static struct ip_conntrack_helper irc_helpers[MAX_PORTS]; -static char irc_names[MAX_PORTS][sizeof("irc-65535")]; - -static void ip_conntrack_irc_fini(void); - -static int __init ip_conntrack_irc_init(void) -{ - int i, ret; - struct ip_conntrack_helper *hlpr; - char *tmpname; - - if (max_dcc_channels < 1) { - printk("ip_conntrack_irc: max_dcc_channels must be a positive integer\n"); - return -EBUSY; - } - - irc_buffer = kmalloc(65536, GFP_KERNEL); - if (!irc_buffer) - return -ENOMEM; - - /* If no port given, default to standard irc port */ - if (ports_c == 0) - ports[ports_c++] = IRC_PORT; - - for (i = 0; i < ports_c; i++) { - hlpr = &irc_helpers[i]; - hlpr->tuple.src.u.tcp.port = htons(ports[i]); - hlpr->tuple.dst.protonum = IPPROTO_TCP; - hlpr->mask.src.u.tcp.port = htons(0xFFFF); - hlpr->mask.dst.protonum = 0xFF; - hlpr->max_expected = max_dcc_channels; - hlpr->timeout = dcc_timeout; - hlpr->me = THIS_MODULE; - hlpr->help = help; - - tmpname = &irc_names[i][0]; - if (ports[i] == IRC_PORT) - sprintf(tmpname, "irc"); - else - sprintf(tmpname, "irc-%d", i); - hlpr->name = tmpname; - - DEBUGP("port #%d: %d\n", i, ports[i]); - - ret = ip_conntrack_helper_register(hlpr); - - if (ret) { - printk("ip_conntrack_irc: ERROR registering port %d\n", - ports[i]); - ip_conntrack_irc_fini(); - return -EBUSY; - } - } - return 0; -} - -/* This function is intentionally _NOT_ defined as __exit, because - * it is needed by the init function */ -static void ip_conntrack_irc_fini(void) -{ - int i; - for (i = 0; i < ports_c; i++) { - DEBUGP("unregistering port %d\n", - ports[i]); - ip_conntrack_helper_unregister(&irc_helpers[i]); - } - kfree(irc_buffer); -} - -module_init(ip_conntrack_irc_init); -module_exit(ip_conntrack_irc_fini); diff --git a/net/ipv4/netfilter/ip_conntrack_netbios_ns.c b/net/ipv4/netfilter/ip_conntrack_netbios_ns.c deleted file mode 100644 index df07c5f1d874..000000000000 --- a/net/ipv4/netfilter/ip_conntrack_netbios_ns.c +++ /dev/null @@ -1,143 +0,0 @@ -/* - * NetBIOS name service broadcast connection tracking helper - * - * (c) 2005 Patrick McHardy - * - * 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 helper tracks locally originating NetBIOS name service - * requests by issuing permanent expectations (valid until - * timing out) matching all reply connections from the - * destination network. The only NetBIOS specific thing is - * actually the port number. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#define NMBD_PORT 137 - -MODULE_AUTHOR("Patrick McHardy "); -MODULE_DESCRIPTION("NetBIOS name service broadcast connection tracking helper"); -MODULE_LICENSE("GPL"); - -static unsigned int timeout = 3; -module_param(timeout, uint, 0400); -MODULE_PARM_DESC(timeout, "timeout for master connection/replies in seconds"); - -static int help(struct sk_buff **pskb, - struct ip_conntrack *ct, enum ip_conntrack_info ctinfo) -{ - struct ip_conntrack_expect *exp; - struct iphdr *iph = ip_hdr(*pskb); - struct rtable *rt = (struct rtable *)(*pskb)->dst; - struct in_device *in_dev; - __be32 mask = 0; - - /* we're only interested in locally generated packets */ - if ((*pskb)->sk == NULL) - goto out; - if (rt == NULL || !(rt->rt_flags & RTCF_BROADCAST)) - goto out; - if (CTINFO2DIR(ctinfo) != IP_CT_DIR_ORIGINAL) - goto out; - - rcu_read_lock(); - in_dev = __in_dev_get_rcu(rt->u.dst.dev); - if (in_dev != NULL) { - for_primary_ifa(in_dev) { - if (ifa->ifa_broadcast == iph->daddr) { - mask = ifa->ifa_mask; - break; - } - } endfor_ifa(in_dev); - } - rcu_read_unlock(); - - if (mask == 0) - goto out; - - exp = ip_conntrack_expect_alloc(ct); - if (exp == NULL) - goto out; - - exp->tuple = ct->tuplehash[IP_CT_DIR_REPLY].tuple; - exp->tuple.src.u.udp.port = htons(NMBD_PORT); - - exp->mask.src.ip = mask; - exp->mask.src.u.udp.port = htons(0xFFFF); - exp->mask.dst.ip = htonl(0xFFFFFFFF); - exp->mask.dst.u.udp.port = htons(0xFFFF); - exp->mask.dst.protonum = 0xFF; - - exp->expectfn = NULL; - exp->flags = IP_CT_EXPECT_PERMANENT; - - ip_conntrack_expect_related(exp); - ip_conntrack_expect_put(exp); - - ip_ct_refresh(ct, *pskb, timeout * HZ); -out: - return NF_ACCEPT; -} - -static struct ip_conntrack_helper helper = { - .name = "netbios-ns", - .tuple = { - .src = { - .u = { - .udp = { - .port = __constant_htons(NMBD_PORT), - } - } - }, - .dst = { - .protonum = IPPROTO_UDP, - }, - }, - .mask = { - .src = { - .u = { - .udp = { - .port = __constant_htons(0xFFFF), - } - } - }, - .dst = { - .protonum = 0xFF, - }, - }, - .max_expected = 1, - .me = THIS_MODULE, - .help = help, -}; - -static int __init ip_conntrack_netbios_ns_init(void) -{ - helper.timeout = timeout; - return ip_conntrack_helper_register(&helper); -} - -static void __exit ip_conntrack_netbios_ns_fini(void) -{ - ip_conntrack_helper_unregister(&helper); -} - -module_init(ip_conntrack_netbios_ns_init); -module_exit(ip_conntrack_netbios_ns_fini); diff --git a/net/ipv4/netfilter/ip_conntrack_netlink.c b/net/ipv4/netfilter/ip_conntrack_netlink.c deleted file mode 100644 index 9228b76ccd9a..000000000000 --- a/net/ipv4/netfilter/ip_conntrack_netlink.c +++ /dev/null @@ -1,1577 +0,0 @@ -/* Connection tracking via netlink socket. Allows for user space - * protocol helpers and general trouble making from userspace. - * - * (C) 2001 by Jay Schulist - * (C) 2002-2005 by Harald Welte - * (C) 2003 by Patrick Mchardy - * (C) 2005-2006 by Pablo Neira Ayuso - * - * I've reworked this stuff to use attributes instead of conntrack - * structures. 5.44 am. I need more tea. --pablo 05/07/11. - * - * Initial connection tracking via netlink development funded and - * generally made possible by Network Robots, Inc. (www.networkrobots.com) - * - * Further development of this code funded by Astaro AG (http://www.astaro.com) - * - * This software may be used and distributed according to the terms - * of the GNU General Public License, incorporated herein by reference. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include -#include - -MODULE_LICENSE("GPL"); - -static char __initdata version[] = "0.90"; - -static inline int -ctnetlink_dump_tuples_proto(struct sk_buff *skb, - const struct ip_conntrack_tuple *tuple, - struct ip_conntrack_protocol *proto) -{ - int ret = 0; - struct nfattr *nest_parms = NFA_NEST(skb, CTA_TUPLE_PROTO); - - NFA_PUT(skb, CTA_PROTO_NUM, sizeof(u_int8_t), &tuple->dst.protonum); - - if (likely(proto->tuple_to_nfattr)) - ret = proto->tuple_to_nfattr(skb, tuple); - - NFA_NEST_END(skb, nest_parms); - - return ret; - -nfattr_failure: - return -1; -} - -static inline int -ctnetlink_dump_tuples_ip(struct sk_buff *skb, - const struct ip_conntrack_tuple *tuple) -{ - struct nfattr *nest_parms = NFA_NEST(skb, CTA_TUPLE_IP); - - NFA_PUT(skb, CTA_IP_V4_SRC, sizeof(__be32), &tuple->src.ip); - NFA_PUT(skb, CTA_IP_V4_DST, sizeof(__be32), &tuple->dst.ip); - - NFA_NEST_END(skb, nest_parms); - - return 0; - -nfattr_failure: - return -1; -} - -static inline int -ctnetlink_dump_tuples(struct sk_buff *skb, - const struct ip_conntrack_tuple *tuple) -{ - int ret; - struct ip_conntrack_protocol *proto; - - ret = ctnetlink_dump_tuples_ip(skb, tuple); - if (unlikely(ret < 0)) - return ret; - - proto = ip_conntrack_proto_find_get(tuple->dst.protonum); - ret = ctnetlink_dump_tuples_proto(skb, tuple, proto); - ip_conntrack_proto_put(proto); - - return ret; -} - -static inline int -ctnetlink_dump_status(struct sk_buff *skb, const struct ip_conntrack *ct) -{ - __be32 status = htonl((u_int32_t) ct->status); - NFA_PUT(skb, CTA_STATUS, sizeof(status), &status); - return 0; - -nfattr_failure: - return -1; -} - -static inline int -ctnetlink_dump_timeout(struct sk_buff *skb, const struct ip_conntrack *ct) -{ - long timeout_l = ct->timeout.expires - jiffies; - __be32 timeout; - - if (timeout_l < 0) - timeout = 0; - else - timeout = htonl(timeout_l / HZ); - - NFA_PUT(skb, CTA_TIMEOUT, sizeof(timeout), &timeout); - return 0; - -nfattr_failure: - return -1; -} - -static inline int -ctnetlink_dump_protoinfo(struct sk_buff *skb, const struct ip_conntrack *ct) -{ - struct ip_conntrack_protocol *proto = ip_conntrack_proto_find_get(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum); - - struct nfattr *nest_proto; - int ret; - - if (!proto->to_nfattr) { - ip_conntrack_proto_put(proto); - return 0; - } - - nest_proto = NFA_NEST(skb, CTA_PROTOINFO); - - ret = proto->to_nfattr(skb, nest_proto, ct); - - ip_conntrack_proto_put(proto); - - NFA_NEST_END(skb, nest_proto); - - return ret; - -nfattr_failure: - ip_conntrack_proto_put(proto); - return -1; -} - -static inline int -ctnetlink_dump_helpinfo(struct sk_buff *skb, const struct ip_conntrack *ct) -{ - struct nfattr *nest_helper; - - if (!ct->helper) - return 0; - - nest_helper = NFA_NEST(skb, CTA_HELP); - NFA_PUT(skb, CTA_HELP_NAME, strlen(ct->helper->name), ct->helper->name); - - if (ct->helper->to_nfattr) - ct->helper->to_nfattr(skb, ct); - - NFA_NEST_END(skb, nest_helper); - - return 0; - -nfattr_failure: - return -1; -} - -#ifdef CONFIG_IP_NF_CT_ACCT -static inline int -ctnetlink_dump_counters(struct sk_buff *skb, const struct ip_conntrack *ct, - enum ip_conntrack_dir dir) -{ - enum ctattr_type type = dir ? CTA_COUNTERS_REPLY: CTA_COUNTERS_ORIG; - struct nfattr *nest_count = NFA_NEST(skb, type); - __be32 tmp; - - tmp = htonl(ct->counters[dir].packets); - NFA_PUT(skb, CTA_COUNTERS32_PACKETS, sizeof(__be32), &tmp); - - tmp = htonl(ct->counters[dir].bytes); - NFA_PUT(skb, CTA_COUNTERS32_BYTES, sizeof(__be32), &tmp); - - NFA_NEST_END(skb, nest_count); - - return 0; - -nfattr_failure: - return -1; -} -#else -#define ctnetlink_dump_counters(a, b, c) (0) -#endif - -#ifdef CONFIG_IP_NF_CONNTRACK_MARK -static inline int -ctnetlink_dump_mark(struct sk_buff *skb, const struct ip_conntrack *ct) -{ - __be32 mark = htonl(ct->mark); - - NFA_PUT(skb, CTA_MARK, sizeof(__be32), &mark); - return 0; - -nfattr_failure: - return -1; -} -#else -#define ctnetlink_dump_mark(a, b) (0) -#endif - -static inline int -ctnetlink_dump_id(struct sk_buff *skb, const struct ip_conntrack *ct) -{ - __be32 id = htonl(ct->id); - NFA_PUT(skb, CTA_ID, sizeof(__be32), &id); - return 0; - -nfattr_failure: - return -1; -} - -static inline int -ctnetlink_dump_use(struct sk_buff *skb, const struct ip_conntrack *ct) -{ - __be32 use = htonl(atomic_read(&ct->ct_general.use)); - - NFA_PUT(skb, CTA_USE, sizeof(__be32), &use); - return 0; - -nfattr_failure: - return -1; -} - -#define tuple(ct, dir) (&(ct)->tuplehash[dir].tuple) - -static int -ctnetlink_fill_info(struct sk_buff *skb, u32 pid, u32 seq, - int event, int nowait, - const struct ip_conntrack *ct) -{ - struct nlmsghdr *nlh; - struct nfgenmsg *nfmsg; - struct nfattr *nest_parms; - unsigned char *b; - - b = skb->tail; - - event |= NFNL_SUBSYS_CTNETLINK << 8; - nlh = NLMSG_PUT(skb, pid, seq, event, sizeof(struct nfgenmsg)); - nfmsg = NLMSG_DATA(nlh); - - nlh->nlmsg_flags = (nowait && pid) ? NLM_F_MULTI : 0; - nfmsg->nfgen_family = AF_INET; - nfmsg->version = NFNETLINK_V0; - nfmsg->res_id = 0; - - nest_parms = NFA_NEST(skb, CTA_TUPLE_ORIG); - if (ctnetlink_dump_tuples(skb, tuple(ct, IP_CT_DIR_ORIGINAL)) < 0) - goto nfattr_failure; - NFA_NEST_END(skb, nest_parms); - - nest_parms = NFA_NEST(skb, CTA_TUPLE_REPLY); - if (ctnetlink_dump_tuples(skb, tuple(ct, IP_CT_DIR_REPLY)) < 0) - goto nfattr_failure; - NFA_NEST_END(skb, nest_parms); - - if (ctnetlink_dump_status(skb, ct) < 0 || - ctnetlink_dump_timeout(skb, ct) < 0 || - ctnetlink_dump_counters(skb, ct, IP_CT_DIR_ORIGINAL) < 0 || - ctnetlink_dump_counters(skb, ct, IP_CT_DIR_REPLY) < 0 || - ctnetlink_dump_protoinfo(skb, ct) < 0 || - ctnetlink_dump_helpinfo(skb, ct) < 0 || - ctnetlink_dump_mark(skb, ct) < 0 || - ctnetlink_dump_id(skb, ct) < 0 || - ctnetlink_dump_use(skb, ct) < 0) - goto nfattr_failure; - - nlh->nlmsg_len = skb->tail - b; - return skb->len; - -nlmsg_failure: -nfattr_failure: - skb_trim(skb, b - skb->data); - return -1; -} - -#ifdef CONFIG_IP_NF_CONNTRACK_EVENTS -static int ctnetlink_conntrack_event(struct notifier_block *this, - unsigned long events, void *ptr) -{ - struct nlmsghdr *nlh; - struct nfgenmsg *nfmsg; - struct nfattr *nest_parms; - struct ip_conntrack *ct = (struct ip_conntrack *)ptr; - struct sk_buff *skb; - unsigned int type; - unsigned char *b; - unsigned int flags = 0, group; - - /* ignore our fake conntrack entry */ - if (ct == &ip_conntrack_untracked) - return NOTIFY_DONE; - - if (events & IPCT_DESTROY) { - type = IPCTNL_MSG_CT_DELETE; - group = NFNLGRP_CONNTRACK_DESTROY; - } else if (events & (IPCT_NEW | IPCT_RELATED)) { - type = IPCTNL_MSG_CT_NEW; - flags = NLM_F_CREATE|NLM_F_EXCL; - group = NFNLGRP_CONNTRACK_NEW; - } else if (events & (IPCT_STATUS | IPCT_PROTOINFO)) { - type = IPCTNL_MSG_CT_NEW; - group = NFNLGRP_CONNTRACK_UPDATE; - } else - return NOTIFY_DONE; - - if (!nfnetlink_has_listeners(group)) - return NOTIFY_DONE; - - skb = alloc_skb(NLMSG_GOODSIZE, GFP_ATOMIC); - if (!skb) - return NOTIFY_DONE; - - b = skb->tail; - - type |= NFNL_SUBSYS_CTNETLINK << 8; - nlh = NLMSG_PUT(skb, 0, 0, type, sizeof(struct nfgenmsg)); - nfmsg = NLMSG_DATA(nlh); - - nlh->nlmsg_flags = flags; - nfmsg->nfgen_family = AF_INET; - nfmsg->version = NFNETLINK_V0; - nfmsg->res_id = 0; - - nest_parms = NFA_NEST(skb, CTA_TUPLE_ORIG); - if (ctnetlink_dump_tuples(skb, tuple(ct, IP_CT_DIR_ORIGINAL)) < 0) - goto nfattr_failure; - NFA_NEST_END(skb, nest_parms); - - nest_parms = NFA_NEST(skb, CTA_TUPLE_REPLY); - if (ctnetlink_dump_tuples(skb, tuple(ct, IP_CT_DIR_REPLY)) < 0) - goto nfattr_failure; - NFA_NEST_END(skb, nest_parms); - - if (events & IPCT_DESTROY) { - if (ctnetlink_dump_counters(skb, ct, IP_CT_DIR_ORIGINAL) < 0 || - ctnetlink_dump_counters(skb, ct, IP_CT_DIR_REPLY) < 0) - goto nfattr_failure; - } else { - if (ctnetlink_dump_status(skb, ct) < 0) - goto nfattr_failure; - - if (ctnetlink_dump_timeout(skb, ct) < 0) - goto nfattr_failure; - - if (events & IPCT_PROTOINFO - && ctnetlink_dump_protoinfo(skb, ct) < 0) - goto nfattr_failure; - - if ((events & IPCT_HELPER || ct->helper) - && ctnetlink_dump_helpinfo(skb, ct) < 0) - goto nfattr_failure; - -#ifdef CONFIG_IP_NF_CONNTRACK_MARK - if ((events & IPCT_MARK || ct->mark) - && ctnetlink_dump_mark(skb, ct) < 0) - goto nfattr_failure; -#endif - - if (events & IPCT_COUNTER_FILLING && - (ctnetlink_dump_counters(skb, ct, IP_CT_DIR_ORIGINAL) < 0 || - ctnetlink_dump_counters(skb, ct, IP_CT_DIR_REPLY) < 0)) - goto nfattr_failure; - } - - nlh->nlmsg_len = skb->tail - b; - nfnetlink_send(skb, 0, group, 0); - return NOTIFY_DONE; - -nlmsg_failure: -nfattr_failure: - kfree_skb(skb); - return NOTIFY_DONE; -} -#endif /* CONFIG_IP_NF_CONNTRACK_EVENTS */ - -static int ctnetlink_done(struct netlink_callback *cb) -{ - if (cb->args[1]) - ip_conntrack_put((struct ip_conntrack *)cb->args[1]); - return 0; -} - -static int -ctnetlink_dump_table(struct sk_buff *skb, struct netlink_callback *cb) -{ - struct ip_conntrack *ct, *last; - struct ip_conntrack_tuple_hash *h; - struct list_head *i; - - read_lock_bh(&ip_conntrack_lock); - last = (struct ip_conntrack *)cb->args[1]; - for (; cb->args[0] < ip_conntrack_htable_size; cb->args[0]++) { -restart: - list_for_each_prev(i, &ip_conntrack_hash[cb->args[0]]) { - h = (struct ip_conntrack_tuple_hash *) i; - if (DIRECTION(h) != IP_CT_DIR_ORIGINAL) - continue; - ct = tuplehash_to_ctrack(h); - if (cb->args[1]) { - if (ct != last) - continue; - cb->args[1] = 0; - } - if (ctnetlink_fill_info(skb, NETLINK_CB(cb->skb).pid, - cb->nlh->nlmsg_seq, - IPCTNL_MSG_CT_NEW, - 1, ct) < 0) { - nf_conntrack_get(&ct->ct_general); - cb->args[1] = (unsigned long)ct; - goto out; - } -#ifdef CONFIG_NF_CT_ACCT - if (NFNL_MSG_TYPE(cb->nlh->nlmsg_type) == - IPCTNL_MSG_CT_GET_CTRZERO) - memset(&ct->counters, 0, sizeof(ct->counters)); -#endif - } - if (cb->args[1]) { - cb->args[1] = 0; - goto restart; - } - } -out: - read_unlock_bh(&ip_conntrack_lock); - if (last) - ip_conntrack_put(last); - - return skb->len; -} - -static const size_t cta_min_ip[CTA_IP_MAX] = { - [CTA_IP_V4_SRC-1] = sizeof(__be32), - [CTA_IP_V4_DST-1] = sizeof(__be32), -}; - -static inline int -ctnetlink_parse_tuple_ip(struct nfattr *attr, struct ip_conntrack_tuple *tuple) -{ - struct nfattr *tb[CTA_IP_MAX]; - - nfattr_parse_nested(tb, CTA_IP_MAX, attr); - - if (nfattr_bad_size(tb, CTA_IP_MAX, cta_min_ip)) - return -EINVAL; - - if (!tb[CTA_IP_V4_SRC-1]) - return -EINVAL; - tuple->src.ip = *(__be32 *)NFA_DATA(tb[CTA_IP_V4_SRC-1]); - - if (!tb[CTA_IP_V4_DST-1]) - return -EINVAL; - tuple->dst.ip = *(__be32 *)NFA_DATA(tb[CTA_IP_V4_DST-1]); - - return 0; -} - -static const size_t cta_min_proto[CTA_PROTO_MAX] = { - [CTA_PROTO_NUM-1] = sizeof(u_int8_t), - [CTA_PROTO_SRC_PORT-1] = sizeof(u_int16_t), - [CTA_PROTO_DST_PORT-1] = sizeof(u_int16_t), - [CTA_PROTO_ICMP_TYPE-1] = sizeof(u_int8_t), - [CTA_PROTO_ICMP_CODE-1] = sizeof(u_int8_t), - [CTA_PROTO_ICMP_ID-1] = sizeof(u_int16_t), -}; - -static inline int -ctnetlink_parse_tuple_proto(struct nfattr *attr, - struct ip_conntrack_tuple *tuple) -{ - struct nfattr *tb[CTA_PROTO_MAX]; - struct ip_conntrack_protocol *proto; - int ret = 0; - - nfattr_parse_nested(tb, CTA_PROTO_MAX, attr); - - if (nfattr_bad_size(tb, CTA_PROTO_MAX, cta_min_proto)) - return -EINVAL; - - if (!tb[CTA_PROTO_NUM-1]) - return -EINVAL; - tuple->dst.protonum = *(u_int8_t *)NFA_DATA(tb[CTA_PROTO_NUM-1]); - - proto = ip_conntrack_proto_find_get(tuple->dst.protonum); - - if (likely(proto->nfattr_to_tuple)) - ret = proto->nfattr_to_tuple(tb, tuple); - - ip_conntrack_proto_put(proto); - - return ret; -} - -static inline int -ctnetlink_parse_tuple(struct nfattr *cda[], struct ip_conntrack_tuple *tuple, - enum ctattr_tuple type) -{ - struct nfattr *tb[CTA_TUPLE_MAX]; - int err; - - memset(tuple, 0, sizeof(*tuple)); - - nfattr_parse_nested(tb, CTA_TUPLE_MAX, cda[type-1]); - - if (!tb[CTA_TUPLE_IP-1]) - return -EINVAL; - - err = ctnetlink_parse_tuple_ip(tb[CTA_TUPLE_IP-1], tuple); - if (err < 0) - return err; - - if (!tb[CTA_TUPLE_PROTO-1]) - return -EINVAL; - - err = ctnetlink_parse_tuple_proto(tb[CTA_TUPLE_PROTO-1], tuple); - if (err < 0) - return err; - - /* orig and expect tuples get DIR_ORIGINAL */ - if (type == CTA_TUPLE_REPLY) - tuple->dst.dir = IP_CT_DIR_REPLY; - else - tuple->dst.dir = IP_CT_DIR_ORIGINAL; - - return 0; -} - -#ifdef CONFIG_IP_NF_NAT_NEEDED -static const size_t cta_min_protonat[CTA_PROTONAT_MAX] = { - [CTA_PROTONAT_PORT_MIN-1] = sizeof(u_int16_t), - [CTA_PROTONAT_PORT_MAX-1] = sizeof(u_int16_t), -}; - -static int ctnetlink_parse_nat_proto(struct nfattr *attr, - const struct ip_conntrack *ct, - struct ip_nat_range *range) -{ - struct nfattr *tb[CTA_PROTONAT_MAX]; - struct ip_nat_protocol *npt; - - nfattr_parse_nested(tb, CTA_PROTONAT_MAX, attr); - - if (nfattr_bad_size(tb, CTA_PROTONAT_MAX, cta_min_protonat)) - return -EINVAL; - - npt = ip_nat_proto_find_get(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum); - - if (!npt->nfattr_to_range) { - ip_nat_proto_put(npt); - return 0; - } - - /* nfattr_to_range returns 1 if it parsed, 0 if not, neg. on error */ - if (npt->nfattr_to_range(tb, range) > 0) - range->flags |= IP_NAT_RANGE_PROTO_SPECIFIED; - - ip_nat_proto_put(npt); - - return 0; -} - -static const size_t cta_min_nat[CTA_NAT_MAX] = { - [CTA_NAT_MINIP-1] = sizeof(__be32), - [CTA_NAT_MAXIP-1] = sizeof(__be32), -}; - -static inline int -ctnetlink_parse_nat(struct nfattr *nat, - const struct ip_conntrack *ct, struct ip_nat_range *range) -{ - struct nfattr *tb[CTA_NAT_MAX]; - int err; - - memset(range, 0, sizeof(*range)); - - nfattr_parse_nested(tb, CTA_NAT_MAX, nat); - - if (nfattr_bad_size(tb, CTA_NAT_MAX, cta_min_nat)) - return -EINVAL; - - if (tb[CTA_NAT_MINIP-1]) - range->min_ip = *(__be32 *)NFA_DATA(tb[CTA_NAT_MINIP-1]); - - if (!tb[CTA_NAT_MAXIP-1]) - range->max_ip = range->min_ip; - else - range->max_ip = *(__be32 *)NFA_DATA(tb[CTA_NAT_MAXIP-1]); - - if (range->min_ip) - range->flags |= IP_NAT_RANGE_MAP_IPS; - - if (!tb[CTA_NAT_PROTO-1]) - return 0; - - err = ctnetlink_parse_nat_proto(tb[CTA_NAT_PROTO-1], ct, range); - if (err < 0) - return err; - - return 0; -} -#endif - -static inline int -ctnetlink_parse_help(struct nfattr *attr, char **helper_name) -{ - struct nfattr *tb[CTA_HELP_MAX]; - - nfattr_parse_nested(tb, CTA_HELP_MAX, attr); - - if (!tb[CTA_HELP_NAME-1]) - return -EINVAL; - - *helper_name = NFA_DATA(tb[CTA_HELP_NAME-1]); - - return 0; -} - -static const size_t cta_min[CTA_MAX] = { - [CTA_STATUS-1] = sizeof(__be32), - [CTA_TIMEOUT-1] = sizeof(__be32), - [CTA_MARK-1] = sizeof(__be32), - [CTA_USE-1] = sizeof(__be32), - [CTA_ID-1] = sizeof(__be32) -}; - -static int -ctnetlink_del_conntrack(struct sock *ctnl, struct sk_buff *skb, - struct nlmsghdr *nlh, struct nfattr *cda[], int *errp) -{ - struct ip_conntrack_tuple_hash *h; - struct ip_conntrack_tuple tuple; - struct ip_conntrack *ct; - int err = 0; - - if (nfattr_bad_size(cda, CTA_MAX, cta_min)) - return -EINVAL; - - if (cda[CTA_TUPLE_ORIG-1]) - err = ctnetlink_parse_tuple(cda, &tuple, CTA_TUPLE_ORIG); - else if (cda[CTA_TUPLE_REPLY-1]) - err = ctnetlink_parse_tuple(cda, &tuple, CTA_TUPLE_REPLY); - else { - /* Flush the whole table */ - ip_conntrack_flush(); - return 0; - } - - if (err < 0) - return err; - - h = ip_conntrack_find_get(&tuple, NULL); - if (!h) - return -ENOENT; - - ct = tuplehash_to_ctrack(h); - - if (cda[CTA_ID-1]) { - u_int32_t id = ntohl(*(__be32 *)NFA_DATA(cda[CTA_ID-1])); - if (ct->id != id) { - ip_conntrack_put(ct); - return -ENOENT; - } - } - if (del_timer(&ct->timeout)) - ct->timeout.function((unsigned long)ct); - - ip_conntrack_put(ct); - - return 0; -} - -static int -ctnetlink_get_conntrack(struct sock *ctnl, struct sk_buff *skb, - struct nlmsghdr *nlh, struct nfattr *cda[], int *errp) -{ - struct ip_conntrack_tuple_hash *h; - struct ip_conntrack_tuple tuple; - struct ip_conntrack *ct; - struct sk_buff *skb2 = NULL; - int err = 0; - - if (nlh->nlmsg_flags & NLM_F_DUMP) { - struct nfgenmsg *msg = NLMSG_DATA(nlh); - u32 rlen; - - if (msg->nfgen_family != AF_INET) - return -EAFNOSUPPORT; - -#ifndef CONFIG_IP_NF_CT_ACCT - if (NFNL_MSG_TYPE(nlh->nlmsg_type) == IPCTNL_MSG_CT_GET_CTRZERO) - return -ENOTSUPP; -#endif - if ((*errp = netlink_dump_start(ctnl, skb, nlh, - ctnetlink_dump_table, - ctnetlink_done)) != 0) - return -EINVAL; - - rlen = NLMSG_ALIGN(nlh->nlmsg_len); - if (rlen > skb->len) - rlen = skb->len; - skb_pull(skb, rlen); - return 0; - } - - if (nfattr_bad_size(cda, CTA_MAX, cta_min)) - return -EINVAL; - - if (cda[CTA_TUPLE_ORIG-1]) - err = ctnetlink_parse_tuple(cda, &tuple, CTA_TUPLE_ORIG); - else if (cda[CTA_TUPLE_REPLY-1]) - err = ctnetlink_parse_tuple(cda, &tuple, CTA_TUPLE_REPLY); - else - return -EINVAL; - - if (err < 0) - return err; - - h = ip_conntrack_find_get(&tuple, NULL); - if (!h) - return -ENOENT; - - ct = tuplehash_to_ctrack(h); - - err = -ENOMEM; - skb2 = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); - if (!skb2) { - ip_conntrack_put(ct); - return -ENOMEM; - } - - err = ctnetlink_fill_info(skb2, NETLINK_CB(skb).pid, nlh->nlmsg_seq, - IPCTNL_MSG_CT_NEW, 1, ct); - ip_conntrack_put(ct); - if (err <= 0) - goto free; - - err = netlink_unicast(ctnl, skb2, NETLINK_CB(skb).pid, MSG_DONTWAIT); - if (err < 0) - goto out; - - return 0; - -free: - kfree_skb(skb2); -out: - return err; -} - -static inline int -ctnetlink_change_status(struct ip_conntrack *ct, struct nfattr *cda[]) -{ - unsigned long d; - unsigned status = ntohl(*(__be32 *)NFA_DATA(cda[CTA_STATUS-1])); - d = ct->status ^ status; - - if (d & (IPS_EXPECTED|IPS_CONFIRMED|IPS_DYING)) - /* unchangeable */ - return -EINVAL; - - if (d & IPS_SEEN_REPLY && !(status & IPS_SEEN_REPLY)) - /* SEEN_REPLY bit can only be set */ - return -EINVAL; - - - if (d & IPS_ASSURED && !(status & IPS_ASSURED)) - /* ASSURED bit can only be set */ - return -EINVAL; - - if (cda[CTA_NAT_SRC-1] || cda[CTA_NAT_DST-1]) { -#ifndef CONFIG_IP_NF_NAT_NEEDED - return -EINVAL; -#else - struct ip_nat_range range; - - if (cda[CTA_NAT_DST-1]) { - if (ctnetlink_parse_nat(cda[CTA_NAT_DST-1], ct, - &range) < 0) - return -EINVAL; - if (ip_nat_initialized(ct, - HOOK2MANIP(NF_IP_PRE_ROUTING))) - return -EEXIST; - ip_nat_setup_info(ct, &range, NF_IP_PRE_ROUTING); - } - if (cda[CTA_NAT_SRC-1]) { - if (ctnetlink_parse_nat(cda[CTA_NAT_SRC-1], ct, - &range) < 0) - return -EINVAL; - if (ip_nat_initialized(ct, - HOOK2MANIP(NF_IP_POST_ROUTING))) - return -EEXIST; - ip_nat_setup_info(ct, &range, NF_IP_POST_ROUTING); - } -#endif - } - - /* Be careful here, modifying NAT bits can screw up things, - * so don't let users modify them directly if they don't pass - * ip_nat_range. */ - ct->status |= status & ~(IPS_NAT_DONE_MASK | IPS_NAT_MASK); - return 0; -} - - -static inline int -ctnetlink_change_helper(struct ip_conntrack *ct, struct nfattr *cda[]) -{ - struct ip_conntrack_helper *helper; - char *helpname; - int err; - - /* don't change helper of sibling connections */ - if (ct->master) - return -EINVAL; - - err = ctnetlink_parse_help(cda[CTA_HELP-1], &helpname); - if (err < 0) - return err; - - helper = __ip_conntrack_helper_find_byname(helpname); - if (!helper) { - if (!strcmp(helpname, "")) - helper = NULL; - else - return -EINVAL; - } - - if (ct->helper) { - if (!helper) { - /* we had a helper before ... */ - ip_ct_remove_expectations(ct); - ct->helper = NULL; - } else { - /* need to zero data of old helper */ - memset(&ct->help, 0, sizeof(ct->help)); - } - } - - ct->helper = helper; - - return 0; -} - -static inline int -ctnetlink_change_timeout(struct ip_conntrack *ct, struct nfattr *cda[]) -{ - u_int32_t timeout = ntohl(*(__be32 *)NFA_DATA(cda[CTA_TIMEOUT-1])); - - if (!del_timer(&ct->timeout)) - return -ETIME; - - ct->timeout.expires = jiffies + timeout * HZ; - add_timer(&ct->timeout); - - return 0; -} - -static inline int -ctnetlink_change_protoinfo(struct ip_conntrack *ct, struct nfattr *cda[]) -{ - struct nfattr *tb[CTA_PROTOINFO_MAX], *attr = cda[CTA_PROTOINFO-1]; - struct ip_conntrack_protocol *proto; - u_int16_t npt = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum; - int err = 0; - - nfattr_parse_nested(tb, CTA_PROTOINFO_MAX, attr); - - proto = ip_conntrack_proto_find_get(npt); - - if (proto->from_nfattr) - err = proto->from_nfattr(tb, ct); - ip_conntrack_proto_put(proto); - - return err; -} - -static int -ctnetlink_change_conntrack(struct ip_conntrack *ct, struct nfattr *cda[]) -{ - int err; - - if (cda[CTA_HELP-1]) { - err = ctnetlink_change_helper(ct, cda); - if (err < 0) - return err; - } - - if (cda[CTA_TIMEOUT-1]) { - err = ctnetlink_change_timeout(ct, cda); - if (err < 0) - return err; - } - - if (cda[CTA_STATUS-1]) { - err = ctnetlink_change_status(ct, cda); - if (err < 0) - return err; - } - - if (cda[CTA_PROTOINFO-1]) { - err = ctnetlink_change_protoinfo(ct, cda); - if (err < 0) - return err; - } - -#if defined(CONFIG_IP_NF_CONNTRACK_MARK) - if (cda[CTA_MARK-1]) - ct->mark = ntohl(*(__be32 *)NFA_DATA(cda[CTA_MARK-1])); -#endif - - return 0; -} - -static int -ctnetlink_create_conntrack(struct nfattr *cda[], - struct ip_conntrack_tuple *otuple, - struct ip_conntrack_tuple *rtuple) -{ - struct ip_conntrack *ct; - int err = -EINVAL; - - ct = ip_conntrack_alloc(otuple, rtuple); - if (ct == NULL || IS_ERR(ct)) - return -ENOMEM; - - if (!cda[CTA_TIMEOUT-1]) - goto err; - ct->timeout.expires = ntohl(*(__be32 *)NFA_DATA(cda[CTA_TIMEOUT-1])); - - ct->timeout.expires = jiffies + ct->timeout.expires * HZ; - ct->status |= IPS_CONFIRMED; - - if (cda[CTA_STATUS-1]) { - err = ctnetlink_change_status(ct, cda); - if (err < 0) - goto err; - } - - if (cda[CTA_PROTOINFO-1]) { - err = ctnetlink_change_protoinfo(ct, cda); - if (err < 0) - goto err; - } - -#if defined(CONFIG_IP_NF_CONNTRACK_MARK) - if (cda[CTA_MARK-1]) - ct->mark = ntohl(*(__be32 *)NFA_DATA(cda[CTA_MARK-1])); -#endif - - ct->helper = ip_conntrack_helper_find_get(rtuple); - - add_timer(&ct->timeout); - ip_conntrack_hash_insert(ct); - - if (ct->helper) - ip_conntrack_helper_put(ct->helper); - - return 0; - -err: - ip_conntrack_free(ct); - return err; -} - -static int -ctnetlink_new_conntrack(struct sock *ctnl, struct sk_buff *skb, - struct nlmsghdr *nlh, struct nfattr *cda[], int *errp) -{ - struct ip_conntrack_tuple otuple, rtuple; - struct ip_conntrack_tuple_hash *h = NULL; - int err = 0; - - if (nfattr_bad_size(cda, CTA_MAX, cta_min)) - return -EINVAL; - - if (cda[CTA_TUPLE_ORIG-1]) { - err = ctnetlink_parse_tuple(cda, &otuple, CTA_TUPLE_ORIG); - if (err < 0) - return err; - } - - if (cda[CTA_TUPLE_REPLY-1]) { - err = ctnetlink_parse_tuple(cda, &rtuple, CTA_TUPLE_REPLY); - if (err < 0) - return err; - } - - write_lock_bh(&ip_conntrack_lock); - if (cda[CTA_TUPLE_ORIG-1]) - h = __ip_conntrack_find(&otuple, NULL); - else if (cda[CTA_TUPLE_REPLY-1]) - h = __ip_conntrack_find(&rtuple, NULL); - - if (h == NULL) { - write_unlock_bh(&ip_conntrack_lock); - err = -ENOENT; - if (nlh->nlmsg_flags & NLM_F_CREATE) - err = ctnetlink_create_conntrack(cda, &otuple, &rtuple); - return err; - } - /* implicit 'else' */ - - /* we only allow nat config for new conntracks */ - if (cda[CTA_NAT_SRC-1] || cda[CTA_NAT_DST-1]) { - err = -EINVAL; - goto out_unlock; - } - - /* We manipulate the conntrack inside the global conntrack table lock, - * so there's no need to increase the refcount */ - err = -EEXIST; - if (!(nlh->nlmsg_flags & NLM_F_EXCL)) - err = ctnetlink_change_conntrack(tuplehash_to_ctrack(h), cda); - -out_unlock: - write_unlock_bh(&ip_conntrack_lock); - return err; -} - -/*********************************************************************** - * EXPECT - ***********************************************************************/ - -static inline int -ctnetlink_exp_dump_tuple(struct sk_buff *skb, - const struct ip_conntrack_tuple *tuple, - enum ctattr_expect type) -{ - struct nfattr *nest_parms = NFA_NEST(skb, type); - - if (ctnetlink_dump_tuples(skb, tuple) < 0) - goto nfattr_failure; - - NFA_NEST_END(skb, nest_parms); - - return 0; - -nfattr_failure: - return -1; -} - -static inline int -ctnetlink_exp_dump_mask(struct sk_buff *skb, - const struct ip_conntrack_tuple *tuple, - const struct ip_conntrack_tuple *mask) -{ - int ret; - struct ip_conntrack_protocol *proto; - struct nfattr *nest_parms = NFA_NEST(skb, CTA_EXPECT_MASK); - - ret = ctnetlink_dump_tuples_ip(skb, mask); - if (unlikely(ret < 0)) - goto nfattr_failure; - - proto = ip_conntrack_proto_find_get(tuple->dst.protonum); - ret = ctnetlink_dump_tuples_proto(skb, mask, proto); - ip_conntrack_proto_put(proto); - if (unlikely(ret < 0)) - goto nfattr_failure; - - NFA_NEST_END(skb, nest_parms); - - return 0; - -nfattr_failure: - return -1; -} - -static inline int -ctnetlink_exp_dump_expect(struct sk_buff *skb, - const struct ip_conntrack_expect *exp) -{ - struct ip_conntrack *master = exp->master; - __be32 timeout = htonl((exp->timeout.expires - jiffies) / HZ); - __be32 id = htonl(exp->id); - - if (ctnetlink_exp_dump_tuple(skb, &exp->tuple, CTA_EXPECT_TUPLE) < 0) - goto nfattr_failure; - if (ctnetlink_exp_dump_mask(skb, &exp->tuple, &exp->mask) < 0) - goto nfattr_failure; - if (ctnetlink_exp_dump_tuple(skb, - &master->tuplehash[IP_CT_DIR_ORIGINAL].tuple, - CTA_EXPECT_MASTER) < 0) - goto nfattr_failure; - - NFA_PUT(skb, CTA_EXPECT_TIMEOUT, sizeof(__be32), &timeout); - NFA_PUT(skb, CTA_EXPECT_ID, sizeof(__be32), &id); - - return 0; - -nfattr_failure: - return -1; -} - -static int -ctnetlink_exp_fill_info(struct sk_buff *skb, u32 pid, u32 seq, - int event, - int nowait, - const struct ip_conntrack_expect *exp) -{ - struct nlmsghdr *nlh; - struct nfgenmsg *nfmsg; - unsigned char *b; - - b = skb->tail; - - event |= NFNL_SUBSYS_CTNETLINK_EXP << 8; - nlh = NLMSG_PUT(skb, pid, seq, event, sizeof(struct nfgenmsg)); - nfmsg = NLMSG_DATA(nlh); - - nlh->nlmsg_flags = (nowait && pid) ? NLM_F_MULTI : 0; - nfmsg->nfgen_family = AF_INET; - nfmsg->version = NFNETLINK_V0; - nfmsg->res_id = 0; - - if (ctnetlink_exp_dump_expect(skb, exp) < 0) - goto nfattr_failure; - - nlh->nlmsg_len = skb->tail - b; - return skb->len; - -nlmsg_failure: -nfattr_failure: - skb_trim(skb, b - skb->data); - return -1; -} - -#ifdef CONFIG_IP_NF_CONNTRACK_EVENTS -static int ctnetlink_expect_event(struct notifier_block *this, - unsigned long events, void *ptr) -{ - struct nlmsghdr *nlh; - struct nfgenmsg *nfmsg; - struct ip_conntrack_expect *exp = (struct ip_conntrack_expect *)ptr; - struct sk_buff *skb; - unsigned int type; - unsigned char *b; - int flags = 0; - - if (events & IPEXP_NEW) { - type = IPCTNL_MSG_EXP_NEW; - flags = NLM_F_CREATE|NLM_F_EXCL; - } else - return NOTIFY_DONE; - - if (!nfnetlink_has_listeners(NFNLGRP_CONNTRACK_EXP_NEW)) - return NOTIFY_DONE; - - skb = alloc_skb(NLMSG_GOODSIZE, GFP_ATOMIC); - if (!skb) - return NOTIFY_DONE; - - b = skb->tail; - - type |= NFNL_SUBSYS_CTNETLINK_EXP << 8; - nlh = NLMSG_PUT(skb, 0, 0, type, sizeof(struct nfgenmsg)); - nfmsg = NLMSG_DATA(nlh); - - nlh->nlmsg_flags = flags; - nfmsg->nfgen_family = AF_INET; - nfmsg->version = NFNETLINK_V0; - nfmsg->res_id = 0; - - if (ctnetlink_exp_dump_expect(skb, exp) < 0) - goto nfattr_failure; - - nlh->nlmsg_len = skb->tail - b; - nfnetlink_send(skb, 0, NFNLGRP_CONNTRACK_EXP_NEW, 0); - return NOTIFY_DONE; - -nlmsg_failure: -nfattr_failure: - kfree_skb(skb); - return NOTIFY_DONE; -} -#endif - -static int -ctnetlink_exp_dump_table(struct sk_buff *skb, struct netlink_callback *cb) -{ - struct ip_conntrack_expect *exp = NULL; - struct list_head *i; - u_int32_t *id = (u_int32_t *) &cb->args[0]; - - read_lock_bh(&ip_conntrack_lock); - list_for_each_prev(i, &ip_conntrack_expect_list) { - exp = (struct ip_conntrack_expect *) i; - if (exp->id <= *id) - continue; - if (ctnetlink_exp_fill_info(skb, NETLINK_CB(cb->skb).pid, - cb->nlh->nlmsg_seq, - IPCTNL_MSG_EXP_NEW, - 1, exp) < 0) - goto out; - *id = exp->id; - } -out: - read_unlock_bh(&ip_conntrack_lock); - - return skb->len; -} - -static const size_t cta_min_exp[CTA_EXPECT_MAX] = { - [CTA_EXPECT_TIMEOUT-1] = sizeof(__be32), - [CTA_EXPECT_ID-1] = sizeof(__be32) -}; - -static int -ctnetlink_get_expect(struct sock *ctnl, struct sk_buff *skb, - struct nlmsghdr *nlh, struct nfattr *cda[], int *errp) -{ - struct ip_conntrack_tuple tuple; - struct ip_conntrack_expect *exp; - struct sk_buff *skb2; - int err = 0; - - if (nfattr_bad_size(cda, CTA_EXPECT_MAX, cta_min_exp)) - return -EINVAL; - - if (nlh->nlmsg_flags & NLM_F_DUMP) { - struct nfgenmsg *msg = NLMSG_DATA(nlh); - u32 rlen; - - if (msg->nfgen_family != AF_INET) - return -EAFNOSUPPORT; - - if ((*errp = netlink_dump_start(ctnl, skb, nlh, - ctnetlink_exp_dump_table, - ctnetlink_done)) != 0) - return -EINVAL; - rlen = NLMSG_ALIGN(nlh->nlmsg_len); - if (rlen > skb->len) - rlen = skb->len; - skb_pull(skb, rlen); - return 0; - } - - if (cda[CTA_EXPECT_MASTER-1]) - err = ctnetlink_parse_tuple(cda, &tuple, CTA_EXPECT_MASTER); - else - return -EINVAL; - - if (err < 0) - return err; - - exp = ip_conntrack_expect_find_get(&tuple); - if (!exp) - return -ENOENT; - - if (cda[CTA_EXPECT_ID-1]) { - __be32 id = *(__be32 *)NFA_DATA(cda[CTA_EXPECT_ID-1]); - if (exp->id != ntohl(id)) { - ip_conntrack_expect_put(exp); - return -ENOENT; - } - } - - err = -ENOMEM; - skb2 = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); - if (!skb2) - goto out; - - err = ctnetlink_exp_fill_info(skb2, NETLINK_CB(skb).pid, - nlh->nlmsg_seq, IPCTNL_MSG_EXP_NEW, - 1, exp); - if (err <= 0) - goto free; - - ip_conntrack_expect_put(exp); - - return netlink_unicast(ctnl, skb2, NETLINK_CB(skb).pid, MSG_DONTWAIT); - -free: - kfree_skb(skb2); -out: - ip_conntrack_expect_put(exp); - return err; -} - -static int -ctnetlink_del_expect(struct sock *ctnl, struct sk_buff *skb, - struct nlmsghdr *nlh, struct nfattr *cda[], int *errp) -{ - struct ip_conntrack_expect *exp, *tmp; - struct ip_conntrack_tuple tuple; - struct ip_conntrack_helper *h; - int err; - - if (nfattr_bad_size(cda, CTA_EXPECT_MAX, cta_min_exp)) - return -EINVAL; - - if (cda[CTA_EXPECT_TUPLE-1]) { - /* delete a single expect by tuple */ - err = ctnetlink_parse_tuple(cda, &tuple, CTA_EXPECT_TUPLE); - if (err < 0) - return err; - - /* bump usage count to 2 */ - exp = ip_conntrack_expect_find_get(&tuple); - if (!exp) - return -ENOENT; - - if (cda[CTA_EXPECT_ID-1]) { - __be32 id = - *(__be32 *)NFA_DATA(cda[CTA_EXPECT_ID-1]); - if (exp->id != ntohl(id)) { - ip_conntrack_expect_put(exp); - return -ENOENT; - } - } - - /* after list removal, usage count == 1 */ - ip_conntrack_unexpect_related(exp); - /* have to put what we 'get' above. - * after this line usage count == 0 */ - ip_conntrack_expect_put(exp); - } else if (cda[CTA_EXPECT_HELP_NAME-1]) { - char *name = NFA_DATA(cda[CTA_EXPECT_HELP_NAME-1]); - - /* delete all expectations for this helper */ - write_lock_bh(&ip_conntrack_lock); - h = __ip_conntrack_helper_find_byname(name); - if (!h) { - write_unlock_bh(&ip_conntrack_lock); - return -EINVAL; - } - list_for_each_entry_safe(exp, tmp, &ip_conntrack_expect_list, - list) { - if (exp->master->helper == h - && del_timer(&exp->timeout)) { - ip_ct_unlink_expect(exp); - ip_conntrack_expect_put(exp); - } - } - write_unlock_bh(&ip_conntrack_lock); - } else { - /* This basically means we have to flush everything*/ - write_lock_bh(&ip_conntrack_lock); - list_for_each_entry_safe(exp, tmp, &ip_conntrack_expect_list, - list) { - if (del_timer(&exp->timeout)) { - ip_ct_unlink_expect(exp); - ip_conntrack_expect_put(exp); - } - } - write_unlock_bh(&ip_conntrack_lock); - } - - return 0; -} -static int -ctnetlink_change_expect(struct ip_conntrack_expect *x, struct nfattr *cda[]) -{ - return -EOPNOTSUPP; -} - -static int -ctnetlink_create_expect(struct nfattr *cda[]) -{ - struct ip_conntrack_tuple tuple, mask, master_tuple; - struct ip_conntrack_tuple_hash *h = NULL; - struct ip_conntrack_expect *exp; - struct ip_conntrack *ct; - int err = 0; - - /* caller guarantees that those three CTA_EXPECT_* exist */ - err = ctnetlink_parse_tuple(cda, &tuple, CTA_EXPECT_TUPLE); - if (err < 0) - return err; - err = ctnetlink_parse_tuple(cda, &mask, CTA_EXPECT_MASK); - if (err < 0) - return err; - err = ctnetlink_parse_tuple(cda, &master_tuple, CTA_EXPECT_MASTER); - if (err < 0) - return err; - - /* Look for master conntrack of this expectation */ - h = ip_conntrack_find_get(&master_tuple, NULL); - if (!h) - return -ENOENT; - ct = tuplehash_to_ctrack(h); - - if (!ct->helper) { - /* such conntrack hasn't got any helper, abort */ - err = -EINVAL; - goto out; - } - - exp = ip_conntrack_expect_alloc(ct); - if (!exp) { - err = -ENOMEM; - goto out; - } - - exp->expectfn = NULL; - exp->flags = 0; - exp->master = ct; - memcpy(&exp->tuple, &tuple, sizeof(struct ip_conntrack_tuple)); - memcpy(&exp->mask, &mask, sizeof(struct ip_conntrack_tuple)); - - err = ip_conntrack_expect_related(exp); - ip_conntrack_expect_put(exp); - -out: - ip_conntrack_put(tuplehash_to_ctrack(h)); - return err; -} - -static int -ctnetlink_new_expect(struct sock *ctnl, struct sk_buff *skb, - struct nlmsghdr *nlh, struct nfattr *cda[], int *errp) -{ - struct ip_conntrack_tuple tuple; - struct ip_conntrack_expect *exp; - int err = 0; - - if (nfattr_bad_size(cda, CTA_EXPECT_MAX, cta_min_exp)) - return -EINVAL; - - if (!cda[CTA_EXPECT_TUPLE-1] - || !cda[CTA_EXPECT_MASK-1] - || !cda[CTA_EXPECT_MASTER-1]) - return -EINVAL; - - err = ctnetlink_parse_tuple(cda, &tuple, CTA_EXPECT_TUPLE); - if (err < 0) - return err; - - write_lock_bh(&ip_conntrack_lock); - exp = __ip_conntrack_expect_find(&tuple); - - if (!exp) { - write_unlock_bh(&ip_conntrack_lock); - err = -ENOENT; - if (nlh->nlmsg_flags & NLM_F_CREATE) - err = ctnetlink_create_expect(cda); - return err; - } - - err = -EEXIST; - if (!(nlh->nlmsg_flags & NLM_F_EXCL)) - err = ctnetlink_change_expect(exp, cda); - write_unlock_bh(&ip_conntrack_lock); - - return err; -} - -#ifdef CONFIG_IP_NF_CONNTRACK_EVENTS -static struct notifier_block ctnl_notifier = { - .notifier_call = ctnetlink_conntrack_event, -}; - -static struct notifier_block ctnl_notifier_exp = { - .notifier_call = ctnetlink_expect_event, -}; -#endif - -static struct nfnl_callback ctnl_cb[IPCTNL_MSG_MAX] = { - [IPCTNL_MSG_CT_NEW] = { .call = ctnetlink_new_conntrack, - .attr_count = CTA_MAX, }, - [IPCTNL_MSG_CT_GET] = { .call = ctnetlink_get_conntrack, - .attr_count = CTA_MAX, }, - [IPCTNL_MSG_CT_DELETE] = { .call = ctnetlink_del_conntrack, - .attr_count = CTA_MAX, }, - [IPCTNL_MSG_CT_GET_CTRZERO] = { .call = ctnetlink_get_conntrack, - .attr_count = CTA_MAX, }, -}; - -static struct nfnl_callback ctnl_exp_cb[IPCTNL_MSG_EXP_MAX] = { - [IPCTNL_MSG_EXP_GET] = { .call = ctnetlink_get_expect, - .attr_count = CTA_EXPECT_MAX, }, - [IPCTNL_MSG_EXP_NEW] = { .call = ctnetlink_new_expect, - .attr_count = CTA_EXPECT_MAX, }, - [IPCTNL_MSG_EXP_DELETE] = { .call = ctnetlink_del_expect, - .attr_count = CTA_EXPECT_MAX, }, -}; - -static struct nfnetlink_subsystem ctnl_subsys = { - .name = "conntrack", - .subsys_id = NFNL_SUBSYS_CTNETLINK, - .cb_count = IPCTNL_MSG_MAX, - .cb = ctnl_cb, -}; - -static struct nfnetlink_subsystem ctnl_exp_subsys = { - .name = "conntrack_expect", - .subsys_id = NFNL_SUBSYS_CTNETLINK_EXP, - .cb_count = IPCTNL_MSG_EXP_MAX, - .cb = ctnl_exp_cb, -}; - -MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_CTNETLINK); -MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_CTNETLINK_EXP); - -static int __init ctnetlink_init(void) -{ - int ret; - - printk("ctnetlink v%s: registering with nfnetlink.\n", version); - ret = nfnetlink_subsys_register(&ctnl_subsys); - if (ret < 0) { - printk("ctnetlink_init: cannot register with nfnetlink.\n"); - goto err_out; - } - - ret = nfnetlink_subsys_register(&ctnl_exp_subsys); - if (ret < 0) { - printk("ctnetlink_init: cannot register exp with nfnetlink.\n"); - goto err_unreg_subsys; - } - -#ifdef CONFIG_IP_NF_CONNTRACK_EVENTS - ret = ip_conntrack_register_notifier(&ctnl_notifier); - if (ret < 0) { - printk("ctnetlink_init: cannot register notifier.\n"); - goto err_unreg_exp_subsys; - } - - ret = ip_conntrack_expect_register_notifier(&ctnl_notifier_exp); - if (ret < 0) { - printk("ctnetlink_init: cannot expect register notifier.\n"); - goto err_unreg_notifier; - } -#endif - - return 0; - -#ifdef CONFIG_IP_NF_CONNTRACK_EVENTS -err_unreg_notifier: - ip_conntrack_unregister_notifier(&ctnl_notifier); -err_unreg_exp_subsys: - nfnetlink_subsys_unregister(&ctnl_exp_subsys); -#endif -err_unreg_subsys: - nfnetlink_subsys_unregister(&ctnl_subsys); -err_out: - return ret; -} - -static void __exit ctnetlink_exit(void) -{ - printk("ctnetlink: unregistering from nfnetlink.\n"); - -#ifdef CONFIG_IP_NF_CONNTRACK_EVENTS - ip_conntrack_expect_unregister_notifier(&ctnl_notifier_exp); - ip_conntrack_unregister_notifier(&ctnl_notifier); -#endif - - nfnetlink_subsys_unregister(&ctnl_exp_subsys); - nfnetlink_subsys_unregister(&ctnl_subsys); - return; -} - -module_init(ctnetlink_init); -module_exit(ctnetlink_exit); diff --git a/net/ipv4/netfilter/ip_conntrack_proto_generic.c b/net/ipv4/netfilter/ip_conntrack_proto_generic.c deleted file mode 100644 index 88af82e98658..000000000000 --- a/net/ipv4/netfilter/ip_conntrack_proto_generic.c +++ /dev/null @@ -1,74 +0,0 @@ -/* (C) 1999-2001 Paul `Rusty' Russell - * (C) 2002-2004 Netfilter Core Team - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include - -unsigned int ip_ct_generic_timeout __read_mostly = 600*HZ; - -static int generic_pkt_to_tuple(const struct sk_buff *skb, - unsigned int dataoff, - struct ip_conntrack_tuple *tuple) -{ - tuple->src.u.all = 0; - tuple->dst.u.all = 0; - - return 1; -} - -static int generic_invert_tuple(struct ip_conntrack_tuple *tuple, - const struct ip_conntrack_tuple *orig) -{ - tuple->src.u.all = 0; - tuple->dst.u.all = 0; - - return 1; -} - -/* Print out the per-protocol part of the tuple. */ -static int generic_print_tuple(struct seq_file *s, - const struct ip_conntrack_tuple *tuple) -{ - return 0; -} - -/* Print out the private part of the conntrack. */ -static int generic_print_conntrack(struct seq_file *s, - const struct ip_conntrack *state) -{ - return 0; -} - -/* Returns verdict for packet, or -1 for invalid. */ -static int packet(struct ip_conntrack *conntrack, - const struct sk_buff *skb, - enum ip_conntrack_info ctinfo) -{ - ip_ct_refresh_acct(conntrack, ctinfo, skb, ip_ct_generic_timeout); - return NF_ACCEPT; -} - -/* Called when a new connection for this protocol found. */ -static int new(struct ip_conntrack *conntrack, const struct sk_buff *skb) -{ - return 1; -} - -struct ip_conntrack_protocol ip_conntrack_generic_protocol = -{ - .proto = 0, - .name = "unknown", - .pkt_to_tuple = generic_pkt_to_tuple, - .invert_tuple = generic_invert_tuple, - .print_tuple = generic_print_tuple, - .print_conntrack = generic_print_conntrack, - .packet = packet, - .new = new, -}; diff --git a/net/ipv4/netfilter/ip_conntrack_proto_gre.c b/net/ipv4/netfilter/ip_conntrack_proto_gre.c deleted file mode 100644 index ac1c49ef36a9..000000000000 --- a/net/ipv4/netfilter/ip_conntrack_proto_gre.c +++ /dev/null @@ -1,328 +0,0 @@ -/* - * ip_conntrack_proto_gre.c - Version 3.0 - * - * Connection tracking protocol helper module for GRE. - * - * GRE is a generic encapsulation protocol, which is generally not very - * suited for NAT, as it has no protocol-specific part as port numbers. - * - * It has an optional key field, which may help us distinguishing two - * connections between the same two hosts. - * - * GRE is defined in RFC 1701 and RFC 1702, as well as RFC 2784 - * - * PPTP is built on top of a modified version of GRE, and has a mandatory - * field called "CallID", which serves us for the same purpose as the key - * field in plain GRE. - * - * Documentation about PPTP can be found in RFC 2637 - * - * (C) 2000-2005 by Harald Welte - * - * Development of this code funded by Astaro AG (http://www.astaro.com/) - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static DEFINE_RWLOCK(ip_ct_gre_lock); - -#include -#include -#include - -#include -#include - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Harald Welte "); -MODULE_DESCRIPTION("netfilter connection tracking protocol helper for GRE"); - -/* shamelessly stolen from ip_conntrack_proto_udp.c */ -#define GRE_TIMEOUT (30*HZ) -#define GRE_STREAM_TIMEOUT (180*HZ) - -#if 0 -#define DEBUGP(format, args...) printk(KERN_DEBUG "%s:%s: " format, __FILE__, __FUNCTION__, ## args) -#define DUMP_TUPLE_GRE(x) printk("%u.%u.%u.%u:0x%x -> %u.%u.%u.%u:0x%x\n", \ - NIPQUAD((x)->src.ip), ntohs((x)->src.u.gre.key), \ - NIPQUAD((x)->dst.ip), ntohs((x)->dst.u.gre.key)) -#else -#define DEBUGP(x, args...) -#define DUMP_TUPLE_GRE(x) -#endif - -/* GRE KEYMAP HANDLING FUNCTIONS */ -static LIST_HEAD(gre_keymap_list); - -static inline int gre_key_cmpfn(const struct ip_ct_gre_keymap *km, - const struct ip_conntrack_tuple *t) -{ - return ((km->tuple.src.ip == t->src.ip) && - (km->tuple.dst.ip == t->dst.ip) && - (km->tuple.dst.protonum == t->dst.protonum) && - (km->tuple.dst.u.all == t->dst.u.all)); -} - -/* look up the source key for a given tuple */ -static __be16 gre_keymap_lookup(struct ip_conntrack_tuple *t) -{ - struct ip_ct_gre_keymap *km; - __be16 key = 0; - - read_lock_bh(&ip_ct_gre_lock); - list_for_each_entry(km, &gre_keymap_list, list) { - if (gre_key_cmpfn(km, t)) { - key = km->tuple.src.u.gre.key; - break; - } - } - read_unlock_bh(&ip_ct_gre_lock); - - DEBUGP("lookup src key 0x%x up key for ", key); - DUMP_TUPLE_GRE(t); - - return key; -} - -/* add a single keymap entry, associate with specified master ct */ -int -ip_ct_gre_keymap_add(struct ip_conntrack *ct, - struct ip_conntrack_tuple *t, int reply) -{ - struct ip_ct_gre_keymap **exist_km, *km; - - if (!ct->helper || strcmp(ct->helper->name, "pptp")) { - DEBUGP("refusing to add GRE keymap to non-pptp session\n"); - return -1; - } - - if (!reply) - exist_km = &ct->help.ct_pptp_info.keymap_orig; - else - exist_km = &ct->help.ct_pptp_info.keymap_reply; - - if (*exist_km) { - /* check whether it's a retransmission */ - list_for_each_entry(km, &gre_keymap_list, list) { - if (gre_key_cmpfn(km, t) && km == *exist_km) - return 0; - } - DEBUGP("trying to override keymap_%s for ct %p\n", - reply? "reply":"orig", ct); - return -EEXIST; - } - - km = kmalloc(sizeof(*km), GFP_ATOMIC); - if (!km) - return -ENOMEM; - - memcpy(&km->tuple, t, sizeof(*t)); - *exist_km = km; - - DEBUGP("adding new entry %p: ", km); - DUMP_TUPLE_GRE(&km->tuple); - - write_lock_bh(&ip_ct_gre_lock); - list_add_tail(&km->list, &gre_keymap_list); - write_unlock_bh(&ip_ct_gre_lock); - - return 0; -} - -/* destroy the keymap entries associated with specified master ct */ -void ip_ct_gre_keymap_destroy(struct ip_conntrack *ct) -{ - DEBUGP("entering for ct %p\n", ct); - - if (!ct->helper || strcmp(ct->helper->name, "pptp")) { - DEBUGP("refusing to destroy GRE keymap to non-pptp session\n"); - return; - } - - write_lock_bh(&ip_ct_gre_lock); - if (ct->help.ct_pptp_info.keymap_orig) { - DEBUGP("removing %p from list\n", - ct->help.ct_pptp_info.keymap_orig); - list_del(&ct->help.ct_pptp_info.keymap_orig->list); - kfree(ct->help.ct_pptp_info.keymap_orig); - ct->help.ct_pptp_info.keymap_orig = NULL; - } - if (ct->help.ct_pptp_info.keymap_reply) { - DEBUGP("removing %p from list\n", - ct->help.ct_pptp_info.keymap_reply); - list_del(&ct->help.ct_pptp_info.keymap_reply->list); - kfree(ct->help.ct_pptp_info.keymap_reply); - ct->help.ct_pptp_info.keymap_reply = NULL; - } - write_unlock_bh(&ip_ct_gre_lock); -} - - -/* PUBLIC CONNTRACK PROTO HELPER FUNCTIONS */ - -/* invert gre part of tuple */ -static int gre_invert_tuple(struct ip_conntrack_tuple *tuple, - const struct ip_conntrack_tuple *orig) -{ - tuple->dst.u.gre.key = orig->src.u.gre.key; - tuple->src.u.gre.key = orig->dst.u.gre.key; - - return 1; -} - -/* gre hdr info to tuple */ -static int gre_pkt_to_tuple(const struct sk_buff *skb, - unsigned int dataoff, - struct ip_conntrack_tuple *tuple) -{ - struct gre_hdr_pptp _pgrehdr, *pgrehdr; - __be16 srckey; - struct gre_hdr _grehdr, *grehdr; - - /* first only delinearize old RFC1701 GRE header */ - grehdr = skb_header_pointer(skb, dataoff, sizeof(_grehdr), &_grehdr); - if (!grehdr || grehdr->version != GRE_VERSION_PPTP) { - /* try to behave like "ip_conntrack_proto_generic" */ - tuple->src.u.all = 0; - tuple->dst.u.all = 0; - return 1; - } - - /* PPTP header is variable length, only need up to the call_id field */ - pgrehdr = skb_header_pointer(skb, dataoff, 8, &_pgrehdr); - if (!pgrehdr) - return 1; - - if (ntohs(grehdr->protocol) != GRE_PROTOCOL_PPTP) { - DEBUGP("GRE_VERSION_PPTP but unknown proto\n"); - return 0; - } - - tuple->dst.u.gre.key = pgrehdr->call_id; - srckey = gre_keymap_lookup(tuple); - tuple->src.u.gre.key = srckey; - - return 1; -} - -/* print gre part of tuple */ -static int gre_print_tuple(struct seq_file *s, - const struct ip_conntrack_tuple *tuple) -{ - return seq_printf(s, "srckey=0x%x dstkey=0x%x ", - ntohs(tuple->src.u.gre.key), - ntohs(tuple->dst.u.gre.key)); -} - -/* print private data for conntrack */ -static int gre_print_conntrack(struct seq_file *s, - const struct ip_conntrack *ct) -{ - return seq_printf(s, "timeout=%u, stream_timeout=%u ", - (ct->proto.gre.timeout / HZ), - (ct->proto.gre.stream_timeout / HZ)); -} - -/* Returns verdict for packet, and may modify conntrack */ -static int gre_packet(struct ip_conntrack *ct, - const struct sk_buff *skb, - enum ip_conntrack_info conntrackinfo) -{ - /* If we've seen traffic both ways, this is a GRE connection. - * Extend timeout. */ - if (ct->status & IPS_SEEN_REPLY) { - ip_ct_refresh_acct(ct, conntrackinfo, skb, - ct->proto.gre.stream_timeout); - /* Also, more likely to be important, and not a probe. */ - set_bit(IPS_ASSURED_BIT, &ct->status); - ip_conntrack_event_cache(IPCT_STATUS, skb); - } else - ip_ct_refresh_acct(ct, conntrackinfo, skb, - ct->proto.gre.timeout); - - return NF_ACCEPT; -} - -/* Called when a new connection for this protocol found. */ -static int gre_new(struct ip_conntrack *ct, - const struct sk_buff *skb) -{ - DEBUGP(": "); - DUMP_TUPLE_GRE(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple); - - /* initialize to sane value. Ideally a conntrack helper - * (e.g. in case of pptp) is increasing them */ - ct->proto.gre.stream_timeout = GRE_STREAM_TIMEOUT; - ct->proto.gre.timeout = GRE_TIMEOUT; - - return 1; -} - -/* Called when a conntrack entry has already been removed from the hashes - * and is about to be deleted from memory */ -static void gre_destroy(struct ip_conntrack *ct) -{ - struct ip_conntrack *master = ct->master; - DEBUGP(" entering\n"); - - if (!master) - DEBUGP("no master !?!\n"); - else - ip_ct_gre_keymap_destroy(master); -} - -/* protocol helper struct */ -static struct ip_conntrack_protocol gre = { - .proto = IPPROTO_GRE, - .name = "gre", - .pkt_to_tuple = gre_pkt_to_tuple, - .invert_tuple = gre_invert_tuple, - .print_tuple = gre_print_tuple, - .print_conntrack = gre_print_conntrack, - .packet = gre_packet, - .new = gre_new, - .destroy = gre_destroy, - .me = THIS_MODULE, -#if defined(CONFIG_IP_NF_CONNTRACK_NETLINK) || \ - defined(CONFIG_IP_NF_CONNTRACK_NETLINK_MODULE) - .tuple_to_nfattr = ip_ct_port_tuple_to_nfattr, - .nfattr_to_tuple = ip_ct_port_nfattr_to_tuple, -#endif -}; - -/* ip_conntrack_proto_gre initialization */ -int __init ip_ct_proto_gre_init(void) -{ - return ip_conntrack_protocol_register(&gre); -} - -/* This cannot be __exit, as it is invoked from ip_conntrack_helper_pptp.c's - * init() code on errors. - */ -void ip_ct_proto_gre_fini(void) -{ - struct list_head *pos, *n; - - /* delete all keymap entries */ - write_lock_bh(&ip_ct_gre_lock); - list_for_each_safe(pos, n, &gre_keymap_list) { - DEBUGP("deleting keymap %p at module unload time\n", pos); - list_del(pos); - kfree(pos); - } - write_unlock_bh(&ip_ct_gre_lock); - - ip_conntrack_protocol_unregister(&gre); -} - -EXPORT_SYMBOL(ip_ct_gre_keymap_add); -EXPORT_SYMBOL(ip_ct_gre_keymap_destroy); diff --git a/net/ipv4/netfilter/ip_conntrack_proto_icmp.c b/net/ipv4/netfilter/ip_conntrack_proto_icmp.c deleted file mode 100644 index e253f3ee52d0..000000000000 --- a/net/ipv4/netfilter/ip_conntrack_proto_icmp.c +++ /dev/null @@ -1,315 +0,0 @@ -/* (C) 1999-2001 Paul `Rusty' Russell - * (C) 2002-2004 Netfilter Core Team - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -unsigned int ip_ct_icmp_timeout __read_mostly = 30*HZ; - -#if 0 -#define DEBUGP printk -#else -#define DEBUGP(format, args...) -#endif - -static int icmp_pkt_to_tuple(const struct sk_buff *skb, - unsigned int dataoff, - struct ip_conntrack_tuple *tuple) -{ - struct icmphdr _hdr, *hp; - - hp = skb_header_pointer(skb, dataoff, sizeof(_hdr), &_hdr); - if (hp == NULL) - return 0; - - tuple->dst.u.icmp.type = hp->type; - tuple->src.u.icmp.id = hp->un.echo.id; - tuple->dst.u.icmp.code = hp->code; - - return 1; -} - -/* Add 1; spaces filled with 0. */ -static const u_int8_t invmap[] = { - [ICMP_ECHO] = ICMP_ECHOREPLY + 1, - [ICMP_ECHOREPLY] = ICMP_ECHO + 1, - [ICMP_TIMESTAMP] = ICMP_TIMESTAMPREPLY + 1, - [ICMP_TIMESTAMPREPLY] = ICMP_TIMESTAMP + 1, - [ICMP_INFO_REQUEST] = ICMP_INFO_REPLY + 1, - [ICMP_INFO_REPLY] = ICMP_INFO_REQUEST + 1, - [ICMP_ADDRESS] = ICMP_ADDRESSREPLY + 1, - [ICMP_ADDRESSREPLY] = ICMP_ADDRESS + 1 -}; - -static int icmp_invert_tuple(struct ip_conntrack_tuple *tuple, - const struct ip_conntrack_tuple *orig) -{ - if (orig->dst.u.icmp.type >= sizeof(invmap) - || !invmap[orig->dst.u.icmp.type]) - return 0; - - tuple->src.u.icmp.id = orig->src.u.icmp.id; - tuple->dst.u.icmp.type = invmap[orig->dst.u.icmp.type] - 1; - tuple->dst.u.icmp.code = orig->dst.u.icmp.code; - return 1; -} - -/* Print out the per-protocol part of the tuple. */ -static int icmp_print_tuple(struct seq_file *s, - const struct ip_conntrack_tuple *tuple) -{ - return seq_printf(s, "type=%u code=%u id=%u ", - tuple->dst.u.icmp.type, - tuple->dst.u.icmp.code, - ntohs(tuple->src.u.icmp.id)); -} - -/* Print out the private part of the conntrack. */ -static int icmp_print_conntrack(struct seq_file *s, - const struct ip_conntrack *conntrack) -{ - return 0; -} - -/* Returns verdict for packet, or -1 for invalid. */ -static int icmp_packet(struct ip_conntrack *ct, - const struct sk_buff *skb, - enum ip_conntrack_info ctinfo) -{ - /* Try to delete connection immediately after all replies: - won't actually vanish as we still have skb, and del_timer - means this will only run once even if count hits zero twice - (theoretically possible with SMP) */ - if (CTINFO2DIR(ctinfo) == IP_CT_DIR_REPLY) { - if (atomic_dec_and_test(&ct->proto.icmp.count) - && del_timer(&ct->timeout)) - ct->timeout.function((unsigned long)ct); - } else { - atomic_inc(&ct->proto.icmp.count); - ip_conntrack_event_cache(IPCT_PROTOINFO_VOLATILE, skb); - ip_ct_refresh_acct(ct, ctinfo, skb, ip_ct_icmp_timeout); - } - - return NF_ACCEPT; -} - -/* Called when a new connection for this protocol found. */ -static int icmp_new(struct ip_conntrack *conntrack, - const struct sk_buff *skb) -{ - static const u_int8_t valid_new[] = { - [ICMP_ECHO] = 1, - [ICMP_TIMESTAMP] = 1, - [ICMP_INFO_REQUEST] = 1, - [ICMP_ADDRESS] = 1 - }; - - if (conntrack->tuplehash[0].tuple.dst.u.icmp.type >= sizeof(valid_new) - || !valid_new[conntrack->tuplehash[0].tuple.dst.u.icmp.type]) { - /* Can't create a new ICMP `conn' with this. */ - DEBUGP("icmp: can't create new conn with type %u\n", - conntrack->tuplehash[0].tuple.dst.u.icmp.type); - DUMP_TUPLE(&conntrack->tuplehash[0].tuple); - return 0; - } - atomic_set(&conntrack->proto.icmp.count, 0); - return 1; -} - -static int -icmp_error_message(struct sk_buff *skb, - enum ip_conntrack_info *ctinfo, - unsigned int hooknum) -{ - struct ip_conntrack_tuple innertuple, origtuple; - struct { - struct icmphdr icmp; - struct iphdr ip; - } _in, *inside; - struct ip_conntrack_protocol *innerproto; - struct ip_conntrack_tuple_hash *h; - int dataoff; - - IP_NF_ASSERT(skb->nfct == NULL); - - /* Not enough header? */ - inside = skb_header_pointer(skb, ip_hdrlen(skb), sizeof(_in), &_in); - if (inside == NULL) - return -NF_ACCEPT; - - /* Ignore ICMP's containing fragments (shouldn't happen) */ - if (inside->ip.frag_off & htons(IP_OFFSET)) { - DEBUGP("icmp_error_track: fragment of proto %u\n", - inside->ip.protocol); - return -NF_ACCEPT; - } - - innerproto = ip_conntrack_proto_find_get(inside->ip.protocol); - dataoff = ip_hdrlen(skb) + sizeof(inside->icmp) + inside->ip.ihl * 4; - /* Are they talking about one of our connections? */ - if (!ip_ct_get_tuple(&inside->ip, skb, dataoff, &origtuple, innerproto)) { - DEBUGP("icmp_error: ! get_tuple p=%u", inside->ip.protocol); - ip_conntrack_proto_put(innerproto); - return -NF_ACCEPT; - } - - /* Ordinarily, we'd expect the inverted tupleproto, but it's - been preserved inside the ICMP. */ - if (!ip_ct_invert_tuple(&innertuple, &origtuple, innerproto)) { - DEBUGP("icmp_error_track: Can't invert tuple\n"); - ip_conntrack_proto_put(innerproto); - return -NF_ACCEPT; - } - ip_conntrack_proto_put(innerproto); - - *ctinfo = IP_CT_RELATED; - - h = ip_conntrack_find_get(&innertuple, NULL); - if (!h) { - /* Locally generated ICMPs will match inverted if they - haven't been SNAT'ed yet */ - /* FIXME: NAT code has to handle half-done double NAT --RR */ - if (hooknum == NF_IP_LOCAL_OUT) - h = ip_conntrack_find_get(&origtuple, NULL); - - if (!h) { - DEBUGP("icmp_error_track: no match\n"); - return -NF_ACCEPT; - } - /* Reverse direction from that found */ - if (DIRECTION(h) != IP_CT_DIR_REPLY) - *ctinfo += IP_CT_IS_REPLY; - } else { - if (DIRECTION(h) == IP_CT_DIR_REPLY) - *ctinfo += IP_CT_IS_REPLY; - } - - /* Update skb to refer to this connection */ - skb->nfct = &tuplehash_to_ctrack(h)->ct_general; - skb->nfctinfo = *ctinfo; - return -NF_ACCEPT; -} - -/* Small and modified version of icmp_rcv */ -static int -icmp_error(struct sk_buff *skb, enum ip_conntrack_info *ctinfo, - unsigned int hooknum) -{ - struct icmphdr _ih, *icmph; - - /* Not enough header? */ - icmph = skb_header_pointer(skb, ip_hdrlen(skb), sizeof(_ih), &_ih); - if (icmph == NULL) { - if (LOG_INVALID(IPPROTO_ICMP)) - nf_log_packet(PF_INET, 0, skb, NULL, NULL, NULL, - "ip_ct_icmp: short packet "); - return -NF_ACCEPT; - } - - /* See ip_conntrack_proto_tcp.c */ - if (ip_conntrack_checksum && hooknum == NF_IP_PRE_ROUTING && - nf_ip_checksum(skb, hooknum, ip_hdrlen(skb), 0)) { - if (LOG_INVALID(IPPROTO_ICMP)) - nf_log_packet(PF_INET, 0, skb, NULL, NULL, NULL, - "ip_ct_icmp: bad ICMP checksum "); - return -NF_ACCEPT; - } - - /* - * 18 is the highest 'known' ICMP type. Anything else is a mystery - * - * RFC 1122: 3.2.2 Unknown ICMP messages types MUST be silently - * discarded. - */ - if (icmph->type > NR_ICMP_TYPES) { - if (LOG_INVALID(IPPROTO_ICMP)) - nf_log_packet(PF_INET, 0, skb, NULL, NULL, NULL, - "ip_ct_icmp: invalid ICMP type "); - return -NF_ACCEPT; - } - - /* Need to track icmp error message? */ - if (icmph->type != ICMP_DEST_UNREACH - && icmph->type != ICMP_SOURCE_QUENCH - && icmph->type != ICMP_TIME_EXCEEDED - && icmph->type != ICMP_PARAMETERPROB - && icmph->type != ICMP_REDIRECT) - return NF_ACCEPT; - - return icmp_error_message(skb, ctinfo, hooknum); -} - -#if defined(CONFIG_IP_NF_CONNTRACK_NETLINK) || \ - defined(CONFIG_IP_NF_CONNTRACK_NETLINK_MODULE) -static int icmp_tuple_to_nfattr(struct sk_buff *skb, - const struct ip_conntrack_tuple *t) -{ - NFA_PUT(skb, CTA_PROTO_ICMP_ID, sizeof(__be16), - &t->src.u.icmp.id); - NFA_PUT(skb, CTA_PROTO_ICMP_TYPE, sizeof(u_int8_t), - &t->dst.u.icmp.type); - NFA_PUT(skb, CTA_PROTO_ICMP_CODE, sizeof(u_int8_t), - &t->dst.u.icmp.code); - - return 0; - -nfattr_failure: - return -1; -} - -static int icmp_nfattr_to_tuple(struct nfattr *tb[], - struct ip_conntrack_tuple *tuple) -{ - if (!tb[CTA_PROTO_ICMP_TYPE-1] - || !tb[CTA_PROTO_ICMP_CODE-1] - || !tb[CTA_PROTO_ICMP_ID-1]) - return -EINVAL; - - tuple->dst.u.icmp.type = - *(u_int8_t *)NFA_DATA(tb[CTA_PROTO_ICMP_TYPE-1]); - tuple->dst.u.icmp.code = - *(u_int8_t *)NFA_DATA(tb[CTA_PROTO_ICMP_CODE-1]); - tuple->src.u.icmp.id = - *(__be16 *)NFA_DATA(tb[CTA_PROTO_ICMP_ID-1]); - - if (tuple->dst.u.icmp.type >= sizeof(invmap) - || !invmap[tuple->dst.u.icmp.type]) - return -EINVAL; - - return 0; -} -#endif - -struct ip_conntrack_protocol ip_conntrack_protocol_icmp = -{ - .proto = IPPROTO_ICMP, - .name = "icmp", - .pkt_to_tuple = icmp_pkt_to_tuple, - .invert_tuple = icmp_invert_tuple, - .print_tuple = icmp_print_tuple, - .print_conntrack = icmp_print_conntrack, - .packet = icmp_packet, - .new = icmp_new, - .error = icmp_error, -#if defined(CONFIG_IP_NF_CONNTRACK_NETLINK) || \ - defined(CONFIG_IP_NF_CONNTRACK_NETLINK_MODULE) - .tuple_to_nfattr = icmp_tuple_to_nfattr, - .nfattr_to_tuple = icmp_nfattr_to_tuple, -#endif -}; diff --git a/net/ipv4/netfilter/ip_conntrack_proto_sctp.c b/net/ipv4/netfilter/ip_conntrack_proto_sctp.c deleted file mode 100644 index 91d0c05c8e86..000000000000 --- a/net/ipv4/netfilter/ip_conntrack_proto_sctp.c +++ /dev/null @@ -1,659 +0,0 @@ -/* - * Connection tracking protocol helper module for SCTP. - * - * SCTP is defined in RFC 2960. References to various sections in this code - * are to this RFC. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -/* - * Added support for proc manipulation of timeouts. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#if 0 -#define DEBUGP(format, ...) printk(format, ## __VA_ARGS__) -#else -#define DEBUGP(format, args...) -#endif - -/* Protects conntrack->proto.sctp */ -static DEFINE_RWLOCK(sctp_lock); - -/* FIXME: Examine ipfilter's timeouts and conntrack transitions more - closely. They're more complex. --RR - - And so for me for SCTP :D -Kiran */ - -static const char *sctp_conntrack_names[] = { - "NONE", - "CLOSED", - "COOKIE_WAIT", - "COOKIE_ECHOED", - "ESTABLISHED", - "SHUTDOWN_SENT", - "SHUTDOWN_RECD", - "SHUTDOWN_ACK_SENT", -}; - -#define SECS * HZ -#define MINS * 60 SECS -#define HOURS * 60 MINS -#define DAYS * 24 HOURS - -static unsigned int ip_ct_sctp_timeout_closed __read_mostly = 10 SECS; -static unsigned int ip_ct_sctp_timeout_cookie_wait __read_mostly = 3 SECS; -static unsigned int ip_ct_sctp_timeout_cookie_echoed __read_mostly = 3 SECS; -static unsigned int ip_ct_sctp_timeout_established __read_mostly = 5 DAYS; -static unsigned int ip_ct_sctp_timeout_shutdown_sent __read_mostly = 300 SECS / 1000; -static unsigned int ip_ct_sctp_timeout_shutdown_recd __read_mostly = 300 SECS / 1000; -static unsigned int ip_ct_sctp_timeout_shutdown_ack_sent __read_mostly = 3 SECS; - -static const unsigned int * sctp_timeouts[] -= { NULL, /* SCTP_CONNTRACK_NONE */ - &ip_ct_sctp_timeout_closed, /* SCTP_CONNTRACK_CLOSED */ - &ip_ct_sctp_timeout_cookie_wait, /* SCTP_CONNTRACK_COOKIE_WAIT */ - &ip_ct_sctp_timeout_cookie_echoed, /* SCTP_CONNTRACK_COOKIE_ECHOED */ - &ip_ct_sctp_timeout_established, /* SCTP_CONNTRACK_ESTABLISHED */ - &ip_ct_sctp_timeout_shutdown_sent, /* SCTP_CONNTRACK_SHUTDOWN_SENT */ - &ip_ct_sctp_timeout_shutdown_recd, /* SCTP_CONNTRACK_SHUTDOWN_RECD */ - &ip_ct_sctp_timeout_shutdown_ack_sent /* SCTP_CONNTRACK_SHUTDOWN_ACK_SENT */ - }; - -#define sNO SCTP_CONNTRACK_NONE -#define sCL SCTP_CONNTRACK_CLOSED -#define sCW SCTP_CONNTRACK_COOKIE_WAIT -#define sCE SCTP_CONNTRACK_COOKIE_ECHOED -#define sES SCTP_CONNTRACK_ESTABLISHED -#define sSS SCTP_CONNTRACK_SHUTDOWN_SENT -#define sSR SCTP_CONNTRACK_SHUTDOWN_RECD -#define sSA SCTP_CONNTRACK_SHUTDOWN_ACK_SENT -#define sIV SCTP_CONNTRACK_MAX - -/* - These are the descriptions of the states: - -NOTE: These state names are tantalizingly similar to the states of an -SCTP endpoint. But the interpretation of the states is a little different, -considering that these are the states of the connection and not of an end -point. Please note the subtleties. -Kiran - -NONE - Nothing so far. -COOKIE WAIT - We have seen an INIT chunk in the original direction, or also - an INIT_ACK chunk in the reply direction. -COOKIE ECHOED - We have seen a COOKIE_ECHO chunk in the original direction. -ESTABLISHED - We have seen a COOKIE_ACK in the reply direction. -SHUTDOWN_SENT - We have seen a SHUTDOWN chunk in the original direction. -SHUTDOWN_RECD - We have seen a SHUTDOWN chunk in the reply directoin. -SHUTDOWN_ACK_SENT - We have seen a SHUTDOWN_ACK chunk in the direction opposite - to that of the SHUTDOWN chunk. -CLOSED - We have seen a SHUTDOWN_COMPLETE chunk in the direction of - the SHUTDOWN chunk. Connection is closed. -*/ - -/* TODO - - I have assumed that the first INIT is in the original direction. - This messes things when an INIT comes in the reply direction in CLOSED - state. - - Check the error type in the reply dir before transitioning from -cookie echoed to closed. - - Sec 5.2.4 of RFC 2960 - - Multi Homing support. -*/ - -/* SCTP conntrack state transitions */ -static const enum sctp_conntrack sctp_conntracks[2][9][SCTP_CONNTRACK_MAX] = { - { -/* ORIGINAL */ -/* sNO, sCL, sCW, sCE, sES, sSS, sSR, sSA */ -/* init */ {sCW, sCW, sCW, sCE, sES, sSS, sSR, sSA}, -/* init_ack */ {sCL, sCL, sCW, sCE, sES, sSS, sSR, sSA}, -/* abort */ {sCL, sCL, sCL, sCL, sCL, sCL, sCL, sCL}, -/* shutdown */ {sCL, sCL, sCW, sCE, sSS, sSS, sSR, sSA}, -/* shutdown_ack */ {sSA, sCL, sCW, sCE, sES, sSA, sSA, sSA}, -/* error */ {sCL, sCL, sCW, sCE, sES, sSS, sSR, sSA},/* Cant have Stale cookie*/ -/* cookie_echo */ {sCL, sCL, sCE, sCE, sES, sSS, sSR, sSA},/* 5.2.4 - Big TODO */ -/* cookie_ack */ {sCL, sCL, sCW, sCE, sES, sSS, sSR, sSA},/* Cant come in orig dir */ -/* shutdown_comp*/ {sCL, sCL, sCW, sCE, sES, sSS, sSR, sCL} - }, - { -/* REPLY */ -/* sNO, sCL, sCW, sCE, sES, sSS, sSR, sSA */ -/* init */ {sIV, sCL, sCW, sCE, sES, sSS, sSR, sSA},/* INIT in sCL Big TODO */ -/* init_ack */ {sIV, sCL, sCW, sCE, sES, sSS, sSR, sSA}, -/* abort */ {sIV, sCL, sCL, sCL, sCL, sCL, sCL, sCL}, -/* shutdown */ {sIV, sCL, sCW, sCE, sSR, sSS, sSR, sSA}, -/* shutdown_ack */ {sIV, sCL, sCW, sCE, sES, sSA, sSA, sSA}, -/* error */ {sIV, sCL, sCW, sCL, sES, sSS, sSR, sSA}, -/* cookie_echo */ {sIV, sCL, sCW, sCE, sES, sSS, sSR, sSA},/* Cant come in reply dir */ -/* cookie_ack */ {sIV, sCL, sCW, sES, sES, sSS, sSR, sSA}, -/* shutdown_comp*/ {sIV, sCL, sCW, sCE, sES, sSS, sSR, sCL} - } -}; - -static int sctp_pkt_to_tuple(const struct sk_buff *skb, - unsigned int dataoff, - struct ip_conntrack_tuple *tuple) -{ - sctp_sctphdr_t _hdr, *hp; - - DEBUGP(__FUNCTION__); - DEBUGP("\n"); - - /* Actually only need first 8 bytes. */ - hp = skb_header_pointer(skb, dataoff, 8, &_hdr); - if (hp == NULL) - return 0; - - tuple->src.u.sctp.port = hp->source; - tuple->dst.u.sctp.port = hp->dest; - return 1; -} - -static int sctp_invert_tuple(struct ip_conntrack_tuple *tuple, - const struct ip_conntrack_tuple *orig) -{ - DEBUGP(__FUNCTION__); - DEBUGP("\n"); - - tuple->src.u.sctp.port = orig->dst.u.sctp.port; - tuple->dst.u.sctp.port = orig->src.u.sctp.port; - return 1; -} - -/* Print out the per-protocol part of the tuple. */ -static int sctp_print_tuple(struct seq_file *s, - const struct ip_conntrack_tuple *tuple) -{ - DEBUGP(__FUNCTION__); - DEBUGP("\n"); - - return seq_printf(s, "sport=%hu dport=%hu ", - ntohs(tuple->src.u.sctp.port), - ntohs(tuple->dst.u.sctp.port)); -} - -/* Print out the private part of the conntrack. */ -static int sctp_print_conntrack(struct seq_file *s, - const struct ip_conntrack *conntrack) -{ - enum sctp_conntrack state; - - DEBUGP(__FUNCTION__); - DEBUGP("\n"); - - read_lock_bh(&sctp_lock); - state = conntrack->proto.sctp.state; - read_unlock_bh(&sctp_lock); - - return seq_printf(s, "%s ", sctp_conntrack_names[state]); -} - -#define for_each_sctp_chunk(skb, sch, _sch, offset, count) \ -for (offset = ip_hdrlen(skb) + sizeof(sctp_sctphdr_t), count = 0; \ - offset < skb->len && \ - (sch = skb_header_pointer(skb, offset, sizeof(_sch), &_sch)); \ - offset += (ntohs(sch->length) + 3) & ~3, count++) - -/* Some validity checks to make sure the chunks are fine */ -static int do_basic_checks(struct ip_conntrack *conntrack, - const struct sk_buff *skb, - char *map) -{ - u_int32_t offset, count; - sctp_chunkhdr_t _sch, *sch; - int flag; - - DEBUGP(__FUNCTION__); - DEBUGP("\n"); - - flag = 0; - - for_each_sctp_chunk (skb, sch, _sch, offset, count) { - DEBUGP("Chunk Num: %d Type: %d\n", count, sch->type); - - if (sch->type == SCTP_CID_INIT - || sch->type == SCTP_CID_INIT_ACK - || sch->type == SCTP_CID_SHUTDOWN_COMPLETE) { - flag = 1; - } - - /* - * Cookie Ack/Echo chunks not the first OR - * Init / Init Ack / Shutdown compl chunks not the only chunks - * OR zero-length. - */ - if (((sch->type == SCTP_CID_COOKIE_ACK - || sch->type == SCTP_CID_COOKIE_ECHO - || flag) - && count !=0) || !sch->length) { - DEBUGP("Basic checks failed\n"); - return 1; - } - - if (map) { - set_bit(sch->type, (void *)map); - } - } - - DEBUGP("Basic checks passed\n"); - return count == 0; -} - -static int new_state(enum ip_conntrack_dir dir, - enum sctp_conntrack cur_state, - int chunk_type) -{ - int i; - - DEBUGP(__FUNCTION__); - DEBUGP("\n"); - - DEBUGP("Chunk type: %d\n", chunk_type); - - switch (chunk_type) { - case SCTP_CID_INIT: - DEBUGP("SCTP_CID_INIT\n"); - i = 0; break; - case SCTP_CID_INIT_ACK: - DEBUGP("SCTP_CID_INIT_ACK\n"); - i = 1; break; - case SCTP_CID_ABORT: - DEBUGP("SCTP_CID_ABORT\n"); - i = 2; break; - case SCTP_CID_SHUTDOWN: - DEBUGP("SCTP_CID_SHUTDOWN\n"); - i = 3; break; - case SCTP_CID_SHUTDOWN_ACK: - DEBUGP("SCTP_CID_SHUTDOWN_ACK\n"); - i = 4; break; - case SCTP_CID_ERROR: - DEBUGP("SCTP_CID_ERROR\n"); - i = 5; break; - case SCTP_CID_COOKIE_ECHO: - DEBUGP("SCTP_CID_COOKIE_ECHO\n"); - i = 6; break; - case SCTP_CID_COOKIE_ACK: - DEBUGP("SCTP_CID_COOKIE_ACK\n"); - i = 7; break; - case SCTP_CID_SHUTDOWN_COMPLETE: - DEBUGP("SCTP_CID_SHUTDOWN_COMPLETE\n"); - i = 8; break; - default: - /* Other chunks like DATA, SACK, HEARTBEAT and - its ACK do not cause a change in state */ - DEBUGP("Unknown chunk type, Will stay in %s\n", - sctp_conntrack_names[cur_state]); - return cur_state; - } - - DEBUGP("dir: %d cur_state: %s chunk_type: %d new_state: %s\n", - dir, sctp_conntrack_names[cur_state], chunk_type, - sctp_conntrack_names[sctp_conntracks[dir][i][cur_state]]); - - return sctp_conntracks[dir][i][cur_state]; -} - -/* Returns verdict for packet, or -1 for invalid. */ -static int sctp_packet(struct ip_conntrack *conntrack, - const struct sk_buff *skb, - enum ip_conntrack_info ctinfo) -{ - enum sctp_conntrack newconntrack, oldsctpstate; - struct iphdr *iph = ip_hdr(skb); - sctp_sctphdr_t _sctph, *sh; - sctp_chunkhdr_t _sch, *sch; - u_int32_t offset, count; - char map[256 / sizeof (char)] = {0}; - - DEBUGP(__FUNCTION__); - DEBUGP("\n"); - - sh = skb_header_pointer(skb, iph->ihl * 4, sizeof(_sctph), &_sctph); - if (sh == NULL) - return -1; - - if (do_basic_checks(conntrack, skb, map) != 0) - return -1; - - /* Check the verification tag (Sec 8.5) */ - if (!test_bit(SCTP_CID_INIT, (void *)map) - && !test_bit(SCTP_CID_SHUTDOWN_COMPLETE, (void *)map) - && !test_bit(SCTP_CID_COOKIE_ECHO, (void *)map) - && !test_bit(SCTP_CID_ABORT, (void *)map) - && !test_bit(SCTP_CID_SHUTDOWN_ACK, (void *)map) - && (sh->vtag != conntrack->proto.sctp.vtag[CTINFO2DIR(ctinfo)])) { - DEBUGP("Verification tag check failed\n"); - return -1; - } - - oldsctpstate = newconntrack = SCTP_CONNTRACK_MAX; - for_each_sctp_chunk (skb, sch, _sch, offset, count) { - write_lock_bh(&sctp_lock); - - /* Special cases of Verification tag check (Sec 8.5.1) */ - if (sch->type == SCTP_CID_INIT) { - /* Sec 8.5.1 (A) */ - if (sh->vtag != 0) { - write_unlock_bh(&sctp_lock); - return -1; - } - } else if (sch->type == SCTP_CID_ABORT) { - /* Sec 8.5.1 (B) */ - if (!(sh->vtag == conntrack->proto.sctp.vtag[CTINFO2DIR(ctinfo)]) - && !(sh->vtag == conntrack->proto.sctp.vtag - [1 - CTINFO2DIR(ctinfo)])) { - write_unlock_bh(&sctp_lock); - return -1; - } - } else if (sch->type == SCTP_CID_SHUTDOWN_COMPLETE) { - /* Sec 8.5.1 (C) */ - if (!(sh->vtag == conntrack->proto.sctp.vtag[CTINFO2DIR(ctinfo)]) - && !(sh->vtag == conntrack->proto.sctp.vtag - [1 - CTINFO2DIR(ctinfo)] - && (sch->flags & 1))) { - write_unlock_bh(&sctp_lock); - return -1; - } - } else if (sch->type == SCTP_CID_COOKIE_ECHO) { - /* Sec 8.5.1 (D) */ - if (!(sh->vtag == conntrack->proto.sctp.vtag[CTINFO2DIR(ctinfo)])) { - write_unlock_bh(&sctp_lock); - return -1; - } - } - - oldsctpstate = conntrack->proto.sctp.state; - newconntrack = new_state(CTINFO2DIR(ctinfo), oldsctpstate, sch->type); - - /* Invalid */ - if (newconntrack == SCTP_CONNTRACK_MAX) { - DEBUGP("ip_conntrack_sctp: Invalid dir=%i ctype=%u conntrack=%u\n", - CTINFO2DIR(ctinfo), sch->type, oldsctpstate); - write_unlock_bh(&sctp_lock); - return -1; - } - - /* If it is an INIT or an INIT ACK note down the vtag */ - if (sch->type == SCTP_CID_INIT - || sch->type == SCTP_CID_INIT_ACK) { - sctp_inithdr_t _inithdr, *ih; - - ih = skb_header_pointer(skb, offset + sizeof(sctp_chunkhdr_t), - sizeof(_inithdr), &_inithdr); - if (ih == NULL) { - write_unlock_bh(&sctp_lock); - return -1; - } - DEBUGP("Setting vtag %x for dir %d\n", - ih->init_tag, !CTINFO2DIR(ctinfo)); - conntrack->proto.sctp.vtag[!CTINFO2DIR(ctinfo)] = ih->init_tag; - } - - conntrack->proto.sctp.state = newconntrack; - if (oldsctpstate != newconntrack) - ip_conntrack_event_cache(IPCT_PROTOINFO, skb); - write_unlock_bh(&sctp_lock); - } - - ip_ct_refresh_acct(conntrack, ctinfo, skb, *sctp_timeouts[newconntrack]); - - if (oldsctpstate == SCTP_CONNTRACK_COOKIE_ECHOED - && CTINFO2DIR(ctinfo) == IP_CT_DIR_REPLY - && newconntrack == SCTP_CONNTRACK_ESTABLISHED) { - DEBUGP("Setting assured bit\n"); - set_bit(IPS_ASSURED_BIT, &conntrack->status); - ip_conntrack_event_cache(IPCT_STATUS, skb); - } - - return NF_ACCEPT; -} - -/* Called when a new connection for this protocol found. */ -static int sctp_new(struct ip_conntrack *conntrack, - const struct sk_buff *skb) -{ - enum sctp_conntrack newconntrack; - struct iphdr *iph = ip_hdr(skb); - sctp_sctphdr_t _sctph, *sh; - sctp_chunkhdr_t _sch, *sch; - u_int32_t offset, count; - char map[256 / sizeof (char)] = {0}; - - DEBUGP(__FUNCTION__); - DEBUGP("\n"); - - sh = skb_header_pointer(skb, iph->ihl * 4, sizeof(_sctph), &_sctph); - if (sh == NULL) - return 0; - - if (do_basic_checks(conntrack, skb, map) != 0) - return 0; - - /* If an OOTB packet has any of these chunks discard (Sec 8.4) */ - if ((test_bit (SCTP_CID_ABORT, (void *)map)) - || (test_bit (SCTP_CID_SHUTDOWN_COMPLETE, (void *)map)) - || (test_bit (SCTP_CID_COOKIE_ACK, (void *)map))) { - return 0; - } - - newconntrack = SCTP_CONNTRACK_MAX; - for_each_sctp_chunk (skb, sch, _sch, offset, count) { - /* Don't need lock here: this conntrack not in circulation yet */ - newconntrack = new_state (IP_CT_DIR_ORIGINAL, - SCTP_CONNTRACK_NONE, sch->type); - - /* Invalid: delete conntrack */ - if (newconntrack == SCTP_CONNTRACK_MAX) { - DEBUGP("ip_conntrack_sctp: invalid new deleting.\n"); - return 0; - } - - /* Copy the vtag into the state info */ - if (sch->type == SCTP_CID_INIT) { - if (sh->vtag == 0) { - sctp_inithdr_t _inithdr, *ih; - - ih = skb_header_pointer(skb, offset + sizeof(sctp_chunkhdr_t), - sizeof(_inithdr), &_inithdr); - if (ih == NULL) - return 0; - - DEBUGP("Setting vtag %x for new conn\n", - ih->init_tag); - - conntrack->proto.sctp.vtag[IP_CT_DIR_REPLY] = - ih->init_tag; - } else { - /* Sec 8.5.1 (A) */ - return 0; - } - } - /* If it is a shutdown ack OOTB packet, we expect a return - shutdown complete, otherwise an ABORT Sec 8.4 (5) and (8) */ - else { - DEBUGP("Setting vtag %x for new conn OOTB\n", - sh->vtag); - conntrack->proto.sctp.vtag[IP_CT_DIR_REPLY] = sh->vtag; - } - - conntrack->proto.sctp.state = newconntrack; - } - - return 1; -} - -static struct ip_conntrack_protocol ip_conntrack_protocol_sctp = { - .proto = IPPROTO_SCTP, - .name = "sctp", - .pkt_to_tuple = sctp_pkt_to_tuple, - .invert_tuple = sctp_invert_tuple, - .print_tuple = sctp_print_tuple, - .print_conntrack = sctp_print_conntrack, - .packet = sctp_packet, - .new = sctp_new, - .destroy = NULL, - .me = THIS_MODULE, -#if defined(CONFIG_IP_NF_CONNTRACK_NETLINK) || \ - defined(CONFIG_IP_NF_CONNTRACK_NETLINK_MODULE) - .tuple_to_nfattr = ip_ct_port_tuple_to_nfattr, - .nfattr_to_tuple = ip_ct_port_nfattr_to_tuple, -#endif -}; - -#ifdef CONFIG_SYSCTL -static ctl_table ip_ct_sysctl_table[] = { - { - .ctl_name = NET_IPV4_NF_CONNTRACK_SCTP_TIMEOUT_CLOSED, - .procname = "ip_conntrack_sctp_timeout_closed", - .data = &ip_ct_sctp_timeout_closed, - .maxlen = sizeof(unsigned int), - .mode = 0644, - .proc_handler = &proc_dointvec_jiffies, - }, - { - .ctl_name = NET_IPV4_NF_CONNTRACK_SCTP_TIMEOUT_COOKIE_WAIT, - .procname = "ip_conntrack_sctp_timeout_cookie_wait", - .data = &ip_ct_sctp_timeout_cookie_wait, - .maxlen = sizeof(unsigned int), - .mode = 0644, - .proc_handler = &proc_dointvec_jiffies, - }, - { - .ctl_name = NET_IPV4_NF_CONNTRACK_SCTP_TIMEOUT_COOKIE_ECHOED, - .procname = "ip_conntrack_sctp_timeout_cookie_echoed", - .data = &ip_ct_sctp_timeout_cookie_echoed, - .maxlen = sizeof(unsigned int), - .mode = 0644, - .proc_handler = &proc_dointvec_jiffies, - }, - { - .ctl_name = NET_IPV4_NF_CONNTRACK_SCTP_TIMEOUT_ESTABLISHED, - .procname = "ip_conntrack_sctp_timeout_established", - .data = &ip_ct_sctp_timeout_established, - .maxlen = sizeof(unsigned int), - .mode = 0644, - .proc_handler = &proc_dointvec_jiffies, - }, - { - .ctl_name = NET_IPV4_NF_CONNTRACK_SCTP_TIMEOUT_SHUTDOWN_SENT, - .procname = "ip_conntrack_sctp_timeout_shutdown_sent", - .data = &ip_ct_sctp_timeout_shutdown_sent, - .maxlen = sizeof(unsigned int), - .mode = 0644, - .proc_handler = &proc_dointvec_jiffies, - }, - { - .ctl_name = NET_IPV4_NF_CONNTRACK_SCTP_TIMEOUT_SHUTDOWN_RECD, - .procname = "ip_conntrack_sctp_timeout_shutdown_recd", - .data = &ip_ct_sctp_timeout_shutdown_recd, - .maxlen = sizeof(unsigned int), - .mode = 0644, - .proc_handler = &proc_dointvec_jiffies, - }, - { - .ctl_name = NET_IPV4_NF_CONNTRACK_SCTP_TIMEOUT_SHUTDOWN_ACK_SENT, - .procname = "ip_conntrack_sctp_timeout_shutdown_ack_sent", - .data = &ip_ct_sctp_timeout_shutdown_ack_sent, - .maxlen = sizeof(unsigned int), - .mode = 0644, - .proc_handler = &proc_dointvec_jiffies, - }, - { .ctl_name = 0 } -}; - -static ctl_table ip_ct_netfilter_table[] = { - { - .ctl_name = NET_IPV4_NETFILTER, - .procname = "netfilter", - .mode = 0555, - .child = ip_ct_sysctl_table, - }, - { .ctl_name = 0 } -}; - -static ctl_table ip_ct_ipv4_table[] = { - { - .ctl_name = NET_IPV4, - .procname = "ipv4", - .mode = 0555, - .child = ip_ct_netfilter_table, - }, - { .ctl_name = 0 } -}; - -static ctl_table ip_ct_net_table[] = { - { - .ctl_name = CTL_NET, - .procname = "net", - .mode = 0555, - .child = ip_ct_ipv4_table, - }, - { .ctl_name = 0 } -}; - -static struct ctl_table_header *ip_ct_sysctl_header; -#endif - -static int __init ip_conntrack_proto_sctp_init(void) -{ - int ret; - - ret = ip_conntrack_protocol_register(&ip_conntrack_protocol_sctp); - if (ret) { - printk("ip_conntrack_proto_sctp: protocol register failed\n"); - goto out; - } - -#ifdef CONFIG_SYSCTL - ip_ct_sysctl_header = register_sysctl_table(ip_ct_net_table); - if (ip_ct_sysctl_header == NULL) { - ret = -ENOMEM; - printk("ip_conntrack_proto_sctp: can't register to sysctl.\n"); - goto cleanup; - } -#endif - - return ret; - -#ifdef CONFIG_SYSCTL - cleanup: - ip_conntrack_protocol_unregister(&ip_conntrack_protocol_sctp); -#endif - out: - DEBUGP("SCTP conntrack module loading %s\n", - ret ? "failed": "succeeded"); - return ret; -} - -static void __exit ip_conntrack_proto_sctp_fini(void) -{ - ip_conntrack_protocol_unregister(&ip_conntrack_protocol_sctp); -#ifdef CONFIG_SYSCTL - unregister_sysctl_table(ip_ct_sysctl_header); -#endif - DEBUGP("SCTP conntrack module unloaded\n"); -} - -module_init(ip_conntrack_proto_sctp_init); -module_exit(ip_conntrack_proto_sctp_fini); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Kiran Kumar Immidi"); -MODULE_DESCRIPTION("Netfilter connection tracking protocol helper for SCTP"); diff --git a/net/ipv4/netfilter/ip_conntrack_proto_tcp.c b/net/ipv4/netfilter/ip_conntrack_proto_tcp.c deleted file mode 100644 index d03436edfd93..000000000000 --- a/net/ipv4/netfilter/ip_conntrack_proto_tcp.c +++ /dev/null @@ -1,1163 +0,0 @@ -/* (C) 1999-2001 Paul `Rusty' Russell - * (C) 2002-2004 Netfilter Core Team - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Jozsef Kadlecsik : - * - Real stateful connection tracking - * - Modified state transitions table - * - Window scaling support added - * - SACK support added - * - * Willy Tarreau: - * - State table bugfixes - * - More robust state changes - * - Tuning timer parameters - * - * version 2.2 - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include - -#if 0 -#define DEBUGP printk -#define DEBUGP_VARS -#else -#define DEBUGP(format, args...) -#endif - -/* Protects conntrack->proto.tcp */ -static DEFINE_RWLOCK(tcp_lock); - -/* "Be conservative in what you do, - be liberal in what you accept from others." - If it's non-zero, we mark only out of window RST segments as INVALID. */ -int ip_ct_tcp_be_liberal __read_mostly = 0; - -/* If it is set to zero, we disable picking up already established - connections. */ -int ip_ct_tcp_loose __read_mostly = 1; - -/* Max number of the retransmitted packets without receiving an (acceptable) - ACK from the destination. If this number is reached, a shorter timer - will be started. */ -int ip_ct_tcp_max_retrans __read_mostly = 3; - - /* FIXME: Examine ipfilter's timeouts and conntrack transitions more - closely. They're more complex. --RR */ - -static const char *tcp_conntrack_names[] = { - "NONE", - "SYN_SENT", - "SYN_RECV", - "ESTABLISHED", - "FIN_WAIT", - "CLOSE_WAIT", - "LAST_ACK", - "TIME_WAIT", - "CLOSE", - "LISTEN" -}; - -#define SECS * HZ -#define MINS * 60 SECS -#define HOURS * 60 MINS -#define DAYS * 24 HOURS - -unsigned int ip_ct_tcp_timeout_syn_sent __read_mostly = 2 MINS; -unsigned int ip_ct_tcp_timeout_syn_recv __read_mostly = 60 SECS; -unsigned int ip_ct_tcp_timeout_established __read_mostly = 5 DAYS; -unsigned int ip_ct_tcp_timeout_fin_wait __read_mostly = 2 MINS; -unsigned int ip_ct_tcp_timeout_close_wait __read_mostly = 60 SECS; -unsigned int ip_ct_tcp_timeout_last_ack __read_mostly = 30 SECS; -unsigned int ip_ct_tcp_timeout_time_wait __read_mostly = 2 MINS; -unsigned int ip_ct_tcp_timeout_close __read_mostly = 10 SECS; - -/* RFC1122 says the R2 limit should be at least 100 seconds. - Linux uses 15 packets as limit, which corresponds - to ~13-30min depending on RTO. */ -unsigned int ip_ct_tcp_timeout_max_retrans __read_mostly = 5 MINS; - -static const unsigned int * tcp_timeouts[] -= { NULL, /* TCP_CONNTRACK_NONE */ - &ip_ct_tcp_timeout_syn_sent, /* TCP_CONNTRACK_SYN_SENT, */ - &ip_ct_tcp_timeout_syn_recv, /* TCP_CONNTRACK_SYN_RECV, */ - &ip_ct_tcp_timeout_established, /* TCP_CONNTRACK_ESTABLISHED, */ - &ip_ct_tcp_timeout_fin_wait, /* TCP_CONNTRACK_FIN_WAIT, */ - &ip_ct_tcp_timeout_close_wait, /* TCP_CONNTRACK_CLOSE_WAIT, */ - &ip_ct_tcp_timeout_last_ack, /* TCP_CONNTRACK_LAST_ACK, */ - &ip_ct_tcp_timeout_time_wait, /* TCP_CONNTRACK_TIME_WAIT, */ - &ip_ct_tcp_timeout_close, /* TCP_CONNTRACK_CLOSE, */ - NULL, /* TCP_CONNTRACK_LISTEN */ - }; - -#define sNO TCP_CONNTRACK_NONE -#define sSS TCP_CONNTRACK_SYN_SENT -#define sSR TCP_CONNTRACK_SYN_RECV -#define sES TCP_CONNTRACK_ESTABLISHED -#define sFW TCP_CONNTRACK_FIN_WAIT -#define sCW TCP_CONNTRACK_CLOSE_WAIT -#define sLA TCP_CONNTRACK_LAST_ACK -#define sTW TCP_CONNTRACK_TIME_WAIT -#define sCL TCP_CONNTRACK_CLOSE -#define sLI TCP_CONNTRACK_LISTEN -#define sIV TCP_CONNTRACK_MAX -#define sIG TCP_CONNTRACK_IGNORE - -/* What TCP flags are set from RST/SYN/FIN/ACK. */ -enum tcp_bit_set { - TCP_SYN_SET, - TCP_SYNACK_SET, - TCP_FIN_SET, - TCP_ACK_SET, - TCP_RST_SET, - TCP_NONE_SET, -}; - -/* - * The TCP state transition table needs a few words... - * - * We are the man in the middle. All the packets go through us - * but might get lost in transit to the destination. - * It is assumed that the destinations can't receive segments - * we haven't seen. - * - * The checked segment is in window, but our windows are *not* - * equivalent with the ones of the sender/receiver. We always - * try to guess the state of the current sender. - * - * The meaning of the states are: - * - * NONE: initial state - * SYN_SENT: SYN-only packet seen - * SYN_RECV: SYN-ACK packet seen - * ESTABLISHED: ACK packet seen - * FIN_WAIT: FIN packet seen - * CLOSE_WAIT: ACK seen (after FIN) - * LAST_ACK: FIN seen (after FIN) - * TIME_WAIT: last ACK seen - * CLOSE: closed connection - * - * LISTEN state is not used. - * - * Packets marked as IGNORED (sIG): - * if they may be either invalid or valid - * and the receiver may send back a connection - * closing RST or a SYN/ACK. - * - * Packets marked as INVALID (sIV): - * if they are invalid - * or we do not support the request (simultaneous open) - */ -static const enum tcp_conntrack tcp_conntracks[2][6][TCP_CONNTRACK_MAX] = { - { -/* ORIGINAL */ -/* sNO, sSS, sSR, sES, sFW, sCW, sLA, sTW, sCL, sLI */ -/*syn*/ { sSS, sSS, sIG, sIG, sIG, sIG, sIG, sSS, sSS, sIV }, -/* - * sNO -> sSS Initialize a new connection - * sSS -> sSS Retransmitted SYN - * sSR -> sIG Late retransmitted SYN? - * sES -> sIG Error: SYNs in window outside the SYN_SENT state - * are errors. Receiver will reply with RST - * and close the connection. - * Or we are not in sync and hold a dead connection. - * sFW -> sIG - * sCW -> sIG - * sLA -> sIG - * sTW -> sSS Reopened connection (RFC 1122). - * sCL -> sSS - */ -/* sNO, sSS, sSR, sES, sFW, sCW, sLA, sTW, sCL, sLI */ -/*synack*/ { sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV }, -/* - * A SYN/ACK from the client is always invalid: - * - either it tries to set up a simultaneous open, which is - * not supported; - * - or the firewall has just been inserted between the two hosts - * during the session set-up. The SYN will be retransmitted - * by the true client (or it'll time out). - */ -/* sNO, sSS, sSR, sES, sFW, sCW, sLA, sTW, sCL, sLI */ -/*fin*/ { sIV, sIV, sFW, sFW, sLA, sLA, sLA, sTW, sCL, sIV }, -/* - * sNO -> sIV Too late and no reason to do anything... - * sSS -> sIV Client migth not send FIN in this state: - * we enforce waiting for a SYN/ACK reply first. - * sSR -> sFW Close started. - * sES -> sFW - * sFW -> sLA FIN seen in both directions, waiting for - * the last ACK. - * Migth be a retransmitted FIN as well... - * sCW -> sLA - * sLA -> sLA Retransmitted FIN. Remain in the same state. - * sTW -> sTW - * sCL -> sCL - */ -/* sNO, sSS, sSR, sES, sFW, sCW, sLA, sTW, sCL, sLI */ -/*ack*/ { sES, sIV, sES, sES, sCW, sCW, sTW, sTW, sCL, sIV }, -/* - * sNO -> sES Assumed. - * sSS -> sIV ACK is invalid: we haven't seen a SYN/ACK yet. - * sSR -> sES Established state is reached. - * sES -> sES :-) - * sFW -> sCW Normal close request answered by ACK. - * sCW -> sCW - * sLA -> sTW Last ACK detected. - * sTW -> sTW Retransmitted last ACK. Remain in the same state. - * sCL -> sCL - */ -/* sNO, sSS, sSR, sES, sFW, sCW, sLA, sTW, sCL, sLI */ -/*rst*/ { sIV, sCL, sCL, sCL, sCL, sCL, sCL, sCL, sCL, sIV }, -/*none*/ { sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV } - }, - { -/* REPLY */ -/* sNO, sSS, sSR, sES, sFW, sCW, sLA, sTW, sCL, sLI */ -/*syn*/ { sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV }, -/* - * sNO -> sIV Never reached. - * sSS -> sIV Simultaneous open, not supported - * sSR -> sIV Simultaneous open, not supported. - * sES -> sIV Server may not initiate a connection. - * sFW -> sIV - * sCW -> sIV - * sLA -> sIV - * sTW -> sIV Reopened connection, but server may not do it. - * sCL -> sIV - */ -/* sNO, sSS, sSR, sES, sFW, sCW, sLA, sTW, sCL, sLI */ -/*synack*/ { sIV, sSR, sSR, sIG, sIG, sIG, sIG, sIG, sIG, sIV }, -/* - * sSS -> sSR Standard open. - * sSR -> sSR Retransmitted SYN/ACK. - * sES -> sIG Late retransmitted SYN/ACK? - * sFW -> sIG Might be SYN/ACK answering ignored SYN - * sCW -> sIG - * sLA -> sIG - * sTW -> sIG - * sCL -> sIG - */ -/* sNO, sSS, sSR, sES, sFW, sCW, sLA, sTW, sCL, sLI */ -/*fin*/ { sIV, sIV, sFW, sFW, sLA, sLA, sLA, sTW, sCL, sIV }, -/* - * sSS -> sIV Server might not send FIN in this state. - * sSR -> sFW Close started. - * sES -> sFW - * sFW -> sLA FIN seen in both directions. - * sCW -> sLA - * sLA -> sLA Retransmitted FIN. - * sTW -> sTW - * sCL -> sCL - */ -/* sNO, sSS, sSR, sES, sFW, sCW, sLA, sTW, sCL, sLI */ -/*ack*/ { sIV, sIG, sSR, sES, sCW, sCW, sTW, sTW, sCL, sIV }, -/* - * sSS -> sIG Might be a half-open connection. - * sSR -> sSR Might answer late resent SYN. - * sES -> sES :-) - * sFW -> sCW Normal close request answered by ACK. - * sCW -> sCW - * sLA -> sTW Last ACK detected. - * sTW -> sTW Retransmitted last ACK. - * sCL -> sCL - */ -/* sNO, sSS, sSR, sES, sFW, sCW, sLA, sTW, sCL, sLI */ -/*rst*/ { sIV, sCL, sCL, sCL, sCL, sCL, sCL, sCL, sCL, sIV }, -/*none*/ { sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV } - } -}; - -static int tcp_pkt_to_tuple(const struct sk_buff *skb, - unsigned int dataoff, - struct ip_conntrack_tuple *tuple) -{ - struct tcphdr _hdr, *hp; - - /* Actually only need first 8 bytes. */ - hp = skb_header_pointer(skb, dataoff, 8, &_hdr); - if (hp == NULL) - return 0; - - tuple->src.u.tcp.port = hp->source; - tuple->dst.u.tcp.port = hp->dest; - - return 1; -} - -static int tcp_invert_tuple(struct ip_conntrack_tuple *tuple, - const struct ip_conntrack_tuple *orig) -{ - tuple->src.u.tcp.port = orig->dst.u.tcp.port; - tuple->dst.u.tcp.port = orig->src.u.tcp.port; - return 1; -} - -/* Print out the per-protocol part of the tuple. */ -static int tcp_print_tuple(struct seq_file *s, - const struct ip_conntrack_tuple *tuple) -{ - return seq_printf(s, "sport=%hu dport=%hu ", - ntohs(tuple->src.u.tcp.port), - ntohs(tuple->dst.u.tcp.port)); -} - -/* Print out the private part of the conntrack. */ -static int tcp_print_conntrack(struct seq_file *s, - const struct ip_conntrack *conntrack) -{ - enum tcp_conntrack state; - - read_lock_bh(&tcp_lock); - state = conntrack->proto.tcp.state; - read_unlock_bh(&tcp_lock); - - return seq_printf(s, "%s ", tcp_conntrack_names[state]); -} - -#if defined(CONFIG_IP_NF_CONNTRACK_NETLINK) || \ - defined(CONFIG_IP_NF_CONNTRACK_NETLINK_MODULE) -static int tcp_to_nfattr(struct sk_buff *skb, struct nfattr *nfa, - const struct ip_conntrack *ct) -{ - struct nfattr *nest_parms; - - read_lock_bh(&tcp_lock); - nest_parms = NFA_NEST(skb, CTA_PROTOINFO_TCP); - NFA_PUT(skb, CTA_PROTOINFO_TCP_STATE, sizeof(u_int8_t), - &ct->proto.tcp.state); - read_unlock_bh(&tcp_lock); - - NFA_NEST_END(skb, nest_parms); - - return 0; - -nfattr_failure: - read_unlock_bh(&tcp_lock); - return -1; -} - -static const size_t cta_min_tcp[CTA_PROTOINFO_TCP_MAX] = { - [CTA_PROTOINFO_TCP_STATE-1] = sizeof(u_int8_t), -}; - -static int nfattr_to_tcp(struct nfattr *cda[], struct ip_conntrack *ct) -{ - struct nfattr *attr = cda[CTA_PROTOINFO_TCP-1]; - struct nfattr *tb[CTA_PROTOINFO_TCP_MAX]; - - /* updates could not contain anything about the private - * protocol info, in that case skip the parsing */ - if (!attr) - return 0; - - nfattr_parse_nested(tb, CTA_PROTOINFO_TCP_MAX, attr); - - if (nfattr_bad_size(tb, CTA_PROTOINFO_TCP_MAX, cta_min_tcp)) - return -EINVAL; - - if (!tb[CTA_PROTOINFO_TCP_STATE-1]) - return -EINVAL; - - write_lock_bh(&tcp_lock); - ct->proto.tcp.state = - *(u_int8_t *)NFA_DATA(tb[CTA_PROTOINFO_TCP_STATE-1]); - write_unlock_bh(&tcp_lock); - - return 0; -} -#endif - -static unsigned int get_conntrack_index(const struct tcphdr *tcph) -{ - if (tcph->rst) return TCP_RST_SET; - else if (tcph->syn) return (tcph->ack ? TCP_SYNACK_SET : TCP_SYN_SET); - else if (tcph->fin) return TCP_FIN_SET; - else if (tcph->ack) return TCP_ACK_SET; - else return TCP_NONE_SET; -} - -/* TCP connection tracking based on 'Real Stateful TCP Packet Filtering - in IP Filter' by Guido van Rooij. - - http://www.nluug.nl/events/sane2000/papers.html - http://www.iae.nl/users/guido/papers/tcp_filtering.ps.gz - - The boundaries and the conditions are changed according to RFC793: - the packet must intersect the window (i.e. segments may be - after the right or before the left edge) and thus receivers may ACK - segments after the right edge of the window. - - td_maxend = max(sack + max(win,1)) seen in reply packets - td_maxwin = max(max(win, 1)) + (sack - ack) seen in sent packets - td_maxwin += seq + len - sender.td_maxend - if seq + len > sender.td_maxend - td_end = max(seq + len) seen in sent packets - - I. Upper bound for valid data: seq <= sender.td_maxend - II. Lower bound for valid data: seq + len >= sender.td_end - receiver.td_maxwin - III. Upper bound for valid ack: sack <= receiver.td_end - IV. Lower bound for valid ack: ack >= receiver.td_end - MAXACKWINDOW - - where sack is the highest right edge of sack block found in the packet. - - The upper bound limit for a valid ack is not ignored - - we doesn't have to deal with fragments. -*/ - -static inline __u32 segment_seq_plus_len(__u32 seq, - size_t len, - struct iphdr *iph, - struct tcphdr *tcph) -{ - return (seq + len - (iph->ihl + tcph->doff)*4 - + (tcph->syn ? 1 : 0) + (tcph->fin ? 1 : 0)); -} - -/* Fixme: what about big packets? */ -#define MAXACKWINCONST 66000 -#define MAXACKWINDOW(sender) \ - ((sender)->td_maxwin > MAXACKWINCONST ? (sender)->td_maxwin \ - : MAXACKWINCONST) - -/* - * Simplified tcp_parse_options routine from tcp_input.c - */ -static void tcp_options(const struct sk_buff *skb, - struct iphdr *iph, - struct tcphdr *tcph, - struct ip_ct_tcp_state *state) -{ - unsigned char buff[(15 * 4) - sizeof(struct tcphdr)]; - unsigned char *ptr; - int length = (tcph->doff*4) - sizeof(struct tcphdr); - - if (!length) - return; - - ptr = skb_header_pointer(skb, - (iph->ihl * 4) + sizeof(struct tcphdr), - length, buff); - BUG_ON(ptr == NULL); - - state->td_scale = - state->flags = 0; - - while (length > 0) { - int opcode=*ptr++; - int opsize; - - switch (opcode) { - case TCPOPT_EOL: - return; - case TCPOPT_NOP: /* Ref: RFC 793 section 3.1 */ - length--; - continue; - default: - opsize=*ptr++; - if (opsize < 2) /* "silly options" */ - return; - if (opsize > length) - break; /* don't parse partial options */ - - if (opcode == TCPOPT_SACK_PERM - && opsize == TCPOLEN_SACK_PERM) - state->flags |= IP_CT_TCP_FLAG_SACK_PERM; - else if (opcode == TCPOPT_WINDOW - && opsize == TCPOLEN_WINDOW) { - state->td_scale = *(u_int8_t *)ptr; - - if (state->td_scale > 14) { - /* See RFC1323 */ - state->td_scale = 14; - } - state->flags |= - IP_CT_TCP_FLAG_WINDOW_SCALE; - } - ptr += opsize - 2; - length -= opsize; - } - } -} - -static void tcp_sack(const struct sk_buff *skb, - struct iphdr *iph, - struct tcphdr *tcph, - __u32 *sack) -{ - unsigned char buff[(15 * 4) - sizeof(struct tcphdr)]; - unsigned char *ptr; - int length = (tcph->doff*4) - sizeof(struct tcphdr); - __u32 tmp; - - if (!length) - return; - - ptr = skb_header_pointer(skb, - (iph->ihl * 4) + sizeof(struct tcphdr), - length, buff); - BUG_ON(ptr == NULL); - - /* Fast path for timestamp-only option */ - if (length == TCPOLEN_TSTAMP_ALIGNED*4 - && *(__be32 *)ptr == htonl((TCPOPT_NOP << 24) - | (TCPOPT_NOP << 16) - | (TCPOPT_TIMESTAMP << 8) - | TCPOLEN_TIMESTAMP)) - return; - - while (length > 0) { - int opcode=*ptr++; - int opsize, i; - - switch (opcode) { - case TCPOPT_EOL: - return; - case TCPOPT_NOP: /* Ref: RFC 793 section 3.1 */ - length--; - continue; - default: - opsize=*ptr++; - if (opsize < 2) /* "silly options" */ - return; - if (opsize > length) - break; /* don't parse partial options */ - - if (opcode == TCPOPT_SACK - && opsize >= (TCPOLEN_SACK_BASE - + TCPOLEN_SACK_PERBLOCK) - && !((opsize - TCPOLEN_SACK_BASE) - % TCPOLEN_SACK_PERBLOCK)) { - for (i = 0; - i < (opsize - TCPOLEN_SACK_BASE); - i += TCPOLEN_SACK_PERBLOCK) { - tmp = ntohl(*((__be32 *)(ptr+i)+1)); - - if (after(tmp, *sack)) - *sack = tmp; - } - return; - } - ptr += opsize - 2; - length -= opsize; - } - } -} - -static int tcp_in_window(struct ip_ct_tcp *state, - enum ip_conntrack_dir dir, - unsigned int index, - const struct sk_buff *skb, - struct iphdr *iph, - struct tcphdr *tcph) -{ - struct ip_ct_tcp_state *sender = &state->seen[dir]; - struct ip_ct_tcp_state *receiver = &state->seen[!dir]; - __u32 seq, ack, sack, end, win, swin; - int res; - - /* - * Get the required data from the packet. - */ - seq = ntohl(tcph->seq); - ack = sack = ntohl(tcph->ack_seq); - win = ntohs(tcph->window); - end = segment_seq_plus_len(seq, skb->len, iph, tcph); - - if (receiver->flags & IP_CT_TCP_FLAG_SACK_PERM) - tcp_sack(skb, iph, tcph, &sack); - - DEBUGP("tcp_in_window: START\n"); - DEBUGP("tcp_in_window: src=%u.%u.%u.%u:%hu dst=%u.%u.%u.%u:%hu " - "seq=%u ack=%u sack=%u win=%u end=%u\n", - NIPQUAD(iph->saddr), ntohs(tcph->source), - NIPQUAD(iph->daddr), ntohs(tcph->dest), - seq, ack, sack, win, end); - DEBUGP("tcp_in_window: sender end=%u maxend=%u maxwin=%u scale=%i " - "receiver end=%u maxend=%u maxwin=%u scale=%i\n", - sender->td_end, sender->td_maxend, sender->td_maxwin, - sender->td_scale, - receiver->td_end, receiver->td_maxend, receiver->td_maxwin, - receiver->td_scale); - - if (sender->td_end == 0) { - /* - * Initialize sender data. - */ - if (tcph->syn && tcph->ack) { - /* - * Outgoing SYN-ACK in reply to a SYN. - */ - sender->td_end = - sender->td_maxend = end; - sender->td_maxwin = (win == 0 ? 1 : win); - - tcp_options(skb, iph, tcph, sender); - /* - * RFC 1323: - * Both sides must send the Window Scale option - * to enable window scaling in either direction. - */ - if (!(sender->flags & IP_CT_TCP_FLAG_WINDOW_SCALE - && receiver->flags & IP_CT_TCP_FLAG_WINDOW_SCALE)) - sender->td_scale = - receiver->td_scale = 0; - } else { - /* - * We are in the middle of a connection, - * its history is lost for us. - * Let's try to use the data from the packet. - */ - sender->td_end = end; - sender->td_maxwin = (win == 0 ? 1 : win); - sender->td_maxend = end + sender->td_maxwin; - } - } else if (((state->state == TCP_CONNTRACK_SYN_SENT - && dir == IP_CT_DIR_ORIGINAL) - || (state->state == TCP_CONNTRACK_SYN_RECV - && dir == IP_CT_DIR_REPLY)) - && after(end, sender->td_end)) { - /* - * RFC 793: "if a TCP is reinitialized ... then it need - * not wait at all; it must only be sure to use sequence - * numbers larger than those recently used." - */ - sender->td_end = - sender->td_maxend = end; - sender->td_maxwin = (win == 0 ? 1 : win); - - tcp_options(skb, iph, tcph, sender); - } - - if (!(tcph->ack)) { - /* - * If there is no ACK, just pretend it was set and OK. - */ - ack = sack = receiver->td_end; - } else if (((tcp_flag_word(tcph) & (TCP_FLAG_ACK|TCP_FLAG_RST)) == - (TCP_FLAG_ACK|TCP_FLAG_RST)) - && (ack == 0)) { - /* - * Broken TCP stacks, that set ACK in RST packets as well - * with zero ack value. - */ - ack = sack = receiver->td_end; - } - - if (seq == end - && (!tcph->rst - || (seq == 0 && state->state == TCP_CONNTRACK_SYN_SENT))) - /* - * Packets contains no data: we assume it is valid - * and check the ack value only. - * However RST segments are always validated by their - * SEQ number, except when seq == 0 (reset sent answering - * SYN. - */ - seq = end = sender->td_end; - - DEBUGP("tcp_in_window: src=%u.%u.%u.%u:%hu dst=%u.%u.%u.%u:%hu " - "seq=%u ack=%u sack =%u win=%u end=%u\n", - NIPQUAD(iph->saddr), ntohs(tcph->source), - NIPQUAD(iph->daddr), ntohs(tcph->dest), - seq, ack, sack, win, end); - DEBUGP("tcp_in_window: sender end=%u maxend=%u maxwin=%u scale=%i " - "receiver end=%u maxend=%u maxwin=%u scale=%i\n", - sender->td_end, sender->td_maxend, sender->td_maxwin, - sender->td_scale, - receiver->td_end, receiver->td_maxend, receiver->td_maxwin, - receiver->td_scale); - - DEBUGP("tcp_in_window: I=%i II=%i III=%i IV=%i\n", - before(seq, sender->td_maxend + 1), - after(end, sender->td_end - receiver->td_maxwin - 1), - before(sack, receiver->td_end + 1), - after(ack, receiver->td_end - MAXACKWINDOW(sender))); - - if (before(seq, sender->td_maxend + 1) && - after(end, sender->td_end - receiver->td_maxwin - 1) && - before(sack, receiver->td_end + 1) && - after(ack, receiver->td_end - MAXACKWINDOW(sender))) { - /* - * Take into account window scaling (RFC 1323). - */ - if (!tcph->syn) - win <<= sender->td_scale; - - /* - * Update sender data. - */ - swin = win + (sack - ack); - if (sender->td_maxwin < swin) - sender->td_maxwin = swin; - if (after(end, sender->td_end)) - sender->td_end = end; - /* - * Update receiver data. - */ - if (after(end, sender->td_maxend)) - receiver->td_maxwin += end - sender->td_maxend; - if (after(sack + win, receiver->td_maxend - 1)) { - receiver->td_maxend = sack + win; - if (win == 0) - receiver->td_maxend++; - } - - /* - * Check retransmissions. - */ - if (index == TCP_ACK_SET) { - if (state->last_dir == dir - && state->last_seq == seq - && state->last_ack == ack - && state->last_end == end - && state->last_win == win) - state->retrans++; - else { - state->last_dir = dir; - state->last_seq = seq; - state->last_ack = ack; - state->last_end = end; - state->last_win = win; - state->retrans = 0; - } - } - res = 1; - } else { - res = 0; - if (sender->flags & IP_CT_TCP_FLAG_BE_LIBERAL || - ip_ct_tcp_be_liberal) - res = 1; - if (!res && LOG_INVALID(IPPROTO_TCP)) - nf_log_packet(PF_INET, 0, skb, NULL, NULL, NULL, - "ip_ct_tcp: %s ", - before(seq, sender->td_maxend + 1) ? - after(end, sender->td_end - receiver->td_maxwin - 1) ? - before(sack, receiver->td_end + 1) ? - after(ack, receiver->td_end - MAXACKWINDOW(sender)) ? "BUG" - : "ACK is under the lower bound (possible overly delayed ACK)" - : "ACK is over the upper bound (ACKed data not seen yet)" - : "SEQ is under the lower bound (already ACKed data retransmitted)" - : "SEQ is over the upper bound (over the window of the receiver)"); - } - - DEBUGP("tcp_in_window: res=%i sender end=%u maxend=%u maxwin=%u " - "receiver end=%u maxend=%u maxwin=%u\n", - res, sender->td_end, sender->td_maxend, sender->td_maxwin, - receiver->td_end, receiver->td_maxend, receiver->td_maxwin); - - return res; -} - -#ifdef CONFIG_IP_NF_NAT_NEEDED -/* Update sender->td_end after NAT successfully mangled the packet */ -void ip_conntrack_tcp_update(struct sk_buff *skb, - struct ip_conntrack *conntrack, - enum ip_conntrack_dir dir) -{ - struct iphdr *iph = ip_hdr(skb); - struct tcphdr *tcph = (void *)iph + ip_hdrlen(skb); - __u32 end; -#ifdef DEBUGP_VARS - struct ip_ct_tcp_state *sender = &conntrack->proto.tcp.seen[dir]; - struct ip_ct_tcp_state *receiver = &conntrack->proto.tcp.seen[!dir]; -#endif - - end = segment_seq_plus_len(ntohl(tcph->seq), skb->len, iph, tcph); - - write_lock_bh(&tcp_lock); - /* - * We have to worry for the ack in the reply packet only... - */ - if (after(end, conntrack->proto.tcp.seen[dir].td_end)) - conntrack->proto.tcp.seen[dir].td_end = end; - conntrack->proto.tcp.last_end = end; - write_unlock_bh(&tcp_lock); - DEBUGP("tcp_update: sender end=%u maxend=%u maxwin=%u scale=%i " - "receiver end=%u maxend=%u maxwin=%u scale=%i\n", - sender->td_end, sender->td_maxend, sender->td_maxwin, - sender->td_scale, - receiver->td_end, receiver->td_maxend, receiver->td_maxwin, - receiver->td_scale); -} - -#endif - -#define TH_FIN 0x01 -#define TH_SYN 0x02 -#define TH_RST 0x04 -#define TH_PUSH 0x08 -#define TH_ACK 0x10 -#define TH_URG 0x20 -#define TH_ECE 0x40 -#define TH_CWR 0x80 - -/* table of valid flag combinations - ECE and CWR are always valid */ -static const u8 tcp_valid_flags[(TH_FIN|TH_SYN|TH_RST|TH_PUSH|TH_ACK|TH_URG) + 1] = -{ - [TH_SYN] = 1, - [TH_SYN|TH_PUSH] = 1, - [TH_SYN|TH_URG] = 1, - [TH_SYN|TH_PUSH|TH_URG] = 1, - [TH_SYN|TH_ACK] = 1, - [TH_SYN|TH_ACK|TH_PUSH] = 1, - [TH_RST] = 1, - [TH_RST|TH_ACK] = 1, - [TH_RST|TH_ACK|TH_PUSH] = 1, - [TH_FIN|TH_ACK] = 1, - [TH_ACK] = 1, - [TH_ACK|TH_PUSH] = 1, - [TH_ACK|TH_URG] = 1, - [TH_ACK|TH_URG|TH_PUSH] = 1, - [TH_FIN|TH_ACK|TH_PUSH] = 1, - [TH_FIN|TH_ACK|TH_URG] = 1, - [TH_FIN|TH_ACK|TH_URG|TH_PUSH] = 1, -}; - -/* Protect conntrack agaist broken packets. Code taken from ipt_unclean.c. */ -static int tcp_error(struct sk_buff *skb, - enum ip_conntrack_info *ctinfo, - unsigned int hooknum) -{ - const unsigned int hdrlen = ip_hdrlen(skb); - struct tcphdr _tcph, *th; - unsigned int tcplen = skb->len - hdrlen; - u_int8_t tcpflags; - - /* Smaller that minimal TCP header? */ - th = skb_header_pointer(skb, hdrlen, - sizeof(_tcph), &_tcph); - if (th == NULL) { - if (LOG_INVALID(IPPROTO_TCP)) - nf_log_packet(PF_INET, 0, skb, NULL, NULL, NULL, - "ip_ct_tcp: short packet "); - return -NF_ACCEPT; - } - - /* Not whole TCP header or malformed packet */ - if (th->doff*4 < sizeof(struct tcphdr) || tcplen < th->doff*4) { - if (LOG_INVALID(IPPROTO_TCP)) - nf_log_packet(PF_INET, 0, skb, NULL, NULL, NULL, - "ip_ct_tcp: truncated/malformed packet "); - return -NF_ACCEPT; - } - - /* Checksum invalid? Ignore. - * We skip checking packets on the outgoing path - * because it is assumed to be correct. - */ - /* FIXME: Source route IP option packets --RR */ - if (ip_conntrack_checksum && hooknum == NF_IP_PRE_ROUTING && - nf_ip_checksum(skb, hooknum, hdrlen, IPPROTO_TCP)) { - if (LOG_INVALID(IPPROTO_TCP)) - nf_log_packet(PF_INET, 0, skb, NULL, NULL, NULL, - "ip_ct_tcp: bad TCP checksum "); - return -NF_ACCEPT; - } - - /* Check TCP flags. */ - tcpflags = (((u_int8_t *)th)[13] & ~(TH_ECE|TH_CWR)); - if (!tcp_valid_flags[tcpflags]) { - if (LOG_INVALID(IPPROTO_TCP)) - nf_log_packet(PF_INET, 0, skb, NULL, NULL, NULL, - "ip_ct_tcp: invalid TCP flag combination "); - return -NF_ACCEPT; - } - - return NF_ACCEPT; -} - -/* Returns verdict for packet, or -1 for invalid. */ -static int tcp_packet(struct ip_conntrack *conntrack, - const struct sk_buff *skb, - enum ip_conntrack_info ctinfo) -{ - enum tcp_conntrack new_state, old_state; - enum ip_conntrack_dir dir; - struct iphdr *iph = ip_hdr(skb); - struct tcphdr *th, _tcph; - unsigned long timeout; - unsigned int index; - - th = skb_header_pointer(skb, iph->ihl * 4, - sizeof(_tcph), &_tcph); - BUG_ON(th == NULL); - - write_lock_bh(&tcp_lock); - old_state = conntrack->proto.tcp.state; - dir = CTINFO2DIR(ctinfo); - index = get_conntrack_index(th); - new_state = tcp_conntracks[dir][index][old_state]; - - switch (new_state) { - case TCP_CONNTRACK_IGNORE: - /* Ignored packets: - * - * a) SYN in ORIGINAL - * b) SYN/ACK in REPLY - * c) ACK in reply direction after initial SYN in original. - */ - if (index == TCP_SYNACK_SET - && conntrack->proto.tcp.last_index == TCP_SYN_SET - && conntrack->proto.tcp.last_dir != dir - && ntohl(th->ack_seq) == - conntrack->proto.tcp.last_end) { - /* This SYN/ACK acknowledges a SYN that we earlier - * ignored as invalid. This means that the client and - * the server are both in sync, while the firewall is - * not. We kill this session and block the SYN/ACK so - * that the client cannot but retransmit its SYN and - * thus initiate a clean new session. - */ - write_unlock_bh(&tcp_lock); - if (LOG_INVALID(IPPROTO_TCP)) - nf_log_packet(PF_INET, 0, skb, NULL, NULL, - NULL, "ip_ct_tcp: " - "killing out of sync session "); - if (del_timer(&conntrack->timeout)) - conntrack->timeout.function((unsigned long) - conntrack); - return -NF_DROP; - } - conntrack->proto.tcp.last_index = index; - conntrack->proto.tcp.last_dir = dir; - conntrack->proto.tcp.last_seq = ntohl(th->seq); - conntrack->proto.tcp.last_end = - segment_seq_plus_len(ntohl(th->seq), skb->len, iph, th); - - write_unlock_bh(&tcp_lock); - if (LOG_INVALID(IPPROTO_TCP)) - nf_log_packet(PF_INET, 0, skb, NULL, NULL, NULL, - "ip_ct_tcp: invalid packet ignored "); - return NF_ACCEPT; - case TCP_CONNTRACK_MAX: - /* Invalid packet */ - DEBUGP("ip_ct_tcp: Invalid dir=%i index=%u ostate=%u\n", - dir, get_conntrack_index(th), - old_state); - write_unlock_bh(&tcp_lock); - if (LOG_INVALID(IPPROTO_TCP)) - nf_log_packet(PF_INET, 0, skb, NULL, NULL, NULL, - "ip_ct_tcp: invalid state "); - return -NF_ACCEPT; - case TCP_CONNTRACK_SYN_SENT: - if (old_state < TCP_CONNTRACK_TIME_WAIT) - break; - if ((conntrack->proto.tcp.seen[dir].flags & - IP_CT_TCP_FLAG_CLOSE_INIT) - || after(ntohl(th->seq), - conntrack->proto.tcp.seen[dir].td_end)) { - /* Attempt to reopen a closed connection. - * Delete this connection and look up again. */ - write_unlock_bh(&tcp_lock); - if (del_timer(&conntrack->timeout)) - conntrack->timeout.function((unsigned long) - conntrack); - return -NF_REPEAT; - } else { - write_unlock_bh(&tcp_lock); - if (LOG_INVALID(IPPROTO_TCP)) - nf_log_packet(PF_INET, 0, skb, NULL, NULL, - NULL, "ip_ct_tcp: invalid SYN"); - return -NF_ACCEPT; - } - case TCP_CONNTRACK_CLOSE: - if (index == TCP_RST_SET - && ((test_bit(IPS_SEEN_REPLY_BIT, &conntrack->status) - && conntrack->proto.tcp.last_index == TCP_SYN_SET) - || (!test_bit(IPS_ASSURED_BIT, &conntrack->status) - && conntrack->proto.tcp.last_index == TCP_ACK_SET)) - && ntohl(th->ack_seq) == conntrack->proto.tcp.last_end) { - /* RST sent to invalid SYN or ACK we had let through - * at a) and c) above: - * - * a) SYN was in window then - * c) we hold a half-open connection. - * - * Delete our connection entry. - * We skip window checking, because packet might ACK - * segments we ignored. */ - goto in_window; - } - /* Just fall through */ - default: - /* Keep compilers happy. */ - break; - } - - if (!tcp_in_window(&conntrack->proto.tcp, dir, index, - skb, iph, th)) { - write_unlock_bh(&tcp_lock); - return -NF_ACCEPT; - } - in_window: - /* From now on we have got in-window packets */ - conntrack->proto.tcp.last_index = index; - - DEBUGP("tcp_conntracks: src=%u.%u.%u.%u:%hu dst=%u.%u.%u.%u:%hu " - "syn=%i ack=%i fin=%i rst=%i old=%i new=%i\n", - NIPQUAD(iph->saddr), ntohs(th->source), - NIPQUAD(iph->daddr), ntohs(th->dest), - (th->syn ? 1 : 0), (th->ack ? 1 : 0), - (th->fin ? 1 : 0), (th->rst ? 1 : 0), - old_state, new_state); - - conntrack->proto.tcp.state = new_state; - if (old_state != new_state - && (new_state == TCP_CONNTRACK_FIN_WAIT - || new_state == TCP_CONNTRACK_CLOSE)) - conntrack->proto.tcp.seen[dir].flags |= IP_CT_TCP_FLAG_CLOSE_INIT; - timeout = conntrack->proto.tcp.retrans >= ip_ct_tcp_max_retrans - && *tcp_timeouts[new_state] > ip_ct_tcp_timeout_max_retrans - ? ip_ct_tcp_timeout_max_retrans : *tcp_timeouts[new_state]; - write_unlock_bh(&tcp_lock); - - ip_conntrack_event_cache(IPCT_PROTOINFO_VOLATILE, skb); - if (new_state != old_state) - ip_conntrack_event_cache(IPCT_PROTOINFO, skb); - - if (!test_bit(IPS_SEEN_REPLY_BIT, &conntrack->status)) { - /* If only reply is a RST, we can consider ourselves not to - have an established connection: this is a fairly common - problem case, so we can delete the conntrack - immediately. --RR */ - if (th->rst) { - if (del_timer(&conntrack->timeout)) - conntrack->timeout.function((unsigned long) - conntrack); - return NF_ACCEPT; - } - } else if (!test_bit(IPS_ASSURED_BIT, &conntrack->status) - && (old_state == TCP_CONNTRACK_SYN_RECV - || old_state == TCP_CONNTRACK_ESTABLISHED) - && new_state == TCP_CONNTRACK_ESTABLISHED) { - /* Set ASSURED if we see see valid ack in ESTABLISHED - after SYN_RECV or a valid answer for a picked up - connection. */ - set_bit(IPS_ASSURED_BIT, &conntrack->status); - ip_conntrack_event_cache(IPCT_STATUS, skb); - } - ip_ct_refresh_acct(conntrack, ctinfo, skb, timeout); - - return NF_ACCEPT; -} - -/* Called when a new connection for this protocol found. */ -static int tcp_new(struct ip_conntrack *conntrack, - const struct sk_buff *skb) -{ - enum tcp_conntrack new_state; - struct iphdr *iph = ip_hdr(skb); - struct tcphdr *th, _tcph; -#ifdef DEBUGP_VARS - struct ip_ct_tcp_state *sender = &conntrack->proto.tcp.seen[0]; - struct ip_ct_tcp_state *receiver = &conntrack->proto.tcp.seen[1]; -#endif - - th = skb_header_pointer(skb, iph->ihl * 4, - sizeof(_tcph), &_tcph); - BUG_ON(th == NULL); - - /* Don't need lock here: this conntrack not in circulation yet */ - new_state - = tcp_conntracks[0][get_conntrack_index(th)] - [TCP_CONNTRACK_NONE]; - - /* Invalid: delete conntrack */ - if (new_state >= TCP_CONNTRACK_MAX) { - DEBUGP("ip_ct_tcp: invalid new deleting.\n"); - return 0; - } - - if (new_state == TCP_CONNTRACK_SYN_SENT) { - /* SYN packet */ - conntrack->proto.tcp.seen[0].td_end = - segment_seq_plus_len(ntohl(th->seq), skb->len, - iph, th); - conntrack->proto.tcp.seen[0].td_maxwin = ntohs(th->window); - if (conntrack->proto.tcp.seen[0].td_maxwin == 0) - conntrack->proto.tcp.seen[0].td_maxwin = 1; - conntrack->proto.tcp.seen[0].td_maxend = - conntrack->proto.tcp.seen[0].td_end; - - tcp_options(skb, iph, th, &conntrack->proto.tcp.seen[0]); - conntrack->proto.tcp.seen[1].flags = 0; - } else if (ip_ct_tcp_loose == 0) { - /* Don't try to pick up connections. */ - return 0; - } else { - /* - * We are in the middle of a connection, - * its history is lost for us. - * Let's try to use the data from the packet. - */ - conntrack->proto.tcp.seen[0].td_end = - segment_seq_plus_len(ntohl(th->seq), skb->len, - iph, th); - conntrack->proto.tcp.seen[0].td_maxwin = ntohs(th->window); - if (conntrack->proto.tcp.seen[0].td_maxwin == 0) - conntrack->proto.tcp.seen[0].td_maxwin = 1; - conntrack->proto.tcp.seen[0].td_maxend = - conntrack->proto.tcp.seen[0].td_end + - conntrack->proto.tcp.seen[0].td_maxwin; - conntrack->proto.tcp.seen[0].td_scale = 0; - - /* We assume SACK and liberal window checking to handle - * window scaling */ - conntrack->proto.tcp.seen[0].flags = - conntrack->proto.tcp.seen[1].flags = IP_CT_TCP_FLAG_SACK_PERM | - IP_CT_TCP_FLAG_BE_LIBERAL; - } - - conntrack->proto.tcp.seen[1].td_end = 0; - conntrack->proto.tcp.seen[1].td_maxend = 0; - conntrack->proto.tcp.seen[1].td_maxwin = 1; - conntrack->proto.tcp.seen[1].td_scale = 0; - - /* tcp_packet will set them */ - conntrack->proto.tcp.state = TCP_CONNTRACK_NONE; - conntrack->proto.tcp.last_index = TCP_NONE_SET; - - DEBUGP("tcp_new: sender end=%u maxend=%u maxwin=%u scale=%i " - "receiver end=%u maxend=%u maxwin=%u scale=%i\n", - sender->td_end, sender->td_maxend, sender->td_maxwin, - sender->td_scale, - receiver->td_end, receiver->td_maxend, receiver->td_maxwin, - receiver->td_scale); - return 1; -} - -struct ip_conntrack_protocol ip_conntrack_protocol_tcp = -{ - .proto = IPPROTO_TCP, - .name = "tcp", - .pkt_to_tuple = tcp_pkt_to_tuple, - .invert_tuple = tcp_invert_tuple, - .print_tuple = tcp_print_tuple, - .print_conntrack = tcp_print_conntrack, - .packet = tcp_packet, - .new = tcp_new, - .error = tcp_error, -#if defined(CONFIG_IP_NF_CONNTRACK_NETLINK) || \ - defined(CONFIG_IP_NF_CONNTRACK_NETLINK_MODULE) - .to_nfattr = tcp_to_nfattr, - .from_nfattr = nfattr_to_tcp, - .tuple_to_nfattr = ip_ct_port_tuple_to_nfattr, - .nfattr_to_tuple = ip_ct_port_nfattr_to_tuple, -#endif -}; diff --git a/net/ipv4/netfilter/ip_conntrack_proto_udp.c b/net/ipv4/netfilter/ip_conntrack_proto_udp.c deleted file mode 100644 index 3b47987bf1bb..000000000000 --- a/net/ipv4/netfilter/ip_conntrack_proto_udp.c +++ /dev/null @@ -1,148 +0,0 @@ -/* (C) 1999-2001 Paul `Rusty' Russell - * (C) 2002-2004 Netfilter Core Team - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -unsigned int ip_ct_udp_timeout __read_mostly = 30*HZ; -unsigned int ip_ct_udp_timeout_stream __read_mostly = 180*HZ; - -static int udp_pkt_to_tuple(const struct sk_buff *skb, - unsigned int dataoff, - struct ip_conntrack_tuple *tuple) -{ - struct udphdr _hdr, *hp; - - /* Actually only need first 8 bytes. */ - hp = skb_header_pointer(skb, dataoff, sizeof(_hdr), &_hdr); - if (hp == NULL) - return 0; - - tuple->src.u.udp.port = hp->source; - tuple->dst.u.udp.port = hp->dest; - - return 1; -} - -static int udp_invert_tuple(struct ip_conntrack_tuple *tuple, - const struct ip_conntrack_tuple *orig) -{ - tuple->src.u.udp.port = orig->dst.u.udp.port; - tuple->dst.u.udp.port = orig->src.u.udp.port; - return 1; -} - -/* Print out the per-protocol part of the tuple. */ -static int udp_print_tuple(struct seq_file *s, - const struct ip_conntrack_tuple *tuple) -{ - return seq_printf(s, "sport=%hu dport=%hu ", - ntohs(tuple->src.u.udp.port), - ntohs(tuple->dst.u.udp.port)); -} - -/* Print out the private part of the conntrack. */ -static int udp_print_conntrack(struct seq_file *s, - const struct ip_conntrack *conntrack) -{ - return 0; -} - -/* Returns verdict for packet, and may modify conntracktype */ -static int udp_packet(struct ip_conntrack *conntrack, - const struct sk_buff *skb, - enum ip_conntrack_info ctinfo) -{ - /* If we've seen traffic both ways, this is some kind of UDP - stream. Extend timeout. */ - if (test_bit(IPS_SEEN_REPLY_BIT, &conntrack->status)) { - ip_ct_refresh_acct(conntrack, ctinfo, skb, - ip_ct_udp_timeout_stream); - /* Also, more likely to be important, and not a probe */ - if (!test_and_set_bit(IPS_ASSURED_BIT, &conntrack->status)) - ip_conntrack_event_cache(IPCT_STATUS, skb); - } else - ip_ct_refresh_acct(conntrack, ctinfo, skb, ip_ct_udp_timeout); - - return NF_ACCEPT; -} - -/* Called when a new connection for this protocol found. */ -static int udp_new(struct ip_conntrack *conntrack, const struct sk_buff *skb) -{ - return 1; -} - -static int udp_error(struct sk_buff *skb, enum ip_conntrack_info *ctinfo, - unsigned int hooknum) -{ - const unsigned int hdrlen = ip_hdrlen(skb); - unsigned int udplen = skb->len - hdrlen; - struct udphdr _hdr, *hdr; - - /* Header is too small? */ - hdr = skb_header_pointer(skb, hdrlen, sizeof(_hdr), &_hdr); - if (hdr == NULL) { - if (LOG_INVALID(IPPROTO_UDP)) - nf_log_packet(PF_INET, 0, skb, NULL, NULL, NULL, - "ip_ct_udp: short packet "); - return -NF_ACCEPT; - } - - /* Truncated/malformed packets */ - if (ntohs(hdr->len) > udplen || ntohs(hdr->len) < sizeof(*hdr)) { - if (LOG_INVALID(IPPROTO_UDP)) - nf_log_packet(PF_INET, 0, skb, NULL, NULL, NULL, - "ip_ct_udp: truncated/malformed packet "); - return -NF_ACCEPT; - } - - /* Packet with no checksum */ - if (!hdr->check) - return NF_ACCEPT; - - /* Checksum invalid? Ignore. - * We skip checking packets on the outgoing path - * because the checksum is assumed to be correct. - * FIXME: Source route IP option packets --RR */ - if (ip_conntrack_checksum && hooknum == NF_IP_PRE_ROUTING && - nf_ip_checksum(skb, hooknum, hdrlen, IPPROTO_UDP)) { - if (LOG_INVALID(IPPROTO_UDP)) - nf_log_packet(PF_INET, 0, skb, NULL, NULL, NULL, - "ip_ct_udp: bad UDP checksum "); - return -NF_ACCEPT; - } - - return NF_ACCEPT; -} - -struct ip_conntrack_protocol ip_conntrack_protocol_udp = -{ - .proto = IPPROTO_UDP, - .name = "udp", - .pkt_to_tuple = udp_pkt_to_tuple, - .invert_tuple = udp_invert_tuple, - .print_tuple = udp_print_tuple, - .print_conntrack = udp_print_conntrack, - .packet = udp_packet, - .new = udp_new, - .error = udp_error, -#if defined(CONFIG_IP_NF_CONNTRACK_NETLINK) || \ - defined(CONFIG_IP_NF_CONNTRACK_NETLINK_MODULE) - .tuple_to_nfattr = ip_ct_port_tuple_to_nfattr, - .nfattr_to_tuple = ip_ct_port_nfattr_to_tuple, -#endif -}; diff --git a/net/ipv4/netfilter/ip_conntrack_sip.c b/net/ipv4/netfilter/ip_conntrack_sip.c deleted file mode 100644 index 7363e2a5cea4..000000000000 --- a/net/ipv4/netfilter/ip_conntrack_sip.c +++ /dev/null @@ -1,520 +0,0 @@ -/* SIP extension for IP connection tracking. - * - * (C) 2005 by Christian Hentschel - * based on RR's ip_conntrack_ftp.c and other modules. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#if 0 -#define DEBUGP printk -#else -#define DEBUGP(format, args...) -#endif - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Christian Hentschel "); -MODULE_DESCRIPTION("SIP connection tracking helper"); - -#define MAX_PORTS 8 -static unsigned short ports[MAX_PORTS]; -static int ports_c; -module_param_array(ports, ushort, &ports_c, 0400); -MODULE_PARM_DESC(ports, "port numbers of sip servers"); - -static unsigned int sip_timeout = SIP_TIMEOUT; -module_param(sip_timeout, uint, 0600); -MODULE_PARM_DESC(sip_timeout, "timeout for the master SIP session"); - -unsigned int (*ip_nat_sip_hook)(struct sk_buff **pskb, - enum ip_conntrack_info ctinfo, - struct ip_conntrack *ct, - const char **dptr); -EXPORT_SYMBOL_GPL(ip_nat_sip_hook); - -unsigned int (*ip_nat_sdp_hook)(struct sk_buff **pskb, - enum ip_conntrack_info ctinfo, - struct ip_conntrack_expect *exp, - const char *dptr); -EXPORT_SYMBOL_GPL(ip_nat_sdp_hook); - -static int digits_len(const char *dptr, const char *limit, int *shift); -static int epaddr_len(const char *dptr, const char *limit, int *shift); -static int skp_digits_len(const char *dptr, const char *limit, int *shift); -static int skp_epaddr_len(const char *dptr, const char *limit, int *shift); - -struct sip_header_nfo { - const char *lname; - const char *sname; - const char *ln_str; - size_t lnlen; - size_t snlen; - size_t ln_strlen; - int case_sensitive; - int (*match_len)(const char *, const char *, int *); -}; - -static struct sip_header_nfo ct_sip_hdrs[] = { - [POS_REG_REQ_URI] = { /* SIP REGISTER request URI */ - .lname = "sip:", - .lnlen = sizeof("sip:") - 1, - .ln_str = ":", - .ln_strlen = sizeof(":") - 1, - .match_len = epaddr_len - }, - [POS_REQ_URI] = { /* SIP request URI */ - .lname = "sip:", - .lnlen = sizeof("sip:") - 1, - .ln_str = "@", - .ln_strlen = sizeof("@") - 1, - .match_len = epaddr_len - }, - [POS_FROM] = { /* SIP From header */ - .lname = "From:", - .lnlen = sizeof("From:") - 1, - .sname = "\r\nf:", - .snlen = sizeof("\r\nf:") - 1, - .ln_str = "sip:", - .ln_strlen = sizeof("sip:") - 1, - .match_len = skp_epaddr_len, - }, - [POS_TO] = { /* SIP To header */ - .lname = "To:", - .lnlen = sizeof("To:") - 1, - .sname = "\r\nt:", - .snlen = sizeof("\r\nt:") - 1, - .ln_str = "sip:", - .ln_strlen = sizeof("sip:") - 1, - .match_len = skp_epaddr_len, - }, - [POS_VIA] = { /* SIP Via header */ - .lname = "Via:", - .lnlen = sizeof("Via:") - 1, - .sname = "\r\nv:", - .snlen = sizeof("\r\nv:") - 1, /* rfc3261 "\r\n" */ - .ln_str = "UDP ", - .ln_strlen = sizeof("UDP ") - 1, - .match_len = epaddr_len, - }, - [POS_CONTACT] = { /* SIP Contact header */ - .lname = "Contact:", - .lnlen = sizeof("Contact:") - 1, - .sname = "\r\nm:", - .snlen = sizeof("\r\nm:") - 1, - .ln_str = "sip:", - .ln_strlen = sizeof("sip:") - 1, - .match_len = skp_epaddr_len - }, - [POS_CONTENT] = { /* SIP Content length header */ - .lname = "Content-Length:", - .lnlen = sizeof("Content-Length:") - 1, - .sname = "\r\nl:", - .snlen = sizeof("\r\nl:") - 1, - .ln_str = ":", - .ln_strlen = sizeof(":") - 1, - .match_len = skp_digits_len - }, - [POS_MEDIA] = { /* SDP media info */ - .case_sensitive = 1, - .lname = "\nm=", - .lnlen = sizeof("\nm=") - 1, - .sname = "\rm=", - .snlen = sizeof("\rm=") - 1, - .ln_str = "audio ", - .ln_strlen = sizeof("audio ") - 1, - .match_len = digits_len - }, - [POS_OWNER] = { /* SDP owner address*/ - .case_sensitive = 1, - .lname = "\no=", - .lnlen = sizeof("\no=") - 1, - .sname = "\ro=", - .snlen = sizeof("\ro=") - 1, - .ln_str = "IN IP4 ", - .ln_strlen = sizeof("IN IP4 ") - 1, - .match_len = epaddr_len - }, - [POS_CONNECTION] = { /* SDP connection info */ - .case_sensitive = 1, - .lname = "\nc=", - .lnlen = sizeof("\nc=") - 1, - .sname = "\rc=", - .snlen = sizeof("\rc=") - 1, - .ln_str = "IN IP4 ", - .ln_strlen = sizeof("IN IP4 ") - 1, - .match_len = epaddr_len - }, - [POS_SDP_HEADER] = { /* SDP version header */ - .case_sensitive = 1, - .lname = "\nv=", - .lnlen = sizeof("\nv=") - 1, - .sname = "\rv=", - .snlen = sizeof("\rv=") - 1, - .ln_str = "=", - .ln_strlen = sizeof("=") - 1, - .match_len = digits_len - } -}; - -/* get line lenght until first CR or LF seen. */ -int ct_sip_lnlen(const char *line, const char *limit) -{ - const char *k = line; - - while ((line <= limit) && (*line == '\r' || *line == '\n')) - line++; - - while (line <= limit) { - if (*line == '\r' || *line == '\n') - break; - line++; - } - return line - k; -} -EXPORT_SYMBOL_GPL(ct_sip_lnlen); - -/* Linear string search, case sensitive. */ -const char *ct_sip_search(const char *needle, const char *haystack, - size_t needle_len, size_t haystack_len, - int case_sensitive) -{ - const char *limit = haystack + (haystack_len - needle_len); - - while (haystack <= limit) { - if (case_sensitive) { - if (strncmp(haystack, needle, needle_len) == 0) - return haystack; - } else { - if (strnicmp(haystack, needle, needle_len) == 0) - return haystack; - } - haystack++; - } - return NULL; -} -EXPORT_SYMBOL_GPL(ct_sip_search); - -static int digits_len(const char *dptr, const char *limit, int *shift) -{ - int len = 0; - while (dptr <= limit && isdigit(*dptr)) { - dptr++; - len++; - } - return len; -} - -/* get digits lenght, skiping blank spaces. */ -static int skp_digits_len(const char *dptr, const char *limit, int *shift) -{ - for (; dptr <= limit && *dptr == ' '; dptr++) - (*shift)++; - - return digits_len(dptr, limit, shift); -} - -/* Simple ipaddr parser.. */ -static int parse_ipaddr(const char *cp, const char **endp, - __be32 *ipaddr, const char *limit) -{ - unsigned long int val; - int i, digit = 0; - - for (i = 0, *ipaddr = 0; cp <= limit && i < 4; i++) { - digit = 0; - if (!isdigit(*cp)) - break; - - val = simple_strtoul(cp, (char **)&cp, 10); - if (val > 0xFF) - return -1; - - ((u_int8_t *)ipaddr)[i] = val; - digit = 1; - - if (*cp != '.') - break; - cp++; - } - if (!digit) - return -1; - - if (endp) - *endp = cp; - - return 0; -} - -/* skip ip address. returns it lenght. */ -static int epaddr_len(const char *dptr, const char *limit, int *shift) -{ - const char *aux = dptr; - __be32 ip; - - if (parse_ipaddr(dptr, &dptr, &ip, limit) < 0) { - DEBUGP("ip: %s parse failed.!\n", dptr); - return 0; - } - - /* Port number */ - if (*dptr == ':') { - dptr++; - dptr += digits_len(dptr, limit, shift); - } - return dptr - aux; -} - -/* get address length, skiping user info. */ -static int skp_epaddr_len(const char *dptr, const char *limit, int *shift) -{ - int s = *shift; - - /* Search for @, but stop at the end of the line. - * We are inside a sip: URI, so we don't need to worry about - * continuation lines. */ - while (dptr <= limit && - *dptr != '@' && *dptr != '\r' && *dptr != '\n') { - (*shift)++; - dptr++; - } - - if (dptr <= limit && *dptr == '@') { - dptr++; - (*shift)++; - } else - *shift = s; - - return epaddr_len(dptr, limit, shift); -} - -/* Returns 0 if not found, -1 error parsing. */ -int ct_sip_get_info(const char *dptr, size_t dlen, - unsigned int *matchoff, - unsigned int *matchlen, - enum sip_header_pos pos) -{ - struct sip_header_nfo *hnfo = &ct_sip_hdrs[pos]; - const char *limit, *aux, *k = dptr; - int shift = 0; - - limit = dptr + (dlen - hnfo->lnlen); - - while (dptr <= limit) { - if ((strncmp(dptr, hnfo->lname, hnfo->lnlen) != 0) && - (hnfo->sname == NULL || - strncmp(dptr, hnfo->sname, hnfo->snlen) != 0)) { - dptr++; - continue; - } - aux = ct_sip_search(hnfo->ln_str, dptr, hnfo->ln_strlen, - ct_sip_lnlen(dptr, limit), - hnfo->case_sensitive); - if (!aux) { - DEBUGP("'%s' not found in '%s'.\n", hnfo->ln_str, - hnfo->lname); - return -1; - } - aux += hnfo->ln_strlen; - - *matchlen = hnfo->match_len(aux, limit, &shift); - if (!*matchlen) - return -1; - - *matchoff = (aux - k) + shift; - - DEBUGP("%s match succeeded! - len: %u\n", hnfo->lname, - *matchlen); - return 1; - } - DEBUGP("%s header not found.\n", hnfo->lname); - return 0; -} -EXPORT_SYMBOL_GPL(ct_sip_get_info); - -static int set_expected_rtp(struct sk_buff **pskb, - struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo, - __be32 ipaddr, u_int16_t port, - const char *dptr) -{ - struct ip_conntrack_expect *exp; - enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); - int ret; - typeof(ip_nat_sdp_hook) ip_nat_sdp; - - exp = ip_conntrack_expect_alloc(ct); - if (exp == NULL) - return NF_DROP; - - exp->tuple.src.ip = ct->tuplehash[!dir].tuple.src.ip; - exp->tuple.src.u.udp.port = 0; - exp->tuple.dst.ip = ipaddr; - exp->tuple.dst.u.udp.port = htons(port); - exp->tuple.dst.protonum = IPPROTO_UDP; - - exp->mask.src.ip = htonl(0xFFFFFFFF); - exp->mask.src.u.udp.port = 0; - exp->mask.dst.ip = htonl(0xFFFFFFFF); - exp->mask.dst.u.udp.port = htons(0xFFFF); - exp->mask.dst.protonum = 0xFF; - - exp->expectfn = NULL; - exp->flags = 0; - - ip_nat_sdp = rcu_dereference(ip_nat_sdp_hook); - if (ip_nat_sdp) - ret = ip_nat_sdp(pskb, ctinfo, exp, dptr); - else { - if (ip_conntrack_expect_related(exp) != 0) - ret = NF_DROP; - else - ret = NF_ACCEPT; - } - ip_conntrack_expect_put(exp); - - return ret; -} - -static int sip_help(struct sk_buff **pskb, - struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo) -{ - unsigned int dataoff, datalen; - const char *dptr; - int ret = NF_ACCEPT; - int matchoff, matchlen; - __be32 ipaddr; - u_int16_t port; - typeof(ip_nat_sip_hook) ip_nat_sip; - - /* No Data ? */ - dataoff = ip_hdrlen(*pskb) + sizeof(struct udphdr); - if (dataoff >= (*pskb)->len) { - DEBUGP("skb->len = %u\n", (*pskb)->len); - return NF_ACCEPT; - } - - ip_ct_refresh(ct, *pskb, sip_timeout * HZ); - - if (!skb_is_nonlinear(*pskb)) - dptr = (*pskb)->data + dataoff; - else { - DEBUGP("Copy of skbuff not supported yet.\n"); - goto out; - } - - ip_nat_sip = rcu_dereference(ip_nat_sip_hook); - if (ip_nat_sip) { - if (!ip_nat_sip(pskb, ctinfo, ct, &dptr)) { - ret = NF_DROP; - goto out; - } - } - - /* After this point NAT, could have mangled skb, so - we need to recalculate payload lenght. */ - datalen = (*pskb)->len - dataoff; - - if (datalen < (sizeof("SIP/2.0 200") - 1)) - goto out; - - /* RTP info only in some SDP pkts */ - if (memcmp(dptr, "INVITE", sizeof("INVITE") - 1) != 0 && - memcmp(dptr, "SIP/2.0 200", sizeof("SIP/2.0 200") - 1) != 0) { - goto out; - } - /* Get ip and port address from SDP packet. */ - if (ct_sip_get_info(dptr, datalen, &matchoff, &matchlen, - POS_CONNECTION) > 0) { - - /* We'll drop only if there are parse problems. */ - if (parse_ipaddr(dptr + matchoff, NULL, &ipaddr, - dptr + datalen) < 0) { - ret = NF_DROP; - goto out; - } - if (ct_sip_get_info(dptr, datalen, &matchoff, &matchlen, - POS_MEDIA) > 0) { - - port = simple_strtoul(dptr + matchoff, NULL, 10); - if (port < 1024) { - ret = NF_DROP; - goto out; - } - ret = set_expected_rtp(pskb, ct, ctinfo, - ipaddr, port, dptr); - } - } -out: - return ret; -} - -static struct ip_conntrack_helper sip[MAX_PORTS]; -static char sip_names[MAX_PORTS][10]; - -static void fini(void) -{ - int i; - for (i = 0; i < ports_c; i++) { - DEBUGP("unregistering helper for port %d\n", ports[i]); - ip_conntrack_helper_unregister(&sip[i]); - } -} - -static int __init init(void) -{ - int i, ret; - char *tmpname; - - if (ports_c == 0) - ports[ports_c++] = SIP_PORT; - - for (i = 0; i < ports_c; i++) { - /* Create helper structure */ - memset(&sip[i], 0, sizeof(struct ip_conntrack_helper)); - - sip[i].tuple.dst.protonum = IPPROTO_UDP; - sip[i].tuple.src.u.udp.port = htons(ports[i]); - sip[i].mask.src.u.udp.port = htons(0xFFFF); - sip[i].mask.dst.protonum = 0xFF; - sip[i].max_expected = 2; - sip[i].timeout = 3 * 60; /* 3 minutes */ - sip[i].me = THIS_MODULE; - sip[i].help = sip_help; - - tmpname = &sip_names[i][0]; - if (ports[i] == SIP_PORT) - sprintf(tmpname, "sip"); - else - sprintf(tmpname, "sip-%d", i); - sip[i].name = tmpname; - - DEBUGP("port #%d: %d\n", i, ports[i]); - - ret = ip_conntrack_helper_register(&sip[i]); - if (ret) { - printk("ERROR registering helper for port %d\n", - ports[i]); - fini(); - return ret; - } - } - return 0; -} - -module_init(init); -module_exit(fini); diff --git a/net/ipv4/netfilter/ip_conntrack_standalone.c b/net/ipv4/netfilter/ip_conntrack_standalone.c deleted file mode 100644 index c32200153d62..000000000000 --- a/net/ipv4/netfilter/ip_conntrack_standalone.c +++ /dev/null @@ -1,962 +0,0 @@ -/* This file contains all the functions required for the standalone - ip_conntrack module. - - These are not required by the compatibility layer. -*/ - -/* (C) 1999-2001 Paul `Rusty' Russell - * (C) 2002-2005 Netfilter Core Team - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef CONFIG_SYSCTL -#include -#endif -#include -#include -#include - -#include -#include -#include -#include - -#if 0 -#define DEBUGP printk -#else -#define DEBUGP(format, args...) -#endif - -MODULE_LICENSE("GPL"); - -extern atomic_t ip_conntrack_count; -DECLARE_PER_CPU(struct ip_conntrack_stat, ip_conntrack_stat); - -static int kill_proto(struct ip_conntrack *i, void *data) -{ - return (i->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum == - *((u_int8_t *) data)); -} - -#ifdef CONFIG_PROC_FS -static int -print_tuple(struct seq_file *s, const struct ip_conntrack_tuple *tuple, - struct ip_conntrack_protocol *proto) -{ - seq_printf(s, "src=%u.%u.%u.%u dst=%u.%u.%u.%u ", - NIPQUAD(tuple->src.ip), NIPQUAD(tuple->dst.ip)); - return proto->print_tuple(s, tuple); -} - -#ifdef CONFIG_IP_NF_CT_ACCT -static unsigned int -seq_print_counters(struct seq_file *s, - const struct ip_conntrack_counter *counter) -{ - return seq_printf(s, "packets=%llu bytes=%llu ", - (unsigned long long)counter->packets, - (unsigned long long)counter->bytes); -} -#else -#define seq_print_counters(x, y) 0 -#endif - -struct ct_iter_state { - unsigned int bucket; -}; - -static struct list_head *ct_get_first(struct seq_file *seq) -{ - struct ct_iter_state *st = seq->private; - - for (st->bucket = 0; - st->bucket < ip_conntrack_htable_size; - st->bucket++) { - if (!list_empty(&ip_conntrack_hash[st->bucket])) - return ip_conntrack_hash[st->bucket].next; - } - return NULL; -} - -static struct list_head *ct_get_next(struct seq_file *seq, struct list_head *head) -{ - struct ct_iter_state *st = seq->private; - - head = head->next; - while (head == &ip_conntrack_hash[st->bucket]) { - if (++st->bucket >= ip_conntrack_htable_size) - return NULL; - head = ip_conntrack_hash[st->bucket].next; - } - return head; -} - -static struct list_head *ct_get_idx(struct seq_file *seq, loff_t pos) -{ - struct list_head *head = ct_get_first(seq); - - if (head) - while (pos && (head = ct_get_next(seq, head))) - pos--; - return pos ? NULL : head; -} - -static void *ct_seq_start(struct seq_file *seq, loff_t *pos) -{ - read_lock_bh(&ip_conntrack_lock); - return ct_get_idx(seq, *pos); -} - -static void *ct_seq_next(struct seq_file *s, void *v, loff_t *pos) -{ - (*pos)++; - return ct_get_next(s, v); -} - -static void ct_seq_stop(struct seq_file *s, void *v) -{ - read_unlock_bh(&ip_conntrack_lock); -} - -static int ct_seq_show(struct seq_file *s, void *v) -{ - const struct ip_conntrack_tuple_hash *hash = v; - const struct ip_conntrack *conntrack = tuplehash_to_ctrack(hash); - struct ip_conntrack_protocol *proto; - - IP_NF_ASSERT(conntrack); - - /* we only want to print DIR_ORIGINAL */ - if (DIRECTION(hash)) - return 0; - - proto = __ip_conntrack_proto_find(conntrack->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum); - IP_NF_ASSERT(proto); - - if (seq_printf(s, "%-8s %u %ld ", - proto->name, - conntrack->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum, - timer_pending(&conntrack->timeout) - ? (long)(conntrack->timeout.expires - jiffies)/HZ - : 0) != 0) - return -ENOSPC; - - if (proto->print_conntrack(s, conntrack)) - return -ENOSPC; - - if (print_tuple(s, &conntrack->tuplehash[IP_CT_DIR_ORIGINAL].tuple, - proto)) - return -ENOSPC; - - if (seq_print_counters(s, &conntrack->counters[IP_CT_DIR_ORIGINAL])) - return -ENOSPC; - - if (!(test_bit(IPS_SEEN_REPLY_BIT, &conntrack->status))) - if (seq_printf(s, "[UNREPLIED] ")) - return -ENOSPC; - - if (print_tuple(s, &conntrack->tuplehash[IP_CT_DIR_REPLY].tuple, - proto)) - return -ENOSPC; - - if (seq_print_counters(s, &conntrack->counters[IP_CT_DIR_REPLY])) - return -ENOSPC; - - if (test_bit(IPS_ASSURED_BIT, &conntrack->status)) - if (seq_printf(s, "[ASSURED] ")) - return -ENOSPC; - -#if defined(CONFIG_IP_NF_CONNTRACK_MARK) - if (seq_printf(s, "mark=%u ", conntrack->mark)) - return -ENOSPC; -#endif - -#ifdef CONFIG_IP_NF_CONNTRACK_SECMARK - if (seq_printf(s, "secmark=%u ", conntrack->secmark)) - return -ENOSPC; -#endif - - if (seq_printf(s, "use=%u\n", atomic_read(&conntrack->ct_general.use))) - return -ENOSPC; - - return 0; -} - -static struct seq_operations ct_seq_ops = { - .start = ct_seq_start, - .next = ct_seq_next, - .stop = ct_seq_stop, - .show = ct_seq_show -}; - -static int ct_open(struct inode *inode, struct file *file) -{ - struct seq_file *seq; - struct ct_iter_state *st; - int ret; - - st = kmalloc(sizeof(struct ct_iter_state), GFP_KERNEL); - if (st == NULL) - return -ENOMEM; - ret = seq_open(file, &ct_seq_ops); - if (ret) - goto out_free; - seq = file->private_data; - seq->private = st; - memset(st, 0, sizeof(struct ct_iter_state)); - return ret; -out_free: - kfree(st); - return ret; -} - -static const struct file_operations ct_file_ops = { - .owner = THIS_MODULE, - .open = ct_open, - .read = seq_read, - .llseek = seq_lseek, - .release = seq_release_private, -}; - -/* expects */ -static void *exp_seq_start(struct seq_file *s, loff_t *pos) -{ - struct list_head *e = &ip_conntrack_expect_list; - loff_t i; - - /* strange seq_file api calls stop even if we fail, - * thus we need to grab lock since stop unlocks */ - read_lock_bh(&ip_conntrack_lock); - - if (list_empty(e)) - return NULL; - - for (i = 0; i <= *pos; i++) { - e = e->next; - if (e == &ip_conntrack_expect_list) - return NULL; - } - return e; -} - -static void *exp_seq_next(struct seq_file *s, void *v, loff_t *pos) -{ - struct list_head *e = v; - - ++*pos; - e = e->next; - - if (e == &ip_conntrack_expect_list) - return NULL; - - return e; -} - -static void exp_seq_stop(struct seq_file *s, void *v) -{ - read_unlock_bh(&ip_conntrack_lock); -} - -static int exp_seq_show(struct seq_file *s, void *v) -{ - struct ip_conntrack_expect *expect = v; - - if (expect->timeout.function) - seq_printf(s, "%ld ", timer_pending(&expect->timeout) - ? (long)(expect->timeout.expires - jiffies)/HZ : 0); - else - seq_printf(s, "- "); - - seq_printf(s, "proto=%u ", expect->tuple.dst.protonum); - - print_tuple(s, &expect->tuple, - __ip_conntrack_proto_find(expect->tuple.dst.protonum)); - return seq_putc(s, '\n'); -} - -static struct seq_operations exp_seq_ops = { - .start = exp_seq_start, - .next = exp_seq_next, - .stop = exp_seq_stop, - .show = exp_seq_show -}; - -static int exp_open(struct inode *inode, struct file *file) -{ - return seq_open(file, &exp_seq_ops); -} - -static const struct file_operations exp_file_ops = { - .owner = THIS_MODULE, - .open = exp_open, - .read = seq_read, - .llseek = seq_lseek, - .release = seq_release -}; - -static void *ct_cpu_seq_start(struct seq_file *seq, loff_t *pos) -{ - int cpu; - - if (*pos == 0) - return SEQ_START_TOKEN; - - for (cpu = *pos-1; cpu < NR_CPUS; ++cpu) { - if (!cpu_possible(cpu)) - continue; - *pos = cpu+1; - return &per_cpu(ip_conntrack_stat, cpu); - } - - return NULL; -} - -static void *ct_cpu_seq_next(struct seq_file *seq, void *v, loff_t *pos) -{ - int cpu; - - for (cpu = *pos; cpu < NR_CPUS; ++cpu) { - if (!cpu_possible(cpu)) - continue; - *pos = cpu+1; - return &per_cpu(ip_conntrack_stat, cpu); - } - - return NULL; -} - -static void ct_cpu_seq_stop(struct seq_file *seq, void *v) -{ -} - -static int ct_cpu_seq_show(struct seq_file *seq, void *v) -{ - unsigned int nr_conntracks = atomic_read(&ip_conntrack_count); - struct ip_conntrack_stat *st = v; - - if (v == SEQ_START_TOKEN) { - seq_printf(seq, "entries searched found new invalid ignore delete delete_list insert insert_failed drop early_drop icmp_error expect_new expect_create expect_delete\n"); - return 0; - } - - seq_printf(seq, "%08x %08x %08x %08x %08x %08x %08x %08x " - "%08x %08x %08x %08x %08x %08x %08x %08x \n", - nr_conntracks, - st->searched, - st->found, - st->new, - st->invalid, - st->ignore, - st->delete, - st->delete_list, - st->insert, - st->insert_failed, - st->drop, - st->early_drop, - st->error, - - st->expect_new, - st->expect_create, - st->expect_delete - ); - return 0; -} - -static struct seq_operations ct_cpu_seq_ops = { - .start = ct_cpu_seq_start, - .next = ct_cpu_seq_next, - .stop = ct_cpu_seq_stop, - .show = ct_cpu_seq_show, -}; - -static int ct_cpu_seq_open(struct inode *inode, struct file *file) -{ - return seq_open(file, &ct_cpu_seq_ops); -} - -static const struct file_operations ct_cpu_seq_fops = { - .owner = THIS_MODULE, - .open = ct_cpu_seq_open, - .read = seq_read, - .llseek = seq_lseek, - .release = seq_release_private, -}; -#endif - -static unsigned int ip_confirm(unsigned int hooknum, - struct sk_buff **pskb, - const struct net_device *in, - const struct net_device *out, - int (*okfn)(struct sk_buff *)) -{ - /* We've seen it coming out the other side: confirm it */ - return ip_conntrack_confirm(pskb); -} - -static unsigned int ip_conntrack_help(unsigned int hooknum, - struct sk_buff **pskb, - const struct net_device *in, - const struct net_device *out, - int (*okfn)(struct sk_buff *)) -{ - struct ip_conntrack *ct; - enum ip_conntrack_info ctinfo; - - /* This is where we call the helper: as the packet goes out. */ - ct = ip_conntrack_get(*pskb, &ctinfo); - if (ct && ct->helper && ctinfo != IP_CT_RELATED + IP_CT_IS_REPLY) { - unsigned int ret; - ret = ct->helper->help(pskb, ct, ctinfo); - if (ret != NF_ACCEPT) - return ret; - } - return NF_ACCEPT; -} - -static unsigned int ip_conntrack_defrag(unsigned int hooknum, - struct sk_buff **pskb, - const struct net_device *in, - const struct net_device *out, - int (*okfn)(struct sk_buff *)) -{ -#if !defined(CONFIG_IP_NF_NAT) && !defined(CONFIG_IP_NF_NAT_MODULE) - /* Previously seen (loopback)? Ignore. Do this before - fragment check. */ - if ((*pskb)->nfct) - return NF_ACCEPT; -#endif - - /* Gather fragments. */ - if (ip_hdr(*pskb)->frag_off & htons(IP_MF | IP_OFFSET)) { - *pskb = ip_ct_gather_frags(*pskb, - hooknum == NF_IP_PRE_ROUTING ? - IP_DEFRAG_CONNTRACK_IN : - IP_DEFRAG_CONNTRACK_OUT); - if (!*pskb) - return NF_STOLEN; - } - return NF_ACCEPT; -} - -static unsigned int ip_conntrack_local(unsigned int hooknum, - struct sk_buff **pskb, - const struct net_device *in, - const struct net_device *out, - int (*okfn)(struct sk_buff *)) -{ - /* root is playing with raw sockets. */ - if ((*pskb)->len < sizeof(struct iphdr) - || ip_hdrlen(*pskb) < sizeof(struct iphdr)) { - if (net_ratelimit()) - printk("ipt_hook: happy cracking.\n"); - return NF_ACCEPT; - } - return ip_conntrack_in(hooknum, pskb, in, out, okfn); -} - -/* Connection tracking may drop packets, but never alters them, so - make it the first hook. */ -static struct nf_hook_ops ip_conntrack_ops[] = { - { - .hook = ip_conntrack_defrag, - .owner = THIS_MODULE, - .pf = PF_INET, - .hooknum = NF_IP_PRE_ROUTING, - .priority = NF_IP_PRI_CONNTRACK_DEFRAG, - }, - { - .hook = ip_conntrack_in, - .owner = THIS_MODULE, - .pf = PF_INET, - .hooknum = NF_IP_PRE_ROUTING, - .priority = NF_IP_PRI_CONNTRACK, - }, - { - .hook = ip_conntrack_defrag, - .owner = THIS_MODULE, - .pf = PF_INET, - .hooknum = NF_IP_LOCAL_OUT, - .priority = NF_IP_PRI_CONNTRACK_DEFRAG, - }, - { - .hook = ip_conntrack_local, - .owner = THIS_MODULE, - .pf = PF_INET, - .hooknum = NF_IP_LOCAL_OUT, - .priority = NF_IP_PRI_CONNTRACK, - }, - { - .hook = ip_conntrack_help, - .owner = THIS_MODULE, - .pf = PF_INET, - .hooknum = NF_IP_POST_ROUTING, - .priority = NF_IP_PRI_CONNTRACK_HELPER, - }, - { - .hook = ip_conntrack_help, - .owner = THIS_MODULE, - .pf = PF_INET, - .hooknum = NF_IP_LOCAL_IN, - .priority = NF_IP_PRI_CONNTRACK_HELPER, - }, - { - .hook = ip_confirm, - .owner = THIS_MODULE, - .pf = PF_INET, - .hooknum = NF_IP_POST_ROUTING, - .priority = NF_IP_PRI_CONNTRACK_CONFIRM, - }, - { - .hook = ip_confirm, - .owner = THIS_MODULE, - .pf = PF_INET, - .hooknum = NF_IP_LOCAL_IN, - .priority = NF_IP_PRI_CONNTRACK_CONFIRM, - }, -}; - -/* Sysctl support */ - -int ip_conntrack_checksum __read_mostly = 1; - -#ifdef CONFIG_SYSCTL - -/* From ip_conntrack_core.c */ -extern int ip_conntrack_max; -extern unsigned int ip_conntrack_htable_size; - -/* From ip_conntrack_proto_tcp.c */ -extern unsigned int ip_ct_tcp_timeout_syn_sent; -extern unsigned int ip_ct_tcp_timeout_syn_recv; -extern unsigned int ip_ct_tcp_timeout_established; -extern unsigned int ip_ct_tcp_timeout_fin_wait; -extern unsigned int ip_ct_tcp_timeout_close_wait; -extern unsigned int ip_ct_tcp_timeout_last_ack; -extern unsigned int ip_ct_tcp_timeout_time_wait; -extern unsigned int ip_ct_tcp_timeout_close; -extern unsigned int ip_ct_tcp_timeout_max_retrans; -extern int ip_ct_tcp_loose; -extern int ip_ct_tcp_be_liberal; -extern int ip_ct_tcp_max_retrans; - -/* From ip_conntrack_proto_udp.c */ -extern unsigned int ip_ct_udp_timeout; -extern unsigned int ip_ct_udp_timeout_stream; - -/* From ip_conntrack_proto_icmp.c */ -extern unsigned int ip_ct_icmp_timeout; - -/* From ip_conntrack_proto_generic.c */ -extern unsigned int ip_ct_generic_timeout; - -/* Log invalid packets of a given protocol */ -static int log_invalid_proto_min = 0; -static int log_invalid_proto_max = 255; - -static struct ctl_table_header *ip_ct_sysctl_header; - -static ctl_table ip_ct_sysctl_table[] = { - { - .ctl_name = NET_IPV4_NF_CONNTRACK_MAX, - .procname = "ip_conntrack_max", - .data = &ip_conntrack_max, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = &proc_dointvec, - }, - { - .ctl_name = NET_IPV4_NF_CONNTRACK_COUNT, - .procname = "ip_conntrack_count", - .data = &ip_conntrack_count, - .maxlen = sizeof(int), - .mode = 0444, - .proc_handler = &proc_dointvec, - }, - { - .ctl_name = NET_IPV4_NF_CONNTRACK_BUCKETS, - .procname = "ip_conntrack_buckets", - .data = &ip_conntrack_htable_size, - .maxlen = sizeof(unsigned int), - .mode = 0444, - .proc_handler = &proc_dointvec, - }, - { - .ctl_name = NET_IPV4_NF_CONNTRACK_CHECKSUM, - .procname = "ip_conntrack_checksum", - .data = &ip_conntrack_checksum, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = &proc_dointvec, - }, - { - .ctl_name = NET_IPV4_NF_CONNTRACK_TCP_TIMEOUT_SYN_SENT, - .procname = "ip_conntrack_tcp_timeout_syn_sent", - .data = &ip_ct_tcp_timeout_syn_sent, - .maxlen = sizeof(unsigned int), - .mode = 0644, - .proc_handler = &proc_dointvec_jiffies, - }, - { - .ctl_name = NET_IPV4_NF_CONNTRACK_TCP_TIMEOUT_SYN_RECV, - .procname = "ip_conntrack_tcp_timeout_syn_recv", - .data = &ip_ct_tcp_timeout_syn_recv, - .maxlen = sizeof(unsigned int), - .mode = 0644, - .proc_handler = &proc_dointvec_jiffies, - }, - { - .ctl_name = NET_IPV4_NF_CONNTRACK_TCP_TIMEOUT_ESTABLISHED, - .procname = "ip_conntrack_tcp_timeout_established", - .data = &ip_ct_tcp_timeout_established, - .maxlen = sizeof(unsigned int), - .mode = 0644, - .proc_handler = &proc_dointvec_jiffies, - }, - { - .ctl_name = NET_IPV4_NF_CONNTRACK_TCP_TIMEOUT_FIN_WAIT, - .procname = "ip_conntrack_tcp_timeout_fin_wait", - .data = &ip_ct_tcp_timeout_fin_wait, - .maxlen = sizeof(unsigned int), - .mode = 0644, - .proc_handler = &proc_dointvec_jiffies, - }, - { - .ctl_name = NET_IPV4_NF_CONNTRACK_TCP_TIMEOUT_CLOSE_WAIT, - .procname = "ip_conntrack_tcp_timeout_close_wait", - .data = &ip_ct_tcp_timeout_close_wait, - .maxlen = sizeof(unsigned int), - .mode = 0644, - .proc_handler = &proc_dointvec_jiffies, - }, - { - .ctl_name = NET_IPV4_NF_CONNTRACK_TCP_TIMEOUT_LAST_ACK, - .procname = "ip_conntrack_tcp_timeout_last_ack", - .data = &ip_ct_tcp_timeout_last_ack, - .maxlen = sizeof(unsigned int), - .mode = 0644, - .proc_handler = &proc_dointvec_jiffies, - }, - { - .ctl_name = NET_IPV4_NF_CONNTRACK_TCP_TIMEOUT_TIME_WAIT, - .procname = "ip_conntrack_tcp_timeout_time_wait", - .data = &ip_ct_tcp_timeout_time_wait, - .maxlen = sizeof(unsigned int), - .mode = 0644, - .proc_handler = &proc_dointvec_jiffies, - }, - { - .ctl_name = NET_IPV4_NF_CONNTRACK_TCP_TIMEOUT_CLOSE, - .procname = "ip_conntrack_tcp_timeout_close", - .data = &ip_ct_tcp_timeout_close, - .maxlen = sizeof(unsigned int), - .mode = 0644, - .proc_handler = &proc_dointvec_jiffies, - }, - { - .ctl_name = NET_IPV4_NF_CONNTRACK_UDP_TIMEOUT, - .procname = "ip_conntrack_udp_timeout", - .data = &ip_ct_udp_timeout, - .maxlen = sizeof(unsigned int), - .mode = 0644, - .proc_handler = &proc_dointvec_jiffies, - }, - { - .ctl_name = NET_IPV4_NF_CONNTRACK_UDP_TIMEOUT_STREAM, - .procname = "ip_conntrack_udp_timeout_stream", - .data = &ip_ct_udp_timeout_stream, - .maxlen = sizeof(unsigned int), - .mode = 0644, - .proc_handler = &proc_dointvec_jiffies, - }, - { - .ctl_name = NET_IPV4_NF_CONNTRACK_ICMP_TIMEOUT, - .procname = "ip_conntrack_icmp_timeout", - .data = &ip_ct_icmp_timeout, - .maxlen = sizeof(unsigned int), - .mode = 0644, - .proc_handler = &proc_dointvec_jiffies, - }, - { - .ctl_name = NET_IPV4_NF_CONNTRACK_GENERIC_TIMEOUT, - .procname = "ip_conntrack_generic_timeout", - .data = &ip_ct_generic_timeout, - .maxlen = sizeof(unsigned int), - .mode = 0644, - .proc_handler = &proc_dointvec_jiffies, - }, - { - .ctl_name = NET_IPV4_NF_CONNTRACK_LOG_INVALID, - .procname = "ip_conntrack_log_invalid", - .data = &ip_ct_log_invalid, - .maxlen = sizeof(unsigned int), - .mode = 0644, - .proc_handler = &proc_dointvec_minmax, - .strategy = &sysctl_intvec, - .extra1 = &log_invalid_proto_min, - .extra2 = &log_invalid_proto_max, - }, - { - .ctl_name = NET_IPV4_NF_CONNTRACK_TCP_TIMEOUT_MAX_RETRANS, - .procname = "ip_conntrack_tcp_timeout_max_retrans", - .data = &ip_ct_tcp_timeout_max_retrans, - .maxlen = sizeof(unsigned int), - .mode = 0644, - .proc_handler = &proc_dointvec_jiffies, - }, - { - .ctl_name = NET_IPV4_NF_CONNTRACK_TCP_LOOSE, - .procname = "ip_conntrack_tcp_loose", - .data = &ip_ct_tcp_loose, - .maxlen = sizeof(unsigned int), - .mode = 0644, - .proc_handler = &proc_dointvec, - }, - { - .ctl_name = NET_IPV4_NF_CONNTRACK_TCP_BE_LIBERAL, - .procname = "ip_conntrack_tcp_be_liberal", - .data = &ip_ct_tcp_be_liberal, - .maxlen = sizeof(unsigned int), - .mode = 0644, - .proc_handler = &proc_dointvec, - }, - { - .ctl_name = NET_IPV4_NF_CONNTRACK_TCP_MAX_RETRANS, - .procname = "ip_conntrack_tcp_max_retrans", - .data = &ip_ct_tcp_max_retrans, - .maxlen = sizeof(unsigned int), - .mode = 0644, - .proc_handler = &proc_dointvec, - }, - { .ctl_name = 0 } -}; - -#define NET_IP_CONNTRACK_MAX 2089 - -static ctl_table ip_ct_netfilter_table[] = { - { - .ctl_name = NET_IPV4_NETFILTER, - .procname = "netfilter", - .mode = 0555, - .child = ip_ct_sysctl_table, - }, - { - .ctl_name = NET_IP_CONNTRACK_MAX, - .procname = "ip_conntrack_max", - .data = &ip_conntrack_max, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = &proc_dointvec - }, - { .ctl_name = 0 } -}; - -static ctl_table ip_ct_ipv4_table[] = { - { - .ctl_name = NET_IPV4, - .procname = "ipv4", - .mode = 0555, - .child = ip_ct_netfilter_table, - }, - { .ctl_name = 0 } -}; - -static ctl_table ip_ct_net_table[] = { - { - .ctl_name = CTL_NET, - .procname = "net", - .mode = 0555, - .child = ip_ct_ipv4_table, - }, - { .ctl_name = 0 } -}; - -EXPORT_SYMBOL(ip_ct_log_invalid); -#endif /* CONFIG_SYSCTL */ - -/* FIXME: Allow NULL functions and sub in pointers to generic for - them. --RR */ -int ip_conntrack_protocol_register(struct ip_conntrack_protocol *proto) -{ - int ret = 0; - - write_lock_bh(&ip_conntrack_lock); - if (ip_ct_protos[proto->proto] != &ip_conntrack_generic_protocol) { - ret = -EBUSY; - goto out; - } - rcu_assign_pointer(ip_ct_protos[proto->proto], proto); - out: - write_unlock_bh(&ip_conntrack_lock); - return ret; -} - -void ip_conntrack_protocol_unregister(struct ip_conntrack_protocol *proto) -{ - write_lock_bh(&ip_conntrack_lock); - rcu_assign_pointer(ip_ct_protos[proto->proto], - &ip_conntrack_generic_protocol); - write_unlock_bh(&ip_conntrack_lock); - synchronize_rcu(); - - /* Remove all contrack entries for this protocol */ - ip_ct_iterate_cleanup(kill_proto, &proto->proto); -} - -static int __init ip_conntrack_standalone_init(void) -{ -#ifdef CONFIG_PROC_FS - struct proc_dir_entry *proc, *proc_exp, *proc_stat; -#endif - int ret = 0; - - ret = ip_conntrack_init(); - if (ret < 0) - return ret; - -#ifdef CONFIG_PROC_FS - ret = -ENOMEM; - proc = proc_net_fops_create("ip_conntrack", 0440, &ct_file_ops); - if (!proc) goto cleanup_init; - - proc_exp = proc_net_fops_create("ip_conntrack_expect", 0440, - &exp_file_ops); - if (!proc_exp) goto cleanup_proc; - - proc_stat = create_proc_entry("ip_conntrack", S_IRUGO, proc_net_stat); - if (!proc_stat) - goto cleanup_proc_exp; - - proc_stat->proc_fops = &ct_cpu_seq_fops; - proc_stat->owner = THIS_MODULE; -#endif - - ret = nf_register_hooks(ip_conntrack_ops, ARRAY_SIZE(ip_conntrack_ops)); - if (ret < 0) { - printk("ip_conntrack: can't register hooks.\n"); - goto cleanup_proc_stat; - } -#ifdef CONFIG_SYSCTL - ip_ct_sysctl_header = register_sysctl_table(ip_ct_net_table); - if (ip_ct_sysctl_header == NULL) { - printk("ip_conntrack: can't register to sysctl.\n"); - ret = -ENOMEM; - goto cleanup_hooks; - } -#endif - return ret; - -#ifdef CONFIG_SYSCTL - cleanup_hooks: - nf_unregister_hooks(ip_conntrack_ops, ARRAY_SIZE(ip_conntrack_ops)); -#endif - cleanup_proc_stat: -#ifdef CONFIG_PROC_FS - remove_proc_entry("ip_conntrack", proc_net_stat); - cleanup_proc_exp: - proc_net_remove("ip_conntrack_expect"); - cleanup_proc: - proc_net_remove("ip_conntrack"); - cleanup_init: -#endif /* CONFIG_PROC_FS */ - ip_conntrack_cleanup(); - return ret; -} - -static void __exit ip_conntrack_standalone_fini(void) -{ - synchronize_net(); -#ifdef CONFIG_SYSCTL - unregister_sysctl_table(ip_ct_sysctl_header); -#endif - nf_unregister_hooks(ip_conntrack_ops, ARRAY_SIZE(ip_conntrack_ops)); -#ifdef CONFIG_PROC_FS - remove_proc_entry("ip_conntrack", proc_net_stat); - proc_net_remove("ip_conntrack_expect"); - proc_net_remove("ip_conntrack"); -#endif /* CONFIG_PROC_FS */ - ip_conntrack_cleanup(); -} - -module_init(ip_conntrack_standalone_init); -module_exit(ip_conntrack_standalone_fini); - -/* Some modules need us, but don't depend directly on any symbol. - They should call this. */ -void need_conntrack(void) -{ -} - -#ifdef CONFIG_IP_NF_CONNTRACK_EVENTS -EXPORT_SYMBOL_GPL(ip_conntrack_chain); -EXPORT_SYMBOL_GPL(ip_conntrack_expect_chain); -EXPORT_SYMBOL_GPL(ip_conntrack_register_notifier); -EXPORT_SYMBOL_GPL(ip_conntrack_unregister_notifier); -EXPORT_SYMBOL_GPL(__ip_ct_event_cache_init); -EXPORT_PER_CPU_SYMBOL_GPL(ip_conntrack_ecache); -#endif -EXPORT_SYMBOL(ip_conntrack_protocol_register); -EXPORT_SYMBOL(ip_conntrack_protocol_unregister); -EXPORT_SYMBOL(ip_ct_get_tuple); -EXPORT_SYMBOL(invert_tuplepr); -EXPORT_SYMBOL(ip_conntrack_alter_reply); -EXPORT_SYMBOL(ip_conntrack_destroyed); -EXPORT_SYMBOL(need_conntrack); -EXPORT_SYMBOL(ip_conntrack_helper_register); -EXPORT_SYMBOL(ip_conntrack_helper_unregister); -EXPORT_SYMBOL(ip_ct_iterate_cleanup); -EXPORT_SYMBOL(__ip_ct_refresh_acct); - -EXPORT_SYMBOL(ip_conntrack_expect_alloc); -EXPORT_SYMBOL(ip_conntrack_expect_put); -EXPORT_SYMBOL_GPL(__ip_conntrack_expect_find); -EXPORT_SYMBOL_GPL(ip_conntrack_expect_find_get); -EXPORT_SYMBOL(ip_conntrack_expect_related); -EXPORT_SYMBOL(ip_conntrack_unexpect_related); -EXPORT_SYMBOL_GPL(ip_conntrack_expect_list); -EXPORT_SYMBOL_GPL(ip_ct_unlink_expect); - -EXPORT_SYMBOL(ip_conntrack_tuple_taken); -EXPORT_SYMBOL(ip_ct_gather_frags); -EXPORT_SYMBOL(ip_conntrack_htable_size); -EXPORT_SYMBOL(ip_conntrack_lock); -EXPORT_SYMBOL(ip_conntrack_hash); -EXPORT_SYMBOL(ip_conntrack_untracked); -EXPORT_SYMBOL_GPL(ip_conntrack_find_get); -#ifdef CONFIG_IP_NF_NAT_NEEDED -EXPORT_SYMBOL(ip_conntrack_tcp_update); -#endif - -EXPORT_SYMBOL_GPL(ip_conntrack_flush); -EXPORT_SYMBOL_GPL(__ip_conntrack_find); - -EXPORT_SYMBOL_GPL(ip_conntrack_alloc); -EXPORT_SYMBOL_GPL(ip_conntrack_free); -EXPORT_SYMBOL_GPL(ip_conntrack_hash_insert); - -EXPORT_SYMBOL_GPL(ip_ct_remove_expectations); - -EXPORT_SYMBOL_GPL(ip_conntrack_helper_find_get); -EXPORT_SYMBOL_GPL(ip_conntrack_helper_put); -EXPORT_SYMBOL_GPL(__ip_conntrack_helper_find_byname); - -EXPORT_SYMBOL_GPL(ip_conntrack_proto_find_get); -EXPORT_SYMBOL_GPL(ip_conntrack_proto_put); -EXPORT_SYMBOL_GPL(__ip_conntrack_proto_find); -EXPORT_SYMBOL_GPL(ip_conntrack_checksum); -#if defined(CONFIG_IP_NF_CONNTRACK_NETLINK) || \ - defined(CONFIG_IP_NF_CONNTRACK_NETLINK_MODULE) -EXPORT_SYMBOL_GPL(ip_ct_port_tuple_to_nfattr); -EXPORT_SYMBOL_GPL(ip_ct_port_nfattr_to_tuple); -#endif diff --git a/net/ipv4/netfilter/ip_conntrack_tftp.c b/net/ipv4/netfilter/ip_conntrack_tftp.c deleted file mode 100644 index afc6809a3888..000000000000 --- a/net/ipv4/netfilter/ip_conntrack_tftp.c +++ /dev/null @@ -1,161 +0,0 @@ -/* (C) 2001-2002 Magnus Boden - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Version: 0.0.7 - * - * Thu 21 Mar 2002 Harald Welte - * - port to newnat API - * - */ - -#include -#include -#include - -#include -#include -#include -#include -#include - -MODULE_AUTHOR("Magnus Boden "); -MODULE_DESCRIPTION("tftp connection tracking helper"); -MODULE_LICENSE("GPL"); - -#define MAX_PORTS 8 -static unsigned short ports[MAX_PORTS]; -static int ports_c; -module_param_array(ports, ushort, &ports_c, 0400); -MODULE_PARM_DESC(ports, "port numbers of tftp servers"); - -#if 0 -#define DEBUGP(format, args...) printk("%s:%s:" format, \ - __FILE__, __FUNCTION__ , ## args) -#else -#define DEBUGP(format, args...) -#endif - -unsigned int (*ip_nat_tftp_hook)(struct sk_buff **pskb, - enum ip_conntrack_info ctinfo, - struct ip_conntrack_expect *exp); -EXPORT_SYMBOL_GPL(ip_nat_tftp_hook); - -static int tftp_help(struct sk_buff **pskb, - struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo) -{ - struct tftphdr _tftph, *tfh; - struct ip_conntrack_expect *exp; - unsigned int ret = NF_ACCEPT; - typeof(ip_nat_tftp_hook) ip_nat_tftp; - - tfh = skb_header_pointer(*pskb, - ip_hdrlen(*pskb) + sizeof(struct udphdr), - sizeof(_tftph), &_tftph); - if (tfh == NULL) - return NF_ACCEPT; - - switch (ntohs(tfh->opcode)) { - /* RRQ and WRQ works the same way */ - case TFTP_OPCODE_READ: - case TFTP_OPCODE_WRITE: - DEBUGP(""); - DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple); - DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_REPLY].tuple); - - exp = ip_conntrack_expect_alloc(ct); - if (exp == NULL) - return NF_DROP; - - exp->tuple = ct->tuplehash[IP_CT_DIR_REPLY].tuple; - exp->mask.src.ip = htonl(0xffffffff); - exp->mask.src.u.udp.port = 0; - exp->mask.dst.ip = htonl(0xffffffff); - exp->mask.dst.u.udp.port = htons(0xffff); - exp->mask.dst.protonum = 0xff; - exp->expectfn = NULL; - exp->flags = 0; - - DEBUGP("expect: "); - DUMP_TUPLE(&exp->tuple); - DUMP_TUPLE(&exp->mask); - ip_nat_tftp = rcu_dereference(ip_nat_tftp_hook); - if (ip_nat_tftp) - ret = ip_nat_tftp(pskb, ctinfo, exp); - else if (ip_conntrack_expect_related(exp) != 0) - ret = NF_DROP; - ip_conntrack_expect_put(exp); - break; - case TFTP_OPCODE_DATA: - case TFTP_OPCODE_ACK: - DEBUGP("Data/ACK opcode\n"); - break; - case TFTP_OPCODE_ERROR: - DEBUGP("Error opcode\n"); - break; - default: - DEBUGP("Unknown opcode\n"); - } - return NF_ACCEPT; -} - -static struct ip_conntrack_helper tftp[MAX_PORTS]; -static char tftp_names[MAX_PORTS][sizeof("tftp-65535")]; - -static void ip_conntrack_tftp_fini(void) -{ - int i; - - for (i = 0 ; i < ports_c; i++) { - DEBUGP("unregistering helper for port %d\n", - ports[i]); - ip_conntrack_helper_unregister(&tftp[i]); - } -} - -static int __init ip_conntrack_tftp_init(void) -{ - int i, ret; - char *tmpname; - - if (ports_c == 0) - ports[ports_c++] = TFTP_PORT; - - for (i = 0; i < ports_c; i++) { - /* Create helper structure */ - memset(&tftp[i], 0, sizeof(struct ip_conntrack_helper)); - - tftp[i].tuple.dst.protonum = IPPROTO_UDP; - tftp[i].tuple.src.u.udp.port = htons(ports[i]); - tftp[i].mask.dst.protonum = 0xFF; - tftp[i].mask.src.u.udp.port = htons(0xFFFF); - tftp[i].max_expected = 1; - tftp[i].timeout = 5 * 60; /* 5 minutes */ - tftp[i].me = THIS_MODULE; - tftp[i].help = tftp_help; - - tmpname = &tftp_names[i][0]; - if (ports[i] == TFTP_PORT) - sprintf(tmpname, "tftp"); - else - sprintf(tmpname, "tftp-%d", i); - tftp[i].name = tmpname; - - DEBUGP("port #%d: %d\n", i, ports[i]); - - ret=ip_conntrack_helper_register(&tftp[i]); - if (ret) { - printk("ERROR registering helper for port %d\n", - ports[i]); - ip_conntrack_tftp_fini(); - return(ret); - } - } - return(0); -} - -module_init(ip_conntrack_tftp_init); -module_exit(ip_conntrack_tftp_fini); diff --git a/net/ipv4/netfilter/ip_nat_amanda.c b/net/ipv4/netfilter/ip_nat_amanda.c deleted file mode 100644 index 85df1a9aed33..000000000000 --- a/net/ipv4/netfilter/ip_nat_amanda.c +++ /dev/null @@ -1,85 +0,0 @@ -/* Amanda extension for TCP NAT alteration. - * (C) 2002 by Brian J. Murrell - * based on a copy of HW's ip_nat_irc.c as well as other modules - * - * 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. - * - * Module load syntax: - * insmod ip_nat_amanda.o - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - - -MODULE_AUTHOR("Brian J. Murrell "); -MODULE_DESCRIPTION("Amanda NAT helper"); -MODULE_LICENSE("GPL"); - -static unsigned int help(struct sk_buff **pskb, - enum ip_conntrack_info ctinfo, - unsigned int matchoff, - unsigned int matchlen, - struct ip_conntrack_expect *exp) -{ - char buffer[sizeof("65535")]; - u_int16_t port; - unsigned int ret; - - /* Connection comes from client. */ - exp->saved_proto.tcp.port = exp->tuple.dst.u.tcp.port; - exp->dir = IP_CT_DIR_ORIGINAL; - - /* When you see the packet, we need to NAT it the same as the - * this one (ie. same IP: it will be TCP and master is UDP). */ - exp->expectfn = ip_nat_follow_master; - - /* Try to get same port: if not, try to change it. */ - for (port = ntohs(exp->saved_proto.tcp.port); port != 0; port++) { - exp->tuple.dst.u.tcp.port = htons(port); - if (ip_conntrack_expect_related(exp) == 0) - break; - } - - if (port == 0) - return NF_DROP; - - sprintf(buffer, "%u", port); - ret = ip_nat_mangle_udp_packet(pskb, exp->master, ctinfo, - matchoff, matchlen, - buffer, strlen(buffer)); - if (ret != NF_ACCEPT) - ip_conntrack_unexpect_related(exp); - return ret; -} - -static void __exit ip_nat_amanda_fini(void) -{ - rcu_assign_pointer(ip_nat_amanda_hook, NULL); - synchronize_rcu(); -} - -static int __init ip_nat_amanda_init(void) -{ - BUG_ON(rcu_dereference(ip_nat_amanda_hook)); - rcu_assign_pointer(ip_nat_amanda_hook, help); - return 0; -} - -module_init(ip_nat_amanda_init); -module_exit(ip_nat_amanda_fini); diff --git a/net/ipv4/netfilter/ip_nat_core.c b/net/ipv4/netfilter/ip_nat_core.c deleted file mode 100644 index cf46930606f2..000000000000 --- a/net/ipv4/netfilter/ip_nat_core.c +++ /dev/null @@ -1,633 +0,0 @@ -/* NAT for netfilter; shared with compatibility layer. */ - -/* (C) 1999-2001 Paul `Rusty' Russell - * (C) 2002-2004 Netfilter Core Team - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include /* For tcp_prot in getorigdst */ -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#if 0 -#define DEBUGP printk -#else -#define DEBUGP(format, args...) -#endif - -DEFINE_RWLOCK(ip_nat_lock); - -/* Calculated at init based on memory size */ -static unsigned int ip_nat_htable_size; - -static struct list_head *bysource; - -#define MAX_IP_NAT_PROTO 256 -static struct ip_nat_protocol *ip_nat_protos[MAX_IP_NAT_PROTO]; - -static inline struct ip_nat_protocol * -__ip_nat_proto_find(u_int8_t protonum) -{ - return rcu_dereference(ip_nat_protos[protonum]); -} - -struct ip_nat_protocol * -ip_nat_proto_find_get(u_int8_t protonum) -{ - struct ip_nat_protocol *p; - - rcu_read_lock(); - p = __ip_nat_proto_find(protonum); - if (!try_module_get(p->me)) - p = &ip_nat_unknown_protocol; - rcu_read_unlock(); - - return p; -} -EXPORT_SYMBOL_GPL(ip_nat_proto_find_get); - -void -ip_nat_proto_put(struct ip_nat_protocol *p) -{ - module_put(p->me); -} -EXPORT_SYMBOL_GPL(ip_nat_proto_put); - -/* We keep an extra hash for each conntrack, for fast searching. */ -static inline unsigned int -hash_by_src(const struct ip_conntrack_tuple *tuple) -{ - /* Original src, to ensure we map it consistently if poss. */ - return jhash_3words((__force u32)tuple->src.ip, tuple->src.u.all, - tuple->dst.protonum, 0) % ip_nat_htable_size; -} - -/* Noone using conntrack by the time this called. */ -static void ip_nat_cleanup_conntrack(struct ip_conntrack *conn) -{ - if (!(conn->status & IPS_NAT_DONE_MASK)) - return; - - write_lock_bh(&ip_nat_lock); - list_del(&conn->nat.info.bysource); - write_unlock_bh(&ip_nat_lock); -} - -/* Is this tuple already taken? (not by us) */ -int -ip_nat_used_tuple(const struct ip_conntrack_tuple *tuple, - const struct ip_conntrack *ignored_conntrack) -{ - /* Conntrack tracking doesn't keep track of outgoing tuples; only - incoming ones. NAT means they don't have a fixed mapping, - so we invert the tuple and look for the incoming reply. - - We could keep a separate hash if this proves too slow. */ - struct ip_conntrack_tuple reply; - - invert_tuplepr(&reply, tuple); - return ip_conntrack_tuple_taken(&reply, ignored_conntrack); -} -EXPORT_SYMBOL(ip_nat_used_tuple); - -/* If we source map this tuple so reply looks like reply_tuple, will - * that meet the constraints of range. */ -static int -in_range(const struct ip_conntrack_tuple *tuple, - const struct ip_nat_range *range) -{ - struct ip_nat_protocol *proto; - int ret = 0; - - /* If we are supposed to map IPs, then we must be in the - range specified, otherwise let this drag us onto a new src IP. */ - if (range->flags & IP_NAT_RANGE_MAP_IPS) { - if (ntohl(tuple->src.ip) < ntohl(range->min_ip) - || ntohl(tuple->src.ip) > ntohl(range->max_ip)) - return 0; - } - - rcu_read_lock(); - proto = __ip_nat_proto_find(tuple->dst.protonum); - if (!(range->flags & IP_NAT_RANGE_PROTO_SPECIFIED) - || proto->in_range(tuple, IP_NAT_MANIP_SRC, - &range->min, &range->max)) - ret = 1; - rcu_read_unlock(); - - return ret; -} - -static inline int -same_src(const struct ip_conntrack *ct, - const struct ip_conntrack_tuple *tuple) -{ - return (ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum - == tuple->dst.protonum - && ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip - == tuple->src.ip - && ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u.all - == tuple->src.u.all); -} - -/* Only called for SRC manip */ -static int -find_appropriate_src(const struct ip_conntrack_tuple *tuple, - struct ip_conntrack_tuple *result, - const struct ip_nat_range *range) -{ - unsigned int h = hash_by_src(tuple); - struct ip_conntrack *ct; - - read_lock_bh(&ip_nat_lock); - list_for_each_entry(ct, &bysource[h], nat.info.bysource) { - if (same_src(ct, tuple)) { - /* Copy source part from reply tuple. */ - invert_tuplepr(result, - &ct->tuplehash[IP_CT_DIR_REPLY].tuple); - result->dst = tuple->dst; - - if (in_range(result, range)) { - read_unlock_bh(&ip_nat_lock); - return 1; - } - } - } - read_unlock_bh(&ip_nat_lock); - return 0; -} - -/* For [FUTURE] fragmentation handling, we want the least-used - src-ip/dst-ip/proto triple. Fairness doesn't come into it. Thus - if the range specifies 1.2.3.4 ports 10000-10005 and 1.2.3.5 ports - 1-65535, we don't do pro-rata allocation based on ports; we choose - the ip with the lowest src-ip/dst-ip/proto usage. -*/ -static void -find_best_ips_proto(struct ip_conntrack_tuple *tuple, - const struct ip_nat_range *range, - const struct ip_conntrack *conntrack, - enum ip_nat_manip_type maniptype) -{ - __be32 *var_ipp; - /* Host order */ - u_int32_t minip, maxip, j; - - /* No IP mapping? Do nothing. */ - if (!(range->flags & IP_NAT_RANGE_MAP_IPS)) - return; - - if (maniptype == IP_NAT_MANIP_SRC) - var_ipp = &tuple->src.ip; - else - var_ipp = &tuple->dst.ip; - - /* Fast path: only one choice. */ - if (range->min_ip == range->max_ip) { - *var_ipp = range->min_ip; - return; - } - - /* Hashing source and destination IPs gives a fairly even - * spread in practice (if there are a small number of IPs - * involved, there usually aren't that many connections - * anyway). The consistency means that servers see the same - * client coming from the same IP (some Internet Banking sites - * like this), even across reboots. */ - minip = ntohl(range->min_ip); - maxip = ntohl(range->max_ip); - j = jhash_2words((__force u32)tuple->src.ip, (__force u32)tuple->dst.ip, 0); - *var_ipp = htonl(minip + j % (maxip - minip + 1)); -} - -/* Manipulate the tuple into the range given. For NF_IP_POST_ROUTING, - * we change the source to map into the range. For NF_IP_PRE_ROUTING - * and NF_IP_LOCAL_OUT, we change the destination to map into the - * range. It might not be possible to get a unique tuple, but we try. - * At worst (or if we race), we will end up with a final duplicate in - * __ip_conntrack_confirm and drop the packet. */ -static void -get_unique_tuple(struct ip_conntrack_tuple *tuple, - const struct ip_conntrack_tuple *orig_tuple, - const struct ip_nat_range *range, - struct ip_conntrack *conntrack, - enum ip_nat_manip_type maniptype) -{ - struct ip_nat_protocol *proto; - - /* 1) If this srcip/proto/src-proto-part is currently mapped, - and that same mapping gives a unique tuple within the given - range, use that. - - This is only required for source (ie. NAT/masq) mappings. - So far, we don't do local source mappings, so multiple - manips not an issue. */ - if (maniptype == IP_NAT_MANIP_SRC) { - if (find_appropriate_src(orig_tuple, tuple, range)) { - DEBUGP("get_unique_tuple: Found current src map\n"); - if (!(range->flags & IP_NAT_RANGE_PROTO_RANDOM)) - if (!ip_nat_used_tuple(tuple, conntrack)) - return; - } - } - - /* 2) Select the least-used IP/proto combination in the given - range. */ - *tuple = *orig_tuple; - find_best_ips_proto(tuple, range, conntrack, maniptype); - - /* 3) The per-protocol part of the manip is made to map into - the range to make a unique tuple. */ - - rcu_read_lock(); - proto = __ip_nat_proto_find(orig_tuple->dst.protonum); - - /* Change protocol info to have some randomization */ - if (range->flags & IP_NAT_RANGE_PROTO_RANDOM) { - proto->unique_tuple(tuple, range, maniptype, conntrack); - goto out; - } - - /* Only bother mapping if it's not already in range and unique */ - if ((!(range->flags & IP_NAT_RANGE_PROTO_SPECIFIED) - || proto->in_range(tuple, maniptype, &range->min, &range->max)) - && !ip_nat_used_tuple(tuple, conntrack)) - goto out; - - /* Last change: get protocol to try to obtain unique tuple. */ - proto->unique_tuple(tuple, range, maniptype, conntrack); -out: - rcu_read_unlock(); -} - -unsigned int -ip_nat_setup_info(struct ip_conntrack *conntrack, - const struct ip_nat_range *range, - unsigned int hooknum) -{ - struct ip_conntrack_tuple curr_tuple, new_tuple; - struct ip_nat_info *info = &conntrack->nat.info; - int have_to_hash = !(conntrack->status & IPS_NAT_DONE_MASK); - enum ip_nat_manip_type maniptype = HOOK2MANIP(hooknum); - - IP_NF_ASSERT(hooknum == NF_IP_PRE_ROUTING - || hooknum == NF_IP_POST_ROUTING - || hooknum == NF_IP_LOCAL_IN - || hooknum == NF_IP_LOCAL_OUT); - BUG_ON(ip_nat_initialized(conntrack, maniptype)); - - /* What we've got will look like inverse of reply. Normally - this is what is in the conntrack, except for prior - manipulations (future optimization: if num_manips == 0, - orig_tp = - conntrack->tuplehash[IP_CT_DIR_ORIGINAL].tuple) */ - invert_tuplepr(&curr_tuple, - &conntrack->tuplehash[IP_CT_DIR_REPLY].tuple); - - get_unique_tuple(&new_tuple, &curr_tuple, range, conntrack, maniptype); - - if (!ip_ct_tuple_equal(&new_tuple, &curr_tuple)) { - struct ip_conntrack_tuple reply; - - /* Alter conntrack table so will recognize replies. */ - invert_tuplepr(&reply, &new_tuple); - ip_conntrack_alter_reply(conntrack, &reply); - - /* Non-atomic: we own this at the moment. */ - if (maniptype == IP_NAT_MANIP_SRC) - conntrack->status |= IPS_SRC_NAT; - else - conntrack->status |= IPS_DST_NAT; - } - - /* Place in source hash if this is the first time. */ - if (have_to_hash) { - unsigned int srchash - = hash_by_src(&conntrack->tuplehash[IP_CT_DIR_ORIGINAL] - .tuple); - write_lock_bh(&ip_nat_lock); - list_add(&info->bysource, &bysource[srchash]); - write_unlock_bh(&ip_nat_lock); - } - - /* It's done. */ - if (maniptype == IP_NAT_MANIP_DST) - set_bit(IPS_DST_NAT_DONE_BIT, &conntrack->status); - else - set_bit(IPS_SRC_NAT_DONE_BIT, &conntrack->status); - - return NF_ACCEPT; -} -EXPORT_SYMBOL(ip_nat_setup_info); - -/* Returns true if succeeded. */ -static int -manip_pkt(u_int16_t proto, - struct sk_buff **pskb, - unsigned int iphdroff, - const struct ip_conntrack_tuple *target, - enum ip_nat_manip_type maniptype) -{ - struct iphdr *iph; - struct ip_nat_protocol *p; - - if (!skb_make_writable(pskb, iphdroff + sizeof(*iph))) - return 0; - - iph = (void *)(*pskb)->data + iphdroff; - - /* Manipulate protcol part. */ - - /* rcu_read_lock()ed by nf_hook_slow */ - p = __ip_nat_proto_find(proto); - if (!p->manip_pkt(pskb, iphdroff, target, maniptype)) - return 0; - - iph = (void *)(*pskb)->data + iphdroff; - - if (maniptype == IP_NAT_MANIP_SRC) { - nf_csum_replace4(&iph->check, iph->saddr, target->src.ip); - iph->saddr = target->src.ip; - } else { - nf_csum_replace4(&iph->check, iph->daddr, target->dst.ip); - iph->daddr = target->dst.ip; - } - return 1; -} - -/* Do packet manipulations according to ip_nat_setup_info. */ -unsigned int ip_nat_packet(struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo, - unsigned int hooknum, - struct sk_buff **pskb) -{ - enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); - unsigned long statusbit; - enum ip_nat_manip_type mtype = HOOK2MANIP(hooknum); - - if (mtype == IP_NAT_MANIP_SRC) - statusbit = IPS_SRC_NAT; - else - statusbit = IPS_DST_NAT; - - /* Invert if this is reply dir. */ - if (dir == IP_CT_DIR_REPLY) - statusbit ^= IPS_NAT_MASK; - - /* Non-atomic: these bits don't change. */ - if (ct->status & statusbit) { - struct ip_conntrack_tuple target; - - /* We are aiming to look like inverse of other direction. */ - invert_tuplepr(&target, &ct->tuplehash[!dir].tuple); - - if (!manip_pkt(target.dst.protonum, pskb, 0, &target, mtype)) - return NF_DROP; - } - return NF_ACCEPT; -} -EXPORT_SYMBOL_GPL(ip_nat_packet); - -/* Dir is direction ICMP is coming from (opposite to packet it contains) */ -int ip_nat_icmp_reply_translation(struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo, - unsigned int hooknum, - struct sk_buff **pskb) -{ - struct { - struct icmphdr icmp; - struct iphdr ip; - } *inside; - struct ip_conntrack_protocol *proto; - struct ip_conntrack_tuple inner, target; - int hdrlen = ip_hdrlen(*pskb); - enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); - unsigned long statusbit; - enum ip_nat_manip_type manip = HOOK2MANIP(hooknum); - - if (!skb_make_writable(pskb, hdrlen + sizeof(*inside))) - return 0; - - inside = (void *)(*pskb)->data + ip_hdrlen(*pskb); - - /* We're actually going to mangle it beyond trivial checksum - adjustment, so make sure the current checksum is correct. */ - if (nf_ip_checksum(*pskb, hooknum, hdrlen, 0)) - return 0; - - /* Must be RELATED */ - IP_NF_ASSERT((*pskb)->nfctinfo == IP_CT_RELATED || - (*pskb)->nfctinfo == IP_CT_RELATED+IP_CT_IS_REPLY); - - /* Redirects on non-null nats must be dropped, else they'll - start talking to each other without our translation, and be - confused... --RR */ - if (inside->icmp.type == ICMP_REDIRECT) { - /* If NAT isn't finished, assume it and drop. */ - if ((ct->status & IPS_NAT_DONE_MASK) != IPS_NAT_DONE_MASK) - return 0; - - if (ct->status & IPS_NAT_MASK) - return 0; - } - - DEBUGP("icmp_reply_translation: translating error %p manp %u dir %s\n", - *pskb, manip, dir == IP_CT_DIR_ORIGINAL ? "ORIG" : "REPLY"); - - /* rcu_read_lock()ed by nf_hook_slow */ - proto = __ip_conntrack_proto_find(inside->ip.protocol); - if (!ip_ct_get_tuple(&inside->ip, *pskb, ip_hdrlen(*pskb) + - sizeof(struct icmphdr) + inside->ip.ihl*4, - &inner, proto)) - return 0; - - /* Change inner back to look like incoming packet. We do the - opposite manip on this hook to normal, because it might not - pass all hooks (locally-generated ICMP). Consider incoming - packet: PREROUTING (DST manip), routing produces ICMP, goes - through POSTROUTING (which must correct the DST manip). */ - if (!manip_pkt(inside->ip.protocol, pskb, - ip_hdrlen(*pskb) + sizeof(inside->icmp), - &ct->tuplehash[!dir].tuple, - !manip)) - return 0; - - if ((*pskb)->ip_summed != CHECKSUM_PARTIAL) { - /* Reloading "inside" here since manip_pkt inner. */ - inside = (void *)(*pskb)->data + ip_hdrlen(*pskb); - inside->icmp.checksum = 0; - inside->icmp.checksum = csum_fold(skb_checksum(*pskb, hdrlen, - (*pskb)->len - hdrlen, - 0)); - } - - /* Change outer to look the reply to an incoming packet - * (proto 0 means don't invert per-proto part). */ - if (manip == IP_NAT_MANIP_SRC) - statusbit = IPS_SRC_NAT; - else - statusbit = IPS_DST_NAT; - - /* Invert if this is reply dir. */ - if (dir == IP_CT_DIR_REPLY) - statusbit ^= IPS_NAT_MASK; - - if (ct->status & statusbit) { - invert_tuplepr(&target, &ct->tuplehash[!dir].tuple); - if (!manip_pkt(0, pskb, 0, &target, manip)) - return 0; - } - - return 1; -} -EXPORT_SYMBOL_GPL(ip_nat_icmp_reply_translation); - -/* Protocol registration. */ -int ip_nat_protocol_register(struct ip_nat_protocol *proto) -{ - int ret = 0; - - write_lock_bh(&ip_nat_lock); - if (ip_nat_protos[proto->protonum] != &ip_nat_unknown_protocol) { - ret = -EBUSY; - goto out; - } - rcu_assign_pointer(ip_nat_protos[proto->protonum], proto); - out: - write_unlock_bh(&ip_nat_lock); - return ret; -} -EXPORT_SYMBOL(ip_nat_protocol_register); - -/* Noone stores the protocol anywhere; simply delete it. */ -void ip_nat_protocol_unregister(struct ip_nat_protocol *proto) -{ - write_lock_bh(&ip_nat_lock); - rcu_assign_pointer(ip_nat_protos[proto->protonum], - &ip_nat_unknown_protocol); - write_unlock_bh(&ip_nat_lock); - synchronize_rcu(); -} -EXPORT_SYMBOL(ip_nat_protocol_unregister); - -#if defined(CONFIG_IP_NF_CONNTRACK_NETLINK) || \ - defined(CONFIG_IP_NF_CONNTRACK_NETLINK_MODULE) -int -ip_nat_port_range_to_nfattr(struct sk_buff *skb, - const struct ip_nat_range *range) -{ - NFA_PUT(skb, CTA_PROTONAT_PORT_MIN, sizeof(__be16), - &range->min.tcp.port); - NFA_PUT(skb, CTA_PROTONAT_PORT_MAX, sizeof(__be16), - &range->max.tcp.port); - - return 0; - -nfattr_failure: - return -1; -} - -int -ip_nat_port_nfattr_to_range(struct nfattr *tb[], struct ip_nat_range *range) -{ - int ret = 0; - - /* we have to return whether we actually parsed something or not */ - - if (tb[CTA_PROTONAT_PORT_MIN-1]) { - ret = 1; - range->min.tcp.port = - *(__be16 *)NFA_DATA(tb[CTA_PROTONAT_PORT_MIN-1]); - } - - if (!tb[CTA_PROTONAT_PORT_MAX-1]) { - if (ret) - range->max.tcp.port = range->min.tcp.port; - } else { - ret = 1; - range->max.tcp.port = - *(__be16 *)NFA_DATA(tb[CTA_PROTONAT_PORT_MAX-1]); - } - - return ret; -} -EXPORT_SYMBOL_GPL(ip_nat_port_nfattr_to_range); -EXPORT_SYMBOL_GPL(ip_nat_port_range_to_nfattr); -#endif - -static int __init ip_nat_init(void) -{ - size_t i; - - /* Leave them the same for the moment. */ - ip_nat_htable_size = ip_conntrack_htable_size; - - /* One vmalloc for both hash tables */ - bysource = vmalloc(sizeof(struct list_head) * ip_nat_htable_size); - if (!bysource) - return -ENOMEM; - - /* Sew in builtin protocols. */ - write_lock_bh(&ip_nat_lock); - for (i = 0; i < MAX_IP_NAT_PROTO; i++) - rcu_assign_pointer(ip_nat_protos[i], &ip_nat_unknown_protocol); - rcu_assign_pointer(ip_nat_protos[IPPROTO_TCP], &ip_nat_protocol_tcp); - rcu_assign_pointer(ip_nat_protos[IPPROTO_UDP], &ip_nat_protocol_udp); - rcu_assign_pointer(ip_nat_protos[IPPROTO_ICMP], &ip_nat_protocol_icmp); - write_unlock_bh(&ip_nat_lock); - - for (i = 0; i < ip_nat_htable_size; i++) { - INIT_LIST_HEAD(&bysource[i]); - } - - /* FIXME: Man, this is a hack. */ - IP_NF_ASSERT(rcu_dereference(ip_conntrack_destroyed) == NULL); - rcu_assign_pointer(ip_conntrack_destroyed, ip_nat_cleanup_conntrack); - - /* Initialize fake conntrack so that NAT will skip it */ - ip_conntrack_untracked.status |= IPS_NAT_DONE_MASK; - return 0; -} - -/* Clear NAT section of all conntracks, in case we're loaded again. */ -static int clean_nat(struct ip_conntrack *i, void *data) -{ - memset(&i->nat, 0, sizeof(i->nat)); - i->status &= ~(IPS_NAT_MASK | IPS_NAT_DONE_MASK | IPS_SEQ_ADJUST); - return 0; -} - -static void __exit ip_nat_cleanup(void) -{ - ip_ct_iterate_cleanup(&clean_nat, NULL); - rcu_assign_pointer(ip_conntrack_destroyed, NULL); - synchronize_rcu(); - vfree(bysource); -} - -MODULE_LICENSE("GPL"); - -module_init(ip_nat_init); -module_exit(ip_nat_cleanup); diff --git a/net/ipv4/netfilter/ip_nat_ftp.c b/net/ipv4/netfilter/ip_nat_ftp.c deleted file mode 100644 index 32e01d8dffcb..000000000000 --- a/net/ipv4/netfilter/ip_nat_ftp.c +++ /dev/null @@ -1,180 +0,0 @@ -/* FTP extension for TCP NAT alteration. */ - -/* (C) 1999-2001 Paul `Rusty' Russell - * (C) 2002-2004 Netfilter Core Team - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Rusty Russell "); -MODULE_DESCRIPTION("ftp NAT helper"); - -#if 0 -#define DEBUGP printk -#else -#define DEBUGP(format, args...) -#endif - -/* FIXME: Time out? --RR */ - -static int -mangle_rfc959_packet(struct sk_buff **pskb, - __be32 newip, - u_int16_t port, - unsigned int matchoff, - unsigned int matchlen, - struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo, - u32 *seq) -{ - char buffer[sizeof("nnn,nnn,nnn,nnn,nnn,nnn")]; - - sprintf(buffer, "%u,%u,%u,%u,%u,%u", - NIPQUAD(newip), port>>8, port&0xFF); - - DEBUGP("calling ip_nat_mangle_tcp_packet\n"); - - *seq += strlen(buffer) - matchlen; - return ip_nat_mangle_tcp_packet(pskb, ct, ctinfo, matchoff, - matchlen, buffer, strlen(buffer)); -} - -/* |1|132.235.1.2|6275| */ -static int -mangle_eprt_packet(struct sk_buff **pskb, - __be32 newip, - u_int16_t port, - unsigned int matchoff, - unsigned int matchlen, - struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo, - u32 *seq) -{ - char buffer[sizeof("|1|255.255.255.255|65535|")]; - - sprintf(buffer, "|1|%u.%u.%u.%u|%u|", NIPQUAD(newip), port); - - DEBUGP("calling ip_nat_mangle_tcp_packet\n"); - - *seq += strlen(buffer) - matchlen; - return ip_nat_mangle_tcp_packet(pskb, ct, ctinfo, matchoff, - matchlen, buffer, strlen(buffer)); -} - -/* |1|132.235.1.2|6275| */ -static int -mangle_epsv_packet(struct sk_buff **pskb, - __be32 newip, - u_int16_t port, - unsigned int matchoff, - unsigned int matchlen, - struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo, - u32 *seq) -{ - char buffer[sizeof("|||65535|")]; - - sprintf(buffer, "|||%u|", port); - - DEBUGP("calling ip_nat_mangle_tcp_packet\n"); - - *seq += strlen(buffer) - matchlen; - return ip_nat_mangle_tcp_packet(pskb, ct, ctinfo, matchoff, - matchlen, buffer, strlen(buffer)); -} - -static int (*mangle[])(struct sk_buff **, __be32, u_int16_t, - unsigned int, - unsigned int, - struct ip_conntrack *, - enum ip_conntrack_info, - u32 *seq) -= { [IP_CT_FTP_PORT] = mangle_rfc959_packet, - [IP_CT_FTP_PASV] = mangle_rfc959_packet, - [IP_CT_FTP_EPRT] = mangle_eprt_packet, - [IP_CT_FTP_EPSV] = mangle_epsv_packet -}; - -/* So, this packet has hit the connection tracking matching code. - Mangle it, and change the expectation to match the new version. */ -static unsigned int ip_nat_ftp(struct sk_buff **pskb, - enum ip_conntrack_info ctinfo, - enum ip_ct_ftp_type type, - unsigned int matchoff, - unsigned int matchlen, - struct ip_conntrack_expect *exp, - u32 *seq) -{ - __be32 newip; - u_int16_t port; - int dir = CTINFO2DIR(ctinfo); - struct ip_conntrack *ct = exp->master; - - DEBUGP("FTP_NAT: type %i, off %u len %u\n", type, matchoff, matchlen); - - /* Connection will come from wherever this packet goes, hence !dir */ - newip = ct->tuplehash[!dir].tuple.dst.ip; - exp->saved_proto.tcp.port = exp->tuple.dst.u.tcp.port; - exp->dir = !dir; - - /* When you see the packet, we need to NAT it the same as the - * this one. */ - exp->expectfn = ip_nat_follow_master; - - /* Try to get same port: if not, try to change it. */ - for (port = ntohs(exp->saved_proto.tcp.port); port != 0; port++) { - exp->tuple.dst.u.tcp.port = htons(port); - if (ip_conntrack_expect_related(exp) == 0) - break; - } - - if (port == 0) - return NF_DROP; - - if (!mangle[type](pskb, newip, port, matchoff, matchlen, ct, ctinfo, - seq)) { - ip_conntrack_unexpect_related(exp); - return NF_DROP; - } - return NF_ACCEPT; -} - -static void __exit ip_nat_ftp_fini(void) -{ - rcu_assign_pointer(ip_nat_ftp_hook, NULL); - synchronize_rcu(); -} - -static int __init ip_nat_ftp_init(void) -{ - BUG_ON(rcu_dereference(ip_nat_ftp_hook)); - rcu_assign_pointer(ip_nat_ftp_hook, ip_nat_ftp); - return 0; -} - -/* Prior to 2.6.11, we had a ports param. No longer, but don't break users. */ -static int warn_set(const char *val, struct kernel_param *kp) -{ - printk(KERN_INFO KBUILD_MODNAME - ": kernel >= 2.6.10 only uses 'ports' for conntrack modules\n"); - return 0; -} -module_param_call(ports, warn_set, NULL, NULL, 0); - -module_init(ip_nat_ftp_init); -module_exit(ip_nat_ftp_fini); diff --git a/net/ipv4/netfilter/ip_nat_helper.c b/net/ipv4/netfilter/ip_nat_helper.c deleted file mode 100644 index 4cddc2951744..000000000000 --- a/net/ipv4/netfilter/ip_nat_helper.c +++ /dev/null @@ -1,436 +0,0 @@ -/* ip_nat_helper.c - generic support functions for NAT helpers - * - * (C) 2000-2002 Harald Welte - * (C) 2003-2004 Netfilter Core Team - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * 14 Jan 2002 Harald Welte : - * - add support for SACK adjustment - * 14 Mar 2002 Harald Welte : - * - merge SACK support into newnat API - * 16 Aug 2002 Brian J. Murrell : - * - make ip_nat_resize_packet more generic (TCP and UDP) - * - add ip_nat_mangle_udp_packet - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#if 0 -#define DEBUGP printk -#define DUMP_OFFSET(x) printk("offset_before=%d, offset_after=%d, correction_pos=%u\n", x->offset_before, x->offset_after, x->correction_pos); -#else -#define DEBUGP(format, args...) -#define DUMP_OFFSET(x) -#endif - -static DEFINE_SPINLOCK(ip_nat_seqofs_lock); - -/* Setup TCP sequence correction given this change at this sequence */ -static inline void -adjust_tcp_sequence(u32 seq, - int sizediff, - struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo) -{ - int dir; - struct ip_nat_seq *this_way, *other_way; - - DEBUGP("ip_nat_resize_packet: old_size = %u, new_size = %u\n", - (*skb)->len, new_size); - - dir = CTINFO2DIR(ctinfo); - - this_way = &ct->nat.info.seq[dir]; - other_way = &ct->nat.info.seq[!dir]; - - DEBUGP("ip_nat_resize_packet: Seq_offset before: "); - DUMP_OFFSET(this_way); - - spin_lock_bh(&ip_nat_seqofs_lock); - - /* SYN adjust. If it's uninitialized, or this is after last - * correction, record it: we don't handle more than one - * adjustment in the window, but do deal with common case of a - * retransmit */ - if (this_way->offset_before == this_way->offset_after - || before(this_way->correction_pos, seq)) { - this_way->correction_pos = seq; - this_way->offset_before = this_way->offset_after; - this_way->offset_after += sizediff; - } - spin_unlock_bh(&ip_nat_seqofs_lock); - - DEBUGP("ip_nat_resize_packet: Seq_offset after: "); - DUMP_OFFSET(this_way); -} - -/* Frobs data inside this packet, which is linear. */ -static void mangle_contents(struct sk_buff *skb, - unsigned int dataoff, - unsigned int match_offset, - unsigned int match_len, - const char *rep_buffer, - unsigned int rep_len) -{ - unsigned char *data; - - BUG_ON(skb_is_nonlinear(skb)); - data = skb_network_header(skb) + dataoff; - - /* move post-replacement */ - memmove(data + match_offset + rep_len, - data + match_offset + match_len, - skb->tail - (data + match_offset + match_len)); - - /* insert data from buffer */ - memcpy(data + match_offset, rep_buffer, rep_len); - - /* update skb info */ - if (rep_len > match_len) { - DEBUGP("ip_nat_mangle_packet: Extending packet by " - "%u from %u bytes\n", rep_len - match_len, - skb->len); - skb_put(skb, rep_len - match_len); - } else { - DEBUGP("ip_nat_mangle_packet: Shrinking packet from " - "%u from %u bytes\n", match_len - rep_len, - skb->len); - __skb_trim(skb, skb->len + rep_len - match_len); - } - - /* fix IP hdr checksum information */ - ip_hdr(skb)->tot_len = htons(skb->len); - ip_send_check(ip_hdr(skb)); -} - -/* Unusual, but possible case. */ -static int enlarge_skb(struct sk_buff **pskb, unsigned int extra) -{ - struct sk_buff *nskb; - - if ((*pskb)->len + extra > 65535) - return 0; - - nskb = skb_copy_expand(*pskb, skb_headroom(*pskb), extra, GFP_ATOMIC); - if (!nskb) - return 0; - - /* Transfer socket to new skb. */ - if ((*pskb)->sk) - skb_set_owner_w(nskb, (*pskb)->sk); - kfree_skb(*pskb); - *pskb = nskb; - return 1; -} - -/* Generic function for mangling variable-length address changes inside - * NATed TCP connections (like the PORT XXX,XXX,XXX,XXX,XXX,XXX - * command in FTP). - * - * Takes care about all the nasty sequence number changes, checksumming, - * skb enlargement, ... - * - * */ -int -ip_nat_mangle_tcp_packet(struct sk_buff **pskb, - struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo, - unsigned int match_offset, - unsigned int match_len, - const char *rep_buffer, - unsigned int rep_len) -{ - struct iphdr *iph; - struct tcphdr *tcph; - int oldlen, datalen; - - if (!skb_make_writable(pskb, (*pskb)->len)) - return 0; - - if (rep_len > match_len - && rep_len - match_len > skb_tailroom(*pskb) - && !enlarge_skb(pskb, rep_len - match_len)) - return 0; - - SKB_LINEAR_ASSERT(*pskb); - - iph = ip_hdr(*pskb); - tcph = (void *)iph + iph->ihl*4; - - oldlen = (*pskb)->len - iph->ihl*4; - mangle_contents(*pskb, iph->ihl*4 + tcph->doff*4, - match_offset, match_len, rep_buffer, rep_len); - - datalen = (*pskb)->len - iph->ihl*4; - if ((*pskb)->ip_summed != CHECKSUM_PARTIAL) { - tcph->check = 0; - tcph->check = tcp_v4_check(datalen, - iph->saddr, iph->daddr, - csum_partial((char *)tcph, - datalen, 0)); - } else - nf_proto_csum_replace2(&tcph->check, *pskb, - htons(oldlen), htons(datalen), 1); - - if (rep_len != match_len) { - set_bit(IPS_SEQ_ADJUST_BIT, &ct->status); - adjust_tcp_sequence(ntohl(tcph->seq), - (int)rep_len - (int)match_len, - ct, ctinfo); - /* Tell TCP window tracking about seq change */ - ip_conntrack_tcp_update(*pskb, ct, CTINFO2DIR(ctinfo)); - } - return 1; -} -EXPORT_SYMBOL(ip_nat_mangle_tcp_packet); - -/* Generic function for mangling variable-length address changes inside - * NATed UDP connections (like the CONNECT DATA XXXXX MESG XXXXX INDEX XXXXX - * command in the Amanda protocol) - * - * Takes care about all the nasty sequence number changes, checksumming, - * skb enlargement, ... - * - * XXX - This function could be merged with ip_nat_mangle_tcp_packet which - * should be fairly easy to do. - */ -int -ip_nat_mangle_udp_packet(struct sk_buff **pskb, - struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo, - unsigned int match_offset, - unsigned int match_len, - const char *rep_buffer, - unsigned int rep_len) -{ - struct iphdr *iph; - struct udphdr *udph; - int datalen, oldlen; - - /* UDP helpers might accidentally mangle the wrong packet */ - iph = ip_hdr(*pskb); - if ((*pskb)->len < iph->ihl*4 + sizeof(*udph) + - match_offset + match_len) - return 0; - - if (!skb_make_writable(pskb, (*pskb)->len)) - return 0; - - if (rep_len > match_len - && rep_len - match_len > skb_tailroom(*pskb) - && !enlarge_skb(pskb, rep_len - match_len)) - return 0; - - iph = ip_hdr(*pskb); - udph = (void *)iph + iph->ihl*4; - - oldlen = (*pskb)->len - iph->ihl*4; - mangle_contents(*pskb, iph->ihl*4 + sizeof(*udph), - match_offset, match_len, rep_buffer, rep_len); - - /* update the length of the UDP packet */ - datalen = (*pskb)->len - iph->ihl*4; - udph->len = htons(datalen); - - /* fix udp checksum if udp checksum was previously calculated */ - if (!udph->check && (*pskb)->ip_summed != CHECKSUM_PARTIAL) - return 1; - - if ((*pskb)->ip_summed != CHECKSUM_PARTIAL) { - udph->check = 0; - udph->check = csum_tcpudp_magic(iph->saddr, iph->daddr, - datalen, IPPROTO_UDP, - csum_partial((char *)udph, - datalen, 0)); - if (!udph->check) - udph->check = CSUM_MANGLED_0; - } else - nf_proto_csum_replace2(&udph->check, *pskb, - htons(oldlen), htons(datalen), 1); - return 1; -} -EXPORT_SYMBOL(ip_nat_mangle_udp_packet); - -/* Adjust one found SACK option including checksum correction */ -static void -sack_adjust(struct sk_buff *skb, - struct tcphdr *tcph, - unsigned int sackoff, - unsigned int sackend, - struct ip_nat_seq *natseq) -{ - while (sackoff < sackend) { - struct tcp_sack_block_wire *sack; - __be32 new_start_seq, new_end_seq; - - sack = (void *)skb->data + sackoff; - if (after(ntohl(sack->start_seq) - natseq->offset_before, - natseq->correction_pos)) - new_start_seq = htonl(ntohl(sack->start_seq) - - natseq->offset_after); - else - new_start_seq = htonl(ntohl(sack->start_seq) - - natseq->offset_before); - - if (after(ntohl(sack->end_seq) - natseq->offset_before, - natseq->correction_pos)) - new_end_seq = htonl(ntohl(sack->end_seq) - - natseq->offset_after); - else - new_end_seq = htonl(ntohl(sack->end_seq) - - natseq->offset_before); - - DEBUGP("sack_adjust: start_seq: %d->%d, end_seq: %d->%d\n", - ntohl(sack->start_seq), new_start_seq, - ntohl(sack->end_seq), new_end_seq); - - nf_proto_csum_replace4(&tcph->check, skb, - sack->start_seq, new_start_seq, 0); - nf_proto_csum_replace4(&tcph->check, skb, - sack->end_seq, new_end_seq, 0); - sack->start_seq = new_start_seq; - sack->end_seq = new_end_seq; - sackoff += sizeof(*sack); - } -} - -/* TCP SACK sequence number adjustment */ -static inline unsigned int -ip_nat_sack_adjust(struct sk_buff **pskb, - struct tcphdr *tcph, - struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo) -{ - unsigned int dir, optoff, optend; - - optoff = ip_hdrlen(*pskb) + sizeof(struct tcphdr); - optend = ip_hdrlen(*pskb) + tcph->doff * 4; - - if (!skb_make_writable(pskb, optend)) - return 0; - - dir = CTINFO2DIR(ctinfo); - - while (optoff < optend) { - /* Usually: option, length. */ - unsigned char *op = (*pskb)->data + optoff; - - switch (op[0]) { - case TCPOPT_EOL: - return 1; - case TCPOPT_NOP: - optoff++; - continue; - default: - /* no partial options */ - if (optoff + 1 == optend - || optoff + op[1] > optend - || op[1] < 2) - return 0; - if (op[0] == TCPOPT_SACK - && op[1] >= 2+TCPOLEN_SACK_PERBLOCK - && ((op[1] - 2) % TCPOLEN_SACK_PERBLOCK) == 0) - sack_adjust(*pskb, tcph, optoff+2, - optoff+op[1], - &ct->nat.info.seq[!dir]); - optoff += op[1]; - } - } - return 1; -} - -/* TCP sequence number adjustment. Returns 1 on success, 0 on failure */ -int -ip_nat_seq_adjust(struct sk_buff **pskb, - struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo) -{ - struct tcphdr *tcph; - int dir; - __be32 newseq, newack; - struct ip_nat_seq *this_way, *other_way; - - dir = CTINFO2DIR(ctinfo); - - this_way = &ct->nat.info.seq[dir]; - other_way = &ct->nat.info.seq[!dir]; - - if (!skb_make_writable(pskb, ip_hdrlen(*pskb) + sizeof(*tcph))) - return 0; - - tcph = (void *)(*pskb)->data + ip_hdrlen(*pskb); - if (after(ntohl(tcph->seq), this_way->correction_pos)) - newseq = htonl(ntohl(tcph->seq) + this_way->offset_after); - else - newseq = htonl(ntohl(tcph->seq) + this_way->offset_before); - - if (after(ntohl(tcph->ack_seq) - other_way->offset_before, - other_way->correction_pos)) - newack = htonl(ntohl(tcph->ack_seq) - other_way->offset_after); - else - newack = htonl(ntohl(tcph->ack_seq) - other_way->offset_before); - - nf_proto_csum_replace4(&tcph->check, *pskb, tcph->seq, newseq, 0); - nf_proto_csum_replace4(&tcph->check, *pskb, tcph->ack_seq, newack, 0); - - DEBUGP("Adjusting sequence number from %u->%u, ack from %u->%u\n", - ntohl(tcph->seq), ntohl(newseq), ntohl(tcph->ack_seq), - ntohl(newack)); - - tcph->seq = newseq; - tcph->ack_seq = newack; - - if (!ip_nat_sack_adjust(pskb, tcph, ct, ctinfo)) - return 0; - - ip_conntrack_tcp_update(*pskb, ct, dir); - - return 1; -} -EXPORT_SYMBOL(ip_nat_seq_adjust); - -/* Setup NAT on this expected conntrack so it follows master. */ -/* If we fail to get a free NAT slot, we'll get dropped on confirm */ -void ip_nat_follow_master(struct ip_conntrack *ct, - struct ip_conntrack_expect *exp) -{ - struct ip_nat_range range; - - /* This must be a fresh one. */ - BUG_ON(ct->status & IPS_NAT_DONE_MASK); - - /* Change src to where master sends to */ - range.flags = IP_NAT_RANGE_MAP_IPS; - range.min_ip = range.max_ip - = ct->master->tuplehash[!exp->dir].tuple.dst.ip; - /* hook doesn't matter, but it has to do source manip */ - ip_nat_setup_info(ct, &range, NF_IP_POST_ROUTING); - - /* For DST manip, map port here to where it's expected. */ - range.flags = (IP_NAT_RANGE_MAP_IPS | IP_NAT_RANGE_PROTO_SPECIFIED); - range.min = range.max = exp->saved_proto; - range.min_ip = range.max_ip - = ct->master->tuplehash[!exp->dir].tuple.src.ip; - /* hook doesn't matter, but it has to do destination manip */ - ip_nat_setup_info(ct, &range, NF_IP_PRE_ROUTING); -} -EXPORT_SYMBOL(ip_nat_follow_master); diff --git a/net/ipv4/netfilter/ip_nat_helper_h323.c b/net/ipv4/netfilter/ip_nat_helper_h323.c deleted file mode 100644 index 0d9444f9236b..000000000000 --- a/net/ipv4/netfilter/ip_nat_helper_h323.c +++ /dev/null @@ -1,611 +0,0 @@ -/* - * H.323 extension for NAT alteration. - * - * Copyright (c) 2006 Jing Min Zhao - * - * This source code is licensed under General Public License version 2. - * - * Based on the 'brute force' H.323 NAT module by - * Jozsef Kadlecsik - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#if 0 -#define DEBUGP printk -#else -#define DEBUGP(format, args...) -#endif - -/****************************************************************************/ -static int set_addr(struct sk_buff **pskb, - unsigned char **data, int dataoff, - unsigned int addroff, __be32 ip, u_int16_t port) -{ - enum ip_conntrack_info ctinfo; - struct ip_conntrack *ct = ip_conntrack_get(*pskb, &ctinfo); - struct { - __be32 ip; - __be16 port; - } __attribute__ ((__packed__)) buf; - struct tcphdr _tcph, *th; - - buf.ip = ip; - buf.port = htons(port); - addroff += dataoff; - - if (ip_hdr(*pskb)->protocol == IPPROTO_TCP) { - if (!ip_nat_mangle_tcp_packet(pskb, ct, ctinfo, - addroff, sizeof(buf), - (char *) &buf, sizeof(buf))) { - if (net_ratelimit()) - printk("ip_nat_h323: ip_nat_mangle_tcp_packet" - " error\n"); - return -1; - } - - /* Relocate data pointer */ - th = skb_header_pointer(*pskb, ip_hdrlen(*pskb), - sizeof(_tcph), &_tcph); - if (th == NULL) - return -1; - *data = (*pskb)->data + ip_hdrlen(*pskb) + - th->doff * 4 + dataoff; - } else { - if (!ip_nat_mangle_udp_packet(pskb, ct, ctinfo, - addroff, sizeof(buf), - (char *) &buf, sizeof(buf))) { - if (net_ratelimit()) - printk("ip_nat_h323: ip_nat_mangle_udp_packet" - " error\n"); - return -1; - } - /* ip_nat_mangle_udp_packet uses skb_make_writable() to copy - * or pull everything in a linear buffer, so we can safely - * use the skb pointers now */ - *data = ((*pskb)->data + ip_hdrlen(*pskb) + - sizeof(struct udphdr)); - } - - return 0; -} - -/****************************************************************************/ -static int set_h225_addr(struct sk_buff **pskb, - unsigned char **data, int dataoff, - TransportAddress * addr, - __be32 ip, u_int16_t port) -{ - return set_addr(pskb, data, dataoff, addr->ipAddress.ip, ip, port); -} - -/****************************************************************************/ -static int set_h245_addr(struct sk_buff **pskb, - unsigned char **data, int dataoff, - H245_TransportAddress * addr, - __be32 ip, u_int16_t port) -{ - return set_addr(pskb, data, dataoff, - addr->unicastAddress.iPAddress.network, ip, port); -} - -/****************************************************************************/ -static int set_sig_addr(struct sk_buff **pskb, struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo, - unsigned char **data, - TransportAddress * addr, int count) -{ - struct ip_ct_h323_master *info = &ct->help.ct_h323_info; - int dir = CTINFO2DIR(ctinfo); - int i; - __be32 ip; - u_int16_t port; - - for (i = 0; i < count; i++) { - if (get_h225_addr(*data, &addr[i], &ip, &port)) { - if (ip == ct->tuplehash[dir].tuple.src.ip && - port == info->sig_port[dir]) { - /* GW->GK */ - - /* Fix for Gnomemeeting */ - if (i > 0 && - get_h225_addr(*data, &addr[0], - &ip, &port) && - (ntohl(ip) & 0xff000000) == 0x7f000000) - i = 0; - - DEBUGP - ("ip_nat_ras: set signal address " - "%u.%u.%u.%u:%hu->%u.%u.%u.%u:%hu\n", - NIPQUAD(ip), port, - NIPQUAD(ct->tuplehash[!dir].tuple.dst. - ip), info->sig_port[!dir]); - return set_h225_addr(pskb, data, 0, &addr[i], - ct->tuplehash[!dir]. - tuple.dst.ip, - info->sig_port[!dir]); - } else if (ip == ct->tuplehash[dir].tuple.dst.ip && - port == info->sig_port[dir]) { - /* GK->GW */ - DEBUGP - ("ip_nat_ras: set signal address " - "%u.%u.%u.%u:%hu->%u.%u.%u.%u:%hu\n", - NIPQUAD(ip), port, - NIPQUAD(ct->tuplehash[!dir].tuple.src. - ip), info->sig_port[!dir]); - return set_h225_addr(pskb, data, 0, &addr[i], - ct->tuplehash[!dir]. - tuple.src.ip, - info->sig_port[!dir]); - } - } - } - - return 0; -} - -/****************************************************************************/ -static int set_ras_addr(struct sk_buff **pskb, struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo, - unsigned char **data, - TransportAddress * addr, int count) -{ - int dir = CTINFO2DIR(ctinfo); - int i; - __be32 ip; - u_int16_t port; - - for (i = 0; i < count; i++) { - if (get_h225_addr(*data, &addr[i], &ip, &port) && - ip == ct->tuplehash[dir].tuple.src.ip && - port == ntohs(ct->tuplehash[dir].tuple.src.u.udp.port)) { - DEBUGP("ip_nat_ras: set rasAddress " - "%u.%u.%u.%u:%hu->%u.%u.%u.%u:%hu\n", - NIPQUAD(ip), port, - NIPQUAD(ct->tuplehash[!dir].tuple.dst.ip), - ntohs(ct->tuplehash[!dir].tuple.dst.u.udp. - port)); - return set_h225_addr(pskb, data, 0, &addr[i], - ct->tuplehash[!dir].tuple.dst.ip, - ntohs(ct->tuplehash[!dir].tuple. - dst.u.udp.port)); - } - } - - return 0; -} - -/****************************************************************************/ -static int nat_rtp_rtcp(struct sk_buff **pskb, struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo, - unsigned char **data, int dataoff, - H245_TransportAddress * addr, - u_int16_t port, u_int16_t rtp_port, - struct ip_conntrack_expect *rtp_exp, - struct ip_conntrack_expect *rtcp_exp) -{ - struct ip_ct_h323_master *info = &ct->help.ct_h323_info; - int dir = CTINFO2DIR(ctinfo); - int i; - u_int16_t nated_port; - - /* Set expectations for NAT */ - rtp_exp->saved_proto.udp.port = rtp_exp->tuple.dst.u.udp.port; - rtp_exp->expectfn = ip_nat_follow_master; - rtp_exp->dir = !dir; - rtcp_exp->saved_proto.udp.port = rtcp_exp->tuple.dst.u.udp.port; - rtcp_exp->expectfn = ip_nat_follow_master; - rtcp_exp->dir = !dir; - - /* Lookup existing expects */ - for (i = 0; i < H323_RTP_CHANNEL_MAX; i++) { - if (info->rtp_port[i][dir] == rtp_port) { - /* Expected */ - - /* Use allocated ports first. This will refresh - * the expects */ - rtp_exp->tuple.dst.u.udp.port = - htons(info->rtp_port[i][dir]); - rtcp_exp->tuple.dst.u.udp.port = - htons(info->rtp_port[i][dir] + 1); - break; - } else if (info->rtp_port[i][dir] == 0) { - /* Not expected */ - break; - } - } - - /* Run out of expectations */ - if (i >= H323_RTP_CHANNEL_MAX) { - if (net_ratelimit()) - printk("ip_nat_h323: out of expectations\n"); - return 0; - } - - /* Try to get a pair of ports. */ - for (nated_port = ntohs(rtp_exp->tuple.dst.u.udp.port); - nated_port != 0; nated_port += 2) { - rtp_exp->tuple.dst.u.udp.port = htons(nated_port); - if (ip_conntrack_expect_related(rtp_exp) == 0) { - rtcp_exp->tuple.dst.u.udp.port = - htons(nated_port + 1); - if (ip_conntrack_expect_related(rtcp_exp) == 0) - break; - ip_conntrack_unexpect_related(rtp_exp); - } - } - - if (nated_port == 0) { /* No port available */ - if (net_ratelimit()) - printk("ip_nat_h323: out of RTP ports\n"); - return 0; - } - - /* Modify signal */ - if (set_h245_addr(pskb, data, dataoff, addr, - ct->tuplehash[!dir].tuple.dst.ip, - (port & 1) ? nated_port + 1 : nated_port) == 0) { - /* Save ports */ - info->rtp_port[i][dir] = rtp_port; - info->rtp_port[i][!dir] = nated_port; - } else { - ip_conntrack_unexpect_related(rtp_exp); - ip_conntrack_unexpect_related(rtcp_exp); - return -1; - } - - /* Success */ - DEBUGP("ip_nat_h323: expect RTP %u.%u.%u.%u:%hu->%u.%u.%u.%u:%hu\n", - NIPQUAD(rtp_exp->tuple.src.ip), - ntohs(rtp_exp->tuple.src.u.udp.port), - NIPQUAD(rtp_exp->tuple.dst.ip), - ntohs(rtp_exp->tuple.dst.u.udp.port)); - DEBUGP("ip_nat_h323: expect RTCP %u.%u.%u.%u:%hu->%u.%u.%u.%u:%hu\n", - NIPQUAD(rtcp_exp->tuple.src.ip), - ntohs(rtcp_exp->tuple.src.u.udp.port), - NIPQUAD(rtcp_exp->tuple.dst.ip), - ntohs(rtcp_exp->tuple.dst.u.udp.port)); - - return 0; -} - -/****************************************************************************/ -static int nat_t120(struct sk_buff **pskb, struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo, - unsigned char **data, int dataoff, - H245_TransportAddress * addr, u_int16_t port, - struct ip_conntrack_expect *exp) -{ - int dir = CTINFO2DIR(ctinfo); - u_int16_t nated_port = port; - - /* Set expectations for NAT */ - exp->saved_proto.tcp.port = exp->tuple.dst.u.tcp.port; - exp->expectfn = ip_nat_follow_master; - exp->dir = !dir; - - /* Try to get same port: if not, try to change it. */ - for (; nated_port != 0; nated_port++) { - exp->tuple.dst.u.tcp.port = htons(nated_port); - if (ip_conntrack_expect_related(exp) == 0) - break; - } - - if (nated_port == 0) { /* No port available */ - if (net_ratelimit()) - printk("ip_nat_h323: out of TCP ports\n"); - return 0; - } - - /* Modify signal */ - if (set_h245_addr(pskb, data, dataoff, addr, - ct->tuplehash[!dir].tuple.dst.ip, nated_port) < 0) { - ip_conntrack_unexpect_related(exp); - return -1; - } - - DEBUGP("ip_nat_h323: expect T.120 %u.%u.%u.%u:%hu->%u.%u.%u.%u:%hu\n", - NIPQUAD(exp->tuple.src.ip), ntohs(exp->tuple.src.u.tcp.port), - NIPQUAD(exp->tuple.dst.ip), ntohs(exp->tuple.dst.u.tcp.port)); - - return 0; -} - -/**************************************************************************** - * This conntrack expect function replaces ip_conntrack_h245_expect() - * which was set by ip_conntrack_helper_h323.c. It calls both - * ip_nat_follow_master() and ip_conntrack_h245_expect() - ****************************************************************************/ -static void ip_nat_h245_expect(struct ip_conntrack *new, - struct ip_conntrack_expect *this) -{ - ip_nat_follow_master(new, this); - ip_conntrack_h245_expect(new, this); -} - -/****************************************************************************/ -static int nat_h245(struct sk_buff **pskb, struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo, - unsigned char **data, int dataoff, - TransportAddress * addr, u_int16_t port, - struct ip_conntrack_expect *exp) -{ - struct ip_ct_h323_master *info = &ct->help.ct_h323_info; - int dir = CTINFO2DIR(ctinfo); - u_int16_t nated_port = port; - - /* Set expectations for NAT */ - exp->saved_proto.tcp.port = exp->tuple.dst.u.tcp.port; - exp->expectfn = ip_nat_h245_expect; - exp->dir = !dir; - - /* Check existing expects */ - if (info->sig_port[dir] == port) - nated_port = info->sig_port[!dir]; - - /* Try to get same port: if not, try to change it. */ - for (; nated_port != 0; nated_port++) { - exp->tuple.dst.u.tcp.port = htons(nated_port); - if (ip_conntrack_expect_related(exp) == 0) - break; - } - - if (nated_port == 0) { /* No port available */ - if (net_ratelimit()) - printk("ip_nat_q931: out of TCP ports\n"); - return 0; - } - - /* Modify signal */ - if (set_h225_addr(pskb, data, dataoff, addr, - ct->tuplehash[!dir].tuple.dst.ip, - nated_port) == 0) { - /* Save ports */ - info->sig_port[dir] = port; - info->sig_port[!dir] = nated_port; - } else { - ip_conntrack_unexpect_related(exp); - return -1; - } - - DEBUGP("ip_nat_q931: expect H.245 %u.%u.%u.%u:%hu->%u.%u.%u.%u:%hu\n", - NIPQUAD(exp->tuple.src.ip), ntohs(exp->tuple.src.u.tcp.port), - NIPQUAD(exp->tuple.dst.ip), ntohs(exp->tuple.dst.u.tcp.port)); - - return 0; -} - -/**************************************************************************** - * This conntrack expect function replaces ip_conntrack_q931_expect() - * which was set by ip_conntrack_helper_h323.c. - ****************************************************************************/ -static void ip_nat_q931_expect(struct ip_conntrack *new, - struct ip_conntrack_expect *this) -{ - struct ip_nat_range range; - - if (this->tuple.src.ip != 0) { /* Only accept calls from GK */ - ip_nat_follow_master(new, this); - goto out; - } - - /* This must be a fresh one. */ - BUG_ON(new->status & IPS_NAT_DONE_MASK); - - /* Change src to where master sends to */ - range.flags = IP_NAT_RANGE_MAP_IPS; - range.min_ip = range.max_ip = new->tuplehash[!this->dir].tuple.src.ip; - - /* hook doesn't matter, but it has to do source manip */ - ip_nat_setup_info(new, &range, NF_IP_POST_ROUTING); - - /* For DST manip, map port here to where it's expected. */ - range.flags = (IP_NAT_RANGE_MAP_IPS | IP_NAT_RANGE_PROTO_SPECIFIED); - range.min = range.max = this->saved_proto; - range.min_ip = range.max_ip = - new->master->tuplehash[!this->dir].tuple.src.ip; - - /* hook doesn't matter, but it has to do destination manip */ - ip_nat_setup_info(new, &range, NF_IP_PRE_ROUTING); - - out: - ip_conntrack_q931_expect(new, this); -} - -/****************************************************************************/ -static int nat_q931(struct sk_buff **pskb, struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo, - unsigned char **data, TransportAddress * addr, int idx, - u_int16_t port, struct ip_conntrack_expect *exp) -{ - struct ip_ct_h323_master *info = &ct->help.ct_h323_info; - int dir = CTINFO2DIR(ctinfo); - u_int16_t nated_port = port; - __be32 ip; - - /* Set expectations for NAT */ - exp->saved_proto.tcp.port = exp->tuple.dst.u.tcp.port; - exp->expectfn = ip_nat_q931_expect; - exp->dir = !dir; - - /* Check existing expects */ - if (info->sig_port[dir] == port) - nated_port = info->sig_port[!dir]; - - /* Try to get same port: if not, try to change it. */ - for (; nated_port != 0; nated_port++) { - exp->tuple.dst.u.tcp.port = htons(nated_port); - if (ip_conntrack_expect_related(exp) == 0) - break; - } - - if (nated_port == 0) { /* No port available */ - if (net_ratelimit()) - printk("ip_nat_ras: out of TCP ports\n"); - return 0; - } - - /* Modify signal */ - if (set_h225_addr(pskb, data, 0, &addr[idx], - ct->tuplehash[!dir].tuple.dst.ip, - nated_port) == 0) { - /* Save ports */ - info->sig_port[dir] = port; - info->sig_port[!dir] = nated_port; - - /* Fix for Gnomemeeting */ - if (idx > 0 && - get_h225_addr(*data, &addr[0], &ip, &port) && - (ntohl(ip) & 0xff000000) == 0x7f000000) { - set_h225_addr_hook(pskb, data, 0, &addr[0], - ct->tuplehash[!dir].tuple.dst.ip, - info->sig_port[!dir]); - } - } else { - ip_conntrack_unexpect_related(exp); - return -1; - } - - /* Success */ - DEBUGP("ip_nat_ras: expect Q.931 %u.%u.%u.%u:%hu->%u.%u.%u.%u:%hu\n", - NIPQUAD(exp->tuple.src.ip), ntohs(exp->tuple.src.u.tcp.port), - NIPQUAD(exp->tuple.dst.ip), ntohs(exp->tuple.dst.u.tcp.port)); - - return 0; -} - -/****************************************************************************/ -static void ip_nat_callforwarding_expect(struct ip_conntrack *new, - struct ip_conntrack_expect *this) -{ - struct ip_nat_range range; - - /* This must be a fresh one. */ - BUG_ON(new->status & IPS_NAT_DONE_MASK); - - /* Change src to where master sends to */ - range.flags = IP_NAT_RANGE_MAP_IPS; - range.min_ip = range.max_ip = new->tuplehash[!this->dir].tuple.src.ip; - - /* hook doesn't matter, but it has to do source manip */ - ip_nat_setup_info(new, &range, NF_IP_POST_ROUTING); - - /* For DST manip, map port here to where it's expected. */ - range.flags = (IP_NAT_RANGE_MAP_IPS | IP_NAT_RANGE_PROTO_SPECIFIED); - range.min = range.max = this->saved_proto; - range.min_ip = range.max_ip = this->saved_ip; - - /* hook doesn't matter, but it has to do destination manip */ - ip_nat_setup_info(new, &range, NF_IP_PRE_ROUTING); - - ip_conntrack_q931_expect(new, this); -} - -/****************************************************************************/ -static int nat_callforwarding(struct sk_buff **pskb, struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo, - unsigned char **data, int dataoff, - TransportAddress * addr, u_int16_t port, - struct ip_conntrack_expect *exp) -{ - int dir = CTINFO2DIR(ctinfo); - u_int16_t nated_port; - - /* Set expectations for NAT */ - exp->saved_ip = exp->tuple.dst.ip; - exp->tuple.dst.ip = ct->tuplehash[!dir].tuple.dst.ip; - exp->saved_proto.tcp.port = exp->tuple.dst.u.tcp.port; - exp->expectfn = ip_nat_callforwarding_expect; - exp->dir = !dir; - - /* Try to get same port: if not, try to change it. */ - for (nated_port = port; nated_port != 0; nated_port++) { - exp->tuple.dst.u.tcp.port = htons(nated_port); - if (ip_conntrack_expect_related(exp) == 0) - break; - } - - if (nated_port == 0) { /* No port available */ - if (net_ratelimit()) - printk("ip_nat_q931: out of TCP ports\n"); - return 0; - } - - /* Modify signal */ - if (!set_h225_addr(pskb, data, dataoff, addr, - ct->tuplehash[!dir].tuple.dst.ip, - nated_port) == 0) { - ip_conntrack_unexpect_related(exp); - return -1; - } - - /* Success */ - DEBUGP("ip_nat_q931: expect Call Forwarding " - "%u.%u.%u.%u:%hu->%u.%u.%u.%u:%hu\n", - NIPQUAD(exp->tuple.src.ip), ntohs(exp->tuple.src.u.tcp.port), - NIPQUAD(exp->tuple.dst.ip), ntohs(exp->tuple.dst.u.tcp.port)); - - return 0; -} - -/****************************************************************************/ -static int __init init(void) -{ - BUG_ON(rcu_dereference(set_h245_addr_hook) != NULL); - BUG_ON(rcu_dereference(set_h225_addr_hook) != NULL); - BUG_ON(rcu_dereference(set_sig_addr_hook) != NULL); - BUG_ON(rcu_dereference(set_ras_addr_hook) != NULL); - BUG_ON(rcu_dereference(nat_rtp_rtcp_hook) != NULL); - BUG_ON(rcu_dereference(nat_t120_hook) != NULL); - BUG_ON(rcu_dereference(nat_h245_hook) != NULL); - BUG_ON(rcu_dereference(nat_callforwarding_hook) != NULL); - BUG_ON(rcu_dereference(nat_q931_hook) != NULL); - - rcu_assign_pointer(set_h245_addr_hook, set_h245_addr); - rcu_assign_pointer(set_h225_addr_hook, set_h225_addr); - rcu_assign_pointer(set_sig_addr_hook, set_sig_addr); - rcu_assign_pointer(set_ras_addr_hook, set_ras_addr); - rcu_assign_pointer(nat_rtp_rtcp_hook, nat_rtp_rtcp); - rcu_assign_pointer(nat_t120_hook, nat_t120); - rcu_assign_pointer(nat_h245_hook, nat_h245); - rcu_assign_pointer(nat_callforwarding_hook, nat_callforwarding); - rcu_assign_pointer(nat_q931_hook, nat_q931); - - DEBUGP("ip_nat_h323: init success\n"); - return 0; -} - -/****************************************************************************/ -static void __exit fini(void) -{ - rcu_assign_pointer(set_h245_addr_hook, NULL); - rcu_assign_pointer(set_h225_addr_hook, NULL); - rcu_assign_pointer(set_sig_addr_hook, NULL); - rcu_assign_pointer(set_ras_addr_hook, NULL); - rcu_assign_pointer(nat_rtp_rtcp_hook, NULL); - rcu_assign_pointer(nat_t120_hook, NULL); - rcu_assign_pointer(nat_h245_hook, NULL); - rcu_assign_pointer(nat_callforwarding_hook, NULL); - rcu_assign_pointer(nat_q931_hook, NULL); - synchronize_rcu(); -} - -/****************************************************************************/ -module_init(init); -module_exit(fini); - -MODULE_AUTHOR("Jing Min Zhao "); -MODULE_DESCRIPTION("H.323 NAT helper"); -MODULE_LICENSE("GPL"); diff --git a/net/ipv4/netfilter/ip_nat_helper_pptp.c b/net/ipv4/netfilter/ip_nat_helper_pptp.c deleted file mode 100644 index 24ce4a5023d7..000000000000 --- a/net/ipv4/netfilter/ip_nat_helper_pptp.c +++ /dev/null @@ -1,350 +0,0 @@ -/* - * ip_nat_pptp.c - Version 3.0 - * - * NAT support for PPTP (Point to Point Tunneling Protocol). - * PPTP is a a protocol for creating virtual private networks. - * It is a specification defined by Microsoft and some vendors - * working with Microsoft. PPTP is built on top of a modified - * version of the Internet Generic Routing Encapsulation Protocol. - * GRE is defined in RFC 1701 and RFC 1702. Documentation of - * PPTP can be found in RFC 2637 - * - * (C) 2000-2005 by Harald Welte - * - * Development of this code funded by Astaro AG (http://www.astaro.com/) - * - * TODO: - NAT to a unique tuple, not to TCP source port - * (needs netfilter tuple reservation) - * - * Changes: - * 2002-02-10 - Version 1.3 - * - Use ip_nat_mangle_tcp_packet() because of cloned skb's - * in local connections (Philip Craig ) - * - add checks for magicCookie and pptp version - * - make argument list of pptp_{out,in}bound_packet() shorter - * - move to C99 style initializers - * - print version number at module loadtime - * 2003-09-22 - Version 1.5 - * - use SNATed tcp sourceport as callid, since we get called before - * TCP header is mangled (Philip Craig ) - * 2004-10-22 - Version 2.0 - * - kernel 2.6.x version - * 2005-06-10 - Version 3.0 - * - kernel >= 2.6.11 version, - * funded by Oxcoda NetBox Blue (http://www.netboxblue.com/) - * - */ - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#define IP_NAT_PPTP_VERSION "3.0" - -#define REQ_CID(req, off) (*(__be16 *)((char *)(req) + (off))) - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Harald Welte "); -MODULE_DESCRIPTION("Netfilter NAT helper module for PPTP"); - - -#if 0 -extern const char *pptp_msg_name[]; -#define DEBUGP(format, args...) printk(KERN_DEBUG "%s:%s: " format, __FILE__, \ - __FUNCTION__, ## args) -#else -#define DEBUGP(format, args...) -#endif - -static void pptp_nat_expected(struct ip_conntrack *ct, - struct ip_conntrack_expect *exp) -{ - struct ip_conntrack *master = ct->master; - struct ip_conntrack_expect *other_exp; - struct ip_conntrack_tuple t; - struct ip_ct_pptp_master *ct_pptp_info; - struct ip_nat_pptp *nat_pptp_info; - struct ip_nat_range range; - - ct_pptp_info = &master->help.ct_pptp_info; - nat_pptp_info = &master->nat.help.nat_pptp_info; - - /* And here goes the grand finale of corrosion... */ - - if (exp->dir == IP_CT_DIR_ORIGINAL) { - DEBUGP("we are PNS->PAC\n"); - /* therefore, build tuple for PAC->PNS */ - t.src.ip = master->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip; - t.src.u.gre.key = master->help.ct_pptp_info.pac_call_id; - t.dst.ip = master->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip; - t.dst.u.gre.key = master->help.ct_pptp_info.pns_call_id; - t.dst.protonum = IPPROTO_GRE; - } else { - DEBUGP("we are PAC->PNS\n"); - /* build tuple for PNS->PAC */ - t.src.ip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip; - t.src.u.gre.key = master->nat.help.nat_pptp_info.pns_call_id; - t.dst.ip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip; - t.dst.u.gre.key = master->nat.help.nat_pptp_info.pac_call_id; - t.dst.protonum = IPPROTO_GRE; - } - - DEBUGP("trying to unexpect other dir: "); - DUMP_TUPLE(&t); - other_exp = ip_conntrack_expect_find_get(&t); - if (other_exp) { - ip_conntrack_unexpect_related(other_exp); - ip_conntrack_expect_put(other_exp); - DEBUGP("success\n"); - } else { - DEBUGP("not found!\n"); - } - - /* This must be a fresh one. */ - BUG_ON(ct->status & IPS_NAT_DONE_MASK); - - /* Change src to where master sends to */ - range.flags = IP_NAT_RANGE_MAP_IPS; - range.min_ip = range.max_ip - = ct->master->tuplehash[!exp->dir].tuple.dst.ip; - if (exp->dir == IP_CT_DIR_ORIGINAL) { - range.flags |= IP_NAT_RANGE_PROTO_SPECIFIED; - range.min = range.max = exp->saved_proto; - } - /* hook doesn't matter, but it has to do source manip */ - ip_nat_setup_info(ct, &range, NF_IP_POST_ROUTING); - - /* For DST manip, map port here to where it's expected. */ - range.flags = IP_NAT_RANGE_MAP_IPS; - range.min_ip = range.max_ip - = ct->master->tuplehash[!exp->dir].tuple.src.ip; - if (exp->dir == IP_CT_DIR_REPLY) { - range.flags |= IP_NAT_RANGE_PROTO_SPECIFIED; - range.min = range.max = exp->saved_proto; - } - /* hook doesn't matter, but it has to do destination manip */ - ip_nat_setup_info(ct, &range, NF_IP_PRE_ROUTING); -} - -/* outbound packets == from PNS to PAC */ -static int -pptp_outbound_pkt(struct sk_buff **pskb, - struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo, - struct PptpControlHeader *ctlh, - union pptp_ctrl_union *pptpReq) - -{ - struct ip_ct_pptp_master *ct_pptp_info = &ct->help.ct_pptp_info; - struct ip_nat_pptp *nat_pptp_info = &ct->nat.help.nat_pptp_info; - u_int16_t msg; - __be16 new_callid; - unsigned int cid_off; - - new_callid = ct_pptp_info->pns_call_id; - - switch (msg = ntohs(ctlh->messageType)) { - case PPTP_OUT_CALL_REQUEST: - cid_off = offsetof(union pptp_ctrl_union, ocreq.callID); - /* FIXME: ideally we would want to reserve a call ID - * here. current netfilter NAT core is not able to do - * this :( For now we use TCP source port. This breaks - * multiple calls within one control session */ - - /* save original call ID in nat_info */ - nat_pptp_info->pns_call_id = ct_pptp_info->pns_call_id; - - /* don't use tcph->source since we are at a DSTmanip - * hook (e.g. PREROUTING) and pkt is not mangled yet */ - new_callid = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u.tcp.port; - - /* save new call ID in ct info */ - ct_pptp_info->pns_call_id = new_callid; - break; - case PPTP_IN_CALL_REPLY: - cid_off = offsetof(union pptp_ctrl_union, icack.callID); - break; - case PPTP_CALL_CLEAR_REQUEST: - cid_off = offsetof(union pptp_ctrl_union, clrreq.callID); - break; - default: - DEBUGP("unknown outbound packet 0x%04x:%s\n", msg, - (msg <= PPTP_MSG_MAX)? - pptp_msg_name[msg]:pptp_msg_name[0]); - /* fall through */ - - case PPTP_SET_LINK_INFO: - /* only need to NAT in case PAC is behind NAT box */ - case PPTP_START_SESSION_REQUEST: - case PPTP_START_SESSION_REPLY: - case PPTP_STOP_SESSION_REQUEST: - case PPTP_STOP_SESSION_REPLY: - case PPTP_ECHO_REQUEST: - case PPTP_ECHO_REPLY: - /* no need to alter packet */ - return NF_ACCEPT; - } - - /* only OUT_CALL_REQUEST, IN_CALL_REPLY, CALL_CLEAR_REQUEST pass - * down to here */ - DEBUGP("altering call id from 0x%04x to 0x%04x\n", - ntohs(REQ_CID(pptpReq, cid_off)), ntohs(new_callid)); - - /* mangle packet */ - if (ip_nat_mangle_tcp_packet(pskb, ct, ctinfo, - cid_off + sizeof(struct pptp_pkt_hdr) + - sizeof(struct PptpControlHeader), - sizeof(new_callid), (char *)&new_callid, - sizeof(new_callid)) == 0) - return NF_DROP; - - return NF_ACCEPT; -} - -static void -pptp_exp_gre(struct ip_conntrack_expect *expect_orig, - struct ip_conntrack_expect *expect_reply) -{ - struct ip_conntrack *ct = expect_orig->master; - struct ip_ct_pptp_master *ct_pptp_info = &ct->help.ct_pptp_info; - struct ip_nat_pptp *nat_pptp_info = &ct->nat.help.nat_pptp_info; - - /* save original PAC call ID in nat_info */ - nat_pptp_info->pac_call_id = ct_pptp_info->pac_call_id; - - /* alter expectation for PNS->PAC direction */ - expect_orig->saved_proto.gre.key = ct_pptp_info->pns_call_id; - expect_orig->tuple.src.u.gre.key = nat_pptp_info->pns_call_id; - expect_orig->tuple.dst.u.gre.key = ct_pptp_info->pac_call_id; - expect_orig->dir = IP_CT_DIR_ORIGINAL; - - /* alter expectation for PAC->PNS direction */ - expect_reply->saved_proto.gre.key = nat_pptp_info->pns_call_id; - expect_reply->tuple.src.u.gre.key = nat_pptp_info->pac_call_id; - expect_reply->tuple.dst.u.gre.key = ct_pptp_info->pns_call_id; - expect_reply->dir = IP_CT_DIR_REPLY; -} - -/* inbound packets == from PAC to PNS */ -static int -pptp_inbound_pkt(struct sk_buff **pskb, - struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo, - struct PptpControlHeader *ctlh, - union pptp_ctrl_union *pptpReq) -{ - struct ip_nat_pptp *nat_pptp_info = &ct->nat.help.nat_pptp_info; - u_int16_t msg; - __be16 new_pcid; - unsigned int pcid_off; - - new_pcid = nat_pptp_info->pns_call_id; - - switch (msg = ntohs(ctlh->messageType)) { - case PPTP_OUT_CALL_REPLY: - pcid_off = offsetof(union pptp_ctrl_union, ocack.peersCallID); - break; - case PPTP_IN_CALL_CONNECT: - pcid_off = offsetof(union pptp_ctrl_union, iccon.peersCallID); - break; - case PPTP_IN_CALL_REQUEST: - /* only need to nat in case PAC is behind NAT box */ - return NF_ACCEPT; - case PPTP_WAN_ERROR_NOTIFY: - pcid_off = offsetof(union pptp_ctrl_union, wanerr.peersCallID); - break; - case PPTP_CALL_DISCONNECT_NOTIFY: - pcid_off = offsetof(union pptp_ctrl_union, disc.callID); - break; - case PPTP_SET_LINK_INFO: - pcid_off = offsetof(union pptp_ctrl_union, setlink.peersCallID); - break; - - default: - DEBUGP("unknown inbound packet %s\n", (msg <= PPTP_MSG_MAX)? - pptp_msg_name[msg]:pptp_msg_name[0]); - /* fall through */ - - case PPTP_START_SESSION_REQUEST: - case PPTP_START_SESSION_REPLY: - case PPTP_STOP_SESSION_REQUEST: - case PPTP_STOP_SESSION_REPLY: - case PPTP_ECHO_REQUEST: - case PPTP_ECHO_REPLY: - /* no need to alter packet */ - return NF_ACCEPT; - } - - /* only OUT_CALL_REPLY, IN_CALL_CONNECT, IN_CALL_REQUEST, - * WAN_ERROR_NOTIFY, CALL_DISCONNECT_NOTIFY pass down here */ - - /* mangle packet */ - DEBUGP("altering peer call id from 0x%04x to 0x%04x\n", - ntohs(REQ_CID(pptpReq, pcid_off)), ntohs(new_pcid)); - - if (ip_nat_mangle_tcp_packet(pskb, ct, ctinfo, - pcid_off + sizeof(struct pptp_pkt_hdr) + - sizeof(struct PptpControlHeader), - sizeof(new_pcid), (char *)&new_pcid, - sizeof(new_pcid)) == 0) - return NF_DROP; - return NF_ACCEPT; -} - - -extern int __init ip_nat_proto_gre_init(void); -extern void __exit ip_nat_proto_gre_fini(void); - -static int __init ip_nat_helper_pptp_init(void) -{ - int ret; - - DEBUGP("%s: registering NAT helper\n", __FILE__); - - ret = ip_nat_proto_gre_init(); - if (ret < 0) - return ret; - - BUG_ON(rcu_dereference(ip_nat_pptp_hook_outbound)); - rcu_assign_pointer(ip_nat_pptp_hook_outbound, pptp_outbound_pkt); - - BUG_ON(rcu_dereference(ip_nat_pptp_hook_inbound)); - rcu_assign_pointer(ip_nat_pptp_hook_inbound, pptp_inbound_pkt); - - BUG_ON(rcu_dereference(ip_nat_pptp_hook_exp_gre)); - rcu_assign_pointer(ip_nat_pptp_hook_exp_gre, pptp_exp_gre); - - BUG_ON(rcu_dereference(ip_nat_pptp_hook_expectfn)); - rcu_assign_pointer(ip_nat_pptp_hook_expectfn, pptp_nat_expected); - - printk("ip_nat_pptp version %s loaded\n", IP_NAT_PPTP_VERSION); - return 0; -} - -static void __exit ip_nat_helper_pptp_fini(void) -{ - DEBUGP("cleanup_module\n" ); - - rcu_assign_pointer(ip_nat_pptp_hook_expectfn, NULL); - rcu_assign_pointer(ip_nat_pptp_hook_exp_gre, NULL); - rcu_assign_pointer(ip_nat_pptp_hook_inbound, NULL); - rcu_assign_pointer(ip_nat_pptp_hook_outbound, NULL); - synchronize_rcu(); - - ip_nat_proto_gre_fini(); - - printk("ip_nat_pptp version %s unloaded\n", IP_NAT_PPTP_VERSION); -} - -module_init(ip_nat_helper_pptp_init); -module_exit(ip_nat_helper_pptp_fini); diff --git a/net/ipv4/netfilter/ip_nat_irc.c b/net/ipv4/netfilter/ip_nat_irc.c deleted file mode 100644 index cfaeea38314f..000000000000 --- a/net/ipv4/netfilter/ip_nat_irc.c +++ /dev/null @@ -1,122 +0,0 @@ -/* IRC extension for TCP NAT alteration. - * (C) 2000-2001 by Harald Welte - * (C) 2004 Rusty Russell IBM Corporation - * based on a copy of RR's ip_nat_ftp.c - * - * ip_nat_irc.c,v 1.16 2001/12/06 07:42:10 laforge Exp - * - * 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. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#if 0 -#define DEBUGP printk -#else -#define DEBUGP(format, args...) -#endif - -MODULE_AUTHOR("Harald Welte "); -MODULE_DESCRIPTION("IRC (DCC) NAT helper"); -MODULE_LICENSE("GPL"); - -static unsigned int help(struct sk_buff **pskb, - enum ip_conntrack_info ctinfo, - unsigned int matchoff, - unsigned int matchlen, - struct ip_conntrack_expect *exp) -{ - u_int16_t port; - unsigned int ret; - - /* "4294967296 65635 " */ - char buffer[18]; - - DEBUGP("IRC_NAT: info (seq %u + %u) in %u\n", - expect->seq, exp_irc_info->len, - ntohl(tcph->seq)); - - /* Reply comes from server. */ - exp->saved_proto.tcp.port = exp->tuple.dst.u.tcp.port; - exp->dir = IP_CT_DIR_REPLY; - - /* When you see the packet, we need to NAT it the same as the - * this one. */ - exp->expectfn = ip_nat_follow_master; - - /* Try to get same port: if not, try to change it. */ - for (port = ntohs(exp->saved_proto.tcp.port); port != 0; port++) { - exp->tuple.dst.u.tcp.port = htons(port); - if (ip_conntrack_expect_related(exp) == 0) - break; - } - - if (port == 0) - return NF_DROP; - - /* strlen("\1DCC CHAT chat AAAAAAAA P\1\n")=27 - * strlen("\1DCC SCHAT chat AAAAAAAA P\1\n")=28 - * strlen("\1DCC SEND F AAAAAAAA P S\1\n")=26 - * strlen("\1DCC MOVE F AAAAAAAA P S\1\n")=26 - * strlen("\1DCC TSEND F AAAAAAAA P S\1\n")=27 - * AAAAAAAAA: bound addr (1.0.0.0==16777216, min 8 digits, - * 255.255.255.255==4294967296, 10 digits) - * P: bound port (min 1 d, max 5d (65635)) - * F: filename (min 1 d ) - * S: size (min 1 d ) - * 0x01, \n: terminators - */ - - /* AAA = "us", ie. where server normally talks to. */ - sprintf(buffer, "%u %u", - ntohl(exp->master->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip), - port); - DEBUGP("ip_nat_irc: Inserting '%s' == %u.%u.%u.%u, port %u\n", - buffer, NIPQUAD(exp->tuple.src.ip), port); - - ret = ip_nat_mangle_tcp_packet(pskb, exp->master, ctinfo, - matchoff, matchlen, buffer, - strlen(buffer)); - if (ret != NF_ACCEPT) - ip_conntrack_unexpect_related(exp); - return ret; -} - -static void __exit ip_nat_irc_fini(void) -{ - rcu_assign_pointer(ip_nat_irc_hook, NULL); - synchronize_rcu(); -} - -static int __init ip_nat_irc_init(void) -{ - BUG_ON(rcu_dereference(ip_nat_irc_hook)); - rcu_assign_pointer(ip_nat_irc_hook, help); - return 0; -} - -/* Prior to 2.6.11, we had a ports param. No longer, but don't break users. */ -static int warn_set(const char *val, struct kernel_param *kp) -{ - printk(KERN_INFO KBUILD_MODNAME - ": kernel >= 2.6.10 only uses 'ports' for conntrack modules\n"); - return 0; -} -module_param_call(ports, warn_set, NULL, NULL, 0); - -module_init(ip_nat_irc_init); -module_exit(ip_nat_irc_fini); diff --git a/net/ipv4/netfilter/ip_nat_proto_gre.c b/net/ipv4/netfilter/ip_nat_proto_gre.c deleted file mode 100644 index 95810202d849..000000000000 --- a/net/ipv4/netfilter/ip_nat_proto_gre.c +++ /dev/null @@ -1,174 +0,0 @@ -/* - * ip_nat_proto_gre.c - Version 2.0 - * - * NAT protocol helper module for GRE. - * - * GRE is a generic encapsulation protocol, which is generally not very - * suited for NAT, as it has no protocol-specific part as port numbers. - * - * It has an optional key field, which may help us distinguishing two - * connections between the same two hosts. - * - * GRE is defined in RFC 1701 and RFC 1702, as well as RFC 2784 - * - * PPTP is built on top of a modified version of GRE, and has a mandatory - * field called "CallID", which serves us for the same purpose as the key - * field in plain GRE. - * - * Documentation about PPTP can be found in RFC 2637 - * - * (C) 2000-2005 by Harald Welte - * - * Development of this code funded by Astaro AG (http://www.astaro.com/) - * - */ - -#include -#include -#include -#include -#include -#include - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Harald Welte "); -MODULE_DESCRIPTION("Netfilter NAT protocol helper module for GRE"); - -#if 0 -#define DEBUGP(format, args...) printk(KERN_DEBUG "%s:%s: " format, __FILE__, \ - __FUNCTION__, ## args) -#else -#define DEBUGP(x, args...) -#endif - -/* is key in given range between min and max */ -static int -gre_in_range(const struct ip_conntrack_tuple *tuple, - enum ip_nat_manip_type maniptype, - const union ip_conntrack_manip_proto *min, - const union ip_conntrack_manip_proto *max) -{ - __be16 key; - - if (maniptype == IP_NAT_MANIP_SRC) - key = tuple->src.u.gre.key; - else - key = tuple->dst.u.gre.key; - - return ntohs(key) >= ntohs(min->gre.key) - && ntohs(key) <= ntohs(max->gre.key); -} - -/* generate unique tuple ... */ -static int -gre_unique_tuple(struct ip_conntrack_tuple *tuple, - const struct ip_nat_range *range, - enum ip_nat_manip_type maniptype, - const struct ip_conntrack *conntrack) -{ - static u_int16_t key; - __be16 *keyptr; - unsigned int min, i, range_size; - - if (maniptype == IP_NAT_MANIP_SRC) - keyptr = &tuple->src.u.gre.key; - else - keyptr = &tuple->dst.u.gre.key; - - if (!(range->flags & IP_NAT_RANGE_PROTO_SPECIFIED)) { - DEBUGP("%p: NATing GRE PPTP\n", conntrack); - min = 1; - range_size = 0xffff; - } else { - min = ntohs(range->min.gre.key); - range_size = ntohs(range->max.gre.key) - min + 1; - } - - DEBUGP("min = %u, range_size = %u\n", min, range_size); - - for (i = 0; i < range_size; i++, key++) { - *keyptr = htons(min + key % range_size); - if (!ip_nat_used_tuple(tuple, conntrack)) - return 1; - } - - DEBUGP("%p: no NAT mapping\n", conntrack); - - return 0; -} - -/* manipulate a GRE packet according to maniptype */ -static int -gre_manip_pkt(struct sk_buff **pskb, - unsigned int iphdroff, - const struct ip_conntrack_tuple *tuple, - enum ip_nat_manip_type maniptype) -{ - struct gre_hdr *greh; - struct gre_hdr_pptp *pgreh; - struct iphdr *iph = (struct iphdr *)((*pskb)->data + iphdroff); - unsigned int hdroff = iphdroff + iph->ihl*4; - - /* pgreh includes two optional 32bit fields which are not required - * to be there. That's where the magic '8' comes from */ - if (!skb_make_writable(pskb, hdroff + sizeof(*pgreh)-8)) - return 0; - - greh = (void *)(*pskb)->data + hdroff; - pgreh = (struct gre_hdr_pptp *) greh; - - /* we only have destination manip of a packet, since 'source key' - * is not present in the packet itself */ - if (maniptype == IP_NAT_MANIP_DST) { - /* key manipulation is always dest */ - switch (greh->version) { - case 0: - if (!greh->key) { - DEBUGP("can't nat GRE w/o key\n"); - break; - } - if (greh->csum) { - /* FIXME: Never tested this code... */ - nf_proto_csum_replace4(gre_csum(greh), *pskb, - *(gre_key(greh)), - tuple->dst.u.gre.key, 0); - } - *(gre_key(greh)) = tuple->dst.u.gre.key; - break; - case GRE_VERSION_PPTP: - DEBUGP("call_id -> 0x%04x\n", - ntohs(tuple->dst.u.gre.key)); - pgreh->call_id = tuple->dst.u.gre.key; - break; - default: - DEBUGP("can't nat unknown GRE version\n"); - return 0; - break; - } - } - return 1; -} - -/* nat helper struct */ -static struct ip_nat_protocol gre = { - .name = "GRE", - .protonum = IPPROTO_GRE, - .manip_pkt = gre_manip_pkt, - .in_range = gre_in_range, - .unique_tuple = gre_unique_tuple, -#if defined(CONFIG_IP_NF_CONNTRACK_NETLINK) || \ - defined(CONFIG_IP_NF_CONNTRACK_NETLINK_MODULE) - .range_to_nfattr = ip_nat_port_range_to_nfattr, - .nfattr_to_range = ip_nat_port_nfattr_to_range, -#endif -}; - -int __init ip_nat_proto_gre_init(void) -{ - return ip_nat_protocol_register(&gre); -} - -void __exit ip_nat_proto_gre_fini(void) -{ - ip_nat_protocol_unregister(&gre); -} diff --git a/net/ipv4/netfilter/ip_nat_proto_icmp.c b/net/ipv4/netfilter/ip_nat_proto_icmp.c deleted file mode 100644 index 22a528ae0380..000000000000 --- a/net/ipv4/netfilter/ip_nat_proto_icmp.c +++ /dev/null @@ -1,87 +0,0 @@ -/* (C) 1999-2001 Paul `Rusty' Russell - * (C) 2002-2004 Netfilter Core Team - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -static int -icmp_in_range(const struct ip_conntrack_tuple *tuple, - enum ip_nat_manip_type maniptype, - const union ip_conntrack_manip_proto *min, - const union ip_conntrack_manip_proto *max) -{ - return ntohs(tuple->src.u.icmp.id) >= ntohs(min->icmp.id) && - ntohs(tuple->src.u.icmp.id) <= ntohs(max->icmp.id); -} - -static int -icmp_unique_tuple(struct ip_conntrack_tuple *tuple, - const struct ip_nat_range *range, - enum ip_nat_manip_type maniptype, - const struct ip_conntrack *conntrack) -{ - static u_int16_t id; - unsigned int range_size; - unsigned int i; - - range_size = ntohs(range->max.icmp.id) - ntohs(range->min.icmp.id) + 1; - /* If no range specified... */ - if (!(range->flags & IP_NAT_RANGE_PROTO_SPECIFIED)) - range_size = 0xFFFF; - - for (i = 0; i < range_size; i++, id++) { - tuple->src.u.icmp.id = htons(ntohs(range->min.icmp.id) + - (id % range_size)); - if (!ip_nat_used_tuple(tuple, conntrack)) - return 1; - } - return 0; -} - -static int -icmp_manip_pkt(struct sk_buff **pskb, - unsigned int iphdroff, - const struct ip_conntrack_tuple *tuple, - enum ip_nat_manip_type maniptype) -{ - struct iphdr *iph = (struct iphdr *)((*pskb)->data + iphdroff); - struct icmphdr *hdr; - unsigned int hdroff = iphdroff + iph->ihl*4; - - if (!skb_make_writable(pskb, hdroff + sizeof(*hdr))) - return 0; - - hdr = (struct icmphdr *)((*pskb)->data + hdroff); - nf_proto_csum_replace2(&hdr->checksum, *pskb, - hdr->un.echo.id, tuple->src.u.icmp.id, 0); - hdr->un.echo.id = tuple->src.u.icmp.id; - return 1; -} - -struct ip_nat_protocol ip_nat_protocol_icmp = { - .name = "ICMP", - .protonum = IPPROTO_ICMP, - .me = THIS_MODULE, - .manip_pkt = icmp_manip_pkt, - .in_range = icmp_in_range, - .unique_tuple = icmp_unique_tuple, -#if defined(CONFIG_IP_NF_CONNTRACK_NETLINK) || \ - defined(CONFIG_IP_NF_CONNTRACK_NETLINK_MODULE) - .range_to_nfattr = ip_nat_port_range_to_nfattr, - .nfattr_to_range = ip_nat_port_nfattr_to_range, -#endif -}; diff --git a/net/ipv4/netfilter/ip_nat_proto_tcp.c b/net/ipv4/netfilter/ip_nat_proto_tcp.c deleted file mode 100644 index 14ff24f53a7a..000000000000 --- a/net/ipv4/netfilter/ip_nat_proto_tcp.c +++ /dev/null @@ -1,154 +0,0 @@ -/* (C) 1999-2001 Paul `Rusty' Russell - * (C) 2002-2004 Netfilter Core Team - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static int -tcp_in_range(const struct ip_conntrack_tuple *tuple, - enum ip_nat_manip_type maniptype, - const union ip_conntrack_manip_proto *min, - const union ip_conntrack_manip_proto *max) -{ - __be16 port; - - if (maniptype == IP_NAT_MANIP_SRC) - port = tuple->src.u.tcp.port; - else - port = tuple->dst.u.tcp.port; - - return ntohs(port) >= ntohs(min->tcp.port) - && ntohs(port) <= ntohs(max->tcp.port); -} - -static int -tcp_unique_tuple(struct ip_conntrack_tuple *tuple, - const struct ip_nat_range *range, - enum ip_nat_manip_type maniptype, - const struct ip_conntrack *conntrack) -{ - static u_int16_t port; - __be16 *portptr; - unsigned int range_size, min, i; - - if (maniptype == IP_NAT_MANIP_SRC) - portptr = &tuple->src.u.tcp.port; - else - portptr = &tuple->dst.u.tcp.port; - - /* If no range specified... */ - if (!(range->flags & IP_NAT_RANGE_PROTO_SPECIFIED)) { - /* If it's dst rewrite, can't change port */ - if (maniptype == IP_NAT_MANIP_DST) - return 0; - - /* Map privileged onto privileged. */ - if (ntohs(*portptr) < 1024) { - /* Loose convention: >> 512 is credential passing */ - if (ntohs(*portptr)<512) { - min = 1; - range_size = 511 - min + 1; - } else { - min = 600; - range_size = 1023 - min + 1; - } - } else { - min = 1024; - range_size = 65535 - 1024 + 1; - } - } else { - min = ntohs(range->min.tcp.port); - range_size = ntohs(range->max.tcp.port) - min + 1; - } - - /* Start from random port to avoid prediction */ - if (range->flags & IP_NAT_RANGE_PROTO_RANDOM) - port = net_random(); - - for (i = 0; i < range_size; i++, port++) { - *portptr = htons(min + port % range_size); - if (!ip_nat_used_tuple(tuple, conntrack)) { - return 1; - } - } - return 0; -} - -static int -tcp_manip_pkt(struct sk_buff **pskb, - unsigned int iphdroff, - const struct ip_conntrack_tuple *tuple, - enum ip_nat_manip_type maniptype) -{ - struct iphdr *iph = (struct iphdr *)((*pskb)->data + iphdroff); - struct tcphdr *hdr; - unsigned int hdroff = iphdroff + iph->ihl*4; - __be32 oldip, newip; - __be16 *portptr, newport, oldport; - int hdrsize = 8; /* TCP connection tracking guarantees this much */ - - /* this could be a inner header returned in icmp packet; in such - cases we cannot update the checksum field since it is outside of - the 8 bytes of transport layer headers we are guaranteed */ - if ((*pskb)->len >= hdroff + sizeof(struct tcphdr)) - hdrsize = sizeof(struct tcphdr); - - if (!skb_make_writable(pskb, hdroff + hdrsize)) - return 0; - - iph = (struct iphdr *)((*pskb)->data + iphdroff); - hdr = (struct tcphdr *)((*pskb)->data + hdroff); - - if (maniptype == IP_NAT_MANIP_SRC) { - /* Get rid of src ip and src pt */ - oldip = iph->saddr; - newip = tuple->src.ip; - newport = tuple->src.u.tcp.port; - portptr = &hdr->source; - } else { - /* Get rid of dst ip and dst pt */ - oldip = iph->daddr; - newip = tuple->dst.ip; - newport = tuple->dst.u.tcp.port; - portptr = &hdr->dest; - } - - oldport = *portptr; - *portptr = newport; - - if (hdrsize < sizeof(*hdr)) - return 1; - - nf_proto_csum_replace4(&hdr->check, *pskb, oldip, newip, 1); - nf_proto_csum_replace2(&hdr->check, *pskb, oldport, newport, 0); - return 1; -} - -struct ip_nat_protocol ip_nat_protocol_tcp = { - .name = "TCP", - .protonum = IPPROTO_TCP, - .me = THIS_MODULE, - .manip_pkt = tcp_manip_pkt, - .in_range = tcp_in_range, - .unique_tuple = tcp_unique_tuple, -#if defined(CONFIG_IP_NF_CONNTRACK_NETLINK) || \ - defined(CONFIG_IP_NF_CONNTRACK_NETLINK_MODULE) - .range_to_nfattr = ip_nat_port_range_to_nfattr, - .nfattr_to_range = ip_nat_port_nfattr_to_range, -#endif -}; diff --git a/net/ipv4/netfilter/ip_nat_proto_udp.c b/net/ipv4/netfilter/ip_nat_proto_udp.c deleted file mode 100644 index dfd521672891..000000000000 --- a/net/ipv4/netfilter/ip_nat_proto_udp.c +++ /dev/null @@ -1,144 +0,0 @@ -/* (C) 1999-2001 Paul `Rusty' Russell - * (C) 2002-2004 Netfilter Core Team - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -static int -udp_in_range(const struct ip_conntrack_tuple *tuple, - enum ip_nat_manip_type maniptype, - const union ip_conntrack_manip_proto *min, - const union ip_conntrack_manip_proto *max) -{ - __be16 port; - - if (maniptype == IP_NAT_MANIP_SRC) - port = tuple->src.u.udp.port; - else - port = tuple->dst.u.udp.port; - - return ntohs(port) >= ntohs(min->udp.port) - && ntohs(port) <= ntohs(max->udp.port); -} - -static int -udp_unique_tuple(struct ip_conntrack_tuple *tuple, - const struct ip_nat_range *range, - enum ip_nat_manip_type maniptype, - const struct ip_conntrack *conntrack) -{ - static u_int16_t port; - __be16 *portptr; - unsigned int range_size, min, i; - - if (maniptype == IP_NAT_MANIP_SRC) - portptr = &tuple->src.u.udp.port; - else - portptr = &tuple->dst.u.udp.port; - - /* If no range specified... */ - if (!(range->flags & IP_NAT_RANGE_PROTO_SPECIFIED)) { - /* If it's dst rewrite, can't change port */ - if (maniptype == IP_NAT_MANIP_DST) - return 0; - - if (ntohs(*portptr) < 1024) { - /* Loose convention: >> 512 is credential passing */ - if (ntohs(*portptr)<512) { - min = 1; - range_size = 511 - min + 1; - } else { - min = 600; - range_size = 1023 - min + 1; - } - } else { - min = 1024; - range_size = 65535 - 1024 + 1; - } - } else { - min = ntohs(range->min.udp.port); - range_size = ntohs(range->max.udp.port) - min + 1; - } - - /* Start from random port to avoid prediction */ - if (range->flags & IP_NAT_RANGE_PROTO_RANDOM) - port = net_random(); - - for (i = 0; i < range_size; i++, port++) { - *portptr = htons(min + port % range_size); - if (!ip_nat_used_tuple(tuple, conntrack)) - return 1; - } - return 0; -} - -static int -udp_manip_pkt(struct sk_buff **pskb, - unsigned int iphdroff, - const struct ip_conntrack_tuple *tuple, - enum ip_nat_manip_type maniptype) -{ - struct iphdr *iph = (struct iphdr *)((*pskb)->data + iphdroff); - struct udphdr *hdr; - unsigned int hdroff = iphdroff + iph->ihl*4; - __be32 oldip, newip; - __be16 *portptr, newport; - - if (!skb_make_writable(pskb, hdroff + sizeof(*hdr))) - return 0; - - iph = (struct iphdr *)((*pskb)->data + iphdroff); - hdr = (struct udphdr *)((*pskb)->data + hdroff); - - if (maniptype == IP_NAT_MANIP_SRC) { - /* Get rid of src ip and src pt */ - oldip = iph->saddr; - newip = tuple->src.ip; - newport = tuple->src.u.udp.port; - portptr = &hdr->source; - } else { - /* Get rid of dst ip and dst pt */ - oldip = iph->daddr; - newip = tuple->dst.ip; - newport = tuple->dst.u.udp.port; - portptr = &hdr->dest; - } - - if (hdr->check || (*pskb)->ip_summed == CHECKSUM_PARTIAL) { - nf_proto_csum_replace4(&hdr->check, *pskb, oldip, newip, 1); - nf_proto_csum_replace2(&hdr->check, *pskb, *portptr, newport, 0); - if (!hdr->check) - hdr->check = CSUM_MANGLED_0; - } - *portptr = newport; - return 1; -} - -struct ip_nat_protocol ip_nat_protocol_udp = { - .name = "UDP", - .protonum = IPPROTO_UDP, - .me = THIS_MODULE, - .manip_pkt = udp_manip_pkt, - .in_range = udp_in_range, - .unique_tuple = udp_unique_tuple, -#if defined(CONFIG_IP_NF_CONNTRACK_NETLINK) || \ - defined(CONFIG_IP_NF_CONNTRACK_NETLINK_MODULE) - .range_to_nfattr = ip_nat_port_range_to_nfattr, - .nfattr_to_range = ip_nat_port_nfattr_to_range, -#endif -}; diff --git a/net/ipv4/netfilter/ip_nat_proto_unknown.c b/net/ipv4/netfilter/ip_nat_proto_unknown.c deleted file mode 100644 index 3bf049517246..000000000000 --- a/net/ipv4/netfilter/ip_nat_proto_unknown.c +++ /dev/null @@ -1,55 +0,0 @@ -/* The "unknown" protocol. This is what is used for protocols we - * don't understand. It's returned by ip_ct_find_proto(). - */ - -/* (C) 1999-2001 Paul `Rusty' Russell - * (C) 2002-2004 Netfilter Core Team - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include - -#include -#include -#include - -static int unknown_in_range(const struct ip_conntrack_tuple *tuple, - enum ip_nat_manip_type manip_type, - const union ip_conntrack_manip_proto *min, - const union ip_conntrack_manip_proto *max) -{ - return 1; -} - -static int unknown_unique_tuple(struct ip_conntrack_tuple *tuple, - const struct ip_nat_range *range, - enum ip_nat_manip_type maniptype, - const struct ip_conntrack *conntrack) -{ - /* Sorry: we can't help you; if it's not unique, we can't frob - anything. */ - return 0; -} - -static int -unknown_manip_pkt(struct sk_buff **pskb, - unsigned int iphdroff, - const struct ip_conntrack_tuple *tuple, - enum ip_nat_manip_type maniptype) -{ - return 1; -} - -struct ip_nat_protocol ip_nat_unknown_protocol = { - .name = "unknown", - /* .me isn't set: getting a ref to this cannot fail. */ - .manip_pkt = unknown_manip_pkt, - .in_range = unknown_in_range, - .unique_tuple = unknown_unique_tuple, -}; diff --git a/net/ipv4/netfilter/ip_nat_rule.c b/net/ipv4/netfilter/ip_nat_rule.c deleted file mode 100644 index 25415a91e023..000000000000 --- a/net/ipv4/netfilter/ip_nat_rule.c +++ /dev/null @@ -1,314 +0,0 @@ -/* (C) 1999-2001 Paul `Rusty' Russell - * (C) 2002-2004 Netfilter Core Team - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -/* Everything about the rules for NAT. */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#if 0 -#define DEBUGP printk -#else -#define DEBUGP(format, args...) -#endif - -#define NAT_VALID_HOOKS ((1<range[0], hooknum); -} - -/* Before 2.6.11 we did implicit source NAT if required. Warn about change. */ -static void warn_if_extra_mangle(__be32 dstip, __be32 srcip) -{ - static int warned = 0; - struct flowi fl = { .nl_u = { .ip4_u = { .daddr = dstip } } }; - struct rtable *rt; - - if (ip_route_output_key(&rt, &fl) != 0) - return; - - if (rt->rt_src != srcip && !warned) { - printk("NAT: no longer support implicit source local NAT\n"); - printk("NAT: packet src %u.%u.%u.%u -> dst %u.%u.%u.%u\n", - NIPQUAD(srcip), NIPQUAD(dstip)); - warned = 1; - } - ip_rt_put(rt); -} - -static unsigned int ipt_dnat_target(struct sk_buff **pskb, - const struct net_device *in, - const struct net_device *out, - unsigned int hooknum, - const struct xt_target *target, - const void *targinfo) -{ - struct ip_conntrack *ct; - enum ip_conntrack_info ctinfo; - const struct ip_nat_multi_range_compat *mr = targinfo; - - IP_NF_ASSERT(hooknum == NF_IP_PRE_ROUTING - || hooknum == NF_IP_LOCAL_OUT); - - ct = ip_conntrack_get(*pskb, &ctinfo); - - /* Connection must be valid and new. */ - IP_NF_ASSERT(ct && (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED)); - - if (hooknum == NF_IP_LOCAL_OUT - && mr->range[0].flags & IP_NAT_RANGE_MAP_IPS) - warn_if_extra_mangle(ip_hdr(*pskb)->daddr, - mr->range[0].min_ip); - - return ip_nat_setup_info(ct, &mr->range[0], hooknum); -} - -static int ipt_snat_checkentry(const char *tablename, - const void *entry, - const struct xt_target *target, - void *targinfo, - unsigned int hook_mask) -{ - struct ip_nat_multi_range_compat *mr = targinfo; - - /* Must be a valid range */ - if (mr->rangesize != 1) { - printk("SNAT: multiple ranges no longer supported\n"); - return 0; - } - return 1; -} - -static int ipt_dnat_checkentry(const char *tablename, - const void *entry, - const struct xt_target *target, - void *targinfo, - unsigned int hook_mask) -{ - struct ip_nat_multi_range_compat *mr = targinfo; - - /* Must be a valid range */ - if (mr->rangesize != 1) { - printk("DNAT: multiple ranges no longer supported\n"); - return 0; - } - if (mr->range[0].flags & IP_NAT_RANGE_PROTO_RANDOM) { - printk("DNAT: port randomization not supported\n"); - return 0; - } - return 1; -} - -inline unsigned int -alloc_null_binding(struct ip_conntrack *conntrack, - struct ip_nat_info *info, - unsigned int hooknum) -{ - /* Force range to this IP; let proto decide mapping for - per-proto parts (hence not IP_NAT_RANGE_PROTO_SPECIFIED). - Use reply in case it's already been mangled (eg local packet). - */ - __be32 ip - = (HOOK2MANIP(hooknum) == IP_NAT_MANIP_SRC - ? conntrack->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip - : conntrack->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip); - struct ip_nat_range range - = { IP_NAT_RANGE_MAP_IPS, ip, ip, { 0 }, { 0 } }; - - DEBUGP("Allocating NULL binding for %p (%u.%u.%u.%u)\n", conntrack, - NIPQUAD(ip)); - return ip_nat_setup_info(conntrack, &range, hooknum); -} - -unsigned int -alloc_null_binding_confirmed(struct ip_conntrack *conntrack, - struct ip_nat_info *info, - unsigned int hooknum) -{ - __be32 ip - = (HOOK2MANIP(hooknum) == IP_NAT_MANIP_SRC - ? conntrack->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip - : conntrack->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip); - u_int16_t all - = (HOOK2MANIP(hooknum) == IP_NAT_MANIP_SRC - ? conntrack->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u.all - : conntrack->tuplehash[IP_CT_DIR_REPLY].tuple.src.u.all); - struct ip_nat_range range - = { IP_NAT_RANGE_MAP_IPS, ip, ip, { all }, { all } }; - - DEBUGP("Allocating NULL binding for confirmed %p (%u.%u.%u.%u)\n", - conntrack, NIPQUAD(ip)); - return ip_nat_setup_info(conntrack, &range, hooknum); -} - -int ip_nat_rule_find(struct sk_buff **pskb, - unsigned int hooknum, - const struct net_device *in, - const struct net_device *out, - struct ip_conntrack *ct, - struct ip_nat_info *info) -{ - int ret; - - ret = ipt_do_table(pskb, hooknum, in, out, &nat_table); - - if (ret == NF_ACCEPT) { - if (!ip_nat_initialized(ct, HOOK2MANIP(hooknum))) - /* NUL mapping */ - ret = alloc_null_binding(ct, info, hooknum); - } - return ret; -} - -static struct xt_target ipt_snat_reg = { - .name = "SNAT", - .family = AF_INET, - .target = ipt_snat_target, - .targetsize = sizeof(struct ip_nat_multi_range_compat), - .table = "nat", - .hooks = 1 << NF_IP_POST_ROUTING, - .checkentry = ipt_snat_checkentry, -}; - -static struct xt_target ipt_dnat_reg = { - .name = "DNAT", - .family = AF_INET, - .target = ipt_dnat_target, - .targetsize = sizeof(struct ip_nat_multi_range_compat), - .table = "nat", - .hooks = (1 << NF_IP_PRE_ROUTING) | (1 << NF_IP_LOCAL_OUT), - .checkentry = ipt_dnat_checkentry, -}; - -int __init ip_nat_rule_init(void) -{ - int ret; - - ret = ipt_register_table(&nat_table, &nat_initial_table.repl); - if (ret != 0) - return ret; - ret = xt_register_target(&ipt_snat_reg); - if (ret != 0) - goto unregister_table; - - ret = xt_register_target(&ipt_dnat_reg); - if (ret != 0) - goto unregister_snat; - - return ret; - - unregister_snat: - xt_unregister_target(&ipt_snat_reg); - unregister_table: - xt_unregister_table(&nat_table); - - return ret; -} - -void ip_nat_rule_cleanup(void) -{ - xt_unregister_target(&ipt_dnat_reg); - xt_unregister_target(&ipt_snat_reg); - ipt_unregister_table(&nat_table); -} diff --git a/net/ipv4/netfilter/ip_nat_sip.c b/net/ipv4/netfilter/ip_nat_sip.c deleted file mode 100644 index 84953601762d..000000000000 --- a/net/ipv4/netfilter/ip_nat_sip.c +++ /dev/null @@ -1,282 +0,0 @@ -/* SIP extension for UDP NAT alteration. - * - * (C) 2005 by Christian Hentschel - * based on RR's ip_nat_ftp.c and other modules. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Christian Hentschel "); -MODULE_DESCRIPTION("SIP NAT helper"); - -#if 0 -#define DEBUGP printk -#else -#define DEBUGP(format, args...) -#endif - -struct addr_map { - struct { - char src[sizeof("nnn.nnn.nnn.nnn:nnnnn")]; - char dst[sizeof("nnn.nnn.nnn.nnn:nnnnn")]; - unsigned int srclen, srciplen; - unsigned int dstlen, dstiplen; - } addr[IP_CT_DIR_MAX]; -}; - -static void addr_map_init(struct ip_conntrack *ct, struct addr_map *map) -{ - struct ip_conntrack_tuple *t; - enum ip_conntrack_dir dir; - unsigned int n; - - for (dir = 0; dir < IP_CT_DIR_MAX; dir++) { - t = &ct->tuplehash[dir].tuple; - - n = sprintf(map->addr[dir].src, "%u.%u.%u.%u", - NIPQUAD(t->src.ip)); - map->addr[dir].srciplen = n; - n += sprintf(map->addr[dir].src + n, ":%u", - ntohs(t->src.u.udp.port)); - map->addr[dir].srclen = n; - - n = sprintf(map->addr[dir].dst, "%u.%u.%u.%u", - NIPQUAD(t->dst.ip)); - map->addr[dir].dstiplen = n; - n += sprintf(map->addr[dir].dst + n, ":%u", - ntohs(t->dst.u.udp.port)); - map->addr[dir].dstlen = n; - } -} - -static int map_sip_addr(struct sk_buff **pskb, enum ip_conntrack_info ctinfo, - struct ip_conntrack *ct, const char **dptr, size_t dlen, - enum sip_header_pos pos, struct addr_map *map) -{ - enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); - unsigned int matchlen, matchoff, addrlen; - char *addr; - - if (ct_sip_get_info(*dptr, dlen, &matchoff, &matchlen, pos) <= 0) - return 1; - - if ((matchlen == map->addr[dir].srciplen || - matchlen == map->addr[dir].srclen) && - memcmp(*dptr + matchoff, map->addr[dir].src, matchlen) == 0) { - addr = map->addr[!dir].dst; - addrlen = map->addr[!dir].dstlen; - } else if ((matchlen == map->addr[dir].dstiplen || - matchlen == map->addr[dir].dstlen) && - memcmp(*dptr + matchoff, map->addr[dir].dst, matchlen) == 0) { - addr = map->addr[!dir].src; - addrlen = map->addr[!dir].srclen; - } else - return 1; - - if (!ip_nat_mangle_udp_packet(pskb, ct, ctinfo, - matchoff, matchlen, addr, addrlen)) - return 0; - *dptr = (*pskb)->data + ip_hdrlen(*pskb) + sizeof(struct udphdr); - return 1; - -} - -static unsigned int ip_nat_sip(struct sk_buff **pskb, - enum ip_conntrack_info ctinfo, - struct ip_conntrack *ct, - const char **dptr) -{ - enum sip_header_pos pos; - struct addr_map map; - int dataoff, datalen; - - dataoff = ip_hdrlen(*pskb) + sizeof(struct udphdr); - datalen = (*pskb)->len - dataoff; - if (datalen < sizeof("SIP/2.0") - 1) - return NF_DROP; - - addr_map_init(ct, &map); - - /* Basic rules: requests and responses. */ - if (strncmp(*dptr, "SIP/2.0", sizeof("SIP/2.0") - 1) != 0) { - /* 10.2: Constructing the REGISTER Request: - * - * The "userinfo" and "@" components of the SIP URI MUST NOT - * be present. - */ - if (datalen >= sizeof("REGISTER") - 1 && - strncmp(*dptr, "REGISTER", sizeof("REGISTER") - 1) == 0) - pos = POS_REG_REQ_URI; - else - pos = POS_REQ_URI; - - if (!map_sip_addr(pskb, ctinfo, ct, dptr, datalen, pos, &map)) - return NF_DROP; - } - - if (!map_sip_addr(pskb, ctinfo, ct, dptr, datalen, POS_FROM, &map) || - !map_sip_addr(pskb, ctinfo, ct, dptr, datalen, POS_TO, &map) || - !map_sip_addr(pskb, ctinfo, ct, dptr, datalen, POS_VIA, &map) || - !map_sip_addr(pskb, ctinfo, ct, dptr, datalen, POS_CONTACT, &map)) - return NF_DROP; - return NF_ACCEPT; -} - -static unsigned int mangle_sip_packet(struct sk_buff **pskb, - enum ip_conntrack_info ctinfo, - struct ip_conntrack *ct, - const char **dptr, size_t dlen, - char *buffer, int bufflen, - enum sip_header_pos pos) -{ - unsigned int matchlen, matchoff; - - if (ct_sip_get_info(*dptr, dlen, &matchoff, &matchlen, pos) <= 0) - return 0; - - if (!ip_nat_mangle_udp_packet(pskb, ct, ctinfo, - matchoff, matchlen, buffer, bufflen)) - return 0; - - /* We need to reload this. Thanks Patrick. */ - *dptr = (*pskb)->data + ip_hdrlen(*pskb) + sizeof(struct udphdr); - return 1; -} - -static int mangle_content_len(struct sk_buff **pskb, - enum ip_conntrack_info ctinfo, - struct ip_conntrack *ct, - const char *dptr) -{ - unsigned int dataoff, matchoff, matchlen; - char buffer[sizeof("65536")]; - int bufflen; - - dataoff = ip_hdrlen(*pskb) + sizeof(struct udphdr); - - /* Get actual SDP lenght */ - if (ct_sip_get_info(dptr, (*pskb)->len - dataoff, &matchoff, - &matchlen, POS_SDP_HEADER) > 0) { - - /* since ct_sip_get_info() give us a pointer passing 'v=' - we need to add 2 bytes in this count. */ - int c_len = (*pskb)->len - dataoff - matchoff + 2; - - /* Now, update SDP lenght */ - if (ct_sip_get_info(dptr, (*pskb)->len - dataoff, &matchoff, - &matchlen, POS_CONTENT) > 0) { - - bufflen = sprintf(buffer, "%u", c_len); - - return ip_nat_mangle_udp_packet(pskb, ct, ctinfo, - matchoff, matchlen, - buffer, bufflen); - } - } - return 0; -} - -static unsigned int mangle_sdp(struct sk_buff **pskb, - enum ip_conntrack_info ctinfo, - struct ip_conntrack *ct, - __be32 newip, u_int16_t port, - const char *dptr) -{ - char buffer[sizeof("nnn.nnn.nnn.nnn")]; - unsigned int dataoff, bufflen; - - dataoff = ip_hdrlen(*pskb) + sizeof(struct udphdr); - - /* Mangle owner and contact info. */ - bufflen = sprintf(buffer, "%u.%u.%u.%u", NIPQUAD(newip)); - if (!mangle_sip_packet(pskb, ctinfo, ct, &dptr, (*pskb)->len - dataoff, - buffer, bufflen, POS_OWNER)) - return 0; - - if (!mangle_sip_packet(pskb, ctinfo, ct, &dptr, (*pskb)->len - dataoff, - buffer, bufflen, POS_CONNECTION)) - return 0; - - /* Mangle media port. */ - bufflen = sprintf(buffer, "%u", port); - if (!mangle_sip_packet(pskb, ctinfo, ct, &dptr, (*pskb)->len - dataoff, - buffer, bufflen, POS_MEDIA)) - return 0; - - return mangle_content_len(pskb, ctinfo, ct, dptr); -} - -/* So, this packet has hit the connection tracking matching code. - Mangle it, and change the expectation to match the new version. */ -static unsigned int ip_nat_sdp(struct sk_buff **pskb, - enum ip_conntrack_info ctinfo, - struct ip_conntrack_expect *exp, - const char *dptr) -{ - struct ip_conntrack *ct = exp->master; - enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); - __be32 newip; - u_int16_t port; - - DEBUGP("ip_nat_sdp():\n"); - - /* Connection will come from reply */ - newip = ct->tuplehash[!dir].tuple.dst.ip; - - exp->tuple.dst.ip = newip; - exp->saved_proto.udp.port = exp->tuple.dst.u.udp.port; - exp->dir = !dir; - - /* When you see the packet, we need to NAT it the same as the - this one. */ - exp->expectfn = ip_nat_follow_master; - - /* Try to get same port: if not, try to change it. */ - for (port = ntohs(exp->saved_proto.udp.port); port != 0; port++) { - exp->tuple.dst.u.udp.port = htons(port); - if (ip_conntrack_expect_related(exp) == 0) - break; - } - - if (port == 0) - return NF_DROP; - - if (!mangle_sdp(pskb, ctinfo, ct, newip, port, dptr)) { - ip_conntrack_unexpect_related(exp); - return NF_DROP; - } - return NF_ACCEPT; -} - -static void __exit fini(void) -{ - rcu_assign_pointer(ip_nat_sip_hook, NULL); - rcu_assign_pointer(ip_nat_sdp_hook, NULL); - synchronize_rcu(); -} - -static int __init init(void) -{ - BUG_ON(rcu_dereference(ip_nat_sip_hook)); - BUG_ON(rcu_dereference(ip_nat_sdp_hook)); - rcu_assign_pointer(ip_nat_sip_hook, ip_nat_sip); - rcu_assign_pointer(ip_nat_sdp_hook, ip_nat_sdp); - return 0; -} - -module_init(init); -module_exit(fini); diff --git a/net/ipv4/netfilter/ip_nat_snmp_basic.c b/net/ipv4/netfilter/ip_nat_snmp_basic.c deleted file mode 100644 index 025e04587789..000000000000 --- a/net/ipv4/netfilter/ip_nat_snmp_basic.c +++ /dev/null @@ -1,1333 +0,0 @@ -/* - * ip_nat_snmp_basic.c - * - * Basic SNMP Application Layer Gateway - * - * This IP NAT module is intended for use with SNMP network - * discovery and monitoring applications where target networks use - * conflicting private address realms. - * - * Static NAT is used to remap the networks from the view of the network - * management system at the IP layer, and this module remaps some application - * layer addresses to match. - * - * The simplest form of ALG is performed, where only tagged IP addresses - * are modified. The module does not need to be MIB aware and only scans - * messages at the ASN.1/BER level. - * - * Currently, only SNMPv1 and SNMPv2 are supported. - * - * More information on ALG and associated issues can be found in - * RFC 2962 - * - * The ASB.1/BER parsing code is derived from the gxsnmp package by Gregory - * McLean & Jochen Friedrich, stripped down for use in the kernel. - * - * Copyright (c) 2000 RP Internet (www.rpi.net.au). - * - * 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 - * - * Author: James Morris - * - * Updates: - * 2000-08-06: Convert to new helper API (Harald Welte). - * - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("James Morris "); -MODULE_DESCRIPTION("Basic SNMP Application Layer Gateway"); - -#define SNMP_PORT 161 -#define SNMP_TRAP_PORT 162 -#define NOCT1(n) (*(u8 *)n) - -static int debug; -static DEFINE_SPINLOCK(snmp_lock); - -/* - * Application layer address mapping mimics the NAT mapping, but - * only for the first octet in this case (a more flexible system - * can be implemented if needed). - */ -struct oct1_map -{ - u_int8_t from; - u_int8_t to; -}; - - -/***************************************************************************** - * - * Basic ASN.1 decoding routines (gxsnmp author Dirk Wisse) - * - *****************************************************************************/ - -/* Class */ -#define ASN1_UNI 0 /* Universal */ -#define ASN1_APL 1 /* Application */ -#define ASN1_CTX 2 /* Context */ -#define ASN1_PRV 3 /* Private */ - -/* Tag */ -#define ASN1_EOC 0 /* End Of Contents */ -#define ASN1_BOL 1 /* Boolean */ -#define ASN1_INT 2 /* Integer */ -#define ASN1_BTS 3 /* Bit String */ -#define ASN1_OTS 4 /* Octet String */ -#define ASN1_NUL 5 /* Null */ -#define ASN1_OJI 6 /* Object Identifier */ -#define ASN1_OJD 7 /* Object Description */ -#define ASN1_EXT 8 /* External */ -#define ASN1_SEQ 16 /* Sequence */ -#define ASN1_SET 17 /* Set */ -#define ASN1_NUMSTR 18 /* Numerical String */ -#define ASN1_PRNSTR 19 /* Printable String */ -#define ASN1_TEXSTR 20 /* Teletext String */ -#define ASN1_VIDSTR 21 /* Video String */ -#define ASN1_IA5STR 22 /* IA5 String */ -#define ASN1_UNITIM 23 /* Universal Time */ -#define ASN1_GENTIM 24 /* General Time */ -#define ASN1_GRASTR 25 /* Graphical String */ -#define ASN1_VISSTR 26 /* Visible String */ -#define ASN1_GENSTR 27 /* General String */ - -/* Primitive / Constructed methods*/ -#define ASN1_PRI 0 /* Primitive */ -#define ASN1_CON 1 /* Constructed */ - -/* - * Error codes. - */ -#define ASN1_ERR_NOERROR 0 -#define ASN1_ERR_DEC_EMPTY 2 -#define ASN1_ERR_DEC_EOC_MISMATCH 3 -#define ASN1_ERR_DEC_LENGTH_MISMATCH 4 -#define ASN1_ERR_DEC_BADVALUE 5 - -/* - * ASN.1 context. - */ -struct asn1_ctx -{ - int error; /* Error condition */ - unsigned char *pointer; /* Octet just to be decoded */ - unsigned char *begin; /* First octet */ - unsigned char *end; /* Octet after last octet */ -}; - -/* - * Octet string (not null terminated) - */ -struct asn1_octstr -{ - unsigned char *data; - unsigned int len; -}; - -static void asn1_open(struct asn1_ctx *ctx, - unsigned char *buf, - unsigned int len) -{ - ctx->begin = buf; - ctx->end = buf + len; - ctx->pointer = buf; - ctx->error = ASN1_ERR_NOERROR; -} - -static unsigned char asn1_octet_decode(struct asn1_ctx *ctx, unsigned char *ch) -{ - if (ctx->pointer >= ctx->end) { - ctx->error = ASN1_ERR_DEC_EMPTY; - return 0; - } - *ch = *(ctx->pointer)++; - return 1; -} - -static unsigned char asn1_tag_decode(struct asn1_ctx *ctx, unsigned int *tag) -{ - unsigned char ch; - - *tag = 0; - - do - { - if (!asn1_octet_decode(ctx, &ch)) - return 0; - *tag <<= 7; - *tag |= ch & 0x7F; - } while ((ch & 0x80) == 0x80); - return 1; -} - -static unsigned char asn1_id_decode(struct asn1_ctx *ctx, - unsigned int *cls, - unsigned int *con, - unsigned int *tag) -{ - unsigned char ch; - - if (!asn1_octet_decode(ctx, &ch)) - return 0; - - *cls = (ch & 0xC0) >> 6; - *con = (ch & 0x20) >> 5; - *tag = (ch & 0x1F); - - if (*tag == 0x1F) { - if (!asn1_tag_decode(ctx, tag)) - return 0; - } - return 1; -} - -static unsigned char asn1_length_decode(struct asn1_ctx *ctx, - unsigned int *def, - unsigned int *len) -{ - unsigned char ch, cnt; - - if (!asn1_octet_decode(ctx, &ch)) - return 0; - - if (ch == 0x80) - *def = 0; - else { - *def = 1; - - if (ch < 0x80) - *len = ch; - else { - cnt = (unsigned char) (ch & 0x7F); - *len = 0; - - while (cnt > 0) { - if (!asn1_octet_decode(ctx, &ch)) - return 0; - *len <<= 8; - *len |= ch; - cnt--; - } - } - } - return 1; -} - -static unsigned char asn1_header_decode(struct asn1_ctx *ctx, - unsigned char **eoc, - unsigned int *cls, - unsigned int *con, - unsigned int *tag) -{ - unsigned int def, len; - - if (!asn1_id_decode(ctx, cls, con, tag)) - return 0; - - def = len = 0; - if (!asn1_length_decode(ctx, &def, &len)) - return 0; - - if (def) - *eoc = ctx->pointer + len; - else - *eoc = NULL; - return 1; -} - -static unsigned char asn1_eoc_decode(struct asn1_ctx *ctx, unsigned char *eoc) -{ - unsigned char ch; - - if (eoc == 0) { - if (!asn1_octet_decode(ctx, &ch)) - return 0; - - if (ch != 0x00) { - ctx->error = ASN1_ERR_DEC_EOC_MISMATCH; - return 0; - } - - if (!asn1_octet_decode(ctx, &ch)) - return 0; - - if (ch != 0x00) { - ctx->error = ASN1_ERR_DEC_EOC_MISMATCH; - return 0; - } - return 1; - } else { - if (ctx->pointer != eoc) { - ctx->error = ASN1_ERR_DEC_LENGTH_MISMATCH; - return 0; - } - return 1; - } -} - -static unsigned char asn1_null_decode(struct asn1_ctx *ctx, unsigned char *eoc) -{ - ctx->pointer = eoc; - return 1; -} - -static unsigned char asn1_long_decode(struct asn1_ctx *ctx, - unsigned char *eoc, - long *integer) -{ - unsigned char ch; - unsigned int len; - - if (!asn1_octet_decode(ctx, &ch)) - return 0; - - *integer = (signed char) ch; - len = 1; - - while (ctx->pointer < eoc) { - if (++len > sizeof (long)) { - ctx->error = ASN1_ERR_DEC_BADVALUE; - return 0; - } - - if (!asn1_octet_decode(ctx, &ch)) - return 0; - - *integer <<= 8; - *integer |= ch; - } - return 1; -} - -static unsigned char asn1_uint_decode(struct asn1_ctx *ctx, - unsigned char *eoc, - unsigned int *integer) -{ - unsigned char ch; - unsigned int len; - - if (!asn1_octet_decode(ctx, &ch)) - return 0; - - *integer = ch; - if (ch == 0) len = 0; - else len = 1; - - while (ctx->pointer < eoc) { - if (++len > sizeof (unsigned int)) { - ctx->error = ASN1_ERR_DEC_BADVALUE; - return 0; - } - - if (!asn1_octet_decode(ctx, &ch)) - return 0; - - *integer <<= 8; - *integer |= ch; - } - return 1; -} - -static unsigned char asn1_ulong_decode(struct asn1_ctx *ctx, - unsigned char *eoc, - unsigned long *integer) -{ - unsigned char ch; - unsigned int len; - - if (!asn1_octet_decode(ctx, &ch)) - return 0; - - *integer = ch; - if (ch == 0) len = 0; - else len = 1; - - while (ctx->pointer < eoc) { - if (++len > sizeof (unsigned long)) { - ctx->error = ASN1_ERR_DEC_BADVALUE; - return 0; - } - - if (!asn1_octet_decode(ctx, &ch)) - return 0; - - *integer <<= 8; - *integer |= ch; - } - return 1; -} - -static unsigned char asn1_octets_decode(struct asn1_ctx *ctx, - unsigned char *eoc, - unsigned char **octets, - unsigned int *len) -{ - unsigned char *ptr; - - *len = 0; - - *octets = kmalloc(eoc - ctx->pointer, GFP_ATOMIC); - if (*octets == NULL) { - if (net_ratelimit()) - printk("OOM in bsalg (%d)\n", __LINE__); - return 0; - } - - ptr = *octets; - while (ctx->pointer < eoc) { - if (!asn1_octet_decode(ctx, (unsigned char *)ptr++)) { - kfree(*octets); - *octets = NULL; - return 0; - } - (*len)++; - } - return 1; -} - -static unsigned char asn1_subid_decode(struct asn1_ctx *ctx, - unsigned long *subid) -{ - unsigned char ch; - - *subid = 0; - - do { - if (!asn1_octet_decode(ctx, &ch)) - return 0; - - *subid <<= 7; - *subid |= ch & 0x7F; - } while ((ch & 0x80) == 0x80); - return 1; -} - -static unsigned char asn1_oid_decode(struct asn1_ctx *ctx, - unsigned char *eoc, - unsigned long **oid, - unsigned int *len) -{ - unsigned long subid; - unsigned int size; - unsigned long *optr; - - size = eoc - ctx->pointer + 1; - *oid = kmalloc(size * sizeof(unsigned long), GFP_ATOMIC); - if (*oid == NULL) { - if (net_ratelimit()) - printk("OOM in bsalg (%d)\n", __LINE__); - return 0; - } - - optr = *oid; - - if (!asn1_subid_decode(ctx, &subid)) { - kfree(*oid); - *oid = NULL; - return 0; - } - - if (subid < 40) { - optr [0] = 0; - optr [1] = subid; - } else if (subid < 80) { - optr [0] = 1; - optr [1] = subid - 40; - } else { - optr [0] = 2; - optr [1] = subid - 80; - } - - *len = 2; - optr += 2; - - while (ctx->pointer < eoc) { - if (++(*len) > size) { - ctx->error = ASN1_ERR_DEC_BADVALUE; - kfree(*oid); - *oid = NULL; - return 0; - } - - if (!asn1_subid_decode(ctx, optr++)) { - kfree(*oid); - *oid = NULL; - return 0; - } - } - return 1; -} - -/***************************************************************************** - * - * SNMP decoding routines (gxsnmp author Dirk Wisse) - * - *****************************************************************************/ - -/* SNMP Versions */ -#define SNMP_V1 0 -#define SNMP_V2C 1 -#define SNMP_V2 2 -#define SNMP_V3 3 - -/* Default Sizes */ -#define SNMP_SIZE_COMM 256 -#define SNMP_SIZE_OBJECTID 128 -#define SNMP_SIZE_BUFCHR 256 -#define SNMP_SIZE_BUFINT 128 -#define SNMP_SIZE_SMALLOBJECTID 16 - -/* Requests */ -#define SNMP_PDU_GET 0 -#define SNMP_PDU_NEXT 1 -#define SNMP_PDU_RESPONSE 2 -#define SNMP_PDU_SET 3 -#define SNMP_PDU_TRAP1 4 -#define SNMP_PDU_BULK 5 -#define SNMP_PDU_INFORM 6 -#define SNMP_PDU_TRAP2 7 - -/* Errors */ -#define SNMP_NOERROR 0 -#define SNMP_TOOBIG 1 -#define SNMP_NOSUCHNAME 2 -#define SNMP_BADVALUE 3 -#define SNMP_READONLY 4 -#define SNMP_GENERROR 5 -#define SNMP_NOACCESS 6 -#define SNMP_WRONGTYPE 7 -#define SNMP_WRONGLENGTH 8 -#define SNMP_WRONGENCODING 9 -#define SNMP_WRONGVALUE 10 -#define SNMP_NOCREATION 11 -#define SNMP_INCONSISTENTVALUE 12 -#define SNMP_RESOURCEUNAVAILABLE 13 -#define SNMP_COMMITFAILED 14 -#define SNMP_UNDOFAILED 15 -#define SNMP_AUTHORIZATIONERROR 16 -#define SNMP_NOTWRITABLE 17 -#define SNMP_INCONSISTENTNAME 18 - -/* General SNMP V1 Traps */ -#define SNMP_TRAP_COLDSTART 0 -#define SNMP_TRAP_WARMSTART 1 -#define SNMP_TRAP_LINKDOWN 2 -#define SNMP_TRAP_LINKUP 3 -#define SNMP_TRAP_AUTFAILURE 4 -#define SNMP_TRAP_EQPNEIGHBORLOSS 5 -#define SNMP_TRAP_ENTSPECIFIC 6 - -/* SNMPv1 Types */ -#define SNMP_NULL 0 -#define SNMP_INTEGER 1 /* l */ -#define SNMP_OCTETSTR 2 /* c */ -#define SNMP_DISPLAYSTR 2 /* c */ -#define SNMP_OBJECTID 3 /* ul */ -#define SNMP_IPADDR 4 /* uc */ -#define SNMP_COUNTER 5 /* ul */ -#define SNMP_GAUGE 6 /* ul */ -#define SNMP_TIMETICKS 7 /* ul */ -#define SNMP_OPAQUE 8 /* c */ - -/* Additional SNMPv2 Types */ -#define SNMP_UINTEGER 5 /* ul */ -#define SNMP_BITSTR 9 /* uc */ -#define SNMP_NSAP 10 /* uc */ -#define SNMP_COUNTER64 11 /* ul */ -#define SNMP_NOSUCHOBJECT 12 -#define SNMP_NOSUCHINSTANCE 13 -#define SNMP_ENDOFMIBVIEW 14 - -union snmp_syntax -{ - unsigned char uc[0]; /* 8 bit unsigned */ - char c[0]; /* 8 bit signed */ - unsigned long ul[0]; /* 32 bit unsigned */ - long l[0]; /* 32 bit signed */ -}; - -struct snmp_object -{ - unsigned long *id; - unsigned int id_len; - unsigned short type; - unsigned int syntax_len; - union snmp_syntax syntax; -}; - -struct snmp_request -{ - unsigned long id; - unsigned int error_status; - unsigned int error_index; -}; - -struct snmp_v1_trap -{ - unsigned long *id; - unsigned int id_len; - unsigned long ip_address; /* pointer */ - unsigned int general; - unsigned int specific; - unsigned long time; -}; - -/* SNMP types */ -#define SNMP_IPA 0 -#define SNMP_CNT 1 -#define SNMP_GGE 2 -#define SNMP_TIT 3 -#define SNMP_OPQ 4 -#define SNMP_C64 6 - -/* SNMP errors */ -#define SERR_NSO 0 -#define SERR_NSI 1 -#define SERR_EOM 2 - -static inline void mangle_address(unsigned char *begin, - unsigned char *addr, - const struct oct1_map *map, - __sum16 *check); -struct snmp_cnv -{ - unsigned int class; - unsigned int tag; - int syntax; -}; - -static struct snmp_cnv snmp_conv [] = -{ - {ASN1_UNI, ASN1_NUL, SNMP_NULL}, - {ASN1_UNI, ASN1_INT, SNMP_INTEGER}, - {ASN1_UNI, ASN1_OTS, SNMP_OCTETSTR}, - {ASN1_UNI, ASN1_OTS, SNMP_DISPLAYSTR}, - {ASN1_UNI, ASN1_OJI, SNMP_OBJECTID}, - {ASN1_APL, SNMP_IPA, SNMP_IPADDR}, - {ASN1_APL, SNMP_CNT, SNMP_COUNTER}, /* Counter32 */ - {ASN1_APL, SNMP_GGE, SNMP_GAUGE}, /* Gauge32 == Unsigned32 */ - {ASN1_APL, SNMP_TIT, SNMP_TIMETICKS}, - {ASN1_APL, SNMP_OPQ, SNMP_OPAQUE}, - - /* SNMPv2 data types and errors */ - {ASN1_UNI, ASN1_BTS, SNMP_BITSTR}, - {ASN1_APL, SNMP_C64, SNMP_COUNTER64}, - {ASN1_CTX, SERR_NSO, SNMP_NOSUCHOBJECT}, - {ASN1_CTX, SERR_NSI, SNMP_NOSUCHINSTANCE}, - {ASN1_CTX, SERR_EOM, SNMP_ENDOFMIBVIEW}, - {0, 0, -1} -}; - -static unsigned char snmp_tag_cls2syntax(unsigned int tag, - unsigned int cls, - unsigned short *syntax) -{ - struct snmp_cnv *cnv; - - cnv = snmp_conv; - - while (cnv->syntax != -1) { - if (cnv->tag == tag && cnv->class == cls) { - *syntax = cnv->syntax; - return 1; - } - cnv++; - } - return 0; -} - -static unsigned char snmp_object_decode(struct asn1_ctx *ctx, - struct snmp_object **obj) -{ - unsigned int cls, con, tag, len, idlen; - unsigned short type; - unsigned char *eoc, *end, *p; - unsigned long *lp, *id; - unsigned long ul; - long l; - - *obj = NULL; - id = NULL; - - if (!asn1_header_decode(ctx, &eoc, &cls, &con, &tag)) - return 0; - - if (cls != ASN1_UNI || con != ASN1_CON || tag != ASN1_SEQ) - return 0; - - if (!asn1_header_decode(ctx, &end, &cls, &con, &tag)) - return 0; - - if (cls != ASN1_UNI || con != ASN1_PRI || tag != ASN1_OJI) - return 0; - - if (!asn1_oid_decode(ctx, end, &id, &idlen)) - return 0; - - if (!asn1_header_decode(ctx, &end, &cls, &con, &tag)) { - kfree(id); - return 0; - } - - if (con != ASN1_PRI) { - kfree(id); - return 0; - } - - type = 0; - if (!snmp_tag_cls2syntax(tag, cls, &type)) { - kfree(id); - return 0; - } - - l = 0; - switch (type) { - case SNMP_INTEGER: - len = sizeof(long); - if (!asn1_long_decode(ctx, end, &l)) { - kfree(id); - return 0; - } - *obj = kmalloc(sizeof(struct snmp_object) + len, - GFP_ATOMIC); - if (*obj == NULL) { - kfree(id); - if (net_ratelimit()) - printk("OOM in bsalg (%d)\n", __LINE__); - return 0; - } - (*obj)->syntax.l[0] = l; - break; - case SNMP_OCTETSTR: - case SNMP_OPAQUE: - if (!asn1_octets_decode(ctx, end, &p, &len)) { - kfree(id); - return 0; - } - *obj = kmalloc(sizeof(struct snmp_object) + len, - GFP_ATOMIC); - if (*obj == NULL) { - kfree(id); - if (net_ratelimit()) - printk("OOM in bsalg (%d)\n", __LINE__); - return 0; - } - memcpy((*obj)->syntax.c, p, len); - kfree(p); - break; - case SNMP_NULL: - case SNMP_NOSUCHOBJECT: - case SNMP_NOSUCHINSTANCE: - case SNMP_ENDOFMIBVIEW: - len = 0; - *obj = kmalloc(sizeof(struct snmp_object), GFP_ATOMIC); - if (*obj == NULL) { - kfree(id); - if (net_ratelimit()) - printk("OOM in bsalg (%d)\n", __LINE__); - return 0; - } - if (!asn1_null_decode(ctx, end)) { - kfree(id); - kfree(*obj); - *obj = NULL; - return 0; - } - break; - case SNMP_OBJECTID: - if (!asn1_oid_decode(ctx, end, (unsigned long **)&lp, &len)) { - kfree(id); - return 0; - } - len *= sizeof(unsigned long); - *obj = kmalloc(sizeof(struct snmp_object) + len, GFP_ATOMIC); - if (*obj == NULL) { - kfree(lp); - kfree(id); - if (net_ratelimit()) - printk("OOM in bsalg (%d)\n", __LINE__); - return 0; - } - memcpy((*obj)->syntax.ul, lp, len); - kfree(lp); - break; - case SNMP_IPADDR: - if (!asn1_octets_decode(ctx, end, &p, &len)) { - kfree(id); - return 0; - } - if (len != 4) { - kfree(p); - kfree(id); - return 0; - } - *obj = kmalloc(sizeof(struct snmp_object) + len, GFP_ATOMIC); - if (*obj == NULL) { - kfree(p); - kfree(id); - if (net_ratelimit()) - printk("OOM in bsalg (%d)\n", __LINE__); - return 0; - } - memcpy((*obj)->syntax.uc, p, len); - kfree(p); - break; - case SNMP_COUNTER: - case SNMP_GAUGE: - case SNMP_TIMETICKS: - len = sizeof(unsigned long); - if (!asn1_ulong_decode(ctx, end, &ul)) { - kfree(id); - return 0; - } - *obj = kmalloc(sizeof(struct snmp_object) + len, GFP_ATOMIC); - if (*obj == NULL) { - kfree(id); - if (net_ratelimit()) - printk("OOM in bsalg (%d)\n", __LINE__); - return 0; - } - (*obj)->syntax.ul[0] = ul; - break; - default: - kfree(id); - return 0; - } - - (*obj)->syntax_len = len; - (*obj)->type = type; - (*obj)->id = id; - (*obj)->id_len = idlen; - - if (!asn1_eoc_decode(ctx, eoc)) { - kfree(id); - kfree(*obj); - *obj = NULL; - return 0; - } - return 1; -} - -static unsigned char snmp_request_decode(struct asn1_ctx *ctx, - struct snmp_request *request) -{ - unsigned int cls, con, tag; - unsigned char *end; - - if (!asn1_header_decode(ctx, &end, &cls, &con, &tag)) - return 0; - - if (cls != ASN1_UNI || con != ASN1_PRI || tag != ASN1_INT) - return 0; - - if (!asn1_ulong_decode(ctx, end, &request->id)) - return 0; - - if (!asn1_header_decode(ctx, &end, &cls, &con, &tag)) - return 0; - - if (cls != ASN1_UNI || con != ASN1_PRI || tag != ASN1_INT) - return 0; - - if (!asn1_uint_decode(ctx, end, &request->error_status)) - return 0; - - if (!asn1_header_decode(ctx, &end, &cls, &con, &tag)) - return 0; - - if (cls != ASN1_UNI || con != ASN1_PRI || tag != ASN1_INT) - return 0; - - if (!asn1_uint_decode(ctx, end, &request->error_index)) - return 0; - - return 1; -} - -/* - * Fast checksum update for possibly oddly-aligned UDP byte, from the - * code example in the draft. - */ -static void fast_csum(__sum16 *csum, - const unsigned char *optr, - const unsigned char *nptr, - int offset) -{ - unsigned char s[4]; - - if (offset & 1) { - s[0] = s[2] = 0; - s[1] = ~*optr; - s[3] = *nptr; - } else { - s[1] = s[3] = 0; - s[0] = ~*optr; - s[2] = *nptr; - } - - *csum = csum_fold(csum_partial(s, 4, ~csum_unfold(*csum))); -} - -/* - * Mangle IP address. - * - begin points to the start of the snmp messgae - * - addr points to the start of the address - */ -static inline void mangle_address(unsigned char *begin, - unsigned char *addr, - const struct oct1_map *map, - __sum16 *check) -{ - if (map->from == NOCT1(addr)) { - u_int32_t old; - - if (debug) - memcpy(&old, (unsigned char *)addr, sizeof(old)); - - *addr = map->to; - - /* Update UDP checksum if being used */ - if (*check) { - fast_csum(check, - &map->from, &map->to, addr - begin); - } - - if (debug) - printk(KERN_DEBUG "bsalg: mapped %u.%u.%u.%u to " - "%u.%u.%u.%u\n", NIPQUAD(old), NIPQUAD(*addr)); - } -} - -static unsigned char snmp_trap_decode(struct asn1_ctx *ctx, - struct snmp_v1_trap *trap, - const struct oct1_map *map, - __sum16 *check) -{ - unsigned int cls, con, tag, len; - unsigned char *end; - - if (!asn1_header_decode(ctx, &end, &cls, &con, &tag)) - return 0; - - if (cls != ASN1_UNI || con != ASN1_PRI || tag != ASN1_OJI) - return 0; - - if (!asn1_oid_decode(ctx, end, &trap->id, &trap->id_len)) - return 0; - - if (!asn1_header_decode(ctx, &end, &cls, &con, &tag)) - goto err_id_free; - - if (!((cls == ASN1_APL && con == ASN1_PRI && tag == SNMP_IPA) || - (cls == ASN1_UNI && con == ASN1_PRI && tag == ASN1_OTS))) - goto err_id_free; - - if (!asn1_octets_decode(ctx, end, (unsigned char **)&trap->ip_address, &len)) - goto err_id_free; - - /* IPv4 only */ - if (len != 4) - goto err_addr_free; - - mangle_address(ctx->begin, ctx->pointer - 4, map, check); - - if (!asn1_header_decode(ctx, &end, &cls, &con, &tag)) - goto err_addr_free; - - if (cls != ASN1_UNI || con != ASN1_PRI || tag != ASN1_INT) - goto err_addr_free; - - if (!asn1_uint_decode(ctx, end, &trap->general)) - goto err_addr_free; - - if (!asn1_header_decode(ctx, &end, &cls, &con, &tag)) - goto err_addr_free; - - if (cls != ASN1_UNI || con != ASN1_PRI || tag != ASN1_INT) - goto err_addr_free; - - if (!asn1_uint_decode(ctx, end, &trap->specific)) - goto err_addr_free; - - if (!asn1_header_decode(ctx, &end, &cls, &con, &tag)) - goto err_addr_free; - - if (!((cls == ASN1_APL && con == ASN1_PRI && tag == SNMP_TIT) || - (cls == ASN1_UNI && con == ASN1_PRI && tag == ASN1_INT))) - goto err_addr_free; - - if (!asn1_ulong_decode(ctx, end, &trap->time)) - goto err_addr_free; - - return 1; - -err_addr_free: - kfree((unsigned long *)trap->ip_address); - -err_id_free: - kfree(trap->id); - - return 0; -} - -/***************************************************************************** - * - * Misc. routines - * - *****************************************************************************/ - -static void hex_dump(unsigned char *buf, size_t len) -{ - size_t i; - - for (i = 0; i < len; i++) { - if (i && !(i % 16)) - printk("\n"); - printk("%02x ", *(buf + i)); - } - printk("\n"); -} - -/* - * Parse and mangle SNMP message according to mapping. - * (And this is the fucking 'basic' method). - */ -static int snmp_parse_mangle(unsigned char *msg, - u_int16_t len, - const struct oct1_map *map, - __sum16 *check) -{ - unsigned char *eoc, *end; - unsigned int cls, con, tag, vers, pdutype; - struct asn1_ctx ctx; - struct asn1_octstr comm; - struct snmp_object **obj; - - if (debug > 1) - hex_dump(msg, len); - - asn1_open(&ctx, msg, len); - - /* - * Start of SNMP message. - */ - if (!asn1_header_decode(&ctx, &eoc, &cls, &con, &tag)) - return 0; - if (cls != ASN1_UNI || con != ASN1_CON || tag != ASN1_SEQ) - return 0; - - /* - * Version 1 or 2 handled. - */ - if (!asn1_header_decode(&ctx, &end, &cls, &con, &tag)) - return 0; - if (cls != ASN1_UNI || con != ASN1_PRI || tag != ASN1_INT) - return 0; - if (!asn1_uint_decode (&ctx, end, &vers)) - return 0; - if (debug > 1) - printk(KERN_DEBUG "bsalg: snmp version: %u\n", vers + 1); - if (vers > 1) - return 1; - - /* - * Community. - */ - if (!asn1_header_decode (&ctx, &end, &cls, &con, &tag)) - return 0; - if (cls != ASN1_UNI || con != ASN1_PRI || tag != ASN1_OTS) - return 0; - if (!asn1_octets_decode(&ctx, end, &comm.data, &comm.len)) - return 0; - if (debug > 1) { - unsigned int i; - - printk(KERN_DEBUG "bsalg: community: "); - for (i = 0; i < comm.len; i++) - printk("%c", comm.data[i]); - printk("\n"); - } - kfree(comm.data); - - /* - * PDU type - */ - if (!asn1_header_decode(&ctx, &eoc, &cls, &con, &pdutype)) - return 0; - if (cls != ASN1_CTX || con != ASN1_CON) - return 0; - if (debug > 1) { - unsigned char *pdus[] = { - [SNMP_PDU_GET] = "get", - [SNMP_PDU_NEXT] = "get-next", - [SNMP_PDU_RESPONSE] = "response", - [SNMP_PDU_SET] = "set", - [SNMP_PDU_TRAP1] = "trapv1", - [SNMP_PDU_BULK] = "bulk", - [SNMP_PDU_INFORM] = "inform", - [SNMP_PDU_TRAP2] = "trapv2" - }; - - if (pdutype > SNMP_PDU_TRAP2) - printk(KERN_DEBUG "bsalg: bad pdu type %u\n", pdutype); - else - printk(KERN_DEBUG "bsalg: pdu: %s\n", pdus[pdutype]); - } - if (pdutype != SNMP_PDU_RESPONSE && - pdutype != SNMP_PDU_TRAP1 && pdutype != SNMP_PDU_TRAP2) - return 1; - - /* - * Request header or v1 trap - */ - if (pdutype == SNMP_PDU_TRAP1) { - struct snmp_v1_trap trap; - unsigned char ret = snmp_trap_decode(&ctx, &trap, map, check); - - if (ret) { - kfree(trap.id); - kfree((unsigned long *)trap.ip_address); - } else - return ret; - - } else { - struct snmp_request req; - - if (!snmp_request_decode(&ctx, &req)) - return 0; - - if (debug > 1) - printk(KERN_DEBUG "bsalg: request: id=0x%lx error_status=%u " - "error_index=%u\n", req.id, req.error_status, - req.error_index); - } - - /* - * Loop through objects, look for IP addresses to mangle. - */ - if (!asn1_header_decode(&ctx, &eoc, &cls, &con, &tag)) - return 0; - - if (cls != ASN1_UNI || con != ASN1_CON || tag != ASN1_SEQ) - return 0; - - obj = kmalloc(sizeof(struct snmp_object), GFP_ATOMIC); - if (obj == NULL) { - if (net_ratelimit()) - printk(KERN_WARNING "OOM in bsalg(%d)\n", __LINE__); - return 0; - } - - while (!asn1_eoc_decode(&ctx, eoc)) { - unsigned int i; - - if (!snmp_object_decode(&ctx, obj)) { - if (*obj) { - kfree((*obj)->id); - kfree(*obj); - } - kfree(obj); - return 0; - } - - if (debug > 1) { - printk(KERN_DEBUG "bsalg: object: "); - for (i = 0; i < (*obj)->id_len; i++) { - if (i > 0) - printk("."); - printk("%lu", (*obj)->id[i]); - } - printk(": type=%u\n", (*obj)->type); - - } - - if ((*obj)->type == SNMP_IPADDR) - mangle_address(ctx.begin, ctx.pointer - 4 , map, check); - - kfree((*obj)->id); - kfree(*obj); - } - kfree(obj); - - if (!asn1_eoc_decode(&ctx, eoc)) - return 0; - - return 1; -} - -/***************************************************************************** - * - * NAT routines. - * - *****************************************************************************/ - -/* - * SNMP translation routine. - */ -static int snmp_translate(struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo, - struct sk_buff **pskb) -{ - struct iphdr *iph = ip_hdr(*pskb); - struct udphdr *udph = (struct udphdr *)((__be32 *)iph + iph->ihl); - u_int16_t udplen = ntohs(udph->len); - u_int16_t paylen = udplen - sizeof(struct udphdr); - int dir = CTINFO2DIR(ctinfo); - struct oct1_map map; - - /* - * Determine mappping for application layer addresses based - * on NAT manipulations for the packet. - */ - if (dir == IP_CT_DIR_ORIGINAL) { - /* SNAT traps */ - map.from = NOCT1(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip); - map.to = NOCT1(&ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip); - } else { - /* DNAT replies */ - map.from = NOCT1(&ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip); - map.to = NOCT1(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip); - } - - if (map.from == map.to) - return NF_ACCEPT; - - if (!snmp_parse_mangle((unsigned char *)udph + sizeof(struct udphdr), - paylen, &map, &udph->check)) { - if (net_ratelimit()) - printk(KERN_WARNING "bsalg: parser failed\n"); - return NF_DROP; - } - return NF_ACCEPT; -} - -/* We don't actually set up expectations, just adjust internal IP - * addresses if this is being NATted */ -static int help(struct sk_buff **pskb, - struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo) -{ - int dir = CTINFO2DIR(ctinfo); - unsigned int ret; - struct iphdr *iph = ip_hdr(*pskb); - struct udphdr *udph = (struct udphdr *)((u_int32_t *)iph + iph->ihl); - - /* SNMP replies and originating SNMP traps get mangled */ - if (udph->source == htons(SNMP_PORT) && dir != IP_CT_DIR_REPLY) - return NF_ACCEPT; - if (udph->dest == htons(SNMP_TRAP_PORT) && dir != IP_CT_DIR_ORIGINAL) - return NF_ACCEPT; - - /* No NAT? */ - if (!(ct->status & IPS_NAT_MASK)) - return NF_ACCEPT; - - /* - * Make sure the packet length is ok. So far, we were only guaranteed - * to have a valid length IP header plus 8 bytes, which means we have - * enough room for a UDP header. Just verify the UDP length field so we - * can mess around with the payload. - */ - if (ntohs(udph->len) != (*pskb)->len - (iph->ihl << 2)) { - if (net_ratelimit()) - printk(KERN_WARNING "SNMP: dropping malformed packet " - "src=%u.%u.%u.%u dst=%u.%u.%u.%u\n", - NIPQUAD(iph->saddr), NIPQUAD(iph->daddr)); - return NF_DROP; - } - - if (!skb_make_writable(pskb, (*pskb)->len)) - return NF_DROP; - - spin_lock_bh(&snmp_lock); - ret = snmp_translate(ct, ctinfo, pskb); - spin_unlock_bh(&snmp_lock); - return ret; -} - -static struct ip_conntrack_helper snmp_helper = { - .max_expected = 0, - .timeout = 180, - .me = THIS_MODULE, - .help = help, - .name = "snmp", - - .tuple = {.src = {.u = {.udp = {.port = __constant_htons(SNMP_PORT)}}}, - .dst = {.protonum = IPPROTO_UDP}, - }, - .mask = {.src = {.u = {0xFFFF}}, - .dst = {.protonum = 0xFF}, - }, -}; - -static struct ip_conntrack_helper snmp_trap_helper = { - .max_expected = 0, - .timeout = 180, - .me = THIS_MODULE, - .help = help, - .name = "snmp_trap", - - .tuple = {.src = {.u = {.udp = {.port = __constant_htons(SNMP_TRAP_PORT)}}}, - .dst = {.protonum = IPPROTO_UDP}, - }, - .mask = {.src = {.u = {0xFFFF}}, - .dst = {.protonum = 0xFF}, - }, -}; - -/***************************************************************************** - * - * Module stuff. - * - *****************************************************************************/ - -static int __init ip_nat_snmp_basic_init(void) -{ - int ret = 0; - - ret = ip_conntrack_helper_register(&snmp_helper); - if (ret < 0) - return ret; - ret = ip_conntrack_helper_register(&snmp_trap_helper); - if (ret < 0) { - ip_conntrack_helper_unregister(&snmp_helper); - return ret; - } - return ret; -} - -static void __exit ip_nat_snmp_basic_fini(void) -{ - ip_conntrack_helper_unregister(&snmp_helper); - ip_conntrack_helper_unregister(&snmp_trap_helper); -} - -module_init(ip_nat_snmp_basic_init); -module_exit(ip_nat_snmp_basic_fini); - -module_param(debug, int, 0600); diff --git a/net/ipv4/netfilter/ip_nat_standalone.c b/net/ipv4/netfilter/ip_nat_standalone.c deleted file mode 100644 index 32f7bf661fc8..000000000000 --- a/net/ipv4/netfilter/ip_nat_standalone.c +++ /dev/null @@ -1,387 +0,0 @@ -/* This file contains all the functions required for the standalone - ip_nat module. - - These are not required by the compatibility layer. -*/ - -/* (C) 1999-2001 Paul `Rusty' Russell - * (C) 2002-2004 Netfilter Core Team - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -/* - * 23 Apr 2001: Harald Welte - * - new API and handling of conntrack/nat helpers - * - now capable of multiple expectations for one master - * */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#if 0 -#define DEBUGP printk -#else -#define DEBUGP(format, args...) -#endif - -#ifdef CONFIG_XFRM -static void nat_decode_session(struct sk_buff *skb, struct flowi *fl) -{ - struct ip_conntrack *ct; - struct ip_conntrack_tuple *t; - enum ip_conntrack_info ctinfo; - enum ip_conntrack_dir dir; - unsigned long statusbit; - - ct = ip_conntrack_get(skb, &ctinfo); - if (ct == NULL) - return; - dir = CTINFO2DIR(ctinfo); - t = &ct->tuplehash[dir].tuple; - - if (dir == IP_CT_DIR_ORIGINAL) - statusbit = IPS_DST_NAT; - else - statusbit = IPS_SRC_NAT; - - if (ct->status & statusbit) { - fl->fl4_dst = t->dst.ip; - if (t->dst.protonum == IPPROTO_TCP || - t->dst.protonum == IPPROTO_UDP) - fl->fl_ip_dport = t->dst.u.tcp.port; - } - - statusbit ^= IPS_NAT_MASK; - - if (ct->status & statusbit) { - fl->fl4_src = t->src.ip; - if (t->dst.protonum == IPPROTO_TCP || - t->dst.protonum == IPPROTO_UDP) - fl->fl_ip_sport = t->src.u.tcp.port; - } -} -#endif - -static unsigned int -ip_nat_fn(unsigned int hooknum, - struct sk_buff **pskb, - const struct net_device *in, - const struct net_device *out, - int (*okfn)(struct sk_buff *)) -{ - struct ip_conntrack *ct; - enum ip_conntrack_info ctinfo; - struct ip_nat_info *info; - /* maniptype == SRC for postrouting. */ - enum ip_nat_manip_type maniptype = HOOK2MANIP(hooknum); - - /* We never see fragments: conntrack defrags on pre-routing - and local-out, and ip_nat_out protects post-routing. */ - IP_NF_ASSERT(!(ip_hdr(*pskb)->frag_off - & htons(IP_MF|IP_OFFSET))); - - ct = ip_conntrack_get(*pskb, &ctinfo); - /* Can't track? It's not due to stress, or conntrack would - have dropped it. Hence it's the user's responsibilty to - packet filter it out, or implement conntrack/NAT for that - protocol. 8) --RR */ - if (!ct) { - /* Exception: ICMP redirect to new connection (not in - hash table yet). We must not let this through, in - case we're doing NAT to the same network. */ - if (ip_hdr(*pskb)->protocol == IPPROTO_ICMP) { - struct icmphdr _hdr, *hp; - - hp = skb_header_pointer(*pskb, ip_hdrlen(*pskb), - sizeof(_hdr), &_hdr); - if (hp != NULL && - hp->type == ICMP_REDIRECT) - return NF_DROP; - } - return NF_ACCEPT; - } - - /* Don't try to NAT if this packet is not conntracked */ - if (ct == &ip_conntrack_untracked) - return NF_ACCEPT; - - switch (ctinfo) { - case IP_CT_RELATED: - case IP_CT_RELATED+IP_CT_IS_REPLY: - if (ip_hdr(*pskb)->protocol == IPPROTO_ICMP) { - if (!ip_nat_icmp_reply_translation(ct, ctinfo, - hooknum, pskb)) - return NF_DROP; - else - return NF_ACCEPT; - } - /* Fall thru... (Only ICMPs can be IP_CT_IS_REPLY) */ - case IP_CT_NEW: - info = &ct->nat.info; - - /* Seen it before? This can happen for loopback, retrans, - or local packets.. */ - if (!ip_nat_initialized(ct, maniptype)) { - unsigned int ret; - - if (unlikely(is_confirmed(ct))) - /* NAT module was loaded late */ - ret = alloc_null_binding_confirmed(ct, info, - hooknum); - else if (hooknum == NF_IP_LOCAL_IN) - /* LOCAL_IN hook doesn't have a chain! */ - ret = alloc_null_binding(ct, info, hooknum); - else - ret = ip_nat_rule_find(pskb, hooknum, - in, out, ct, - info); - - if (ret != NF_ACCEPT) { - return ret; - } - } else - DEBUGP("Already setup manip %s for ct %p\n", - maniptype == IP_NAT_MANIP_SRC ? "SRC" : "DST", - ct); - break; - - default: - /* ESTABLISHED */ - IP_NF_ASSERT(ctinfo == IP_CT_ESTABLISHED - || ctinfo == (IP_CT_ESTABLISHED+IP_CT_IS_REPLY)); - info = &ct->nat.info; - } - - IP_NF_ASSERT(info); - return ip_nat_packet(ct, ctinfo, hooknum, pskb); -} - -static unsigned int -ip_nat_in(unsigned int hooknum, - struct sk_buff **pskb, - const struct net_device *in, - const struct net_device *out, - int (*okfn)(struct sk_buff *)) -{ - unsigned int ret; - __be32 daddr = ip_hdr(*pskb)->daddr; - - ret = ip_nat_fn(hooknum, pskb, in, out, okfn); - if (ret != NF_DROP && ret != NF_STOLEN - && daddr != ip_hdr(*pskb)->daddr) { - dst_release((*pskb)->dst); - (*pskb)->dst = NULL; - } - return ret; -} - -static unsigned int -ip_nat_out(unsigned int hooknum, - struct sk_buff **pskb, - const struct net_device *in, - const struct net_device *out, - int (*okfn)(struct sk_buff *)) -{ -#ifdef CONFIG_XFRM - struct ip_conntrack *ct; - enum ip_conntrack_info ctinfo; -#endif - unsigned int ret; - - /* root is playing with raw sockets. */ - if ((*pskb)->len < sizeof(struct iphdr) - || ip_hdrlen(*pskb) < sizeof(struct iphdr)) - return NF_ACCEPT; - - ret = ip_nat_fn(hooknum, pskb, in, out, okfn); -#ifdef CONFIG_XFRM - if (ret != NF_DROP && ret != NF_STOLEN - && (ct = ip_conntrack_get(*pskb, &ctinfo)) != NULL) { - enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); - - if (ct->tuplehash[dir].tuple.src.ip != - ct->tuplehash[!dir].tuple.dst.ip - || ct->tuplehash[dir].tuple.src.u.all != - ct->tuplehash[!dir].tuple.dst.u.all - ) - return ip_xfrm_me_harder(pskb) == 0 ? ret : NF_DROP; - } -#endif - return ret; -} - -static unsigned int -ip_nat_local_fn(unsigned int hooknum, - struct sk_buff **pskb, - const struct net_device *in, - const struct net_device *out, - int (*okfn)(struct sk_buff *)) -{ - struct ip_conntrack *ct; - enum ip_conntrack_info ctinfo; - unsigned int ret; - - /* root is playing with raw sockets. */ - if ((*pskb)->len < sizeof(struct iphdr) - || ip_hdrlen(*pskb) < sizeof(struct iphdr)) - return NF_ACCEPT; - - ret = ip_nat_fn(hooknum, pskb, in, out, okfn); - if (ret != NF_DROP && ret != NF_STOLEN - && (ct = ip_conntrack_get(*pskb, &ctinfo)) != NULL) { - enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); - - if (ct->tuplehash[dir].tuple.dst.ip != - ct->tuplehash[!dir].tuple.src.ip) { - if (ip_route_me_harder(pskb, RTN_UNSPEC)) - ret = NF_DROP; - } -#ifdef CONFIG_XFRM - else if (ct->tuplehash[dir].tuple.dst.u.all != - ct->tuplehash[!dir].tuple.src.u.all) - if (ip_xfrm_me_harder(pskb)) - ret = NF_DROP; -#endif - - } - return ret; -} - -static unsigned int -ip_nat_adjust(unsigned int hooknum, - struct sk_buff **pskb, - const struct net_device *in, - const struct net_device *out, - int (*okfn)(struct sk_buff *)) -{ - struct ip_conntrack *ct; - enum ip_conntrack_info ctinfo; - - ct = ip_conntrack_get(*pskb, &ctinfo); - if (ct && test_bit(IPS_SEQ_ADJUST_BIT, &ct->status)) { - DEBUGP("ip_nat_standalone: adjusting sequence number\n"); - if (!ip_nat_seq_adjust(pskb, ct, ctinfo)) - return NF_DROP; - } - return NF_ACCEPT; -} - -/* We must be after connection tracking and before packet filtering. */ - -static struct nf_hook_ops ip_nat_ops[] = { - /* Before packet filtering, change destination */ - { - .hook = ip_nat_in, - .owner = THIS_MODULE, - .pf = PF_INET, - .hooknum = NF_IP_PRE_ROUTING, - .priority = NF_IP_PRI_NAT_DST, - }, - /* After packet filtering, change source */ - { - .hook = ip_nat_out, - .owner = THIS_MODULE, - .pf = PF_INET, - .hooknum = NF_IP_POST_ROUTING, - .priority = NF_IP_PRI_NAT_SRC, - }, - /* After conntrack, adjust sequence number */ - { - .hook = ip_nat_adjust, - .owner = THIS_MODULE, - .pf = PF_INET, - .hooknum = NF_IP_POST_ROUTING, - .priority = NF_IP_PRI_NAT_SEQ_ADJUST, - }, - /* Before packet filtering, change destination */ - { - .hook = ip_nat_local_fn, - .owner = THIS_MODULE, - .pf = PF_INET, - .hooknum = NF_IP_LOCAL_OUT, - .priority = NF_IP_PRI_NAT_DST, - }, - /* After packet filtering, change source */ - { - .hook = ip_nat_fn, - .owner = THIS_MODULE, - .pf = PF_INET, - .hooknum = NF_IP_LOCAL_IN, - .priority = NF_IP_PRI_NAT_SRC, - }, - /* After conntrack, adjust sequence number */ - { - .hook = ip_nat_adjust, - .owner = THIS_MODULE, - .pf = PF_INET, - .hooknum = NF_IP_LOCAL_IN, - .priority = NF_IP_PRI_NAT_SEQ_ADJUST, - }, -}; - -static int __init ip_nat_standalone_init(void) -{ - int ret = 0; - - need_conntrack(); - -#ifdef CONFIG_XFRM - BUG_ON(ip_nat_decode_session != NULL); - ip_nat_decode_session = nat_decode_session; -#endif - ret = ip_nat_rule_init(); - if (ret < 0) { - printk("ip_nat_init: can't setup rules.\n"); - goto cleanup_decode_session; - } - ret = nf_register_hooks(ip_nat_ops, ARRAY_SIZE(ip_nat_ops)); - if (ret < 0) { - printk("ip_nat_init: can't register hooks.\n"); - goto cleanup_rule_init; - } - return ret; - - cleanup_rule_init: - ip_nat_rule_cleanup(); - cleanup_decode_session: -#ifdef CONFIG_XFRM - ip_nat_decode_session = NULL; - synchronize_net(); -#endif - return ret; -} - -static void __exit ip_nat_standalone_fini(void) -{ - nf_unregister_hooks(ip_nat_ops, ARRAY_SIZE(ip_nat_ops)); - ip_nat_rule_cleanup(); -#ifdef CONFIG_XFRM - ip_nat_decode_session = NULL; - synchronize_net(); -#endif -} - -module_init(ip_nat_standalone_init); -module_exit(ip_nat_standalone_fini); - -MODULE_LICENSE("GPL"); diff --git a/net/ipv4/netfilter/ip_nat_tftp.c b/net/ipv4/netfilter/ip_nat_tftp.c deleted file mode 100644 index 604793536fc1..000000000000 --- a/net/ipv4/netfilter/ip_nat_tftp.c +++ /dev/null @@ -1,70 +0,0 @@ -/* (C) 2001-2002 Magnus Boden - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Version: 0.0.7 - * - * Thu 21 Mar 2002 Harald Welte - * - Port to newnat API - * - * This module currently supports DNAT: - * iptables -t nat -A PREROUTING -d x.x.x.x -j DNAT --to-dest x.x.x.y - * - * and SNAT: - * iptables -t nat -A POSTROUTING { -j MASQUERADE , -j SNAT --to-source x.x.x.x } - * - * It has not been tested with - * -j SNAT --to-source x.x.x.x-x.x.x.y since I only have one external ip - * If you do test this please let me know if it works or not. - * - */ - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -MODULE_AUTHOR("Magnus Boden "); -MODULE_DESCRIPTION("tftp NAT helper"); -MODULE_LICENSE("GPL"); - -static unsigned int help(struct sk_buff **pskb, - enum ip_conntrack_info ctinfo, - struct ip_conntrack_expect *exp) -{ - struct ip_conntrack *ct = exp->master; - - exp->saved_proto.udp.port - = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u.udp.port; - exp->dir = IP_CT_DIR_REPLY; - exp->expectfn = ip_nat_follow_master; - if (ip_conntrack_expect_related(exp) != 0) - return NF_DROP; - return NF_ACCEPT; -} - -static void __exit ip_nat_tftp_fini(void) -{ - rcu_assign_pointer(ip_nat_tftp_hook, NULL); - synchronize_rcu(); -} - -static int __init ip_nat_tftp_init(void) -{ - BUG_ON(rcu_dereference(ip_nat_tftp_hook)); - rcu_assign_pointer(ip_nat_tftp_hook, help); - return 0; -} - -module_init(ip_nat_tftp_init); -module_exit(ip_nat_tftp_fini); diff --git a/net/ipv4/netfilter/ipt_CLUSTERIP.c b/net/ipv4/netfilter/ipt_CLUSTERIP.c index d3b16817a991..40e273421398 100644 --- a/net/ipv4/netfilter/ipt_CLUSTERIP.c +++ b/net/ipv4/netfilter/ipt_CLUSTERIP.c @@ -21,15 +21,12 @@ #include #include #include - -#include - #include - #include #include #include -#include +#include +#include #define CLUSTERIP_VERSION "0.8" @@ -310,15 +307,16 @@ target(struct sk_buff **pskb, const void *targinfo) { const struct ipt_clusterip_tgt_info *cipinfo = targinfo; + struct nf_conn *ct; enum ip_conntrack_info ctinfo; - u_int32_t *mark, hash; + u_int32_t hash; /* don't need to clusterip_config_get() here, since refcount * is only decremented by destroy() - and ip_tables guarantees * that the ->target() function isn't called after ->destroy() */ - mark = nf_ct_get_mark((*pskb), &ctinfo); - if (mark == NULL) { + ct = nf_ct_get(*pskb, &ctinfo); + if (ct == NULL) { printk(KERN_ERR "CLUSTERIP: no conntrack!\n"); /* FIXME: need to drop invalid ones, since replies * to outgoing connections of other nodes will be @@ -341,7 +339,7 @@ target(struct sk_buff **pskb, switch (ctinfo) { case IP_CT_NEW: - *mark = hash; + ct->mark = hash; break; case IP_CT_RELATED: case IP_CT_RELATED+IP_CT_IS_REPLY: @@ -358,7 +356,7 @@ target(struct sk_buff **pskb, #ifdef DEBUG_CLUSTERP DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple); #endif - DEBUGP("hash=%u ct_hash=%u ", hash, *mark); + DEBUGP("hash=%u ct_hash=%u ", hash, ct->mark); if (!clusterip_responsible(cipinfo->config, hash)) { DEBUGP("not responsible\n"); return NF_DROP; diff --git a/net/ipv4/netfilter/ipt_MASQUERADE.c b/net/ipv4/netfilter/ipt_MASQUERADE.c index b5955f3a3f8f..d4f2d7775330 100644 --- a/net/ipv4/netfilter/ipt_MASQUERADE.c +++ b/net/ipv4/netfilter/ipt_MASQUERADE.c @@ -19,12 +19,8 @@ #include #include #include -#include -#ifdef CONFIG_NF_NAT_NEEDED #include -#else -#include -#endif +#include #include MODULE_LICENSE("GPL"); @@ -48,7 +44,7 @@ masquerade_check(const char *tablename, void *targinfo, unsigned int hook_mask) { - const struct ip_nat_multi_range_compat *mr = targinfo; + const struct nf_nat_multi_range_compat *mr = targinfo; if (mr->range[0].flags & IP_NAT_RANGE_MAP_IPS) { DEBUGP("masquerade_check: bad MAP_IPS.\n"); @@ -69,33 +65,26 @@ masquerade_target(struct sk_buff **pskb, const struct xt_target *target, const void *targinfo) { -#ifdef CONFIG_NF_NAT_NEEDED + struct nf_conn *ct; struct nf_conn_nat *nat; -#endif - struct ip_conntrack *ct; enum ip_conntrack_info ctinfo; - struct ip_nat_range newrange; - const struct ip_nat_multi_range_compat *mr; + struct nf_nat_range newrange; + const struct nf_nat_multi_range_compat *mr; struct rtable *rt; __be32 newsrc; - IP_NF_ASSERT(hooknum == NF_IP_POST_ROUTING); + NF_CT_ASSERT(hooknum == NF_IP_POST_ROUTING); - ct = ip_conntrack_get(*pskb, &ctinfo); -#ifdef CONFIG_NF_NAT_NEEDED + ct = nf_ct_get(*pskb, &ctinfo); nat = nfct_nat(ct); -#endif - IP_NF_ASSERT(ct && (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED + + NF_CT_ASSERT(ct && (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED || ctinfo == IP_CT_RELATED + IP_CT_IS_REPLY)); /* Source address is 0.0.0.0 - locally generated packet that is * probably not supposed to be masqueraded. */ -#ifdef CONFIG_NF_NAT_NEEDED if (ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3.ip == 0) -#else - if (ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip == 0) -#endif return NF_ACCEPT; mr = targinfo; @@ -107,40 +96,30 @@ masquerade_target(struct sk_buff **pskb, } write_lock_bh(&masq_lock); -#ifdef CONFIG_NF_NAT_NEEDED nat->masq_index = out->ifindex; -#else - ct->nat.masq_index = out->ifindex; -#endif write_unlock_bh(&masq_lock); /* Transfer from original range. */ - newrange = ((struct ip_nat_range) + newrange = ((struct nf_nat_range) { mr->range[0].flags | IP_NAT_RANGE_MAP_IPS, newsrc, newsrc, mr->range[0].min, mr->range[0].max }); /* Hand modified range to generic setup. */ - return ip_nat_setup_info(ct, &newrange, hooknum); + return nf_nat_setup_info(ct, &newrange, hooknum); } static inline int -device_cmp(struct ip_conntrack *i, void *ifindex) +device_cmp(struct nf_conn *i, void *ifindex) { - int ret; -#ifdef CONFIG_NF_NAT_NEEDED struct nf_conn_nat *nat = nfct_nat(i); + int ret; if (!nat) return 0; -#endif read_lock_bh(&masq_lock); -#ifdef CONFIG_NF_NAT_NEEDED ret = (nat->masq_index == (int)(long)ifindex); -#else - ret = (i->nat.masq_index == (int)(long)ifindex); -#endif read_unlock_bh(&masq_lock); return ret; @@ -156,9 +135,9 @@ static int masq_device_event(struct notifier_block *this, /* Device was downed. Search entire table for conntracks which were associated with that device, and forget them. */ - IP_NF_ASSERT(dev->ifindex != 0); + NF_CT_ASSERT(dev->ifindex != 0); - ip_ct_iterate_cleanup(device_cmp, (void *)(long)dev->ifindex); + nf_ct_iterate_cleanup(device_cmp, (void *)(long)dev->ifindex); } return NOTIFY_DONE; @@ -174,9 +153,9 @@ static int masq_inet_event(struct notifier_block *this, /* IP address was deleted. Search entire table for conntracks which were associated with that device, and forget them. */ - IP_NF_ASSERT(dev->ifindex != 0); + NF_CT_ASSERT(dev->ifindex != 0); - ip_ct_iterate_cleanup(device_cmp, (void *)(long)dev->ifindex); + nf_ct_iterate_cleanup(device_cmp, (void *)(long)dev->ifindex); } return NOTIFY_DONE; @@ -194,7 +173,7 @@ static struct xt_target masquerade = { .name = "MASQUERADE", .family = AF_INET, .target = masquerade_target, - .targetsize = sizeof(struct ip_nat_multi_range_compat), + .targetsize = sizeof(struct nf_nat_multi_range_compat), .table = "nat", .hooks = 1 << NF_IP_POST_ROUTING, .checkentry = masquerade_check, diff --git a/net/ipv4/netfilter/ipt_NETMAP.c b/net/ipv4/netfilter/ipt_NETMAP.c index d03f165722da..068c69bce30e 100644 --- a/net/ipv4/netfilter/ipt_NETMAP.c +++ b/net/ipv4/netfilter/ipt_NETMAP.c @@ -16,11 +16,7 @@ #include #include #include -#ifdef CONFIG_NF_NAT_NEEDED #include -#else -#include -#endif #define MODULENAME "NETMAP" MODULE_LICENSE("GPL"); @@ -40,7 +36,7 @@ check(const char *tablename, void *targinfo, unsigned int hook_mask) { - const struct ip_nat_multi_range_compat *mr = targinfo; + const struct nf_nat_multi_range_compat *mr = targinfo; if (!(mr->range[0].flags & IP_NAT_RANGE_MAP_IPS)) { DEBUGP(MODULENAME":check: bad MAP_IPS.\n"); @@ -61,16 +57,16 @@ target(struct sk_buff **pskb, const struct xt_target *target, const void *targinfo) { - struct ip_conntrack *ct; + struct nf_conn *ct; enum ip_conntrack_info ctinfo; __be32 new_ip, netmask; - const struct ip_nat_multi_range_compat *mr = targinfo; - struct ip_nat_range newrange; + const struct nf_nat_multi_range_compat *mr = targinfo; + struct nf_nat_range newrange; - IP_NF_ASSERT(hooknum == NF_IP_PRE_ROUTING + NF_CT_ASSERT(hooknum == NF_IP_PRE_ROUTING || hooknum == NF_IP_POST_ROUTING || hooknum == NF_IP_LOCAL_OUT); - ct = ip_conntrack_get(*pskb, &ctinfo); + ct = nf_ct_get(*pskb, &ctinfo); netmask = ~(mr->range[0].min_ip ^ mr->range[0].max_ip); @@ -80,20 +76,20 @@ target(struct sk_buff **pskb, new_ip = ip_hdr(*pskb)->saddr & ~netmask; new_ip |= mr->range[0].min_ip & netmask; - newrange = ((struct ip_nat_range) + newrange = ((struct nf_nat_range) { mr->range[0].flags | IP_NAT_RANGE_MAP_IPS, new_ip, new_ip, mr->range[0].min, mr->range[0].max }); /* Hand modified range to generic setup. */ - return ip_nat_setup_info(ct, &newrange, hooknum); + return nf_nat_setup_info(ct, &newrange, hooknum); } static struct xt_target target_module = { .name = MODULENAME, .family = AF_INET, .target = target, - .targetsize = sizeof(struct ip_nat_multi_range_compat), + .targetsize = sizeof(struct nf_nat_multi_range_compat), .table = "nat", .hooks = (1 << NF_IP_PRE_ROUTING) | (1 << NF_IP_POST_ROUTING) | (1 << NF_IP_LOCAL_OUT), diff --git a/net/ipv4/netfilter/ipt_REDIRECT.c b/net/ipv4/netfilter/ipt_REDIRECT.c index c2b6b80670f8..68cc76a198eb 100644 --- a/net/ipv4/netfilter/ipt_REDIRECT.c +++ b/net/ipv4/netfilter/ipt_REDIRECT.c @@ -19,11 +19,7 @@ #include #include #include -#ifdef CONFIG_NF_NAT_NEEDED #include -#else -#include -#endif MODULE_LICENSE("GPL"); MODULE_AUTHOR("Netfilter Core Team "); @@ -43,7 +39,7 @@ redirect_check(const char *tablename, void *targinfo, unsigned int hook_mask) { - const struct ip_nat_multi_range_compat *mr = targinfo; + const struct nf_nat_multi_range_compat *mr = targinfo; if (mr->range[0].flags & IP_NAT_RANGE_MAP_IPS) { DEBUGP("redirect_check: bad MAP_IPS.\n"); @@ -64,17 +60,17 @@ redirect_target(struct sk_buff **pskb, const struct xt_target *target, const void *targinfo) { - struct ip_conntrack *ct; + struct nf_conn *ct; enum ip_conntrack_info ctinfo; __be32 newdst; - const struct ip_nat_multi_range_compat *mr = targinfo; - struct ip_nat_range newrange; + const struct nf_nat_multi_range_compat *mr = targinfo; + struct nf_nat_range newrange; - IP_NF_ASSERT(hooknum == NF_IP_PRE_ROUTING + NF_CT_ASSERT(hooknum == NF_IP_PRE_ROUTING || hooknum == NF_IP_LOCAL_OUT); - ct = ip_conntrack_get(*pskb, &ctinfo); - IP_NF_ASSERT(ct && (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED)); + ct = nf_ct_get(*pskb, &ctinfo); + NF_CT_ASSERT(ct && (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED)); /* Local packets: make them go to loopback */ if (hooknum == NF_IP_LOCAL_OUT) @@ -96,20 +92,20 @@ redirect_target(struct sk_buff **pskb, } /* Transfer from original range. */ - newrange = ((struct ip_nat_range) + newrange = ((struct nf_nat_range) { mr->range[0].flags | IP_NAT_RANGE_MAP_IPS, newdst, newdst, mr->range[0].min, mr->range[0].max }); /* Hand modified range to generic setup. */ - return ip_nat_setup_info(ct, &newrange, hooknum); + return nf_nat_setup_info(ct, &newrange, hooknum); } static struct xt_target redirect_reg = { .name = "REDIRECT", .family = AF_INET, .target = redirect_target, - .targetsize = sizeof(struct ip_nat_multi_range_compat), + .targetsize = sizeof(struct nf_nat_multi_range_compat), .table = "nat", .hooks = (1 << NF_IP_PRE_ROUTING) | (1 << NF_IP_LOCAL_OUT), .checkentry = redirect_check, diff --git a/net/ipv4/netfilter/ipt_SAME.c b/net/ipv4/netfilter/ipt_SAME.c index bd4404e5c688..fe76ffc0caed 100644 --- a/net/ipv4/netfilter/ipt_SAME.c +++ b/net/ipv4/netfilter/ipt_SAME.c @@ -35,11 +35,7 @@ #include #include #include -#ifdef CONFIG_NF_NAT_NEEDED #include -#else -#include -#endif #include MODULE_LICENSE("GPL"); @@ -138,17 +134,17 @@ same_target(struct sk_buff **pskb, const struct xt_target *target, const void *targinfo) { - struct ip_conntrack *ct; + struct nf_conn *ct; enum ip_conntrack_info ctinfo; u_int32_t tmpip, aindex; __be32 new_ip; const struct ipt_same_info *same = targinfo; - struct ip_nat_range newrange; - const struct ip_conntrack_tuple *t; + struct nf_nat_range newrange; + const struct nf_conntrack_tuple *t; - IP_NF_ASSERT(hooknum == NF_IP_PRE_ROUTING || + NF_CT_ASSERT(hooknum == NF_IP_PRE_ROUTING || hooknum == NF_IP_POST_ROUTING); - ct = ip_conntrack_get(*pskb, &ctinfo); + ct = nf_ct_get(*pskb, &ctinfo); t = &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple; @@ -157,17 +153,10 @@ same_target(struct sk_buff **pskb, Here we calculate the index in same->iparray which holds the ipaddress we should use */ -#ifdef CONFIG_NF_NAT_NEEDED tmpip = ntohl(t->src.u3.ip); if (!(same->info & IPT_SAME_NODST)) tmpip += ntohl(t->dst.u3.ip); -#else - tmpip = ntohl(t->src.ip); - - if (!(same->info & IPT_SAME_NODST)) - tmpip += ntohl(t->dst.ip); -#endif aindex = tmpip % same->ipnum; new_ip = htonl(same->iparray[aindex]); @@ -178,13 +167,13 @@ same_target(struct sk_buff **pskb, NIPQUAD(new_ip)); /* Transfer from original range. */ - newrange = ((struct ip_nat_range) + newrange = ((struct nf_nat_range) { same->range[0].flags, new_ip, new_ip, /* FIXME: Use ports from correct range! */ same->range[0].min, same->range[0].max }); /* Hand modified range to generic setup. */ - return ip_nat_setup_info(ct, &newrange, hooknum); + return nf_nat_setup_info(ct, &newrange, hooknum); } static struct xt_target same_reg = { diff --git a/net/ipv4/netfilter/nf_nat_h323.c b/net/ipv4/netfilter/nf_nat_h323.c index 3c58fea0d391..fcebc968d37f 100644 --- a/net/ipv4/netfilter/nf_nat_h323.c +++ b/net/ipv4/netfilter/nf_nat_h323.c @@ -33,7 +33,7 @@ static int set_addr(struct sk_buff **pskb, unsigned int addroff, __be32 ip, __be16 port) { enum ip_conntrack_info ctinfo; - struct nf_conn *ct = ip_conntrack_get(*pskb, &ctinfo); + struct nf_conn *ct = nf_ct_get(*pskb, &ctinfo); struct { __be32 ip; __be16 port; @@ -383,7 +383,7 @@ static int nat_h245(struct sk_buff **pskb, struct nf_conn *ct, static void ip_nat_q931_expect(struct nf_conn *new, struct nf_conntrack_expect *this) { - struct ip_nat_range range; + struct nf_nat_range range; if (this->tuple.src.u3.ip != 0) { /* Only accept calls from GK */ nf_nat_follow_master(new, this); diff --git a/net/ipv4/netfilter/nf_nat_pptp.c b/net/ipv4/netfilter/nf_nat_pptp.c index 7ba341c22eaa..a66888749ceb 100644 --- a/net/ipv4/netfilter/nf_nat_pptp.c +++ b/net/ipv4/netfilter/nf_nat_pptp.c @@ -53,7 +53,7 @@ static void pptp_nat_expected(struct nf_conn *ct, struct nf_conntrack_tuple t; struct nf_ct_pptp_master *ct_pptp_info; struct nf_nat_pptp *nat_pptp_info; - struct ip_nat_range range; + struct nf_nat_range range; ct_pptp_info = &nfct_help(master)->help.ct_pptp_info; nat_pptp_info = &nfct_nat(master)->help.nat_pptp_info; diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig index 54698af6d0af..c558f3214255 100644 --- a/net/netfilter/Kconfig +++ b/net/netfilter/Kconfig @@ -25,6 +25,7 @@ config NETFILTER_NETLINK_LOG and is also scheduled to replace the old syslog-based ipt_LOG and ip6t_LOG modules. +# Rename this to NF_CONNTRACK in a 2.6.25 config NF_CONNTRACK_ENABLED tristate "Netfilter connection tracking support" help @@ -39,42 +40,9 @@ config NF_CONNTRACK_ENABLED To compile it as a module, choose M here. If unsure, say N. -choice - prompt "Netfilter connection tracking support" - depends on NF_CONNTRACK_ENABLED - -config NF_CONNTRACK_SUPPORT - bool "Layer 3 Independent Connection tracking" - help - Layer 3 independent connection tracking is experimental scheme - which generalize ip_conntrack to support other layer 3 protocols. - - This is required to do Masquerading or other kinds of Network - Address Translation (except for Fast NAT). It can also be used to - enhance packet filtering (see `Connection state match support' - below). - -config IP_NF_CONNTRACK_SUPPORT - bool "Layer 3 Dependent Connection tracking (OBSOLETE)" - help - The old, Layer 3 dependent ip_conntrack subsystem of netfilter. - - This is required to do Masquerading or other kinds of Network - Address Translation (except for Fast NAT). It can also be used to - enhance packet filtering (see `Connection state match support' - below). - -endchoice - config NF_CONNTRACK tristate - default m if NF_CONNTRACK_SUPPORT && NF_CONNTRACK_ENABLED=m - default y if NF_CONNTRACK_SUPPORT && NF_CONNTRACK_ENABLED=y - -config IP_NF_CONNTRACK - tristate - default m if IP_NF_CONNTRACK_SUPPORT && NF_CONNTRACK_ENABLED=m - default y if IP_NF_CONNTRACK_SUPPORT && NF_CONNTRACK_ENABLED=y + default NF_CONNTRACK_ENABLED config NF_CT_ACCT bool "Connection tracking flow accounting" @@ -303,9 +271,8 @@ config NETFILTER_XT_TARGET_CONNMARK tristate '"CONNMARK" target support' depends on NETFILTER_XTABLES depends on IP_NF_MANGLE || IP6_NF_MANGLE - depends on IP_NF_CONNTRACK || NF_CONNTRACK - select IP_NF_CONNTRACK_MARK if IP_NF_CONNTRACK - select NF_CONNTRACK_MARK if NF_CONNTRACK + depends on NF_CONNTRACK + select NF_CONNTRACK_MARK help This option adds a `CONNMARK' target, which allows one to manipulate the connection mark value. Similar to the MARK target, but @@ -366,7 +333,7 @@ config NETFILTER_XT_TARGET_NOTRACK tristate '"NOTRACK" target support' depends on NETFILTER_XTABLES depends on IP_NF_RAW || IP6_NF_RAW - depends on IP_NF_CONNTRACK || NF_CONNTRACK + depends on NF_CONNTRACK help The NOTRACK target allows a select rule to specify which packets *not* to enter the conntrack/NAT @@ -387,9 +354,7 @@ config NETFILTER_XT_TARGET_SECMARK config NETFILTER_XT_TARGET_CONNSECMARK tristate '"CONNSECMARK" target support' - depends on NETFILTER_XTABLES && \ - ((NF_CONNTRACK && NF_CONNTRACK_SECMARK) || \ - (IP_NF_CONNTRACK && IP_NF_CONNTRACK_SECMARK)) + depends on NETFILTER_XTABLES && NF_CONNTRACK && NF_CONNTRACK_SECMARK help The CONNSECMARK target copies security markings from packets to connections, and restores security markings from connections @@ -437,9 +402,8 @@ config NETFILTER_XT_MATCH_COMMENT config NETFILTER_XT_MATCH_CONNBYTES tristate '"connbytes" per-connection counter match support' depends on NETFILTER_XTABLES - depends on IP_NF_CONNTRACK || NF_CONNTRACK - select IP_NF_CT_ACCT if IP_NF_CONNTRACK - select NF_CT_ACCT if NF_CONNTRACK + depends on NF_CONNTRACK + select NF_CT_ACCT help This option adds a `connbytes' match, which allows you to match the number of bytes and/or packets for each direction within a connection. @@ -450,9 +414,8 @@ config NETFILTER_XT_MATCH_CONNBYTES config NETFILTER_XT_MATCH_CONNMARK tristate '"connmark" connection mark match support' depends on NETFILTER_XTABLES - depends on IP_NF_CONNTRACK || NF_CONNTRACK - select IP_NF_CONNTRACK_MARK if IP_NF_CONNTRACK - select NF_CONNTRACK_MARK if NF_CONNTRACK + depends on NF_CONNTRACK + select NF_CONNTRACK_MARK help This option adds a `connmark' match, which allows you to match the connection mark value previously set for the session by `CONNMARK'. @@ -464,7 +427,7 @@ config NETFILTER_XT_MATCH_CONNMARK config NETFILTER_XT_MATCH_CONNTRACK tristate '"conntrack" connection tracking match support' depends on NETFILTER_XTABLES - depends on IP_NF_CONNTRACK || NF_CONNTRACK + depends on NF_CONNTRACK help This is a general conntrack match module, a superset of the state match. @@ -508,7 +471,7 @@ config NETFILTER_XT_MATCH_ESP config NETFILTER_XT_MATCH_HELPER tristate '"helper" match support' depends on NETFILTER_XTABLES - depends on IP_NF_CONNTRACK || NF_CONNTRACK + depends on NF_CONNTRACK help Helper matching allows you to match packets in dynamic connections tracked by a conntrack-helper, ie. ip_conntrack_ftp @@ -632,7 +595,7 @@ config NETFILTER_XT_MATCH_SCTP config NETFILTER_XT_MATCH_STATE tristate '"state" match support' depends on NETFILTER_XTABLES - depends on IP_NF_CONNTRACK || NF_CONNTRACK + depends on NF_CONNTRACK help Connection state matching allows you to match packets based on their relationship to a tracked connection (ie. previous packets). This diff --git a/net/netfilter/xt_CONNMARK.c b/net/netfilter/xt_CONNMARK.c index 795c058b16a5..b03ce009d0bf 100644 --- a/net/netfilter/xt_CONNMARK.c +++ b/net/netfilter/xt_CONNMARK.c @@ -30,10 +30,7 @@ MODULE_ALIAS("ipt_CONNMARK"); #include #include -#include -#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE) #include -#endif static unsigned int target(struct sk_buff **pskb, @@ -44,40 +41,33 @@ target(struct sk_buff **pskb, const void *targinfo) { const struct xt_connmark_target_info *markinfo = targinfo; + struct nf_conn *ct; + enum ip_conntrack_info ctinfo; u_int32_t diff; u_int32_t mark; u_int32_t newmark; - u_int32_t ctinfo; - u_int32_t *ctmark = nf_ct_get_mark(*pskb, &ctinfo); - if (ctmark) { + ct = nf_ct_get(*pskb, &ctinfo); + if (ct) { switch(markinfo->mode) { case XT_CONNMARK_SET: - newmark = (*ctmark & ~markinfo->mask) | markinfo->mark; - if (newmark != *ctmark) { - *ctmark = newmark; -#if defined(CONFIG_IP_NF_CONNTRACK) || defined(CONFIG_IP_NF_CONNTRACK_MODULE) - ip_conntrack_event_cache(IPCT_MARK, *pskb); -#else + newmark = (ct->mark & ~markinfo->mask) | markinfo->mark; + if (newmark != ct->mark) { + ct->mark = newmark; nf_conntrack_event_cache(IPCT_MARK, *pskb); -#endif } break; case XT_CONNMARK_SAVE: - newmark = (*ctmark & ~markinfo->mask) | + newmark = (ct->mark & ~markinfo->mask) | ((*pskb)->mark & markinfo->mask); - if (*ctmark != newmark) { - *ctmark = newmark; -#if defined(CONFIG_IP_NF_CONNTRACK) || defined(CONFIG_IP_NF_CONNTRACK_MODULE) - ip_conntrack_event_cache(IPCT_MARK, *pskb); -#else + if (ct->mark != newmark) { + ct->mark = newmark; nf_conntrack_event_cache(IPCT_MARK, *pskb); -#endif } break; case XT_CONNMARK_RESTORE: mark = (*pskb)->mark; - diff = (*ctmark ^ mark) & markinfo->mask; + diff = (ct->mark ^ mark) & markinfo->mask; (*pskb)->mark = mark ^ diff; break; } diff --git a/net/netfilter/xt_CONNSECMARK.c b/net/netfilter/xt_CONNSECMARK.c index 1ab0db641f96..81c0c58bab47 100644 --- a/net/netfilter/xt_CONNSECMARK.c +++ b/net/netfilter/xt_CONNSECMARK.c @@ -19,7 +19,7 @@ #include #include #include -#include +#include #define PFX "CONNSECMARK: " @@ -36,12 +36,12 @@ MODULE_ALIAS("ip6t_CONNSECMARK"); static void secmark_save(struct sk_buff *skb) { if (skb->secmark) { - u32 *connsecmark; + struct nf_conn *ct; enum ip_conntrack_info ctinfo; - connsecmark = nf_ct_get_secmark(skb, &ctinfo); - if (connsecmark && !*connsecmark) - *connsecmark = skb->secmark; + ct = nf_ct_get(skb, &ctinfo); + if (ct && !ct->secmark) + ct->secmark = skb->secmark; } } @@ -52,12 +52,12 @@ static void secmark_save(struct sk_buff *skb) static void secmark_restore(struct sk_buff *skb) { if (!skb->secmark) { - u32 *connsecmark; + struct nf_conn *ct; enum ip_conntrack_info ctinfo; - connsecmark = nf_ct_get_secmark(skb, &ctinfo); - if (connsecmark && *connsecmark) - skb->secmark = *connsecmark; + ct = nf_ct_get(skb, &ctinfo); + if (ct && ct->secmark) + skb->secmark = ct->secmark; } } diff --git a/net/netfilter/xt_NOTRACK.c b/net/netfilter/xt_NOTRACK.c index b874a2008b2b..5085fb3d1e2d 100644 --- a/net/netfilter/xt_NOTRACK.c +++ b/net/netfilter/xt_NOTRACK.c @@ -5,7 +5,7 @@ #include #include -#include +#include MODULE_LICENSE("GPL"); MODULE_ALIAS("ipt_NOTRACK"); @@ -26,7 +26,7 @@ target(struct sk_buff **pskb, If there is a real ct entry correspondig to this packet, it'll hang aroun till timing out. We don't deal with it for performance reasons. JK */ - nf_ct_untrack(*pskb); + (*pskb)->nfct = &nf_conntrack_untracked.ct_general; (*pskb)->nfctinfo = IP_CT_NEW; nf_conntrack_get((*pskb)->nfct); diff --git a/net/netfilter/xt_connbytes.c b/net/netfilter/xt_connbytes.c index 302043bc41b2..fec9316a1e10 100644 --- a/net/netfilter/xt_connbytes.c +++ b/net/netfilter/xt_connbytes.c @@ -12,9 +12,9 @@ */ #include #include -#include #include #include +#include #include #include @@ -35,13 +35,17 @@ match(const struct sk_buff *skb, int *hotdrop) { const struct xt_connbytes_info *sinfo = matchinfo; + struct nf_conn *ct; + enum ip_conntrack_info ctinfo; u_int64_t what = 0; /* initialize to make gcc happy */ u_int64_t bytes = 0; u_int64_t pkts = 0; const struct ip_conntrack_counter *counters; - if (!(counters = nf_ct_get_counters(skb))) - return 0; /* no match */ + ct = nf_ct_get(skb, &ctinfo); + if (!ct) + return 0; + counters = ct->counters; switch (sinfo->what) { case XT_CONNBYTES_PKTS: diff --git a/net/netfilter/xt_connmark.c b/net/netfilter/xt_connmark.c index 36c2defff238..e1803256c792 100644 --- a/net/netfilter/xt_connmark.c +++ b/net/netfilter/xt_connmark.c @@ -21,16 +21,15 @@ #include #include +#include +#include +#include MODULE_AUTHOR("Henrik Nordstrom "); MODULE_DESCRIPTION("IP tables connmark match module"); MODULE_LICENSE("GPL"); MODULE_ALIAS("ipt_connmark"); -#include -#include -#include - static int match(const struct sk_buff *skb, const struct net_device *in, @@ -42,12 +41,14 @@ match(const struct sk_buff *skb, int *hotdrop) { const struct xt_connmark_info *info = matchinfo; - u_int32_t ctinfo; - const u_int32_t *ctmark = nf_ct_get_mark(skb, &ctinfo); - if (!ctmark) + struct nf_conn *ct; + enum ip_conntrack_info ctinfo; + + ct = nf_ct_get(skb, &ctinfo); + if (!ct) return 0; - return (((*ctmark) & info->mask) == info->mark) ^ info->invert; + return (((ct->mark) & info->mask) == info->mark) ^ info->invert; } static int diff --git a/net/netfilter/xt_conntrack.c b/net/netfilter/xt_conntrack.c index 2885c378288e..f4ea8fe07a53 100644 --- a/net/netfilter/xt_conntrack.c +++ b/net/netfilter/xt_conntrack.c @@ -10,121 +10,15 @@ #include #include - -#if defined(CONFIG_IP_NF_CONNTRACK) || defined(CONFIG_IP_NF_CONNTRACK_MODULE) -#include -#include -#else -#include -#endif - #include #include -#include +#include MODULE_LICENSE("GPL"); MODULE_AUTHOR("Marc Boucher "); MODULE_DESCRIPTION("iptables connection tracking match module"); MODULE_ALIAS("ipt_conntrack"); -#if defined(CONFIG_IP_NF_CONNTRACK) || defined(CONFIG_IP_NF_CONNTRACK_MODULE) - -static int -match(const struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - const struct xt_match *match, - const void *matchinfo, - int offset, - unsigned int protoff, - int *hotdrop) -{ - const struct xt_conntrack_info *sinfo = matchinfo; - struct ip_conntrack *ct; - enum ip_conntrack_info ctinfo; - unsigned int statebit; - - ct = ip_conntrack_get((struct sk_buff *)skb, &ctinfo); - -#define FWINV(bool, invflg) ((bool) ^ !!(sinfo->invflags & invflg)) - - if (ct == &ip_conntrack_untracked) - statebit = XT_CONNTRACK_STATE_UNTRACKED; - else if (ct) - statebit = XT_CONNTRACK_STATE_BIT(ctinfo); - else - statebit = XT_CONNTRACK_STATE_INVALID; - - if (sinfo->flags & XT_CONNTRACK_STATE) { - if (ct) { - if (test_bit(IPS_SRC_NAT_BIT, &ct->status)) - statebit |= XT_CONNTRACK_STATE_SNAT; - if (test_bit(IPS_DST_NAT_BIT, &ct->status)) - statebit |= XT_CONNTRACK_STATE_DNAT; - } - if (FWINV((statebit & sinfo->statemask) == 0, - XT_CONNTRACK_STATE)) - return 0; - } - - if (ct == NULL) { - if (sinfo->flags & ~XT_CONNTRACK_STATE) - return 0; - return 1; - } - - if (sinfo->flags & XT_CONNTRACK_PROTO && - FWINV(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum != - sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.protonum, - XT_CONNTRACK_PROTO)) - return 0; - - if (sinfo->flags & XT_CONNTRACK_ORIGSRC && - FWINV((ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip & - sinfo->sipmsk[IP_CT_DIR_ORIGINAL].s_addr) != - sinfo->tuple[IP_CT_DIR_ORIGINAL].src.ip, - XT_CONNTRACK_ORIGSRC)) - return 0; - - if (sinfo->flags & XT_CONNTRACK_ORIGDST && - FWINV((ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip & - sinfo->dipmsk[IP_CT_DIR_ORIGINAL].s_addr) != - sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.ip, - XT_CONNTRACK_ORIGDST)) - return 0; - - if (sinfo->flags & XT_CONNTRACK_REPLSRC && - FWINV((ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip & - sinfo->sipmsk[IP_CT_DIR_REPLY].s_addr) != - sinfo->tuple[IP_CT_DIR_REPLY].src.ip, - XT_CONNTRACK_REPLSRC)) - return 0; - - if (sinfo->flags & XT_CONNTRACK_REPLDST && - FWINV((ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip & - sinfo->dipmsk[IP_CT_DIR_REPLY].s_addr) != - sinfo->tuple[IP_CT_DIR_REPLY].dst.ip, - XT_CONNTRACK_REPLDST)) - return 0; - - if (sinfo->flags & XT_CONNTRACK_STATUS && - FWINV((ct->status & sinfo->statusmask) == 0, - XT_CONNTRACK_STATUS)) - return 0; - - if (sinfo->flags & XT_CONNTRACK_EXPIRES) { - unsigned long expires = timer_pending(&ct->timeout) ? - (ct->timeout.expires - jiffies)/HZ : 0; - - if (FWINV(!(expires >= sinfo->expires_min && - expires <= sinfo->expires_max), - XT_CONNTRACK_EXPIRES)) - return 0; - } - return 1; -} - -#else /* CONFIG_IP_NF_CONNTRACK */ static int match(const struct sk_buff *skb, const struct net_device *in, @@ -220,8 +114,6 @@ match(const struct sk_buff *skb, return 1; } -#endif /* CONFIG_NF_IP_CONNTRACK */ - static int checkentry(const char *tablename, const void *ip, diff --git a/net/netfilter/xt_helper.c b/net/netfilter/xt_helper.c index 407d1d5da8a1..bc70b26ba5b4 100644 --- a/net/netfilter/xt_helper.c +++ b/net/netfilter/xt_helper.c @@ -13,18 +13,11 @@ #include #include #include -#if defined(CONFIG_IP_NF_CONNTRACK) || defined(CONFIG_IP_NF_CONNTRACK_MODULE) -#include -#include -#include -#else #include #include #include -#endif #include #include -#include MODULE_LICENSE("GPL"); MODULE_AUTHOR("Martin Josefsson "); @@ -38,55 +31,6 @@ MODULE_ALIAS("ip6t_helper"); #define DEBUGP(format, args...) #endif -#if defined(CONFIG_IP_NF_CONNTRACK) || defined(CONFIG_IP_NF_CONNTRACK_MODULE) -static int -match(const struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - const struct xt_match *match, - const void *matchinfo, - int offset, - unsigned int protoff, - int *hotdrop) -{ - const struct xt_helper_info *info = matchinfo; - struct ip_conntrack *ct; - enum ip_conntrack_info ctinfo; - int ret = info->invert; - - ct = ip_conntrack_get((struct sk_buff *)skb, &ctinfo); - if (!ct) { - DEBUGP("xt_helper: Eek! invalid conntrack?\n"); - return ret; - } - - if (!ct->master) { - DEBUGP("xt_helper: conntrack %p has no master\n", ct); - return ret; - } - - read_lock_bh(&ip_conntrack_lock); - if (!ct->master->helper) { - DEBUGP("xt_helper: master ct %p has no helper\n", - exp->expectant); - goto out_unlock; - } - - DEBUGP("master's name = %s , info->name = %s\n", - ct->master->helper->name, info->name); - - if (info->name[0] == '\0') - ret ^= 1; - else - ret ^= !strncmp(ct->master->helper->name, info->name, - strlen(ct->master->helper->name)); -out_unlock: - read_unlock_bh(&ip_conntrack_lock); - return ret; -} - -#else /* CONFIG_IP_NF_CONNTRACK */ - static int match(const struct sk_buff *skb, const struct net_device *in, @@ -134,7 +78,6 @@ out_unlock: read_unlock_bh(&nf_conntrack_lock); return ret; } -#endif static int check(const char *tablename, const void *inf, diff --git a/net/netfilter/xt_state.c b/net/netfilter/xt_state.c index df37b912163a..149294f7df71 100644 --- a/net/netfilter/xt_state.c +++ b/net/netfilter/xt_state.c @@ -10,7 +10,7 @@ #include #include -#include +#include #include #include @@ -36,7 +36,7 @@ match(const struct sk_buff *skb, if (nf_ct_is_untracked(skb)) statebit = XT_STATE_UNTRACKED; - else if (!nf_ct_get_ctinfo(skb, &ctinfo)) + else if (!nf_ct_get(skb, &ctinfo)) statebit = XT_STATE_INVALID; else statebit = XT_STATE_BIT(ctinfo); -- cgit v1.2.3 From f2645101350c6db66f0a1e72648909cc411f2b38 Mon Sep 17 00:00:00 2001 From: Gerrit Renker Date: Tue, 20 Mar 2007 15:01:14 -0300 Subject: [CCID3]: Add documentation for socket options This updates the documentation on CCID3-specific options. Signed-off-by: Gerrit Renker Acked-by: Ian McDonald Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: David S. Miller --- Documentation/networking/dccp.txt | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'Documentation') diff --git a/Documentation/networking/dccp.txt b/Documentation/networking/dccp.txt index 387482e46c47..4504cc59e405 100644 --- a/Documentation/networking/dccp.txt +++ b/Documentation/networking/dccp.txt @@ -57,6 +57,16 @@ DCCP_SOCKOPT_SEND_CSCOV is for the receiver and has a different meaning: it coverage value are also acceptable. The higher the number, the more restrictive this setting (see [RFC 4340, sec. 9.2.1]). +The following two options apply to CCID 3 exclusively and are getsockopt()-only. +In either case, a TFRC info struct (defined in ) is returned. +DCCP_SOCKOPT_CCID_RX_INFO + Returns a `struct tfrc_rx_info' in optval; the buffer for optval and + optlen must be set to at least sizeof(struct tfrc_rx_info). +DCCP_SOCKOPT_CCID_TX_INFO + Returns a `struct tfrc_tx_info' in optval; the buffer for optval and + optlen must be set to at least sizeof(struct tfrc_tx_info). + + Sysctl variables ================ Several DCCP default parameters can be managed by the following sysctls -- cgit v1.2.3 From 516299d2f5b6f9703b9b388faf91898dc636a678 Mon Sep 17 00:00:00 2001 From: Michael Milner Date: Thu, 12 Apr 2007 22:14:23 -0700 Subject: [NETFILTER]: bridge-nf: filter bridged IPv4/IPv6 encapsulated in pppoe traffic The attached patch by Michael Milner adds support for using iptables and ip6tables on bridged traffic encapsulated in ppoe frames, similar to what's already supported for vlan. Signed-off-by: Michael Milner Signed-off-by: Bart De Schuymer Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller --- Documentation/networking/ip-sysctl.txt | 7 +++- include/linux/if_pppox.h | 3 ++ include/linux/netfilter_bridge.h | 11 ++++- include/linux/sysctl.h | 1 + net/bridge/br_netfilter.c | 77 ++++++++++++++++++++++++++++++++-- 5 files changed, 92 insertions(+), 7 deletions(-) (limited to 'Documentation') diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt index 054c515bd726..af6a63ab9026 100644 --- a/Documentation/networking/ip-sysctl.txt +++ b/Documentation/networking/ip-sysctl.txt @@ -1015,7 +1015,12 @@ bridge-nf-call-ip6tables - BOOLEAN Default: 1 bridge-nf-filter-vlan-tagged - BOOLEAN - 1 : pass bridged vlan-tagged ARP/IP traffic to arptables/iptables. + 1 : pass bridged vlan-tagged ARP/IP/IPv6 traffic to {arp,ip,ip6}tables. + 0 : disable this. + Default: 1 + +bridge-nf-filter-pppoe-tagged - BOOLEAN + 1 : pass bridged pppoe-tagged IP/IPv6 traffic to {ip,ip6}tables. 0 : disable this. Default: 1 diff --git a/include/linux/if_pppox.h b/include/linux/if_pppox.h index 29d6579ff1a0..6f987be60fe2 100644 --- a/include/linux/if_pppox.h +++ b/include/linux/if_pppox.h @@ -111,6 +111,9 @@ struct pppoe_hdr { struct pppoe_tag tag[0]; } __attribute__ ((packed)); +/* Length of entire PPPoE + PPP header */ +#define PPPOE_SES_HLEN 8 + #ifdef __KERNEL__ #include diff --git a/include/linux/netfilter_bridge.h b/include/linux/netfilter_bridge.h index 55689f39f77a..19060030bac9 100644 --- a/include/linux/netfilter_bridge.h +++ b/include/linux/netfilter_bridge.h @@ -7,6 +7,7 @@ #include #include #include +#include /* Bridge Hooks */ /* After promisc drops, checksum checks. */ @@ -58,8 +59,14 @@ static inline int nf_bridge_maybe_copy_header(struct sk_buff *skb) * enough room for the encapsulating header (if there is one). */ static inline int nf_bridge_pad(const struct sk_buff *skb) { - return (skb->nf_bridge && skb->protocol == htons(ETH_P_8021Q)) - ? VLAN_HLEN : 0; + int padding = 0; + + if (skb->nf_bridge && skb->protocol == htons(ETH_P_8021Q)) + padding = VLAN_HLEN; + else if (skb->nf_bridge && skb->protocol == htons(ETH_P_PPP_SES)) + padding = PPPOE_SES_HLEN; + + return padding; } struct bridge_skb_cb { diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h index df2d9ed20a4e..47f1c53332ce 100644 --- a/include/linux/sysctl.h +++ b/include/linux/sysctl.h @@ -792,6 +792,7 @@ enum { NET_BRIDGE_NF_CALL_IPTABLES = 2, NET_BRIDGE_NF_CALL_IP6TABLES = 3, NET_BRIDGE_NF_FILTER_VLAN_TAGGED = 4, + NET_BRIDGE_NF_FILTER_PPPOE_TAGGED = 5, }; /* CTL_FS names: */ diff --git a/net/bridge/br_netfilter.c b/net/bridge/br_netfilter.c index fd70d041e51f..9b2986b182ba 100644 --- a/net/bridge/br_netfilter.c +++ b/net/bridge/br_netfilter.c @@ -29,6 +29,8 @@ #include #include #include +#include +#include #include #include #include @@ -57,8 +59,10 @@ static int brnf_call_iptables __read_mostly = 1; static int brnf_call_ip6tables __read_mostly = 1; static int brnf_call_arptables __read_mostly = 1; static int brnf_filter_vlan_tagged __read_mostly = 1; +static int brnf_filter_pppoe_tagged __read_mostly = 1; #else #define brnf_filter_vlan_tagged 1 +#define brnf_filter_pppoe_tagged 1 #endif static inline __be16 vlan_proto(const struct sk_buff *skb) @@ -81,6 +85,22 @@ static inline __be16 vlan_proto(const struct sk_buff *skb) vlan_proto(skb) == htons(ETH_P_ARP) && \ brnf_filter_vlan_tagged) +static inline __be16 pppoe_proto(const struct sk_buff *skb) +{ + return *((__be16 *)(skb_mac_header(skb) + ETH_HLEN + + sizeof(struct pppoe_hdr))); +} + +#define IS_PPPOE_IP(skb) \ + (skb->protocol == htons(ETH_P_PPP_SES) && \ + pppoe_proto(skb) == htons(PPP_IP) && \ + brnf_filter_pppoe_tagged) + +#define IS_PPPOE_IPV6(skb) \ + (skb->protocol == htons(ETH_P_PPP_SES) && \ + pppoe_proto(skb) == htons(PPP_IPV6) && \ + brnf_filter_pppoe_tagged) + /* We need these fake structures to make netfilter happy -- * lots of places assume that skb->dst != NULL, which isn't * all that unreasonable. @@ -128,6 +148,8 @@ static inline void nf_bridge_save_header(struct sk_buff *skb) if (skb->protocol == htons(ETH_P_8021Q)) header_size += VLAN_HLEN; + else if (skb->protocol == htons(ETH_P_PPP_SES)) + header_size += PPPOE_SES_HLEN; skb_copy_from_linear_data_offset(skb, -header_size, skb->nf_bridge->data, header_size); @@ -144,6 +166,8 @@ int nf_bridge_copy_header(struct sk_buff *skb) if (skb->protocol == htons(ETH_P_8021Q)) header_size += VLAN_HLEN; + else if (skb->protocol == htons(ETH_P_PPP_SES)) + header_size += PPPOE_SES_HLEN; err = skb_cow(skb, header_size); if (err) @@ -154,6 +178,8 @@ int nf_bridge_copy_header(struct sk_buff *skb) if (skb->protocol == htons(ETH_P_8021Q)) __skb_push(skb, VLAN_HLEN); + else if (skb->protocol == htons(ETH_P_PPP_SES)) + __skb_push(skb, PPPOE_SES_HLEN); return 0; } @@ -177,6 +203,9 @@ static int br_nf_pre_routing_finish_ipv6(struct sk_buff *skb) if (skb->protocol == htons(ETH_P_8021Q)) { skb_push(skb, VLAN_HLEN); skb->network_header -= VLAN_HLEN; + } else if (skb->protocol == htons(ETH_P_PPP_SES)) { + skb_push(skb, PPPOE_SES_HLEN); + skb->network_header -= PPPOE_SES_HLEN; } NF_HOOK_THRESH(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL, br_handle_frame_finish, 1); @@ -258,6 +287,9 @@ static int br_nf_pre_routing_finish_bridge(struct sk_buff *skb) if (skb->protocol == htons(ETH_P_8021Q)) { skb_pull(skb, VLAN_HLEN); skb->network_header += VLAN_HLEN; + } else if (skb->protocol == htons(ETH_P_PPP_SES)) { + skb_pull(skb, PPPOE_SES_HLEN); + skb->network_header += PPPOE_SES_HLEN; } skb->dst->output(skb); } @@ -328,6 +360,10 @@ bridged_dnat: htons(ETH_P_8021Q)) { skb_push(skb, VLAN_HLEN); skb->network_header -= VLAN_HLEN; + } else if(skb->protocol == + htons(ETH_P_PPP_SES)) { + skb_push(skb, PPPOE_SES_HLEN); + skb->network_header -= PPPOE_SES_HLEN; } NF_HOOK_THRESH(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL, @@ -347,6 +383,9 @@ bridged_dnat: if (skb->protocol == htons(ETH_P_8021Q)) { skb_push(skb, VLAN_HLEN); skb->network_header -= VLAN_HLEN; + } else if (skb->protocol == htons(ETH_P_PPP_SES)) { + skb_push(skb, PPPOE_SES_HLEN); + skb->network_header -= PPPOE_SES_HLEN; } NF_HOOK_THRESH(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL, br_handle_frame_finish, 1); @@ -489,7 +528,8 @@ static unsigned int br_nf_pre_routing(unsigned int hook, struct sk_buff **pskb, __u32 len; struct sk_buff *skb = *pskb; - if (skb->protocol == htons(ETH_P_IPV6) || IS_VLAN_IPV6(skb)) { + if (skb->protocol == htons(ETH_P_IPV6) || IS_VLAN_IPV6(skb) || + IS_PPPOE_IPV6(skb)) { #ifdef CONFIG_SYSCTL if (!brnf_call_ip6tables) return NF_ACCEPT; @@ -500,6 +540,9 @@ static unsigned int br_nf_pre_routing(unsigned int hook, struct sk_buff **pskb, if (skb->protocol == htons(ETH_P_8021Q)) { skb_pull_rcsum(skb, VLAN_HLEN); skb->network_header += VLAN_HLEN; + } else if (skb->protocol == htons(ETH_P_PPP_SES)) { + skb_pull_rcsum(skb, PPPOE_SES_HLEN); + skb->network_header += PPPOE_SES_HLEN; } return br_nf_pre_routing_ipv6(hook, skb, in, out, okfn); } @@ -508,7 +551,8 @@ static unsigned int br_nf_pre_routing(unsigned int hook, struct sk_buff **pskb, return NF_ACCEPT; #endif - if (skb->protocol != htons(ETH_P_IP) && !IS_VLAN_IP(skb)) + if (skb->protocol != htons(ETH_P_IP) && !IS_VLAN_IP(skb) && + !IS_PPPOE_IP(skb)) return NF_ACCEPT; if ((skb = skb_share_check(*pskb, GFP_ATOMIC)) == NULL) @@ -517,6 +561,9 @@ static unsigned int br_nf_pre_routing(unsigned int hook, struct sk_buff **pskb, if (skb->protocol == htons(ETH_P_8021Q)) { skb_pull_rcsum(skb, VLAN_HLEN); skb->network_header += VLAN_HLEN; + } else if (skb->protocol == htons(ETH_P_PPP_SES)) { + skb_pull_rcsum(skb, PPPOE_SES_HLEN); + skb->network_header += PPPOE_SES_HLEN; } if (!pskb_may_pull(skb, sizeof(struct iphdr))) @@ -598,6 +645,9 @@ static int br_nf_forward_finish(struct sk_buff *skb) if (skb->protocol == htons(ETH_P_8021Q)) { skb_push(skb, VLAN_HLEN); skb->network_header -= VLAN_HLEN; + } else if (skb->protocol == htons(ETH_P_PPP_SES)) { + skb_push(skb, PPPOE_SES_HLEN); + skb->network_header -= PPPOE_SES_HLEN; } NF_HOOK_THRESH(PF_BRIDGE, NF_BR_FORWARD, skb, in, skb->dev, br_forward_finish, 1); @@ -626,7 +676,8 @@ static unsigned int br_nf_forward_ip(unsigned int hook, struct sk_buff **pskb, if (!parent) return NF_DROP; - if (skb->protocol == htons(ETH_P_IP) || IS_VLAN_IP(skb)) + if (skb->protocol == htons(ETH_P_IP) || IS_VLAN_IP(skb) || + IS_PPPOE_IP(skb)) pf = PF_INET; else pf = PF_INET6; @@ -634,6 +685,9 @@ static unsigned int br_nf_forward_ip(unsigned int hook, struct sk_buff **pskb, if (skb->protocol == htons(ETH_P_8021Q)) { skb_pull(*pskb, VLAN_HLEN); (*pskb)->network_header += VLAN_HLEN; + } else if (skb->protocol == htons(ETH_P_PPP_SES)) { + skb_pull(*pskb, PPPOE_SES_HLEN); + (*pskb)->network_header += PPPOE_SES_HLEN; } nf_bridge = skb->nf_bridge; @@ -726,6 +780,9 @@ static unsigned int br_nf_local_out(unsigned int hook, struct sk_buff **pskb, if (skb->protocol == htons(ETH_P_8021Q)) { skb_push(skb, VLAN_HLEN); skb->network_header -= VLAN_HLEN; + } else if (skb->protocol == htons(ETH_P_PPP_SES)) { + skb_push(skb, PPPOE_SES_HLEN); + skb->network_header -= PPPOE_SES_HLEN; } NF_HOOK(PF_BRIDGE, NF_BR_FORWARD, skb, realindev, skb->dev, @@ -771,7 +828,8 @@ static unsigned int br_nf_post_routing(unsigned int hook, struct sk_buff **pskb, if (!realoutdev) return NF_DROP; - if (skb->protocol == htons(ETH_P_IP) || IS_VLAN_IP(skb)) + if (skb->protocol == htons(ETH_P_IP) || IS_VLAN_IP(skb) || + IS_PPPOE_IP(skb)) pf = PF_INET; else pf = PF_INET6; @@ -793,6 +851,9 @@ static unsigned int br_nf_post_routing(unsigned int hook, struct sk_buff **pskb, if (skb->protocol == htons(ETH_P_8021Q)) { skb_pull(skb, VLAN_HLEN); skb->network_header += VLAN_HLEN; + } else if (skb->protocol == htons(ETH_P_PPP_SES)) { + skb_pull(skb, PPPOE_SES_HLEN); + skb->network_header += PPPOE_SES_HLEN; } nf_bridge_save_header(skb); @@ -930,6 +991,14 @@ static ctl_table brnf_table[] = { .mode = 0644, .proc_handler = &brnf_sysctl_call_tables, }, + { + .ctl_name = NET_BRIDGE_NF_FILTER_PPPOE_TAGGED, + .procname = "bridge-nf-filter-pppoe-tagged", + .data = &brnf_filter_pppoe_tagged, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = &brnf_sysctl_call_tables, + }, { .ctl_name = 0 } }; -- cgit v1.2.3 From 9e101eab153073d8a1fc7ea22b20af65de8ab44b Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 23 Apr 2007 12:20:55 -0700 Subject: [WIRELESS]: Remove wext over netlink. As scheduled, this patch removes the pointless wext over netlink code. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville Signed-off-by: David S. Miller --- Documentation/feature-removal-schedule.txt | 12 - include/net/iw_handler.h | 10 - net/core/rtnetlink.c | 31 -- net/core/wireless.c | 735 ----------------------------- net/wireless/Kconfig | 10 - 5 files changed, 798 deletions(-) (limited to 'Documentation') diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt index 9817b60e70a3..976c8a1bd7cd 100644 --- a/Documentation/feature-removal-schedule.txt +++ b/Documentation/feature-removal-schedule.txt @@ -285,18 +285,6 @@ Who: Richard Purdie --------------------------- -What: Wireless extensions over netlink (CONFIG_NET_WIRELESS_RTNETLINK) -When: with the merge of wireless-dev, 2.6.22 or later -Why: The option/code is - * not enabled on most kernels - * not required by any userspace tools (except an experimental one, - and even there only for some parts, others use ioctl) - * pointless since wext is no longer evolving and the ioctl - interface needs to be kept -Who: Johannes Berg - ---------------------------- - What: i8xx_tco watchdog driver When: in 2.6.22 Why: the i8xx_tco watchdog driver has been replaced by the iTCO_wdt diff --git a/include/net/iw_handler.h b/include/net/iw_handler.h index 8a830188354d..909ca87d13b7 100644 --- a/include/net/iw_handler.h +++ b/include/net/iw_handler.h @@ -440,16 +440,6 @@ extern int dev_get_wireless_info(char * buffer, char **start, off_t offset, /* Handle IOCTLs, called in net/core/dev.c */ extern int wireless_process_ioctl(struct ifreq *ifr, unsigned int cmd); -/* Handle RtNetlink requests, called in net/core/rtnetlink.c */ -extern int wireless_rtnetlink_set(struct net_device * dev, - char * data, - int len); -extern int wireless_rtnetlink_get(struct net_device * dev, - char * data, - int len, - char ** p_buf, - int * p_len); - /* Second : functions that may be called by driver modules */ /* Send a single event to user space */ diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index 75cea8ea4cf3..4fe0f4b3a345 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -51,10 +51,6 @@ #include #include #include -#ifdef CONFIG_NET_WIRELESS_RTNETLINK -#include -#include -#endif /* CONFIG_NET_WIRELESS_RTNETLINK */ struct rtnl_link { @@ -684,17 +680,6 @@ static int rtnl_setlink(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) modified = 1; } -#ifdef CONFIG_NET_WIRELESS_RTNETLINK - if (tb[IFLA_WIRELESS]) { - /* Call Wireless Extensions. - * Various stuff checked in there... */ - err = wireless_rtnetlink_set(dev, nla_data(tb[IFLA_WIRELESS]), - nla_len(tb[IFLA_WIRELESS])); - if (err < 0) - goto errout_dev; - } -#endif /* CONFIG_NET_WIRELESS_RTNETLINK */ - if (tb[IFLA_BROADCAST]) { nla_memcpy(dev->broadcast, tb[IFLA_BROADCAST], dev->addr_len); send_addr_notify = 1; @@ -758,22 +743,6 @@ static int rtnl_getlink(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg) } else return -EINVAL; - -#ifdef CONFIG_NET_WIRELESS_RTNETLINK - if (tb[IFLA_WIRELESS]) { - /* Call Wireless Extensions. We need to know the size before - * we can alloc. Various stuff checked in there... */ - err = wireless_rtnetlink_get(dev, nla_data(tb[IFLA_WIRELESS]), - nla_len(tb[IFLA_WIRELESS]), - &iw_buf, &iw_buf_len); - if (err < 0) - goto errout; - - /* Payload is at an offset in buffer */ - iw = iw_buf + IW_EV_POINT_OFF; - } -#endif /* CONFIG_NET_WIRELESS_RTNETLINK */ - nskb = nlmsg_new(if_nlmsg_size(iw_buf_len), GFP_KERNEL); if (nskb == NULL) { err = -ENOBUFS; diff --git a/net/core/wireless.c b/net/core/wireless.c index 86db63d7f760..fba295e05e7a 100644 --- a/net/core/wireless.c +++ b/net/core/wireless.c @@ -104,12 +104,10 @@ /* Debugging stuff */ #undef WE_IOCTL_DEBUG /* Debug IOCTL API */ -#undef WE_RTNETLINK_DEBUG /* Debug RtNetlink API */ #undef WE_EVENT_DEBUG /* Debug Event dispatcher */ #undef WE_SPY_DEBUG /* Debug enhanced spy support */ /* Options */ -//CONFIG_NET_WIRELESS_RTNETLINK /* Wireless requests over RtNetlink */ #define WE_EVENT_RTNETLINK /* Propagate events using RtNetlink */ #define WE_SET_EVENT /* Generate an event on some set commands */ @@ -1145,739 +1143,6 @@ int wireless_process_ioctl(struct ifreq *ifr, unsigned int cmd) return -EINVAL; } -/********************** RTNETLINK REQUEST API **********************/ -/* - * The alternate user space API to configure all those Wireless Extensions - * is through RtNetlink. - * This API support only the new driver API (iw_handler). - * - * This RtNetlink API use the same query/reply model as the ioctl API. - * Maximum effort has been done to fit in the RtNetlink model, and - * we support both RtNetlink Set and RtNelink Get operations. - * On the other hand, we don't offer Dump operations because of the - * following reasons : - * o Large number of parameters, most optional - * o Large size of some parameters (> 100 bytes) - * o Each parameters need to be extracted from hardware - * o Scan requests can take seconds and disable network activity. - * Because of this high cost/overhead, we want to return only the - * parameters the user application is really interested in. - * We could offer partial Dump using the IW_DESCR_FLAG_DUMP flag. - * - * The API uses the standard RtNetlink socket. When the RtNetlink code - * find a IFLA_WIRELESS field in a RtNetlink SET_LINK request, - * it calls here. - */ - -#ifdef CONFIG_NET_WIRELESS_RTNETLINK -/* ---------------------------------------------------------------- */ -/* - * Wrapper to call a standard Wireless Extension GET handler. - * We do various checks and call the handler with the proper args. - */ -static int rtnetlink_standard_get(struct net_device * dev, - struct iw_event * request, - int request_len, - iw_handler handler, - char ** p_buf, - int * p_len) -{ - const struct iw_ioctl_description * descr = NULL; - unsigned int cmd; - union iwreq_data * wrqu; - int hdr_len; - struct iw_request_info info; - char * buffer = NULL; - int buffer_size = 0; - int ret = -EINVAL; - - /* Get the description of the Request */ - cmd = request->cmd; - if ((cmd - SIOCIWFIRST) >= standard_ioctl_num) - return -EOPNOTSUPP; - descr = &(standard_ioctl[cmd - SIOCIWFIRST]); - -#ifdef WE_RTNETLINK_DEBUG - printk(KERN_DEBUG "%s (WE.r) : Found standard handler for 0x%04X\n", - dev->name, cmd); - printk(KERN_DEBUG "%s (WE.r) : Header type : %d, Token type : %d, size : %d, token : %d\n", dev->name, descr->header_type, descr->token_type, descr->token_size, descr->max_tokens); -#endif /* WE_RTNETLINK_DEBUG */ - - /* Check if wrqu is complete */ - hdr_len = event_type_size[descr->header_type]; - if (request_len < hdr_len) { -#ifdef WE_RTNETLINK_DEBUG - printk(KERN_DEBUG - "%s (WE.r) : Wireless request too short (%d)\n", - dev->name, request_len); -#endif /* WE_RTNETLINK_DEBUG */ - return -EINVAL; - } - - /* Prepare the call */ - info.cmd = cmd; - info.flags = 0; - - /* Check if we have extra data in the reply or not */ - if (descr->header_type != IW_HEADER_TYPE_POINT) { - - /* Create the kernel buffer that we will return. - * It's at an offset to match the TYPE_POINT case... */ - buffer_size = request_len + IW_EV_POINT_OFF; - buffer = kmalloc(buffer_size, GFP_KERNEL); - if (buffer == NULL) { - return -ENOMEM; - } - /* Copy event data */ - memcpy(buffer + IW_EV_POINT_OFF, request, request_len); - /* Use our own copy of wrqu */ - wrqu = (union iwreq_data *) (buffer + IW_EV_POINT_OFF - + IW_EV_LCP_PK_LEN); - - /* No extra arguments. Trivial to handle */ - ret = handler(dev, &info, wrqu, NULL); - - } else { - union iwreq_data wrqu_point; - char * extra = NULL; - int extra_size = 0; - - /* Get a temp copy of wrqu (skip pointer) */ - memcpy(((char *) &wrqu_point) + IW_EV_POINT_OFF, - ((char *) request) + IW_EV_LCP_PK_LEN, - IW_EV_POINT_LEN - IW_EV_LCP_PK_LEN); - - /* Calculate space needed by arguments. Always allocate - * for max space. Easier, and won't last long... */ - extra_size = descr->max_tokens * descr->token_size; - /* Support for very large requests */ - if ((descr->flags & IW_DESCR_FLAG_NOMAX) && - (wrqu_point.data.length > descr->max_tokens)) - extra_size = (wrqu_point.data.length - * descr->token_size); - buffer_size = extra_size + IW_EV_POINT_PK_LEN + IW_EV_POINT_OFF; -#ifdef WE_RTNETLINK_DEBUG - printk(KERN_DEBUG "%s (WE.r) : Malloc %d bytes (%d bytes)\n", - dev->name, extra_size, buffer_size); -#endif /* WE_RTNETLINK_DEBUG */ - - /* Create the kernel buffer that we will return */ - buffer = kmalloc(buffer_size, GFP_KERNEL); - if (buffer == NULL) { - return -ENOMEM; - } - - /* Put wrqu in the right place (just before extra). - * Leave space for IWE header and dummy pointer... - * Note that IW_EV_LCP_PK_LEN==4 bytes, so it's still aligned. - */ - memcpy(buffer + IW_EV_LCP_PK_LEN + IW_EV_POINT_OFF, - ((char *) &wrqu_point) + IW_EV_POINT_OFF, - IW_EV_POINT_PK_LEN - IW_EV_LCP_PK_LEN); - wrqu = (union iwreq_data *) (buffer + IW_EV_LCP_PK_LEN); - - /* Extra comes logically after that. Offset +12 bytes. */ - extra = buffer + IW_EV_POINT_OFF + IW_EV_POINT_PK_LEN; - - /* Call the handler */ - ret = handler(dev, &info, wrqu, extra); - - /* Calculate real returned length */ - extra_size = (wrqu->data.length * descr->token_size); - /* Re-adjust reply size */ - request->len = extra_size + IW_EV_POINT_PK_LEN; - - /* Put the iwe header where it should, i.e. scrap the - * dummy pointer. */ - memcpy(buffer + IW_EV_POINT_OFF, request, IW_EV_LCP_PK_LEN); - -#ifdef WE_RTNETLINK_DEBUG - printk(KERN_DEBUG "%s (WE.r) : Reply 0x%04X, hdr_len %d, tokens %d, extra_size %d, buffer_size %d\n", dev->name, cmd, hdr_len, wrqu->data.length, extra_size, buffer_size); -#endif /* WE_RTNETLINK_DEBUG */ - - /* Check if there is enough buffer up there */ - if (wrqu_point.data.length < wrqu->data.length) - ret = -E2BIG; - } - - /* Return the buffer to the caller */ - if (!ret) { - *p_buf = buffer; - *p_len = request->len; - } else { - /* Cleanup */ - if (buffer) - kfree(buffer); - } - - return ret; -} - -/* ---------------------------------------------------------------- */ -/* - * Wrapper to call a standard Wireless Extension SET handler. - * We do various checks and call the handler with the proper args. - */ -static inline int rtnetlink_standard_set(struct net_device * dev, - struct iw_event * request, - int request_len, - iw_handler handler) -{ - const struct iw_ioctl_description * descr = NULL; - unsigned int cmd; - union iwreq_data * wrqu; - union iwreq_data wrqu_point; - int hdr_len; - char * extra = NULL; - int extra_size = 0; - struct iw_request_info info; - int ret = -EINVAL; - - /* Get the description of the Request */ - cmd = request->cmd; - if ((cmd - SIOCIWFIRST) >= standard_ioctl_num) - return -EOPNOTSUPP; - descr = &(standard_ioctl[cmd - SIOCIWFIRST]); - -#ifdef WE_RTNETLINK_DEBUG - printk(KERN_DEBUG "%s (WE.r) : Found standard SET handler for 0x%04X\n", - dev->name, cmd); - printk(KERN_DEBUG "%s (WE.r) : Header type : %d, Token type : %d, size : %d, token : %d\n", dev->name, descr->header_type, descr->token_type, descr->token_size, descr->max_tokens); -#endif /* WE_RTNETLINK_DEBUG */ - - /* Extract fixed header from request. This is properly aligned. */ - wrqu = (union iwreq_data *) (((char *) request) + IW_EV_LCP_PK_LEN); - - /* Check if wrqu is complete */ - hdr_len = event_type_pk_size[descr->header_type]; - if (request_len < hdr_len) { -#ifdef WE_RTNETLINK_DEBUG - printk(KERN_DEBUG - "%s (WE.r) : Wireless request too short (%d)\n", - dev->name, request_len); -#endif /* WE_RTNETLINK_DEBUG */ - return -EINVAL; - } - - /* Prepare the call */ - info.cmd = cmd; - info.flags = 0; - - /* Check if we have extra data in the request or not */ - if (descr->header_type != IW_HEADER_TYPE_POINT) { - - /* No extra arguments. Trivial to handle */ - ret = handler(dev, &info, wrqu, NULL); - - } else { - int extra_len; - - /* Put wrqu in the right place (skip pointer) */ - memcpy(((char *) &wrqu_point) + IW_EV_POINT_OFF, - wrqu, IW_EV_POINT_PK_LEN - IW_EV_LCP_PK_LEN); - /* Don't forget about the event code... */ - wrqu = &wrqu_point; - - /* Check if number of token fits within bounds */ - if (wrqu_point.data.length > descr->max_tokens) - return -E2BIG; - if (wrqu_point.data.length < descr->min_tokens) - return -EINVAL; - - /* Real length of payload */ - extra_len = wrqu_point.data.length * descr->token_size; - - /* Check if request is self consistent */ - if ((request_len - hdr_len) < extra_len) { -#ifdef WE_RTNETLINK_DEBUG - printk(KERN_DEBUG "%s (WE.r) : Wireless request data too short (%d)\n", - dev->name, extra_size); -#endif /* WE_RTNETLINK_DEBUG */ - return -EINVAL; - } - -#ifdef WE_RTNETLINK_DEBUG - printk(KERN_DEBUG "%s (WE.r) : Malloc %d bytes\n", - dev->name, extra_size); -#endif /* WE_RTNETLINK_DEBUG */ - - /* Always allocate for max space. Easier, and won't last - * long... */ - extra_size = descr->max_tokens * descr->token_size; - extra = kmalloc(extra_size, GFP_KERNEL); - if (extra == NULL) - return -ENOMEM; - - /* Copy extra in aligned buffer */ - memcpy(extra, ((char *) request) + hdr_len, extra_len); - - /* Call the handler */ - ret = handler(dev, &info, &wrqu_point, extra); - } - -#ifdef WE_SET_EVENT - /* Generate an event to notify listeners of the change */ - if ((descr->flags & IW_DESCR_FLAG_EVENT) && - ((ret == 0) || (ret == -EIWCOMMIT))) { - if (descr->flags & IW_DESCR_FLAG_RESTRICT) - /* If the event is restricted, don't - * export the payload */ - wireless_send_event(dev, cmd, wrqu, NULL); - else - wireless_send_event(dev, cmd, wrqu, extra); - } -#endif /* WE_SET_EVENT */ - - /* Cleanup - I told you it wasn't that long ;-) */ - if (extra) - kfree(extra); - - /* Call commit handler if needed and defined */ - if (ret == -EIWCOMMIT) - ret = call_commit_handler(dev); - - return ret; -} - -/* ---------------------------------------------------------------- */ -/* - * Wrapper to call a private Wireless Extension GET handler. - * Same as above... - * It's not as nice and slimline as the standard wrapper. The cause - * is struct iw_priv_args, which was not really designed for the - * job we are going here. - * - * IMPORTANT : This function prevent to set and get data on the same - * IOCTL and enforce the SET/GET convention. Not doing it would be - * far too hairy... - * If you need to set and get data at the same time, please don't use - * a iw_handler but process it in your ioctl handler (i.e. use the - * old driver API). - */ -static inline int rtnetlink_private_get(struct net_device * dev, - struct iw_event * request, - int request_len, - iw_handler handler, - char ** p_buf, - int * p_len) -{ - const struct iw_priv_args * descr = NULL; - unsigned int cmd; - union iwreq_data * wrqu; - int hdr_len; - struct iw_request_info info; - int extra_size = 0; - int i; - char * buffer = NULL; - int buffer_size = 0; - int ret = -EINVAL; - - /* Get the description of the Request */ - cmd = request->cmd; - for (i = 0; i < dev->wireless_handlers->num_private_args; i++) - if (cmd == dev->wireless_handlers->private_args[i].cmd) { - descr = &(dev->wireless_handlers->private_args[i]); - break; - } - if (descr == NULL) - return -EOPNOTSUPP; - -#ifdef WE_RTNETLINK_DEBUG - printk(KERN_DEBUG "%s (WE.r) : Found private handler for 0x%04X\n", - dev->name, cmd); - printk(KERN_DEBUG "%s (WE.r) : Name %s, set %X, get %X\n", - dev->name, descr->name, descr->set_args, descr->get_args); -#endif /* WE_RTNETLINK_DEBUG */ - - /* Compute the max size of the get arguments */ - extra_size = get_priv_size(descr->get_args); - - /* Does it fits in wrqu ? */ - if ((descr->get_args & IW_PRIV_SIZE_FIXED) && - (extra_size <= IFNAMSIZ)) { - hdr_len = extra_size; - extra_size = 0; - } else { - hdr_len = IW_EV_POINT_PK_LEN; - } - - /* Check if wrqu is complete */ - if (request_len < hdr_len) { -#ifdef WE_RTNETLINK_DEBUG - printk(KERN_DEBUG - "%s (WE.r) : Wireless request too short (%d)\n", - dev->name, request_len); -#endif /* WE_RTNETLINK_DEBUG */ - return -EINVAL; - } - - /* Prepare the call */ - info.cmd = cmd; - info.flags = 0; - - /* Check if we have a pointer to user space data or not. */ - if (extra_size == 0) { - - /* Create the kernel buffer that we will return. - * It's at an offset to match the TYPE_POINT case... */ - buffer_size = request_len + IW_EV_POINT_OFF; - buffer = kmalloc(buffer_size, GFP_KERNEL); - if (buffer == NULL) { - return -ENOMEM; - } - /* Copy event data */ - memcpy(buffer + IW_EV_POINT_OFF, request, request_len); - /* Use our own copy of wrqu */ - wrqu = (union iwreq_data *) (buffer + IW_EV_POINT_OFF - + IW_EV_LCP_PK_LEN); - - /* No extra arguments. Trivial to handle */ - ret = handler(dev, &info, wrqu, (char *) wrqu); - - } else { - char * extra; - - /* Buffer for full reply */ - buffer_size = extra_size + IW_EV_POINT_PK_LEN + IW_EV_POINT_OFF; - -#ifdef WE_RTNETLINK_DEBUG - printk(KERN_DEBUG "%s (WE.r) : Malloc %d bytes (%d bytes)\n", - dev->name, extra_size, buffer_size); -#endif /* WE_RTNETLINK_DEBUG */ - - /* Create the kernel buffer that we will return */ - buffer = kmalloc(buffer_size, GFP_KERNEL); - if (buffer == NULL) { - return -ENOMEM; - } - - /* Put wrqu in the right place (just before extra). - * Leave space for IWE header and dummy pointer... - * Note that IW_EV_LCP_PK_LEN==4 bytes, so it's still aligned. - */ - memcpy(buffer + IW_EV_LCP_PK_LEN + IW_EV_POINT_OFF, - ((char *) request) + IW_EV_LCP_PK_LEN, - IW_EV_POINT_PK_LEN - IW_EV_LCP_PK_LEN); - wrqu = (union iwreq_data *) (buffer + IW_EV_LCP_PK_LEN); - - /* Extra comes logically after that. Offset +12 bytes. */ - extra = buffer + IW_EV_POINT_OFF + IW_EV_POINT_PK_LEN; - - /* Call the handler */ - ret = handler(dev, &info, wrqu, extra); - - /* Adjust for the actual length if it's variable, - * avoid leaking kernel bits outside. */ - if (!(descr->get_args & IW_PRIV_SIZE_FIXED)) - extra_size = adjust_priv_size(descr->get_args, wrqu); - /* Re-adjust reply size */ - request->len = extra_size + IW_EV_POINT_PK_LEN; - - /* Put the iwe header where it should, i.e. scrap the - * dummy pointer. */ - memcpy(buffer + IW_EV_POINT_OFF, request, IW_EV_LCP_PK_LEN); - -#ifdef WE_RTNETLINK_DEBUG - printk(KERN_DEBUG "%s (WE.r) : Reply 0x%04X, hdr_len %d, tokens %d, extra_size %d, buffer_size %d\n", dev->name, cmd, hdr_len, wrqu->data.length, extra_size, buffer_size); -#endif /* WE_RTNETLINK_DEBUG */ - } - - /* Return the buffer to the caller */ - if (!ret) { - *p_buf = buffer; - *p_len = request->len; - } else { - /* Cleanup */ - if (buffer) - kfree(buffer); - } - - return ret; -} - -/* ---------------------------------------------------------------- */ -/* - * Wrapper to call a private Wireless Extension SET handler. - * Same as above... - * It's not as nice and slimline as the standard wrapper. The cause - * is struct iw_priv_args, which was not really designed for the - * job we are going here. - * - * IMPORTANT : This function prevent to set and get data on the same - * IOCTL and enforce the SET/GET convention. Not doing it would be - * far too hairy... - * If you need to set and get data at the same time, please don't use - * a iw_handler but process it in your ioctl handler (i.e. use the - * old driver API). - */ -static inline int rtnetlink_private_set(struct net_device * dev, - struct iw_event * request, - int request_len, - iw_handler handler) -{ - const struct iw_priv_args * descr = NULL; - unsigned int cmd; - union iwreq_data * wrqu; - union iwreq_data wrqu_point; - int hdr_len; - char * extra = NULL; - int extra_size = 0; - int offset = 0; /* For sub-ioctls */ - struct iw_request_info info; - int i; - int ret = -EINVAL; - - /* Get the description of the Request */ - cmd = request->cmd; - for (i = 0; i < dev->wireless_handlers->num_private_args; i++) - if (cmd == dev->wireless_handlers->private_args[i].cmd) { - descr = &(dev->wireless_handlers->private_args[i]); - break; - } - if (descr == NULL) - return -EOPNOTSUPP; - -#ifdef WE_RTNETLINK_DEBUG - printk(KERN_DEBUG "%s (WE.r) : Found private handler for 0x%04X\n", - ifr->ifr_name, cmd); - printk(KERN_DEBUG "%s (WE.r) : Name %s, set %X, get %X\n", - dev->name, descr->name, descr->set_args, descr->get_args); -#endif /* WE_RTNETLINK_DEBUG */ - - /* Compute the size of the set arguments */ - /* Check for sub-ioctl handler */ - if (descr->name[0] == '\0') - /* Reserve one int for sub-ioctl index */ - offset = sizeof(__u32); - - /* Size of set arguments */ - extra_size = get_priv_size(descr->set_args); - - /* Does it fits in wrqu ? */ - if ((descr->set_args & IW_PRIV_SIZE_FIXED) && - (extra_size <= IFNAMSIZ)) { - hdr_len = IW_EV_LCP_PK_LEN + extra_size; - extra_size = 0; - } else { - hdr_len = IW_EV_POINT_PK_LEN; - } - - /* Extract fixed header from request. This is properly aligned. */ - wrqu = (union iwreq_data *) (((char *) request) + IW_EV_LCP_PK_LEN); - - /* Check if wrqu is complete */ - if (request_len < hdr_len) { -#ifdef WE_RTNETLINK_DEBUG - printk(KERN_DEBUG - "%s (WE.r) : Wireless request too short (%d)\n", - dev->name, request_len); -#endif /* WE_RTNETLINK_DEBUG */ - return -EINVAL; - } - - /* Prepare the call */ - info.cmd = cmd; - info.flags = 0; - - /* Check if we have a pointer to user space data or not. */ - if (extra_size == 0) { - - /* No extra arguments. Trivial to handle */ - ret = handler(dev, &info, wrqu, (char *) wrqu); - - } else { - int extra_len; - - /* Put wrqu in the right place (skip pointer) */ - memcpy(((char *) &wrqu_point) + IW_EV_POINT_OFF, - wrqu, IW_EV_POINT_PK_LEN - IW_EV_LCP_PK_LEN); - - /* Does it fits within bounds ? */ - if (wrqu_point.data.length > (descr->set_args & - IW_PRIV_SIZE_MASK)) - return -E2BIG; - - /* Real length of payload */ - extra_len = adjust_priv_size(descr->set_args, &wrqu_point); - - /* Check if request is self consistent */ - if ((request_len - hdr_len) < extra_len) { -#ifdef WE_RTNETLINK_DEBUG - printk(KERN_DEBUG "%s (WE.r) : Wireless request data too short (%d)\n", - dev->name, extra_size); -#endif /* WE_RTNETLINK_DEBUG */ - return -EINVAL; - } - -#ifdef WE_RTNETLINK_DEBUG - printk(KERN_DEBUG "%s (WE.r) : Malloc %d bytes\n", - dev->name, extra_size); -#endif /* WE_RTNETLINK_DEBUG */ - - /* Always allocate for max space. Easier, and won't last - * long... */ - extra = kmalloc(extra_size, GFP_KERNEL); - if (extra == NULL) - return -ENOMEM; - - /* Copy extra in aligned buffer */ - memcpy(extra, ((char *) request) + hdr_len, extra_len); - - /* Call the handler */ - ret = handler(dev, &info, &wrqu_point, extra); - - /* Cleanup - I told you it wasn't that long ;-) */ - kfree(extra); - } - - /* Call commit handler if needed and defined */ - if (ret == -EIWCOMMIT) - ret = call_commit_handler(dev); - - return ret; -} - -/* ---------------------------------------------------------------- */ -/* - * Main RtNetlink dispatcher. Called from the main networking code - * (do_getlink() in net/core/rtnetlink.c). - * Check the type of Request and call the appropriate wrapper... - */ -int wireless_rtnetlink_get(struct net_device * dev, - char * data, - int len, - char ** p_buf, - int * p_len) -{ - struct iw_event * request = (struct iw_event *) data; - iw_handler handler; - - /* Check length */ - if (len < IW_EV_LCP_PK_LEN) { - printk(KERN_DEBUG "%s (WE.r) : RtNetlink request too short (%d)\n", - dev->name, len); - return -EINVAL; - } - - /* ReCheck length (len may have padding) */ - if (request->len > len) { - printk(KERN_DEBUG "%s (WE.r) : RtNetlink request len invalid (%d-%d)\n", - dev->name, request->len, len); - return -EINVAL; - } - - /* Only accept GET requests in here */ - if (!IW_IS_GET(request->cmd)) - return -EOPNOTSUPP; - - /* If command is `get the encoding parameters', check if - * the user has the right to do it */ - if (request->cmd == SIOCGIWENCODE || - request->cmd == SIOCGIWENCODEEXT) { - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - } - - /* Special cases */ - if (request->cmd == SIOCGIWSTATS) - /* Get Wireless Stats */ - return rtnetlink_standard_get(dev, - request, - request->len, - &iw_handler_get_iwstats, - p_buf, p_len); - if (request->cmd == SIOCGIWPRIV) { - /* Check if we have some wireless handlers defined */ - if (dev->wireless_handlers == NULL) - return -EOPNOTSUPP; - /* Get Wireless Stats */ - return rtnetlink_standard_get(dev, - request, - request->len, - &iw_handler_get_private, - p_buf, p_len); - } - - /* Basic check */ - if (!netif_device_present(dev)) - return -ENODEV; - - /* Try to find the handler */ - handler = get_handler(dev, request->cmd); - if (handler != NULL) { - /* Standard and private are not the same */ - if (request->cmd < SIOCIWFIRSTPRIV) - return rtnetlink_standard_get(dev, - request, - request->len, - handler, - p_buf, p_len); - else - return rtnetlink_private_get(dev, - request, - request->len, - handler, - p_buf, p_len); - } - - return -EOPNOTSUPP; -} - -/* ---------------------------------------------------------------- */ -/* - * Main RtNetlink dispatcher. Called from the main networking code - * (do_setlink() in net/core/rtnetlink.c). - * Check the type of Request and call the appropriate wrapper... - */ -int wireless_rtnetlink_set(struct net_device * dev, - char * data, - int len) -{ - struct iw_event * request = (struct iw_event *) data; - iw_handler handler; - - /* Check length */ - if (len < IW_EV_LCP_PK_LEN) { - printk(KERN_DEBUG "%s (WE.r) : RtNetlink request too short (%d)\n", - dev->name, len); - return -EINVAL; - } - - /* ReCheck length (len may have padding) */ - if (request->len > len) { - printk(KERN_DEBUG "%s (WE.r) : RtNetlink request len invalid (%d-%d)\n", - dev->name, request->len, len); - return -EINVAL; - } - - /* Only accept SET requests in here */ - if (!IW_IS_SET(request->cmd)) - return -EOPNOTSUPP; - - /* Basic check */ - if (!netif_device_present(dev)) - return -ENODEV; - - /* New driver API : try to find the handler */ - handler = get_handler(dev, request->cmd); - if (handler != NULL) { - /* Standard and private are not the same */ - if (request->cmd < SIOCIWFIRSTPRIV) - return rtnetlink_standard_set(dev, - request, - request->len, - handler); - else - return rtnetlink_private_set(dev, - request, - request->len, - handler); - } - - return -EOPNOTSUPP; -} -#endif /* CONFIG_NET_WIRELESS_RTNETLINK */ - /************************* EVENT PROCESSING *************************/ /* diff --git a/net/wireless/Kconfig b/net/wireless/Kconfig index 1863c0b07d45..a228d56a91b8 100644 --- a/net/wireless/Kconfig +++ b/net/wireless/Kconfig @@ -14,13 +14,3 @@ config WIRELESS_EXT Say N (if you can) unless you know you need wireless extensions for external modules. - -config NET_WIRELESS_RTNETLINK - bool "Wireless Extension API over RtNetlink" - depends on WIRELESS_EXT - ---help--- - Support the Wireless Extension API over the RtNetlink socket - in addition to the traditional ioctl interface (selected above). - - For now, few tools use this facility, but it might grow in the - future. The only downside is that it adds 4.5 kB to your kernel. -- cgit v1.2.3 From 48491e6bdb8fa73751cc95f740175ec799db5d55 Mon Sep 17 00:00:00 2001 From: "Robert P. J. Day" Date: Thu, 26 Apr 2007 00:59:27 -0700 Subject: [NET]: Delete unused header file linux/if_wanpipe_common.h Delete the unreferenced header file include/linux/if_wanpipe_common.h, as well as the reference to it in the Doc file. Signed-off-by: Robert P. J. Day Signed-off-by: Andrew Morton Signed-off-by: David S. Miller --- Documentation/networking/wan-router.txt | 1 - include/linux/if_wanpipe_common.h | 58 --------------------------------- 2 files changed, 59 deletions(-) delete mode 100644 include/linux/if_wanpipe_common.h (limited to 'Documentation') diff --git a/Documentation/networking/wan-router.txt b/Documentation/networking/wan-router.txt index 653978dcea7f..07dd6d9930a1 100644 --- a/Documentation/networking/wan-router.txt +++ b/Documentation/networking/wan-router.txt @@ -250,7 +250,6 @@ PRODUCT COMPONENTS AND RELATED FILES sdladrv.h SDLA support module API definitions sdlasfm.h SDLA firmware module definitions if_wanpipe.h WANPIPE Socket definitions - if_wanpipe_common.h WANPIPE Socket/Driver common definitions. sdlapci.h WANPIPE PCI definitions diff --git a/include/linux/if_wanpipe_common.h b/include/linux/if_wanpipe_common.h deleted file mode 100644 index 6e5461d69fdd..000000000000 --- a/include/linux/if_wanpipe_common.h +++ /dev/null @@ -1,58 +0,0 @@ -/***************************************************************************** -* if_wanipe_common.h Sangoma Driver/Socket common area definitions. -* -* Author: Nenad Corbic -* -* Copyright: (c) 2000 Sangoma Technologies Inc. -* -* 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. -* ============================================================================ -* Jan 13, 2000 Nenad Corbic Initial version -*****************************************************************************/ - - -#ifndef _WANPIPE_SOCK_DRIVER_COMMON_H -#define _WANPIPE_SOCK_DRIVER_COMMON_H - -typedef struct { - struct net_device *slave; - atomic_t packet_sent; - atomic_t receive_block; - atomic_t command; - atomic_t disconnect; - atomic_t driver_busy; - long common_critical; - struct timer_list *tx_timer; - struct sock *sk; /* Wanpipe Sock bind's here */ - int (*func)(struct sk_buff *skb, struct net_device *dev, - struct sock *sk); - - struct work_struct wanpipe_work; /* deferred keventd work */ - unsigned char rw_bind; /* Sock bind state */ - unsigned char usedby; - unsigned char state; - unsigned char svc; - unsigned short lcn; - void *mbox; -} wanpipe_common_t; - - -enum { - WANSOCK_UNCONFIGURED, /* link/channel is not configured */ - WANSOCK_DISCONNECTED, /* link/channel is disconnected */ - WANSOCK_CONNECTING, /* connection is in progress */ - WANSOCK_CONNECTED, /* link/channel is operational */ - WANSOCK_LIMIT, /* for verification only */ - WANSOCK_DUALPORT, /* for Dual Port cards */ - WANSOCK_DISCONNECTING, - WANSOCK_BINDED, - WANSOCK_BIND_LISTEN, - WANSOCK_LISTEN -}; - -#endif - - -- cgit v1.2.3 From 9198d2220d29b87ac3a05a3b791c50bb8a014d63 Mon Sep 17 00:00:00 2001 From: "Alexandra N. Kossovsky" Date: Thu, 26 Apr 2007 01:40:13 -0700 Subject: [NET]: bonding documentation fix for multiple bonding interfaces Fix bonding driver documentation for the case of multiple bonding interfaces. Signed-off-by: "Alexandra N. Kossovsky" Acked-by: Jay Vosburgh Signed-off-by: Andrew Morton Signed-off-by: David S. Miller --- Documentation/networking/bonding.txt | 35 ++--------------------------------- 1 file changed, 2 insertions(+), 33 deletions(-) (limited to 'Documentation') diff --git a/Documentation/networking/bonding.txt b/Documentation/networking/bonding.txt index de809e58092f..1da566630831 100644 --- a/Documentation/networking/bonding.txt +++ b/Documentation/networking/bonding.txt @@ -920,40 +920,9 @@ options, you may wish to use the "max_bonds" module parameter, documented above. To create multiple bonding devices with differing options, it -is necessary to load the bonding driver multiple times. Note that -current versions of the sysconfig network initialization scripts -handle this automatically; if your distro uses these scripts, no -special action is needed. See the section Configuring Bonding -Devices, above, if you're not sure about your network initialization -scripts. - - To load multiple instances of the module, it is necessary to -specify a different name for each instance (the module loading system -requires that every loaded module, even multiple instances of the same -module, have a unique name). This is accomplished by supplying -multiple sets of bonding options in /etc/modprobe.conf, for example: - -alias bond0 bonding -options bond0 -o bond0 mode=balance-rr miimon=100 - -alias bond1 bonding -options bond1 -o bond1 mode=balance-alb miimon=50 - - will load the bonding module two times. The first instance is -named "bond0" and creates the bond0 device in balance-rr mode with an -miimon of 100. The second instance is named "bond1" and creates the -bond1 device in balance-alb mode with an miimon of 50. - - In some circumstances (typically with older distributions), -the above does not work, and the second bonding instance never sees -its options. In that case, the second options line can be substituted -as follows: - -install bond1 /sbin/modprobe --ignore-install bonding -o bond1 \ - mode=balance-alb miimon=50 +is necessary to use bonding parameters exported by sysfs, documented +in the section below. - This may be repeated any number of times, specifying a new and -unique name in place of bond1 for each subsequent instance. 3.4 Configuring Bonding Manually via Sysfs ------------------------------------------ -- cgit v1.2.3 From 7318226ea2931a627f3572e5f4804c91ca19ecbc Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 26 Apr 2007 15:46:23 -0700 Subject: [AF_RXRPC]: Key facility changes for AF_RXRPC Export the keyring key type definition and document its availability. Add alternative types into the key's type_data union to make it more useful. Not all users necessarily want to use it as a list_head (AF_RXRPC doesn't, for example), so make it clear that it can be used in other ways. Signed-off-by: David Howells Signed-off-by: David S. Miller --- Documentation/keys.txt | 12 ++++++++++++ include/linux/key.h | 2 ++ security/keys/keyring.c | 2 ++ 3 files changed, 16 insertions(+) (limited to 'Documentation') diff --git a/Documentation/keys.txt b/Documentation/keys.txt index 60c665d9cfaa..81d9aa097298 100644 --- a/Documentation/keys.txt +++ b/Documentation/keys.txt @@ -859,6 +859,18 @@ payload contents" for more information. void unregister_key_type(struct key_type *type); +Under some circumstances, it may be desirable to desirable to deal with a +bundle of keys. The facility provides access to the keyring type for managing +such a bundle: + + struct key_type key_type_keyring; + +This can be used with a function such as request_key() to find a specific +keyring in a process's keyrings. A keyring thus found can then be searched +with keyring_search(). Note that it is not possible to use request_key() to +search a specific keyring, so using keyrings in this way is of limited utility. + + =================================== NOTES ON ACCESSING PAYLOAD CONTENTS =================================== diff --git a/include/linux/key.h b/include/linux/key.h index 169f05e4863e..a9220e75782e 100644 --- a/include/linux/key.h +++ b/include/linux/key.h @@ -160,6 +160,8 @@ struct key { */ union { struct list_head link; + unsigned long x[2]; + void *p[2]; } type_data; /* key data diff --git a/security/keys/keyring.c b/security/keys/keyring.c index ad45ce73964b..88292e3dee96 100644 --- a/security/keys/keyring.c +++ b/security/keys/keyring.c @@ -66,6 +66,8 @@ struct key_type key_type_keyring = { .read = keyring_read, }; +EXPORT_SYMBOL(key_type_keyring); + /* * semaphore to serialise link/link calls to prevent two link calls in parallel * introducing a cycle -- cgit v1.2.3 From 17926a79320afa9b95df6b977b40cca6d8713cea Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 26 Apr 2007 15:48:28 -0700 Subject: [AF_RXRPC]: Provide secure RxRPC sockets for use by userspace and kernel both Provide AF_RXRPC sockets that can be used to talk to AFS servers, or serve answers to AFS clients. KerberosIV security is fully supported. The patches and some example test programs can be found in: http://people.redhat.com/~dhowells/rxrpc/ This will eventually replace the old implementation of kernel-only RxRPC currently resident in net/rxrpc/. Signed-off-by: David Howells Signed-off-by: David S. Miller --- Documentation/networking/rxrpc.txt | 663 +++++++++++++++++++ include/keys/rxrpc-type.h | 22 + include/linux/net.h | 2 +- include/linux/rxrpc.h | 62 ++ include/linux/socket.h | 5 +- include/net/af_rxrpc.h | 17 + include/rxrpc/packet.h | 85 ++- net/Kconfig | 1 + net/Makefile | 1 + net/core/sock.c | 6 +- net/rxrpc/Kconfig | 37 ++ net/rxrpc/Makefile | 31 +- net/rxrpc/af_rxrpc.c | 754 ++++++++++++++++++++++ net/rxrpc/ar-accept.c | 399 ++++++++++++ net/rxrpc/ar-ack.c | 1250 ++++++++++++++++++++++++++++++++++++ net/rxrpc/ar-call.c | 787 +++++++++++++++++++++++ net/rxrpc/ar-connection.c | 895 ++++++++++++++++++++++++++ net/rxrpc/ar-connevent.c | 387 +++++++++++ net/rxrpc/ar-error.c | 253 ++++++++ net/rxrpc/ar-input.c | 791 +++++++++++++++++++++++ net/rxrpc/ar-internal.h | 842 ++++++++++++++++++++++++ net/rxrpc/ar-key.c | 334 ++++++++++ net/rxrpc/ar-local.c | 309 +++++++++ net/rxrpc/ar-output.c | 658 +++++++++++++++++++ net/rxrpc/ar-peer.c | 273 ++++++++ net/rxrpc/ar-proc.c | 247 +++++++ net/rxrpc/ar-recvmsg.c | 366 +++++++++++ net/rxrpc/ar-security.c | 258 ++++++++ net/rxrpc/ar-skbuff.c | 118 ++++ net/rxrpc/ar-transport.c | 276 ++++++++ net/rxrpc/rxkad.c | 1153 +++++++++++++++++++++++++++++++++ 31 files changed, 11275 insertions(+), 7 deletions(-) create mode 100644 Documentation/networking/rxrpc.txt create mode 100644 include/keys/rxrpc-type.h create mode 100644 include/linux/rxrpc.h create mode 100644 include/net/af_rxrpc.h create mode 100644 net/rxrpc/Kconfig create mode 100644 net/rxrpc/af_rxrpc.c create mode 100644 net/rxrpc/ar-accept.c create mode 100644 net/rxrpc/ar-ack.c create mode 100644 net/rxrpc/ar-call.c create mode 100644 net/rxrpc/ar-connection.c create mode 100644 net/rxrpc/ar-connevent.c create mode 100644 net/rxrpc/ar-error.c create mode 100644 net/rxrpc/ar-input.c create mode 100644 net/rxrpc/ar-internal.h create mode 100644 net/rxrpc/ar-key.c create mode 100644 net/rxrpc/ar-local.c create mode 100644 net/rxrpc/ar-output.c create mode 100644 net/rxrpc/ar-peer.c create mode 100644 net/rxrpc/ar-proc.c create mode 100644 net/rxrpc/ar-recvmsg.c create mode 100644 net/rxrpc/ar-security.c create mode 100644 net/rxrpc/ar-skbuff.c create mode 100644 net/rxrpc/ar-transport.c create mode 100644 net/rxrpc/rxkad.c (limited to 'Documentation') diff --git a/Documentation/networking/rxrpc.txt b/Documentation/networking/rxrpc.txt new file mode 100644 index 000000000000..fb809b738a0d --- /dev/null +++ b/Documentation/networking/rxrpc.txt @@ -0,0 +1,663 @@ + ====================== + RxRPC NETWORK PROTOCOL + ====================== + +The RxRPC protocol driver provides a reliable two-phase transport on top of UDP +that can be used to perform RxRPC remote operations. This is done over sockets +of AF_RXRPC family, using sendmsg() and recvmsg() with control data to send and +receive data, aborts and errors. + +Contents of this document: + + (*) Overview. + + (*) RxRPC protocol summary. + + (*) AF_RXRPC driver model. + + (*) Control messages. + + (*) Socket options. + + (*) Security. + + (*) Example client usage. + + (*) Example server usage. + + +======== +OVERVIEW +======== + +RxRPC is a two-layer protocol. There is a session layer which provides +reliable virtual connections using UDP over IPv4 (or IPv6) as the transport +layer, but implements a real network protocol; and there's the presentation +layer which renders structured data to binary blobs and back again using XDR +(as does SunRPC): + + +-------------+ + | Application | + +-------------+ + | XDR | Presentation + +-------------+ + | RxRPC | Session + +-------------+ + | UDP | Transport + +-------------+ + + +AF_RXRPC provides: + + (1) Part of an RxRPC facility for both kernel and userspace applications by + making the session part of it a Linux network protocol (AF_RXRPC). + + (2) A two-phase protocol. The client transmits a blob (the request) and then + receives a blob (the reply), and the server receives the request and then + transmits the reply. + + (3) Retention of the reusable bits of the transport system set up for one call + to speed up subsequent calls. + + (4) A secure protocol, using the Linux kernel's key retention facility to + manage security on the client end. The server end must of necessity be + more active in security negotiations. + +AF_RXRPC does not provide XDR marshalling/presentation facilities. That is +left to the application. AF_RXRPC only deals in blobs. Even the operation ID +is just the first four bytes of the request blob, and as such is beyond the +kernel's interest. + + +Sockets of AF_RXRPC family are: + + (1) created as type SOCK_DGRAM; + + (2) provided with a protocol of the type of underlying transport they're going + to use - currently only PF_INET is supported. + + +The Andrew File System (AFS) is an example of an application that uses this and +that has both kernel (filesystem) and userspace (utility) components. + + +====================== +RXRPC PROTOCOL SUMMARY +====================== + +An overview of the RxRPC protocol: + + (*) RxRPC sits on top of another networking protocol (UDP is the only option + currently), and uses this to provide network transport. UDP ports, for + example, provide transport endpoints. + + (*) RxRPC supports multiple virtual "connections" from any given transport + endpoint, thus allowing the endpoints to be shared, even to the same + remote endpoint. + + (*) Each connection goes to a particular "service". A connection may not go + to multiple services. A service may be considered the RxRPC equivalent of + a port number. AF_RXRPC permits multiple services to share an endpoint. + + (*) Client-originating packets are marked, thus a transport endpoint can be + shared between client and server connections (connections have a + direction). + + (*) Up to a billion connections may be supported concurrently between one + local transport endpoint and one service on one remote endpoint. An RxRPC + connection is described by seven numbers: + + Local address } + Local port } Transport (UDP) address + Remote address } + Remote port } + Direction + Connection ID + Service ID + + (*) Each RxRPC operation is a "call". A connection may make up to four + billion calls, but only up to four calls may be in progress on a + connection at any one time. + + (*) Calls are two-phase and asymmetric: the client sends its request data, + which the service receives; then the service transmits the reply data + which the client receives. + + (*) The data blobs are of indefinite size, the end of a phase is marked with a + flag in the packet. The number of packets of data making up one blob may + not exceed 4 billion, however, as this would cause the sequence number to + wrap. + + (*) The first four bytes of the request data are the service operation ID. + + (*) Security is negotiated on a per-connection basis. The connection is + initiated by the first data packet on it arriving. If security is + requested, the server then issues a "challenge" and then the client + replies with a "response". If the response is successful, the security is + set for the lifetime of that connection, and all subsequent calls made + upon it use that same security. In the event that the server lets a + connection lapse before the client, the security will be renegotiated if + the client uses the connection again. + + (*) Calls use ACK packets to handle reliability. Data packets are also + explicitly sequenced per call. + + (*) There are two types of positive acknowledgement: hard-ACKs and soft-ACKs. + A hard-ACK indicates to the far side that all the data received to a point + has been received and processed; a soft-ACK indicates that the data has + been received but may yet be discarded and re-requested. The sender may + not discard any transmittable packets until they've been hard-ACK'd. + + (*) Reception of a reply data packet implicitly hard-ACK's all the data + packets that make up the request. + + (*) An call is complete when the request has been sent, the reply has been + received and the final hard-ACK on the last packet of the reply has + reached the server. + + (*) An call may be aborted by either end at any time up to its completion. + + +===================== +AF_RXRPC DRIVER MODEL +===================== + +About the AF_RXRPC driver: + + (*) The AF_RXRPC protocol transparently uses internal sockets of the transport + protocol to represent transport endpoints. + + (*) AF_RXRPC sockets map onto RxRPC connection bundles. Actual RxRPC + connections are handled transparently. One client socket may be used to + make multiple simultaneous calls to the same service. One server socket + may handle calls from many clients. + + (*) Additional parallel client connections will be initiated to support extra + concurrent calls, up to a tunable limit. + + (*) Each connection is retained for a certain amount of time [tunable] after + the last call currently using it has completed in case a new call is made + that could reuse it. + + (*) Each internal UDP socket is retained [tunable] for a certain amount of + time [tunable] after the last connection using it discarded, in case a new + connection is made that could use it. + + (*) A client-side connection is only shared between calls if they have have + the same key struct describing their security (and assuming the calls + would otherwise share the connection). Non-secured calls would also be + able to share connections with each other. + + (*) A server-side connection is shared if the client says it is. + + (*) ACK'ing is handled by the protocol driver automatically, including ping + replying. + + (*) SO_KEEPALIVE automatically pings the other side to keep the connection + alive [TODO]. + + (*) If an ICMP error is received, all calls affected by that error will be + aborted with an appropriate network error passed through recvmsg(). + + +Interaction with the user of the RxRPC socket: + + (*) A socket is made into a server socket by binding an address with a + non-zero service ID. + + (*) In the client, sending a request is achieved with one or more sendmsgs, + followed by the reply being received with one or more recvmsgs. + + (*) The first sendmsg for a request to be sent from a client contains a tag to + be used in all other sendmsgs or recvmsgs associated with that call. The + tag is carried in the control data. + + (*) connect() is used to supply a default destination address for a client + socket. This may be overridden by supplying an alternate address to the + first sendmsg() of a call (struct msghdr::msg_name). + + (*) If connect() is called on an unbound client, a random local port will + bound before the operation takes place. + + (*) A server socket may also be used to make client calls. To do this, the + first sendmsg() of the call must specify the target address. The server's + transport endpoint is used to send the packets. + + (*) Once the application has received the last message associated with a call, + the tag is guaranteed not to be seen again, and so it can be used to pin + client resources. A new call can then be initiated with the same tag + without fear of interference. + + (*) In the server, a request is received with one or more recvmsgs, then the + the reply is transmitted with one or more sendmsgs, and then the final ACK + is received with a last recvmsg. + + (*) When sending data for a call, sendmsg is given MSG_MORE if there's more + data to come on that call. + + (*) When receiving data for a call, recvmsg flags MSG_MORE if there's more + data to come for that call. + + (*) When receiving data or messages for a call, MSG_EOR is flagged by recvmsg + to indicate the terminal message for that call. + + (*) A call may be aborted by adding an abort control message to the control + data. Issuing an abort terminates the kernel's use of that call's tag. + Any messages waiting in the receive queue for that call will be discarded. + + (*) Aborts, busy notifications and challenge packets are delivered by recvmsg, + and control data messages will be set to indicate the context. Receiving + an abort or a busy message terminates the kernel's use of that call's tag. + + (*) The control data part of the msghdr struct is used for a number of things: + + (*) The tag of the intended or affected call. + + (*) Sending or receiving errors, aborts and busy notifications. + + (*) Notifications of incoming calls. + + (*) Sending debug requests and receiving debug replies [TODO]. + + (*) When the kernel has received and set up an incoming call, it sends a + message to server application to let it know there's a new call awaiting + its acceptance [recvmsg reports a special control message]. The server + application then uses sendmsg to assign a tag to the new call. Once that + is done, the first part of the request data will be delivered by recvmsg. + + (*) The server application has to provide the server socket with a keyring of + secret keys corresponding to the security types it permits. When a secure + connection is being set up, the kernel looks up the appropriate secret key + in the keyring and then sends a challenge packet to the client and + receives a response packet. The kernel then checks the authorisation of + the packet and either aborts the connection or sets up the security. + + (*) The name of the key a client will use to secure its communications is + nominated by a socket option. + + +Notes on recvmsg: + + (*) If there's a sequence of data messages belonging to a particular call on + the receive queue, then recvmsg will keep working through them until: + + (a) it meets the end of that call's received data, + + (b) it meets a non-data message, + + (c) it meets a message belonging to a different call, or + + (d) it fills the user buffer. + + If recvmsg is called in blocking mode, it will keep sleeping, awaiting the + reception of further data, until one of the above four conditions is met. + + (2) MSG_PEEK operates similarly, but will return immediately if it has put any + data in the buffer rather than sleeping until it can fill the buffer. + + (3) If a data message is only partially consumed in filling a user buffer, + then the remainder of that message will be left on the front of the queue + for the next taker. MSG_TRUNC will never be flagged. + + (4) If there is more data to be had on a call (it hasn't copied the last byte + of the last data message in that phase yet), then MSG_MORE will be + flagged. + + +================ +CONTROL MESSAGES +================ + +AF_RXRPC makes use of control messages in sendmsg() and recvmsg() to multiplex +calls, to invoke certain actions and to report certain conditions. These are: + + MESSAGE ID SRT DATA MEANING + ======================= === =========== =============================== + RXRPC_USER_CALL_ID sr- User ID App's call specifier + RXRPC_ABORT srt Abort code Abort code to issue/received + RXRPC_ACK -rt n/a Final ACK received + RXRPC_NET_ERROR -rt error num Network error on call + RXRPC_BUSY -rt n/a Call rejected (server busy) + RXRPC_LOCAL_ERROR -rt error num Local error encountered + RXRPC_NEW_CALL -r- n/a New call received + RXRPC_ACCEPT s-- n/a Accept new call + + (SRT = usable in Sendmsg / delivered by Recvmsg / Terminal message) + + (*) RXRPC_USER_CALL_ID + + This is used to indicate the application's call ID. It's an unsigned long + that the app specifies in the client by attaching it to the first data + message or in the server by passing it in association with an RXRPC_ACCEPT + message. recvmsg() passes it in conjunction with all messages except + those of the RXRPC_NEW_CALL message. + + (*) RXRPC_ABORT + + This is can be used by an application to abort a call by passing it to + sendmsg, or it can be delivered by recvmsg to indicate a remote abort was + received. Either way, it must be associated with an RXRPC_USER_CALL_ID to + specify the call affected. If an abort is being sent, then error EBADSLT + will be returned if there is no call with that user ID. + + (*) RXRPC_ACK + + This is delivered to a server application to indicate that the final ACK + of a call was received from the client. It will be associated with an + RXRPC_USER_CALL_ID to indicate the call that's now complete. + + (*) RXRPC_NET_ERROR + + This is delivered to an application to indicate that an ICMP error message + was encountered in the process of trying to talk to the peer. An + errno-class integer value will be included in the control message data + indicating the problem, and an RXRPC_USER_CALL_ID will indicate the call + affected. + + (*) RXRPC_BUSY + + This is delivered to a client application to indicate that a call was + rejected by the server due to the server being busy. It will be + associated with an RXRPC_USER_CALL_ID to indicate the rejected call. + + (*) RXRPC_LOCAL_ERROR + + This is delivered to an application to indicate that a local error was + encountered and that a call has been aborted because of it. An + errno-class integer value will be included in the control message data + indicating the problem, and an RXRPC_USER_CALL_ID will indicate the call + affected. + + (*) RXRPC_NEW_CALL + + This is delivered to indicate to a server application that a new call has + arrived and is awaiting acceptance. No user ID is associated with this, + as a user ID must subsequently be assigned by doing an RXRPC_ACCEPT. + + (*) RXRPC_ACCEPT + + This is used by a server application to attempt to accept a call and + assign it a user ID. It should be associated with an RXRPC_USER_CALL_ID + to indicate the user ID to be assigned. If there is no call to be + accepted (it may have timed out, been aborted, etc.), then sendmsg will + return error ENODATA. If the user ID is already in use by another call, + then error EBADSLT will be returned. + + +============== +SOCKET OPTIONS +============== + +AF_RXRPC sockets support a few socket options at the SOL_RXRPC level: + + (*) RXRPC_SECURITY_KEY + + This is used to specify the description of the key to be used. The key is + extracted from the calling process's keyrings with request_key() and + should be of "rxrpc" type. + + The optval pointer points to the description string, and optlen indicates + how long the string is, without the NUL terminator. + + (*) RXRPC_SECURITY_KEYRING + + Similar to above but specifies a keyring of server secret keys to use (key + type "keyring"). See the "Security" section. + + (*) RXRPC_EXCLUSIVE_CONNECTION + + This is used to request that new connections should be used for each call + made subsequently on this socket. optval should be NULL and optlen 0. + + (*) RXRPC_MIN_SECURITY_LEVEL + + This is used to specify the minimum security level required for calls on + this socket. optval must point to an int containing one of the following + values: + + (a) RXRPC_SECURITY_PLAIN + + Encrypted checksum only. + + (b) RXRPC_SECURITY_AUTH + + Encrypted checksum plus packet padded and first eight bytes of packet + encrypted - which includes the actual packet length. + + (c) RXRPC_SECURITY_ENCRYPTED + + Encrypted checksum plus entire packet padded and encrypted, including + actual packet length. + + +======== +SECURITY +======== + +Currently, only the kerberos 4 equivalent protocol has been implemented +(security index 2 - rxkad). This requires the rxkad module to be loaded and, +on the client, tickets of the appropriate type to be obtained from the AFS +kaserver or the kerberos server and installed as "rxrpc" type keys. This is +normally done using the klog program. An example simple klog program can be +found at: + + http://people.redhat.com/~dhowells/rxrpc/klog.c + +The payload provided to add_key() on the client should be of the following +form: + + struct rxrpc_key_sec2_v1 { + uint16_t security_index; /* 2 */ + uint16_t ticket_length; /* length of ticket[] */ + uint32_t expiry; /* time at which expires */ + uint8_t kvno; /* key version number */ + uint8_t __pad[3]; + uint8_t session_key[8]; /* DES session key */ + uint8_t ticket[0]; /* the encrypted ticket */ + }; + +Where the ticket blob is just appended to the above structure. + + +For the server, keys of type "rxrpc_s" must be made available to the server. +They have a description of ":" (eg: "52:2" for an +rxkad key for the AFS VL service). When such a key is created, it should be +given the server's secret key as the instantiation data (see the example +below). + + add_key("rxrpc_s", "52:2", secret_key, 8, keyring); + +A keyring is passed to the server socket by naming it in a sockopt. The server +socket then looks the server secret keys up in this keyring when secure +incoming connections are made. This can be seen in an example program that can +be found at: + + http://people.redhat.com/~dhowells/rxrpc/listen.c + + +==================== +EXAMPLE CLIENT USAGE +==================== + +A client would issue an operation by: + + (1) An RxRPC socket is set up by: + + client = socket(AF_RXRPC, SOCK_DGRAM, PF_INET); + + Where the third parameter indicates the protocol family of the transport + socket used - usually IPv4 but it can also be IPv6 [TODO]. + + (2) A local address can optionally be bound: + + struct sockaddr_rxrpc srx = { + .srx_family = AF_RXRPC, + .srx_service = 0, /* we're a client */ + .transport_type = SOCK_DGRAM, /* type of transport socket */ + .transport.sin_family = AF_INET, + .transport.sin_port = htons(7000), /* AFS callback */ + .transport.sin_address = 0, /* all local interfaces */ + }; + bind(client, &srx, sizeof(srx)); + + This specifies the local UDP port to be used. If not given, a random + non-privileged port will be used. A UDP port may be shared between + several unrelated RxRPC sockets. Security is handled on a basis of + per-RxRPC virtual connection. + + (3) The security is set: + + const char *key = "AFS:cambridge.redhat.com"; + setsockopt(client, SOL_RXRPC, RXRPC_SECURITY_KEY, key, strlen(key)); + + This issues a request_key() to get the key representing the security + context. The minimum security level can be set: + + unsigned int sec = RXRPC_SECURITY_ENCRYPTED; + setsockopt(client, SOL_RXRPC, RXRPC_MIN_SECURITY_LEVEL, + &sec, sizeof(sec)); + + (4) The server to be contacted can then be specified (alternatively this can + be done through sendmsg): + + struct sockaddr_rxrpc srx = { + .srx_family = AF_RXRPC, + .srx_service = VL_SERVICE_ID, + .transport_type = SOCK_DGRAM, /* type of transport socket */ + .transport.sin_family = AF_INET, + .transport.sin_port = htons(7005), /* AFS volume manager */ + .transport.sin_address = ..., + }; + connect(client, &srx, sizeof(srx)); + + (5) The request data should then be posted to the server socket using a series + of sendmsg() calls, each with the following control message attached: + + RXRPC_USER_CALL_ID - specifies the user ID for this call + + MSG_MORE should be set in msghdr::msg_flags on all but the last part of + the request. Multiple requests may be made simultaneously. + + If a call is intended to go to a destination other then the default + specified through connect(), then msghdr::msg_name should be set on the + first request message of that call. + + (6) The reply data will then be posted to the server socket for recvmsg() to + pick up. MSG_MORE will be flagged by recvmsg() if there's more reply data + for a particular call to be read. MSG_EOR will be set on the terminal + read for a call. + + All data will be delivered with the following control message attached: + + RXRPC_USER_CALL_ID - specifies the user ID for this call + + If an abort or error occurred, this will be returned in the control data + buffer instead, and MSG_EOR will be flagged to indicate the end of that + call. + + +==================== +EXAMPLE SERVER USAGE +==================== + +A server would be set up to accept operations in the following manner: + + (1) An RxRPC socket is created by: + + server = socket(AF_RXRPC, SOCK_DGRAM, PF_INET); + + Where the third parameter indicates the address type of the transport + socket used - usually IPv4. + + (2) Security is set up if desired by giving the socket a keyring with server + secret keys in it: + + keyring = add_key("keyring", "AFSkeys", NULL, 0, + KEY_SPEC_PROCESS_KEYRING); + + const char secret_key[8] = { + 0xa7, 0x83, 0x8a, 0xcb, 0xc7, 0x83, 0xec, 0x94 }; + add_key("rxrpc_s", "52:2", secret_key, 8, keyring); + + setsockopt(server, SOL_RXRPC, RXRPC_SECURITY_KEYRING, "AFSkeys", 7); + + The keyring can be manipulated after it has been given to the socket. This + permits the server to add more keys, replace keys, etc. whilst it is live. + + (2) A local address must then be bound: + + struct sockaddr_rxrpc srx = { + .srx_family = AF_RXRPC, + .srx_service = VL_SERVICE_ID, /* RxRPC service ID */ + .transport_type = SOCK_DGRAM, /* type of transport socket */ + .transport.sin_family = AF_INET, + .transport.sin_port = htons(7000), /* AFS callback */ + .transport.sin_address = 0, /* all local interfaces */ + }; + bind(server, &srx, sizeof(srx)); + + (3) The server is then set to listen out for incoming calls: + + listen(server, 100); + + (4) The kernel notifies the server of pending incoming connections by sending + it a message for each. This is received with recvmsg() on the server + socket. It has no data, and has a single dataless control message + attached: + + RXRPC_NEW_CALL + + The address that can be passed back by recvmsg() at this point should be + ignored since the call for which the message was posted may have gone by + the time it is accepted - in which case the first call still on the queue + will be accepted. + + (5) The server then accepts the new call by issuing a sendmsg() with two + pieces of control data and no actual data: + + RXRPC_ACCEPT - indicate connection acceptance + RXRPC_USER_CALL_ID - specify user ID for this call + + (6) The first request data packet will then be posted to the server socket for + recvmsg() to pick up. At that point, the RxRPC address for the call can + be read from the address fields in the msghdr struct. + + Subsequent request data will be posted to the server socket for recvmsg() + to collect as it arrives. All but the last piece of the request data will + be delivered with MSG_MORE flagged. + + All data will be delivered with the following control message attached: + + RXRPC_USER_CALL_ID - specifies the user ID for this call + + (8) The reply data should then be posted to the server socket using a series + of sendmsg() calls, each with the following control messages attached: + + RXRPC_USER_CALL_ID - specifies the user ID for this call + + MSG_MORE should be set in msghdr::msg_flags on all but the last message + for a particular call. + + (9) The final ACK from the client will be posted for retrieval by recvmsg() + when it is received. It will take the form of a dataless message with two + control messages attached: + + RXRPC_USER_CALL_ID - specifies the user ID for this call + RXRPC_ACK - indicates final ACK (no data) + + MSG_EOR will be flagged to indicate that this is the final message for + this call. + +(10) Up to the point the final packet of reply data is sent, the call can be + aborted by calling sendmsg() with a dataless message with the following + control messages attached: + + RXRPC_USER_CALL_ID - specifies the user ID for this call + RXRPC_ABORT - indicates abort code (4 byte data) + + Any packets waiting in the socket's receive queue will be discarded if + this is issued. + +Note that all the communications for a particular service take place through +the one server socket, using control messages on sendmsg() and recvmsg() to +determine the call affected. diff --git a/include/keys/rxrpc-type.h b/include/keys/rxrpc-type.h new file mode 100644 index 000000000000..e2ee73aef0ee --- /dev/null +++ b/include/keys/rxrpc-type.h @@ -0,0 +1,22 @@ +/* RxRPC key type + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * 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. + */ + +#ifndef _KEYS_RXRPC_TYPE_H +#define _KEYS_RXRPC_TYPE_H + +#include + +/* + * key type for AF_RXRPC keys + */ +extern struct key_type key_type_rxrpc; + +#endif /* _KEYS_USER_TYPE_H */ diff --git a/include/linux/net.h b/include/linux/net.h index 4db21e63d8d2..efc45177b503 100644 --- a/include/linux/net.h +++ b/include/linux/net.h @@ -24,7 +24,7 @@ struct poll_table_struct; struct inode; -#define NPROTO 33 /* should be enough for now.. */ +#define NPROTO 34 /* should be enough for now.. */ #define SYS_SOCKET 1 /* sys_socket(2) */ #define SYS_BIND 2 /* sys_bind(2) */ diff --git a/include/linux/rxrpc.h b/include/linux/rxrpc.h new file mode 100644 index 000000000000..f7b826b565c7 --- /dev/null +++ b/include/linux/rxrpc.h @@ -0,0 +1,62 @@ +/* AF_RXRPC parameters + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * 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. + */ + +#ifndef _LINUX_RXRPC_H +#define _LINUX_RXRPC_H + +#include +#include + +/* + * RxRPC socket address + */ +struct sockaddr_rxrpc { + sa_family_t srx_family; /* address family */ + u16 srx_service; /* service desired */ + u16 transport_type; /* type of transport socket (SOCK_DGRAM) */ + u16 transport_len; /* length of transport address */ + union { + sa_family_t family; /* transport address family */ + struct sockaddr_in sin; /* IPv4 transport address */ + struct sockaddr_in6 sin6; /* IPv6 transport address */ + } transport; +}; + +/* + * RxRPC socket options + */ +#define RXRPC_SECURITY_KEY 1 /* [clnt] set client security key */ +#define RXRPC_SECURITY_KEYRING 2 /* [srvr] set ring of server security keys */ +#define RXRPC_EXCLUSIVE_CONNECTION 3 /* [clnt] use exclusive RxRPC connection */ +#define RXRPC_MIN_SECURITY_LEVEL 4 /* minimum security level */ + +/* + * RxRPC control messages + * - terminal messages mean that a user call ID tag can be recycled + */ +#define RXRPC_USER_CALL_ID 1 /* user call ID specifier */ +#define RXRPC_ABORT 2 /* abort request / notification [terminal] */ +#define RXRPC_ACK 3 /* [Server] RPC op final ACK received [terminal] */ +#define RXRPC_NET_ERROR 5 /* network error received [terminal] */ +#define RXRPC_BUSY 6 /* server busy received [terminal] */ +#define RXRPC_LOCAL_ERROR 7 /* local error generated [terminal] */ +#define RXRPC_NEW_CALL 8 /* [Server] new incoming call notification */ +#define RXRPC_ACCEPT 9 /* [Server] accept request */ + +/* + * RxRPC security levels + */ +#define RXRPC_SECURITY_PLAIN 0 /* plain secure-checksummed packets only */ +#define RXRPC_SECURITY_AUTH 1 /* authenticated packets */ +#define RXRPC_SECURITY_ENCRYPT 2 /* encrypted packets */ + + +#endif /* _LINUX_RXRPC_H */ diff --git a/include/linux/socket.h b/include/linux/socket.h index fcd35a210e7f..6e7c9483a6a6 100644 --- a/include/linux/socket.h +++ b/include/linux/socket.h @@ -188,7 +188,8 @@ struct ucred { #define AF_TIPC 30 /* TIPC sockets */ #define AF_BLUETOOTH 31 /* Bluetooth sockets */ #define AF_IUCV 32 /* IUCV sockets */ -#define AF_MAX 33 /* For now.. */ +#define AF_RXRPC 33 /* RxRPC sockets */ +#define AF_MAX 34 /* For now.. */ /* Protocol families, same as address families. */ #define PF_UNSPEC AF_UNSPEC @@ -222,6 +223,7 @@ struct ucred { #define PF_TIPC AF_TIPC #define PF_BLUETOOTH AF_BLUETOOTH #define PF_IUCV AF_IUCV +#define PF_RXRPC AF_RXRPC #define PF_MAX AF_MAX /* Maximum queue length specifiable by listen. */ @@ -284,6 +286,7 @@ struct ucred { #define SOL_DCCP 269 #define SOL_NETLINK 270 #define SOL_TIPC 271 +#define SOL_RXRPC 272 /* IPX options */ #define IPX_TYPE 1 diff --git a/include/net/af_rxrpc.h b/include/net/af_rxrpc.h new file mode 100644 index 000000000000..b01ca2589d69 --- /dev/null +++ b/include/net/af_rxrpc.h @@ -0,0 +1,17 @@ +/* RxRPC definitions + * + * Copyright (C) 2006 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * 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. + */ + +#ifndef _NET_RXRPC_H +#define _NET_RXRPC_H + +#include + +#endif /* _NET_RXRPC_H */ diff --git a/include/rxrpc/packet.h b/include/rxrpc/packet.h index 1447f0aaa0eb..452a9bb02d48 100644 --- a/include/rxrpc/packet.h +++ b/include/rxrpc/packet.h @@ -33,7 +33,8 @@ struct rxrpc_header #define RXRPC_MAXCALLS 4 /* max active calls per conn */ #define RXRPC_CHANNELMASK (RXRPC_MAXCALLS-1) /* mask for channel ID */ #define RXRPC_CIDMASK (~RXRPC_CHANNELMASK) /* mask for connection ID */ -#define RXRPC_CIDSHIFT 2 /* shift for connection ID */ +#define RXRPC_CIDSHIFT ilog2(RXRPC_MAXCALLS) /* shift for connection ID */ +#define RXRPC_CID_INC (1 << RXRPC_CIDSHIFT) /* connection ID increment */ __be32 callNumber; /* call ID (0 for connection-level packets) */ #define RXRPC_PROCESS_MAXCALLS (1<<2) /* maximum number of active calls per conn (power of 2) */ @@ -62,7 +63,10 @@ struct rxrpc_header uint8_t userStatus; /* app-layer defined status */ uint8_t securityIndex; /* security protocol ID */ - __be16 _rsvd; /* reserved (used by kerberos security as cksum) */ + union { + __be16 _rsvd; /* reserved */ + __be16 cksum; /* kerberos security checksum */ + }; __be16 serviceId; /* service ID */ } __attribute__((packed)); @@ -124,4 +128,81 @@ struct rxrpc_ackpacket } __attribute__((packed)); +/* + * ACK packets can have a further piece of information tagged on the end + */ +struct rxrpc_ackinfo { + __be32 rxMTU; /* maximum Rx MTU size (bytes) [AFS 3.3] */ + __be32 maxMTU; /* maximum interface MTU size (bytes) [AFS 3.3] */ + __be32 rwind; /* Rx window size (packets) [AFS 3.4] */ + __be32 jumbo_max; /* max packets to stick into a jumbo packet [AFS 3.5] */ +}; + +/*****************************************************************************/ +/* + * Kerberos security type-2 challenge packet + */ +struct rxkad_challenge { + __be32 version; /* version of this challenge type */ + __be32 nonce; /* encrypted random number */ + __be32 min_level; /* minimum security level */ + __be32 __padding; /* padding to 8-byte boundary */ +} __attribute__((packed)); + +/*****************************************************************************/ +/* + * Kerberos security type-2 response packet + */ +struct rxkad_response { + __be32 version; /* version of this reponse type */ + __be32 __pad; + + /* encrypted bit of the response */ + struct { + __be32 epoch; /* current epoch */ + __be32 cid; /* parent connection ID */ + __be32 checksum; /* checksum */ + __be32 securityIndex; /* security type */ + __be32 call_id[4]; /* encrypted call IDs */ + __be32 inc_nonce; /* challenge nonce + 1 */ + __be32 level; /* desired level */ + } encrypted; + + __be32 kvno; /* Kerberos key version number */ + __be32 ticket_len; /* Kerberos ticket length */ +} __attribute__((packed)); + +/*****************************************************************************/ +/* + * RxRPC-level abort codes + */ +#define RX_CALL_DEAD -1 /* call/conn has been inactive and is shut down */ +#define RX_INVALID_OPERATION -2 /* invalid operation requested / attempted */ +#define RX_CALL_TIMEOUT -3 /* call timeout exceeded */ +#define RX_EOF -4 /* unexpected end of data on read op */ +#define RX_PROTOCOL_ERROR -5 /* low-level protocol error */ +#define RX_USER_ABORT -6 /* generic user abort */ +#define RX_ADDRINUSE -7 /* UDP port in use */ +#define RX_DEBUGI_BADTYPE -8 /* bad debugging packet type */ + +/* + * Rx kerberos security abort codes + * - unfortunately we have no generalised security abort codes to say things + * like "unsupported security", so we have to use these instead and hope the + * other side understands + */ +#define RXKADINCONSISTENCY 19270400 /* security module structure inconsistent */ +#define RXKADPACKETSHORT 19270401 /* packet too short for security challenge */ +#define RXKADLEVELFAIL 19270402 /* security level negotiation failed */ +#define RXKADTICKETLEN 19270403 /* ticket length too short or too long */ +#define RXKADOUTOFSEQUENCE 19270404 /* packet had bad sequence number */ +#define RXKADNOAUTH 19270405 /* caller not authorised */ +#define RXKADBADKEY 19270406 /* illegal key: bad parity or weak */ +#define RXKADBADTICKET 19270407 /* security object was passed a bad ticket */ +#define RXKADUNKNOWNKEY 19270408 /* ticket contained unknown key version number */ +#define RXKADEXPIRED 19270409 /* authentication expired */ +#define RXKADSEALEDINCON 19270410 /* sealed data inconsistent */ +#define RXKADDATALEN 19270411 /* user data too long */ +#define RXKADILLEGALLEVEL 19270412 /* caller not authorised to use encrypted conns */ + #endif /* _LINUX_RXRPC_PACKET_H */ diff --git a/net/Kconfig b/net/Kconfig index ae1817dc51b8..2fc8e77b1e62 100644 --- a/net/Kconfig +++ b/net/Kconfig @@ -212,6 +212,7 @@ endmenu source "net/ax25/Kconfig" source "net/irda/Kconfig" source "net/bluetooth/Kconfig" +source "net/rxrpc/Kconfig" config FIB_RULES bool diff --git a/net/Makefile b/net/Makefile index 29bbe19d87f1..6b74d4118c5b 100644 --- a/net/Makefile +++ b/net/Makefile @@ -38,6 +38,7 @@ obj-$(CONFIG_IRDA) += irda/ obj-$(CONFIG_BT) += bluetooth/ obj-$(CONFIG_SUNRPC) += sunrpc/ obj-$(CONFIG_RXRPC) += rxrpc/ +obj-$(CONFIG_AF_RXRPC) += rxrpc/ obj-$(CONFIG_ATM) += atm/ obj-$(CONFIG_DECNET) += decnet/ obj-$(CONFIG_ECONET) += econet/ diff --git a/net/core/sock.c b/net/core/sock.c index 043bdc05d211..22183c2ef284 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -154,7 +154,8 @@ static const char *af_family_key_strings[AF_MAX+1] = { "sk_lock-21" , "sk_lock-AF_SNA" , "sk_lock-AF_IRDA" , "sk_lock-AF_PPPOX" , "sk_lock-AF_WANPIPE" , "sk_lock-AF_LLC" , "sk_lock-27" , "sk_lock-28" , "sk_lock-29" , - "sk_lock-AF_TIPC" , "sk_lock-AF_BLUETOOTH", "sk_lock-AF_MAX" + "sk_lock-AF_TIPC" , "sk_lock-AF_BLUETOOTH", "sk_lock-IUCV" , + "sk_lock-AF_RXRPC" , "sk_lock-AF_MAX" }; static const char *af_family_slock_key_strings[AF_MAX+1] = { "slock-AF_UNSPEC", "slock-AF_UNIX" , "slock-AF_INET" , @@ -167,7 +168,8 @@ static const char *af_family_slock_key_strings[AF_MAX+1] = { "slock-21" , "slock-AF_SNA" , "slock-AF_IRDA" , "slock-AF_PPPOX" , "slock-AF_WANPIPE" , "slock-AF_LLC" , "slock-27" , "slock-28" , "slock-29" , - "slock-AF_TIPC" , "slock-AF_BLUETOOTH", "slock-AF_MAX" + "slock-AF_TIPC" , "slock-AF_BLUETOOTH", "slock-AF_IUCV" , + "slock-AF_RXRPC" , "slock-AF_MAX" }; #endif diff --git a/net/rxrpc/Kconfig b/net/rxrpc/Kconfig new file mode 100644 index 000000000000..d72380e304ae --- /dev/null +++ b/net/rxrpc/Kconfig @@ -0,0 +1,37 @@ +# +# RxRPC session sockets +# + +config AF_RXRPC + tristate "RxRPC session sockets" + depends on EXPERIMENTAL + help + Say Y or M here to include support for RxRPC session sockets (just + the transport part, not the presentation part: (un)marshalling is + left to the application). + + These are used for AFS kernel filesystem and userspace utilities. + + This module at the moment only supports client operations and is + currently incomplete. + + See Documentation/networking/rxrpc.txt. + + +config AF_RXRPC_DEBUG + bool "RxRPC dynamic debugging" + depends on AF_RXRPC + help + Say Y here to make runtime controllable debugging messages appear. + + See Documentation/networking/rxrpc.txt. + + +config RXKAD + tristate "RxRPC Kerberos security" + depends on AF_RXRPC && KEYS + help + Provide kerberos 4 and AFS kaserver security handling for AF_RXRPC + through the use of the key retention service. + + See Documentation/networking/rxrpc.txt. diff --git a/net/rxrpc/Makefile b/net/rxrpc/Makefile index 6efcb6f162a0..07bf82ffec6a 100644 --- a/net/rxrpc/Makefile +++ b/net/rxrpc/Makefile @@ -4,6 +4,35 @@ #CFLAGS += -finstrument-functions +af-rxrpc-objs := \ + af_rxrpc.o \ + ar-accept.o \ + ar-ack.o \ + ar-call.o \ + ar-connection.o \ + ar-connevent.o \ + ar-error.o \ + ar-input.o \ + ar-key.o \ + ar-local.o \ + ar-output.o \ + ar-peer.o \ + ar-recvmsg.o \ + ar-security.o \ + ar-skbuff.o \ + ar-transport.o + +ifeq ($(CONFIG_PROC_FS),y) +af-rxrpc-objs += ar-proc.o +endif + +obj-$(CONFIG_AF_RXRPC) += af-rxrpc.o + +obj-$(CONFIG_RXKAD) += rxkad.o + +# +# obsolete RxRPC interface, still used by fs/afs/ +# rxrpc-objs := \ call.o \ connection.o \ @@ -22,4 +51,4 @@ ifeq ($(CONFIG_SYSCTL),y) rxrpc-objs += sysctl.o endif -obj-$(CONFIG_RXRPC) := rxrpc.o +obj-$(CONFIG_RXRPC) += rxrpc.o diff --git a/net/rxrpc/af_rxrpc.c b/net/rxrpc/af_rxrpc.c new file mode 100644 index 000000000000..bfa8822e2286 --- /dev/null +++ b/net/rxrpc/af_rxrpc.c @@ -0,0 +1,754 @@ +/* AF_RXRPC implementation + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "ar-internal.h" + +MODULE_DESCRIPTION("RxRPC network protocol"); +MODULE_AUTHOR("Red Hat, Inc."); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_NETPROTO(PF_RXRPC); + +unsigned rxrpc_debug; // = RXRPC_DEBUG_KPROTO; +module_param_named(debug, rxrpc_debug, uint, S_IWUSR | S_IRUGO); +MODULE_PARM_DESC(rxrpc_debug, "RxRPC debugging mask"); + +static int sysctl_rxrpc_max_qlen __read_mostly = 10; + +static struct proto rxrpc_proto; +static const struct proto_ops rxrpc_rpc_ops; + +/* local epoch for detecting local-end reset */ +__be32 rxrpc_epoch; + +/* current debugging ID */ +atomic_t rxrpc_debug_id; + +/* count of skbs currently in use */ +atomic_t rxrpc_n_skbs; + +static void rxrpc_sock_destructor(struct sock *); + +/* + * see if an RxRPC socket is currently writable + */ +static inline int rxrpc_writable(struct sock *sk) +{ + return atomic_read(&sk->sk_wmem_alloc) < (size_t) sk->sk_sndbuf; +} + +/* + * wait for write bufferage to become available + */ +static void rxrpc_write_space(struct sock *sk) +{ + _enter("%p", sk); + read_lock(&sk->sk_callback_lock); + if (rxrpc_writable(sk)) { + if (sk->sk_sleep && waitqueue_active(sk->sk_sleep)) + wake_up_interruptible(sk->sk_sleep); + sk_wake_async(sk, 2, POLL_OUT); + } + read_unlock(&sk->sk_callback_lock); +} + +/* + * validate an RxRPC address + */ +static int rxrpc_validate_address(struct rxrpc_sock *rx, + struct sockaddr_rxrpc *srx, + int len) +{ + if (len < sizeof(struct sockaddr_rxrpc)) + return -EINVAL; + + if (srx->srx_family != AF_RXRPC) + return -EAFNOSUPPORT; + + if (srx->transport_type != SOCK_DGRAM) + return -ESOCKTNOSUPPORT; + + len -= offsetof(struct sockaddr_rxrpc, transport); + if (srx->transport_len < sizeof(sa_family_t) || + srx->transport_len > len) + return -EINVAL; + + if (srx->transport.family != rx->proto) + return -EAFNOSUPPORT; + + switch (srx->transport.family) { + case AF_INET: + _debug("INET: %x @ %u.%u.%u.%u", + ntohs(srx->transport.sin.sin_port), + NIPQUAD(srx->transport.sin.sin_addr)); + if (srx->transport_len > 8) + memset((void *)&srx->transport + 8, 0, + srx->transport_len - 8); + break; + + case AF_INET6: + default: + return -EAFNOSUPPORT; + } + + return 0; +} + +/* + * bind a local address to an RxRPC socket + */ +static int rxrpc_bind(struct socket *sock, struct sockaddr *saddr, int len) +{ + struct sockaddr_rxrpc *srx = (struct sockaddr_rxrpc *) saddr; + struct sock *sk = sock->sk; + struct rxrpc_local *local; + struct rxrpc_sock *rx = rxrpc_sk(sk), *prx; + __be16 service_id; + int ret; + + _enter("%p,%p,%d", rx, saddr, len); + + ret = rxrpc_validate_address(rx, srx, len); + if (ret < 0) + goto error; + + lock_sock(&rx->sk); + + if (rx->sk.sk_state != RXRPC_UNCONNECTED) { + ret = -EINVAL; + goto error_unlock; + } + + memcpy(&rx->srx, srx, sizeof(rx->srx)); + + /* find a local transport endpoint if we don't have one already */ + local = rxrpc_lookup_local(&rx->srx); + if (IS_ERR(local)) { + ret = PTR_ERR(local); + goto error_unlock; + } + + rx->local = local; + if (srx->srx_service) { + service_id = htons(srx->srx_service); + write_lock_bh(&local->services_lock); + list_for_each_entry(prx, &local->services, listen_link) { + if (prx->service_id == service_id) + goto service_in_use; + } + + rx->service_id = service_id; + list_add_tail(&rx->listen_link, &local->services); + write_unlock_bh(&local->services_lock); + + rx->sk.sk_state = RXRPC_SERVER_BOUND; + } else { + rx->sk.sk_state = RXRPC_CLIENT_BOUND; + } + + release_sock(&rx->sk); + _leave(" = 0"); + return 0; + +service_in_use: + ret = -EADDRINUSE; + write_unlock_bh(&local->services_lock); +error_unlock: + release_sock(&rx->sk); +error: + _leave(" = %d", ret); + return ret; +} + +/* + * set the number of pending calls permitted on a listening socket + */ +static int rxrpc_listen(struct socket *sock, int backlog) +{ + struct sock *sk = sock->sk; + struct rxrpc_sock *rx = rxrpc_sk(sk); + int ret; + + _enter("%p,%d", rx, backlog); + + lock_sock(&rx->sk); + + switch (rx->sk.sk_state) { + case RXRPC_UNCONNECTED: + ret = -EADDRNOTAVAIL; + break; + case RXRPC_CLIENT_BOUND: + case RXRPC_CLIENT_CONNECTED: + default: + ret = -EBUSY; + break; + case RXRPC_SERVER_BOUND: + ASSERT(rx->local != NULL); + sk->sk_max_ack_backlog = backlog; + rx->sk.sk_state = RXRPC_SERVER_LISTENING; + ret = 0; + break; + } + + release_sock(&rx->sk); + _leave(" = %d", ret); + return ret; +} + +/* + * find a transport by address + */ +static struct rxrpc_transport *rxrpc_name_to_transport(struct socket *sock, + struct sockaddr *addr, + int addr_len, int flags) +{ + struct sockaddr_rxrpc *srx = (struct sockaddr_rxrpc *) addr; + struct rxrpc_transport *trans; + struct rxrpc_sock *rx = rxrpc_sk(sock->sk); + struct rxrpc_peer *peer; + + _enter("%p,%p,%d,%d", rx, addr, addr_len, flags); + + ASSERT(rx->local != NULL); + ASSERT(rx->sk.sk_state > RXRPC_UNCONNECTED); + + if (rx->srx.transport_type != srx->transport_type) + return ERR_PTR(-ESOCKTNOSUPPORT); + if (rx->srx.transport.family != srx->transport.family) + return ERR_PTR(-EAFNOSUPPORT); + + /* find a remote transport endpoint from the local one */ + peer = rxrpc_get_peer(srx, GFP_KERNEL); + if (IS_ERR(peer)) + return ERR_PTR(PTR_ERR(peer)); + + /* find a transport */ + trans = rxrpc_get_transport(rx->local, peer, GFP_KERNEL); + rxrpc_put_peer(peer); + _leave(" = %p", trans); + return trans; +} + +/* + * connect an RxRPC socket + * - this just targets it at a specific destination; no actual connection + * negotiation takes place + */ +static int rxrpc_connect(struct socket *sock, struct sockaddr *addr, + int addr_len, int flags) +{ + struct sockaddr_rxrpc *srx = (struct sockaddr_rxrpc *) addr; + struct sock *sk = sock->sk; + struct rxrpc_transport *trans; + struct rxrpc_local *local; + struct rxrpc_sock *rx = rxrpc_sk(sk); + int ret; + + _enter("%p,%p,%d,%d", rx, addr, addr_len, flags); + + ret = rxrpc_validate_address(rx, srx, addr_len); + if (ret < 0) { + _leave(" = %d [bad addr]", ret); + return ret; + } + + lock_sock(&rx->sk); + + switch (rx->sk.sk_state) { + case RXRPC_UNCONNECTED: + /* find a local transport endpoint if we don't have one already */ + ASSERTCMP(rx->local, ==, NULL); + rx->srx.srx_family = AF_RXRPC; + rx->srx.srx_service = 0; + rx->srx.transport_type = srx->transport_type; + rx->srx.transport_len = sizeof(sa_family_t); + rx->srx.transport.family = srx->transport.family; + local = rxrpc_lookup_local(&rx->srx); + if (IS_ERR(local)) { + release_sock(&rx->sk); + return PTR_ERR(local); + } + rx->local = local; + rx->sk.sk_state = RXRPC_CLIENT_BOUND; + case RXRPC_CLIENT_BOUND: + break; + case RXRPC_CLIENT_CONNECTED: + release_sock(&rx->sk); + return -EISCONN; + default: + release_sock(&rx->sk); + return -EBUSY; /* server sockets can't connect as well */ + } + + trans = rxrpc_name_to_transport(sock, addr, addr_len, flags); + if (IS_ERR(trans)) { + release_sock(&rx->sk); + _leave(" = %ld", PTR_ERR(trans)); + return PTR_ERR(trans); + } + + rx->trans = trans; + rx->service_id = htons(srx->srx_service); + rx->sk.sk_state = RXRPC_CLIENT_CONNECTED; + + release_sock(&rx->sk); + return 0; +} + +/* + * send a message through an RxRPC socket + * - in a client this does a number of things: + * - finds/sets up a connection for the security specified (if any) + * - initiates a call (ID in control data) + * - ends the request phase of a call (if MSG_MORE is not set) + * - sends a call data packet + * - may send an abort (abort code in control data) + */ +static int rxrpc_sendmsg(struct kiocb *iocb, struct socket *sock, + struct msghdr *m, size_t len) +{ + struct rxrpc_transport *trans; + struct rxrpc_sock *rx = rxrpc_sk(sock->sk); + int ret; + + _enter(",{%d},,%zu", rx->sk.sk_state, len); + + if (m->msg_flags & MSG_OOB) + return -EOPNOTSUPP; + + if (m->msg_name) { + ret = rxrpc_validate_address(rx, m->msg_name, m->msg_namelen); + if (ret < 0) { + _leave(" = %d [bad addr]", ret); + return ret; + } + } + + trans = NULL; + lock_sock(&rx->sk); + + if (m->msg_name) { + ret = -EISCONN; + trans = rxrpc_name_to_transport(sock, m->msg_name, + m->msg_namelen, 0); + if (IS_ERR(trans)) { + ret = PTR_ERR(trans); + trans = NULL; + goto out; + } + } else { + trans = rx->trans; + if (trans) + atomic_inc(&trans->usage); + } + + switch (rx->sk.sk_state) { + case RXRPC_SERVER_LISTENING: + if (!m->msg_name) { + ret = rxrpc_server_sendmsg(iocb, rx, m, len); + break; + } + case RXRPC_SERVER_BOUND: + case RXRPC_CLIENT_BOUND: + if (!m->msg_name) { + ret = -ENOTCONN; + break; + } + case RXRPC_CLIENT_CONNECTED: + ret = rxrpc_client_sendmsg(iocb, rx, trans, m, len); + break; + default: + ret = -ENOTCONN; + break; + } + +out: + release_sock(&rx->sk); + if (trans) + rxrpc_put_transport(trans); + _leave(" = %d", ret); + return ret; +} + +/* + * set RxRPC socket options + */ +static int rxrpc_setsockopt(struct socket *sock, int level, int optname, + char __user *optval, int optlen) +{ + struct rxrpc_sock *rx = rxrpc_sk(sock->sk); + unsigned min_sec_level; + int ret; + + _enter(",%d,%d,,%d", level, optname, optlen); + + lock_sock(&rx->sk); + ret = -EOPNOTSUPP; + + if (level == SOL_RXRPC) { + switch (optname) { + case RXRPC_EXCLUSIVE_CONNECTION: + ret = -EINVAL; + if (optlen != 0) + goto error; + ret = -EISCONN; + if (rx->sk.sk_state != RXRPC_UNCONNECTED) + goto error; + set_bit(RXRPC_SOCK_EXCLUSIVE_CONN, &rx->flags); + goto success; + + case RXRPC_SECURITY_KEY: + ret = -EINVAL; + if (rx->key) + goto error; + ret = -EISCONN; + if (rx->sk.sk_state != RXRPC_UNCONNECTED) + goto error; + ret = rxrpc_request_key(rx, optval, optlen); + goto error; + + case RXRPC_SECURITY_KEYRING: + ret = -EINVAL; + if (rx->key) + goto error; + ret = -EISCONN; + if (rx->sk.sk_state != RXRPC_UNCONNECTED) + goto error; + ret = rxrpc_server_keyring(rx, optval, optlen); + goto error; + + case RXRPC_MIN_SECURITY_LEVEL: + ret = -EINVAL; + if (optlen != sizeof(unsigned)) + goto error; + ret = -EISCONN; + if (rx->sk.sk_state != RXRPC_UNCONNECTED) + goto error; + ret = get_user(min_sec_level, + (unsigned __user *) optval); + if (ret < 0) + goto error; + ret = -EINVAL; + if (min_sec_level > RXRPC_SECURITY_MAX) + goto error; + rx->min_sec_level = min_sec_level; + goto success; + + default: + break; + } + } + +success: + ret = 0; +error: + release_sock(&rx->sk); + return ret; +} + +/* + * permit an RxRPC socket to be polled + */ +static unsigned int rxrpc_poll(struct file *file, struct socket *sock, + poll_table *wait) +{ + unsigned int mask; + struct sock *sk = sock->sk; + + poll_wait(file, sk->sk_sleep, wait); + mask = 0; + + /* the socket is readable if there are any messages waiting on the Rx + * queue */ + if (!skb_queue_empty(&sk->sk_receive_queue)) + mask |= POLLIN | POLLRDNORM; + + /* the socket is writable if there is space to add new data to the + * socket; there is no guarantee that any particular call in progress + * on the socket may have space in the Tx ACK window */ + if (rxrpc_writable(sk)) + mask |= POLLOUT | POLLWRNORM; + + return mask; +} + +/* + * create an RxRPC socket + */ +static int rxrpc_create(struct socket *sock, int protocol) +{ + struct rxrpc_sock *rx; + struct sock *sk; + + _enter("%p,%d", sock, protocol); + + /* we support transport protocol UDP only */ + if (protocol != PF_INET) + return -EPROTONOSUPPORT; + + if (sock->type != SOCK_DGRAM) + return -ESOCKTNOSUPPORT; + + sock->ops = &rxrpc_rpc_ops; + sock->state = SS_UNCONNECTED; + + sk = sk_alloc(PF_RXRPC, GFP_KERNEL, &rxrpc_proto, 1); + if (!sk) + return -ENOMEM; + + sock_init_data(sock, sk); + sk->sk_state = RXRPC_UNCONNECTED; + sk->sk_write_space = rxrpc_write_space; + sk->sk_max_ack_backlog = sysctl_rxrpc_max_qlen; + sk->sk_destruct = rxrpc_sock_destructor; + + rx = rxrpc_sk(sk); + rx->proto = protocol; + rx->calls = RB_ROOT; + + INIT_LIST_HEAD(&rx->listen_link); + INIT_LIST_HEAD(&rx->secureq); + INIT_LIST_HEAD(&rx->acceptq); + rwlock_init(&rx->call_lock); + memset(&rx->srx, 0, sizeof(rx->srx)); + + _leave(" = 0 [%p]", rx); + return 0; +} + +/* + * RxRPC socket destructor + */ +static void rxrpc_sock_destructor(struct sock *sk) +{ + _enter("%p", sk); + + rxrpc_purge_queue(&sk->sk_receive_queue); + + BUG_TRAP(!atomic_read(&sk->sk_wmem_alloc)); + BUG_TRAP(sk_unhashed(sk)); + BUG_TRAP(!sk->sk_socket); + + if (!sock_flag(sk, SOCK_DEAD)) { + printk("Attempt to release alive rxrpc socket: %p\n", sk); + return; + } +} + +/* + * release an RxRPC socket + */ +static int rxrpc_release_sock(struct sock *sk) +{ + struct rxrpc_sock *rx = rxrpc_sk(sk); + + _enter("%p{%d,%d}", sk, sk->sk_state, atomic_read(&sk->sk_refcnt)); + + /* declare the socket closed for business */ + sock_orphan(sk); + sk->sk_shutdown = SHUTDOWN_MASK; + + spin_lock_bh(&sk->sk_receive_queue.lock); + sk->sk_state = RXRPC_CLOSE; + spin_unlock_bh(&sk->sk_receive_queue.lock); + + ASSERTCMP(rx->listen_link.next, !=, LIST_POISON1); + + if (!list_empty(&rx->listen_link)) { + write_lock_bh(&rx->local->services_lock); + list_del(&rx->listen_link); + write_unlock_bh(&rx->local->services_lock); + } + + /* try to flush out this socket */ + rxrpc_release_calls_on_socket(rx); + flush_scheduled_work(); + rxrpc_purge_queue(&sk->sk_receive_queue); + + if (rx->conn) { + rxrpc_put_connection(rx->conn); + rx->conn = NULL; + } + + if (rx->bundle) { + rxrpc_put_bundle(rx->trans, rx->bundle); + rx->bundle = NULL; + } + if (rx->trans) { + rxrpc_put_transport(rx->trans); + rx->trans = NULL; + } + if (rx->local) { + rxrpc_put_local(rx->local); + rx->local = NULL; + } + + key_put(rx->key); + rx->key = NULL; + key_put(rx->securities); + rx->securities = NULL; + sock_put(sk); + + _leave(" = 0"); + return 0; +} + +/* + * release an RxRPC BSD socket on close() or equivalent + */ +static int rxrpc_release(struct socket *sock) +{ + struct sock *sk = sock->sk; + + _enter("%p{%p}", sock, sk); + + if (!sk) + return 0; + + sock->sk = NULL; + + return rxrpc_release_sock(sk); +} + +/* + * RxRPC network protocol + */ +static const struct proto_ops rxrpc_rpc_ops = { + .family = PF_UNIX, + .owner = THIS_MODULE, + .release = rxrpc_release, + .bind = rxrpc_bind, + .connect = rxrpc_connect, + .socketpair = sock_no_socketpair, + .accept = sock_no_accept, + .getname = sock_no_getname, + .poll = rxrpc_poll, + .ioctl = sock_no_ioctl, + .listen = rxrpc_listen, + .shutdown = sock_no_shutdown, + .setsockopt = rxrpc_setsockopt, + .getsockopt = sock_no_getsockopt, + .sendmsg = rxrpc_sendmsg, + .recvmsg = rxrpc_recvmsg, + .mmap = sock_no_mmap, + .sendpage = sock_no_sendpage, +}; + +static struct proto rxrpc_proto = { + .name = "RXRPC", + .owner = THIS_MODULE, + .obj_size = sizeof(struct rxrpc_sock), + .max_header = sizeof(struct rxrpc_header), +}; + +static struct net_proto_family rxrpc_family_ops = { + .family = PF_RXRPC, + .create = rxrpc_create, + .owner = THIS_MODULE, +}; + +/* + * initialise and register the RxRPC protocol + */ +static int __init af_rxrpc_init(void) +{ + struct sk_buff *dummy_skb; + int ret = -1; + + BUILD_BUG_ON(sizeof(struct rxrpc_skb_priv) > sizeof(dummy_skb->cb)); + + rxrpc_epoch = htonl(xtime.tv_sec); + + rxrpc_call_jar = kmem_cache_create( + "rxrpc_call_jar", sizeof(struct rxrpc_call), 0, + SLAB_HWCACHE_ALIGN, NULL, NULL); + if (!rxrpc_call_jar) { + printk(KERN_NOTICE "RxRPC: Failed to allocate call jar\n"); + ret = -ENOMEM; + goto error_call_jar; + } + + ret = proto_register(&rxrpc_proto, 1); + if (ret < 0) { + printk(KERN_CRIT "RxRPC: Cannot register protocol\n"); + goto error_proto; + } + + ret = sock_register(&rxrpc_family_ops); + if (ret < 0) { + printk(KERN_CRIT "RxRPC: Cannot register socket family\n"); + goto error_sock; + } + + ret = register_key_type(&key_type_rxrpc); + if (ret < 0) { + printk(KERN_CRIT "RxRPC: Cannot register client key type\n"); + goto error_key_type; + } + + ret = register_key_type(&key_type_rxrpc_s); + if (ret < 0) { + printk(KERN_CRIT "RxRPC: Cannot register server key type\n"); + goto error_key_type_s; + } + +#ifdef CONFIG_PROC_FS + proc_net_fops_create("rxrpc_calls", 0, &rxrpc_call_seq_fops); + proc_net_fops_create("rxrpc_conns", 0, &rxrpc_connection_seq_fops); +#endif + return 0; + +error_key_type_s: + unregister_key_type(&key_type_rxrpc); +error_key_type: + sock_unregister(PF_RXRPC); +error_sock: + proto_unregister(&rxrpc_proto); +error_proto: + kmem_cache_destroy(rxrpc_call_jar); +error_call_jar: + return ret; +} + +/* + * unregister the RxRPC protocol + */ +static void __exit af_rxrpc_exit(void) +{ + _enter(""); + unregister_key_type(&key_type_rxrpc_s); + unregister_key_type(&key_type_rxrpc); + sock_unregister(PF_RXRPC); + proto_unregister(&rxrpc_proto); + rxrpc_destroy_all_calls(); + rxrpc_destroy_all_connections(); + rxrpc_destroy_all_transports(); + rxrpc_destroy_all_peers(); + rxrpc_destroy_all_locals(); + + ASSERTCMP(atomic_read(&rxrpc_n_skbs), ==, 0); + + _debug("flush scheduled work"); + flush_scheduled_work(); + proc_net_remove("rxrpc_conns"); + proc_net_remove("rxrpc_calls"); + kmem_cache_destroy(rxrpc_call_jar); + _leave(""); +} + +module_init(af_rxrpc_init); +module_exit(af_rxrpc_exit); diff --git a/net/rxrpc/ar-accept.c b/net/rxrpc/ar-accept.c new file mode 100644 index 000000000000..e7af780cd6f9 --- /dev/null +++ b/net/rxrpc/ar-accept.c @@ -0,0 +1,399 @@ +/* incoming call handling + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ar-internal.h" + +/* + * generate a connection-level abort + */ +static int rxrpc_busy(struct rxrpc_local *local, struct sockaddr_rxrpc *srx, + struct rxrpc_header *hdr) +{ + struct msghdr msg; + struct kvec iov[1]; + size_t len; + int ret; + + _enter("%d,,", local->debug_id); + + msg.msg_name = &srx->transport.sin; + msg.msg_namelen = sizeof(srx->transport.sin); + msg.msg_control = NULL; + msg.msg_controllen = 0; + msg.msg_flags = 0; + + hdr->seq = 0; + hdr->type = RXRPC_PACKET_TYPE_BUSY; + hdr->flags = 0; + hdr->userStatus = 0; + hdr->_rsvd = 0; + + iov[0].iov_base = hdr; + iov[0].iov_len = sizeof(*hdr); + + len = iov[0].iov_len; + + hdr->serial = htonl(1); + _proto("Tx BUSY %%%u", ntohl(hdr->serial)); + + ret = kernel_sendmsg(local->socket, &msg, iov, 1, len); + if (ret < 0) { + _leave(" = -EAGAIN [sendmsg failed: %d]", ret); + return -EAGAIN; + } + + _leave(" = 0"); + return 0; +} + +/* + * accept an incoming call that needs peer, transport and/or connection setting + * up + */ +static int rxrpc_accept_incoming_call(struct rxrpc_local *local, + struct rxrpc_sock *rx, + struct sk_buff *skb, + struct sockaddr_rxrpc *srx) +{ + struct rxrpc_connection *conn; + struct rxrpc_transport *trans; + struct rxrpc_skb_priv *sp, *nsp; + struct rxrpc_peer *peer; + struct rxrpc_call *call; + struct sk_buff *notification; + int ret; + + _enter(""); + + sp = rxrpc_skb(skb); + + /* get a notification message to send to the server app */ + notification = alloc_skb(0, GFP_NOFS); + rxrpc_new_skb(notification); + notification->mark = RXRPC_SKB_MARK_NEW_CALL; + + peer = rxrpc_get_peer(srx, GFP_NOIO); + if (IS_ERR(peer)) { + _debug("no peer"); + ret = -EBUSY; + goto error; + } + + trans = rxrpc_get_transport(local, peer, GFP_NOIO); + rxrpc_put_peer(peer); + if (!trans) { + _debug("no trans"); + ret = -EBUSY; + goto error; + } + + conn = rxrpc_incoming_connection(trans, &sp->hdr, GFP_NOIO); + rxrpc_put_transport(trans); + if (IS_ERR(conn)) { + _debug("no conn"); + ret = PTR_ERR(conn); + goto error; + } + + call = rxrpc_incoming_call(rx, conn, &sp->hdr, GFP_NOIO); + rxrpc_put_connection(conn); + if (IS_ERR(call)) { + _debug("no call"); + ret = PTR_ERR(call); + goto error; + } + + /* attach the call to the socket */ + read_lock_bh(&local->services_lock); + if (rx->sk.sk_state == RXRPC_CLOSE) + goto invalid_service; + + write_lock(&rx->call_lock); + if (!test_and_set_bit(RXRPC_CALL_INIT_ACCEPT, &call->flags)) { + rxrpc_get_call(call); + + spin_lock(&call->conn->state_lock); + if (sp->hdr.securityIndex > 0 && + call->conn->state == RXRPC_CONN_SERVER_UNSECURED) { + _debug("await conn sec"); + list_add_tail(&call->accept_link, &rx->secureq); + call->conn->state = RXRPC_CONN_SERVER_CHALLENGING; + atomic_inc(&call->conn->usage); + set_bit(RXRPC_CONN_CHALLENGE, &call->conn->events); + schedule_work(&call->conn->processor); + } else { + _debug("conn ready"); + call->state = RXRPC_CALL_SERVER_ACCEPTING; + list_add_tail(&call->accept_link, &rx->acceptq); + rxrpc_get_call(call); + nsp = rxrpc_skb(notification); + nsp->call = call; + + ASSERTCMP(atomic_read(&call->usage), >=, 3); + + _debug("notify"); + spin_lock(&call->lock); + ret = rxrpc_queue_rcv_skb(call, notification, true, + false); + spin_unlock(&call->lock); + notification = NULL; + if (ret < 0) + BUG(); + } + spin_unlock(&call->conn->state_lock); + + _debug("queued"); + } + write_unlock(&rx->call_lock); + + _debug("process"); + rxrpc_fast_process_packet(call, skb); + + _debug("done"); + read_unlock_bh(&local->services_lock); + rxrpc_free_skb(notification); + rxrpc_put_call(call); + _leave(" = 0"); + return 0; + +invalid_service: + _debug("invalid"); + read_unlock_bh(&local->services_lock); + + read_lock_bh(&call->state_lock); + if (!test_bit(RXRPC_CALL_RELEASE, &call->flags) && + !test_and_set_bit(RXRPC_CALL_RELEASE, &call->events)) { + rxrpc_get_call(call); + schedule_work(&call->processor); + } + read_unlock_bh(&call->state_lock); + rxrpc_put_call(call); + ret = -ECONNREFUSED; +error: + rxrpc_free_skb(notification); + _leave(" = %d", ret); + return ret; +} + +/* + * accept incoming calls that need peer, transport and/or connection setting up + * - the packets we get are all incoming client DATA packets that have seq == 1 + */ +void rxrpc_accept_incoming_calls(struct work_struct *work) +{ + struct rxrpc_local *local = + container_of(work, struct rxrpc_local, acceptor); + struct rxrpc_skb_priv *sp; + struct sockaddr_rxrpc srx; + struct rxrpc_sock *rx; + struct sk_buff *skb; + __be16 service_id; + int ret; + + _enter("%d", local->debug_id); + + read_lock_bh(&rxrpc_local_lock); + if (atomic_read(&local->usage) > 0) + rxrpc_get_local(local); + else + local = NULL; + read_unlock_bh(&rxrpc_local_lock); + if (!local) { + _leave(" [local dead]"); + return; + } + +process_next_packet: + skb = skb_dequeue(&local->accept_queue); + if (!skb) { + rxrpc_put_local(local); + _leave("\n"); + return; + } + + _net("incoming call skb %p", skb); + + sp = rxrpc_skb(skb); + + /* determine the remote address */ + memset(&srx, 0, sizeof(srx)); + srx.srx_family = AF_RXRPC; + srx.transport.family = local->srx.transport.family; + srx.transport_type = local->srx.transport_type; + switch (srx.transport.family) { + case AF_INET: + srx.transport_len = sizeof(struct sockaddr_in); + srx.transport.sin.sin_port = udp_hdr(skb)->source; + srx.transport.sin.sin_addr.s_addr = ip_hdr(skb)->saddr; + break; + default: + goto busy; + } + + /* get the socket providing the service */ + service_id = sp->hdr.serviceId; + read_lock_bh(&local->services_lock); + list_for_each_entry(rx, &local->services, listen_link) { + if (rx->service_id == service_id && + rx->sk.sk_state != RXRPC_CLOSE) + goto found_service; + } + read_unlock_bh(&local->services_lock); + goto invalid_service; + +found_service: + _debug("found service %hd", ntohs(rx->service_id)); + if (sk_acceptq_is_full(&rx->sk)) + goto backlog_full; + sk_acceptq_added(&rx->sk); + sock_hold(&rx->sk); + read_unlock_bh(&local->services_lock); + + ret = rxrpc_accept_incoming_call(local, rx, skb, &srx); + if (ret < 0) + sk_acceptq_removed(&rx->sk); + sock_put(&rx->sk); + switch (ret) { + case -ECONNRESET: /* old calls are ignored */ + case -ECONNABORTED: /* aborted calls are reaborted or ignored */ + case 0: + goto process_next_packet; + case -ECONNREFUSED: + goto invalid_service; + case -EBUSY: + goto busy; + case -EKEYREJECTED: + goto security_mismatch; + default: + BUG(); + } + +backlog_full: + read_unlock_bh(&local->services_lock); +busy: + rxrpc_busy(local, &srx, &sp->hdr); + rxrpc_free_skb(skb); + goto process_next_packet; + +invalid_service: + skb->priority = RX_INVALID_OPERATION; + rxrpc_reject_packet(local, skb); + goto process_next_packet; + + /* can't change connection security type mid-flow */ +security_mismatch: + skb->priority = RX_PROTOCOL_ERROR; + rxrpc_reject_packet(local, skb); + goto process_next_packet; +} + +/* + * handle acceptance of a call by userspace + * - assign the user call ID to the call at the front of the queue + */ +int rxrpc_accept_call(struct rxrpc_sock *rx, unsigned long user_call_ID) +{ + struct rxrpc_call *call; + struct rb_node *parent, **pp; + int ret; + + _enter(",%lx", user_call_ID); + + ASSERT(!irqs_disabled()); + + write_lock(&rx->call_lock); + + ret = -ENODATA; + if (list_empty(&rx->acceptq)) + goto out; + + /* check the user ID isn't already in use */ + ret = -EBADSLT; + pp = &rx->calls.rb_node; + parent = NULL; + while (*pp) { + parent = *pp; + call = rb_entry(parent, struct rxrpc_call, sock_node); + + if (user_call_ID < call->user_call_ID) + pp = &(*pp)->rb_left; + else if (user_call_ID > call->user_call_ID) + pp = &(*pp)->rb_right; + else + goto out; + } + + /* dequeue the first call and check it's still valid */ + call = list_entry(rx->acceptq.next, struct rxrpc_call, accept_link); + list_del_init(&call->accept_link); + sk_acceptq_removed(&rx->sk); + + write_lock_bh(&call->state_lock); + switch (call->state) { + case RXRPC_CALL_SERVER_ACCEPTING: + call->state = RXRPC_CALL_SERVER_RECV_REQUEST; + break; + case RXRPC_CALL_REMOTELY_ABORTED: + case RXRPC_CALL_LOCALLY_ABORTED: + ret = -ECONNABORTED; + goto out_release; + case RXRPC_CALL_NETWORK_ERROR: + ret = call->conn->error; + goto out_release; + case RXRPC_CALL_DEAD: + ret = -ETIME; + goto out_discard; + default: + BUG(); + } + + /* formalise the acceptance */ + call->user_call_ID = user_call_ID; + rb_link_node(&call->sock_node, parent, pp); + rb_insert_color(&call->sock_node, &rx->calls); + if (test_and_set_bit(RXRPC_CALL_HAS_USERID, &call->flags)) + BUG(); + if (test_and_set_bit(RXRPC_CALL_ACCEPTED, &call->events)) + BUG(); + schedule_work(&call->processor); + + write_unlock_bh(&call->state_lock); + write_unlock(&rx->call_lock); + _leave(" = 0"); + return 0; + + /* if the call is already dying or dead, then we leave the socket's ref + * on it to be released by rxrpc_dead_call_expired() as induced by + * rxrpc_release_call() */ +out_release: + _debug("release %p", call); + if (!test_bit(RXRPC_CALL_RELEASED, &call->flags) && + !test_and_set_bit(RXRPC_CALL_RELEASE, &call->events)) + schedule_work(&call->processor); +out_discard: + write_unlock_bh(&call->state_lock); + _debug("discard %p", call); +out: + write_unlock(&rx->call_lock); + _leave(" = %d", ret); + return ret; +} diff --git a/net/rxrpc/ar-ack.c b/net/rxrpc/ar-ack.c new file mode 100644 index 000000000000..8f7764eca96c --- /dev/null +++ b/net/rxrpc/ar-ack.c @@ -0,0 +1,1250 @@ +/* Management of Tx window, Tx resend, ACKs and out-of-sequence reception + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "ar-internal.h" + +static unsigned rxrpc_ack_defer = 1; + +static const char *rxrpc_acks[] = { + "---", "REQ", "DUP", "OOS", "WIN", "MEM", "PNG", "PNR", "DLY", "IDL", + "-?-" +}; + +static const s8 rxrpc_ack_priority[] = { + [0] = 0, + [RXRPC_ACK_DELAY] = 1, + [RXRPC_ACK_REQUESTED] = 2, + [RXRPC_ACK_IDLE] = 3, + [RXRPC_ACK_PING_RESPONSE] = 4, + [RXRPC_ACK_DUPLICATE] = 5, + [RXRPC_ACK_OUT_OF_SEQUENCE] = 6, + [RXRPC_ACK_EXCEEDS_WINDOW] = 7, + [RXRPC_ACK_NOSPACE] = 8, +}; + +/* + * propose an ACK be sent + */ +void __rxrpc_propose_ACK(struct rxrpc_call *call, uint8_t ack_reason, + __be32 serial, bool immediate) +{ + unsigned long expiry; + s8 prior = rxrpc_ack_priority[ack_reason]; + + ASSERTCMP(prior, >, 0); + + _enter("{%d},%s,%%%x,%u", + call->debug_id, rxrpc_acks[ack_reason], ntohl(serial), + immediate); + + if (prior < rxrpc_ack_priority[call->ackr_reason]) { + if (immediate) + goto cancel_timer; + return; + } + + /* update DELAY, IDLE, REQUESTED and PING_RESPONSE ACK serial + * numbers */ + if (prior == rxrpc_ack_priority[call->ackr_reason]) { + if (prior <= 4) + call->ackr_serial = serial; + if (immediate) + goto cancel_timer; + return; + } + + call->ackr_reason = ack_reason; + call->ackr_serial = serial; + + switch (ack_reason) { + case RXRPC_ACK_DELAY: + _debug("run delay timer"); + call->ack_timer.expires = jiffies + rxrpc_ack_timeout * HZ; + add_timer(&call->ack_timer); + return; + + case RXRPC_ACK_IDLE: + if (!immediate) { + _debug("run defer timer"); + expiry = 1; + goto run_timer; + } + goto cancel_timer; + + case RXRPC_ACK_REQUESTED: + if (!rxrpc_ack_defer) + goto cancel_timer; + if (!immediate || serial == cpu_to_be32(1)) { + _debug("run defer timer"); + expiry = rxrpc_ack_defer; + goto run_timer; + } + + default: + _debug("immediate ACK"); + goto cancel_timer; + } + +run_timer: + expiry += jiffies; + if (!timer_pending(&call->ack_timer) || + time_after(call->ack_timer.expires, expiry)) + mod_timer(&call->ack_timer, expiry); + return; + +cancel_timer: + _debug("cancel timer %%%u", ntohl(serial)); + try_to_del_timer_sync(&call->ack_timer); + read_lock_bh(&call->state_lock); + if (call->state <= RXRPC_CALL_COMPLETE && + !test_and_set_bit(RXRPC_CALL_ACK, &call->events)) + schedule_work(&call->processor); + read_unlock_bh(&call->state_lock); +} + +/* + * propose an ACK be sent, locking the call structure + */ +void rxrpc_propose_ACK(struct rxrpc_call *call, uint8_t ack_reason, + __be32 serial, bool immediate) +{ + s8 prior = rxrpc_ack_priority[ack_reason]; + + if (prior > rxrpc_ack_priority[call->ackr_reason]) { + spin_lock_bh(&call->lock); + __rxrpc_propose_ACK(call, ack_reason, serial, immediate); + spin_unlock_bh(&call->lock); + } +} + +/* + * set the resend timer + */ +static void rxrpc_set_resend(struct rxrpc_call *call, u8 resend, + unsigned long resend_at) +{ + read_lock_bh(&call->state_lock); + if (call->state >= RXRPC_CALL_COMPLETE) + resend = 0; + + if (resend & 1) { + _debug("SET RESEND"); + set_bit(RXRPC_CALL_RESEND, &call->events); + } + + if (resend & 2) { + _debug("MODIFY RESEND TIMER"); + set_bit(RXRPC_CALL_RUN_RTIMER, &call->flags); + mod_timer(&call->resend_timer, resend_at); + } else { + _debug("KILL RESEND TIMER"); + del_timer_sync(&call->resend_timer); + clear_bit(RXRPC_CALL_RESEND_TIMER, &call->events); + clear_bit(RXRPC_CALL_RUN_RTIMER, &call->flags); + } + read_unlock_bh(&call->state_lock); +} + +/* + * resend packets + */ +static void rxrpc_resend(struct rxrpc_call *call) +{ + struct rxrpc_skb_priv *sp; + struct rxrpc_header *hdr; + struct sk_buff *txb; + unsigned long *p_txb, resend_at; + int loop, stop; + u8 resend; + + _enter("{%d,%d,%d,%d},", + call->acks_hard, call->acks_unacked, + atomic_read(&call->sequence), + CIRC_CNT(call->acks_head, call->acks_tail, call->acks_winsz)); + + stop = 0; + resend = 0; + resend_at = 0; + + for (loop = call->acks_tail; + loop != call->acks_head || stop; + loop = (loop + 1) & (call->acks_winsz - 1) + ) { + p_txb = call->acks_window + loop; + smp_read_barrier_depends(); + if (*p_txb & 1) + continue; + + txb = (struct sk_buff *) *p_txb; + sp = rxrpc_skb(txb); + + if (sp->need_resend) { + sp->need_resend = 0; + + /* each Tx packet has a new serial number */ + sp->hdr.serial = + htonl(atomic_inc_return(&call->conn->serial)); + + hdr = (struct rxrpc_header *) txb->head; + hdr->serial = sp->hdr.serial; + + _proto("Tx DATA %%%u { #%d }", + ntohl(sp->hdr.serial), ntohl(sp->hdr.seq)); + if (rxrpc_send_packet(call->conn->trans, txb) < 0) { + stop = 0; + sp->resend_at = jiffies + 3; + } else { + sp->resend_at = + jiffies + rxrpc_resend_timeout * HZ; + } + } + + if (time_after_eq(jiffies + 1, sp->resend_at)) { + sp->need_resend = 1; + resend |= 1; + } else if (resend & 2) { + if (time_before(sp->resend_at, resend_at)) + resend_at = sp->resend_at; + } else { + resend_at = sp->resend_at; + resend |= 2; + } + } + + rxrpc_set_resend(call, resend, resend_at); + _leave(""); +} + +/* + * handle resend timer expiry + */ +static void rxrpc_resend_timer(struct rxrpc_call *call) +{ + struct rxrpc_skb_priv *sp; + struct sk_buff *txb; + unsigned long *p_txb, resend_at; + int loop; + u8 resend; + + _enter("%d,%d,%d", + call->acks_tail, call->acks_unacked, call->acks_head); + + resend = 0; + resend_at = 0; + + for (loop = call->acks_unacked; + loop != call->acks_head; + loop = (loop + 1) & (call->acks_winsz - 1) + ) { + p_txb = call->acks_window + loop; + smp_read_barrier_depends(); + txb = (struct sk_buff *) (*p_txb & ~1); + sp = rxrpc_skb(txb); + + ASSERT(!(*p_txb & 1)); + + if (sp->need_resend) { + ; + } else if (time_after_eq(jiffies + 1, sp->resend_at)) { + sp->need_resend = 1; + resend |= 1; + } else if (resend & 2) { + if (time_before(sp->resend_at, resend_at)) + resend_at = sp->resend_at; + } else { + resend_at = sp->resend_at; + resend |= 2; + } + } + + rxrpc_set_resend(call, resend, resend_at); + _leave(""); +} + +/* + * process soft ACKs of our transmitted packets + * - these indicate packets the peer has or has not received, but hasn't yet + * given to the consumer, and so can still be discarded and re-requested + */ +static int rxrpc_process_soft_ACKs(struct rxrpc_call *call, + struct rxrpc_ackpacket *ack, + struct sk_buff *skb) +{ + struct rxrpc_skb_priv *sp; + struct sk_buff *txb; + unsigned long *p_txb, resend_at; + int loop; + u8 sacks[RXRPC_MAXACKS], resend; + + _enter("{%d,%d},{%d},", + call->acks_hard, + CIRC_CNT(call->acks_head, call->acks_tail, call->acks_winsz), + ack->nAcks); + + if (skb_copy_bits(skb, 0, sacks, ack->nAcks) < 0) + goto protocol_error; + + resend = 0; + resend_at = 0; + for (loop = 0; loop < ack->nAcks; loop++) { + p_txb = call->acks_window; + p_txb += (call->acks_tail + loop) & (call->acks_winsz - 1); + smp_read_barrier_depends(); + txb = (struct sk_buff *) (*p_txb & ~1); + sp = rxrpc_skb(txb); + + switch (sacks[loop]) { + case RXRPC_ACK_TYPE_ACK: + sp->need_resend = 0; + *p_txb |= 1; + break; + case RXRPC_ACK_TYPE_NACK: + sp->need_resend = 1; + *p_txb &= ~1; + resend = 1; + break; + default: + _debug("Unsupported ACK type %d", sacks[loop]); + goto protocol_error; + } + } + + smp_mb(); + call->acks_unacked = (call->acks_tail + loop) & (call->acks_winsz - 1); + + /* anything not explicitly ACK'd is implicitly NACK'd, but may just not + * have been received or processed yet by the far end */ + for (loop = call->acks_unacked; + loop != call->acks_head; + loop = (loop + 1) & (call->acks_winsz - 1) + ) { + p_txb = call->acks_window + loop; + smp_read_barrier_depends(); + txb = (struct sk_buff *) (*p_txb & ~1); + sp = rxrpc_skb(txb); + + if (*p_txb & 1) { + /* packet must have been discarded */ + sp->need_resend = 1; + *p_txb &= ~1; + resend |= 1; + } else if (sp->need_resend) { + ; + } else if (time_after_eq(jiffies + 1, sp->resend_at)) { + sp->need_resend = 1; + resend |= 1; + } else if (resend & 2) { + if (time_before(sp->resend_at, resend_at)) + resend_at = sp->resend_at; + } else { + resend_at = sp->resend_at; + resend |= 2; + } + } + + rxrpc_set_resend(call, resend, resend_at); + _leave(" = 0"); + return 0; + +protocol_error: + _leave(" = -EPROTO"); + return -EPROTO; +} + +/* + * discard hard-ACK'd packets from the Tx window + */ +static void rxrpc_rotate_tx_window(struct rxrpc_call *call, u32 hard) +{ + struct rxrpc_skb_priv *sp; + unsigned long _skb; + int tail = call->acks_tail, old_tail; + int win = CIRC_CNT(call->acks_head, tail, call->acks_winsz); + + _enter("{%u,%u},%u", call->acks_hard, win, hard); + + ASSERTCMP(hard - call->acks_hard, <=, win); + + while (call->acks_hard < hard) { + smp_read_barrier_depends(); + _skb = call->acks_window[tail] & ~1; + sp = rxrpc_skb((struct sk_buff *) _skb); + rxrpc_free_skb((struct sk_buff *) _skb); + old_tail = tail; + tail = (tail + 1) & (call->acks_winsz - 1); + call->acks_tail = tail; + if (call->acks_unacked == old_tail) + call->acks_unacked = tail; + call->acks_hard++; + } + + wake_up(&call->tx_waitq); +} + +/* + * clear the Tx window in the event of a failure + */ +static void rxrpc_clear_tx_window(struct rxrpc_call *call) +{ + rxrpc_rotate_tx_window(call, atomic_read(&call->sequence)); +} + +/* + * drain the out of sequence received packet queue into the packet Rx queue + */ +static int rxrpc_drain_rx_oos_queue(struct rxrpc_call *call) +{ + struct rxrpc_skb_priv *sp; + struct sk_buff *skb; + bool terminal; + int ret; + + _enter("{%d,%d}", call->rx_data_post, call->rx_first_oos); + + spin_lock_bh(&call->lock); + + ret = -ECONNRESET; + if (test_bit(RXRPC_CALL_RELEASED, &call->flags)) + goto socket_unavailable; + + skb = skb_dequeue(&call->rx_oos_queue); + if (skb) { + sp = rxrpc_skb(skb); + + _debug("drain OOS packet %d [%d]", + ntohl(sp->hdr.seq), call->rx_first_oos); + + if (ntohl(sp->hdr.seq) != call->rx_first_oos) { + skb_queue_head(&call->rx_oos_queue, skb); + call->rx_first_oos = ntohl(rxrpc_skb(skb)->hdr.seq); + _debug("requeue %p {%u}", skb, call->rx_first_oos); + } else { + skb->mark = RXRPC_SKB_MARK_DATA; + terminal = ((sp->hdr.flags & RXRPC_LAST_PACKET) && + !(sp->hdr.flags & RXRPC_CLIENT_INITIATED)); + ret = rxrpc_queue_rcv_skb(call, skb, true, terminal); + BUG_ON(ret < 0); + _debug("drain #%u", call->rx_data_post); + call->rx_data_post++; + + /* find out what the next packet is */ + skb = skb_peek(&call->rx_oos_queue); + if (skb) + call->rx_first_oos = + ntohl(rxrpc_skb(skb)->hdr.seq); + else + call->rx_first_oos = 0; + _debug("peek %p {%u}", skb, call->rx_first_oos); + } + } + + ret = 0; +socket_unavailable: + spin_unlock_bh(&call->lock); + _leave(" = %d", ret); + return ret; +} + +/* + * insert an out of sequence packet into the buffer + */ +static void rxrpc_insert_oos_packet(struct rxrpc_call *call, + struct sk_buff *skb) +{ + struct rxrpc_skb_priv *sp, *psp; + struct sk_buff *p; + u32 seq; + + sp = rxrpc_skb(skb); + seq = ntohl(sp->hdr.seq); + _enter(",,{%u}", seq); + + skb->destructor = rxrpc_packet_destructor; + ASSERTCMP(sp->call, ==, NULL); + sp->call = call; + rxrpc_get_call(call); + + /* insert into the buffer in sequence order */ + spin_lock_bh(&call->lock); + + skb_queue_walk(&call->rx_oos_queue, p) { + psp = rxrpc_skb(p); + if (ntohl(psp->hdr.seq) > seq) { + _debug("insert oos #%u before #%u", + seq, ntohl(psp->hdr.seq)); + skb_insert(p, skb, &call->rx_oos_queue); + goto inserted; + } + } + + _debug("append oos #%u", seq); + skb_queue_tail(&call->rx_oos_queue, skb); +inserted: + + /* we might now have a new front to the queue */ + if (call->rx_first_oos == 0 || seq < call->rx_first_oos) + call->rx_first_oos = seq; + + read_lock(&call->state_lock); + if (call->state < RXRPC_CALL_COMPLETE && + call->rx_data_post == call->rx_first_oos) { + _debug("drain rx oos now"); + set_bit(RXRPC_CALL_DRAIN_RX_OOS, &call->events); + } + read_unlock(&call->state_lock); + + spin_unlock_bh(&call->lock); + _leave(" [stored #%u]", call->rx_first_oos); +} + +/* + * clear the Tx window on final ACK reception + */ +static void rxrpc_zap_tx_window(struct rxrpc_call *call) +{ + struct rxrpc_skb_priv *sp; + struct sk_buff *skb; + unsigned long _skb, *acks_window; + uint8_t winsz = call->acks_winsz; + int tail; + + acks_window = call->acks_window; + call->acks_window = NULL; + + while (CIRC_CNT(call->acks_head, call->acks_tail, winsz) > 0) { + tail = call->acks_tail; + smp_read_barrier_depends(); + _skb = acks_window[tail] & ~1; + smp_mb(); + call->acks_tail = (call->acks_tail + 1) & (winsz - 1); + + skb = (struct sk_buff *) _skb; + sp = rxrpc_skb(skb); + _debug("+++ clear Tx %u", ntohl(sp->hdr.seq)); + rxrpc_free_skb(skb); + } + + kfree(acks_window); +} + +/* + * process packets in the reception queue + */ +static int rxrpc_process_rx_queue(struct rxrpc_call *call, + u32 *_abort_code) +{ + struct rxrpc_ackpacket ack; + struct rxrpc_skb_priv *sp; + struct sk_buff *skb; + bool post_ACK; + int latest; + u32 hard, tx; + + _enter(""); + +process_further: + skb = skb_dequeue(&call->rx_queue); + if (!skb) + return -EAGAIN; + + _net("deferred skb %p", skb); + + sp = rxrpc_skb(skb); + + _debug("process %s [st %d]", rxrpc_pkts[sp->hdr.type], call->state); + + post_ACK = false; + + switch (sp->hdr.type) { + /* data packets that wind up here have been received out of + * order, need security processing or are jumbo packets */ + case RXRPC_PACKET_TYPE_DATA: + _proto("OOSQ DATA %%%u { #%u }", + ntohl(sp->hdr.serial), ntohl(sp->hdr.seq)); + + /* secured packets must be verified and possibly decrypted */ + if (rxrpc_verify_packet(call, skb, _abort_code) < 0) + goto protocol_error; + + rxrpc_insert_oos_packet(call, skb); + goto process_further; + + /* partial ACK to process */ + case RXRPC_PACKET_TYPE_ACK: + if (skb_copy_bits(skb, 0, &ack, sizeof(ack)) < 0) { + _debug("extraction failure"); + goto protocol_error; + } + if (!skb_pull(skb, sizeof(ack))) + BUG(); + + latest = ntohl(sp->hdr.serial); + hard = ntohl(ack.firstPacket); + tx = atomic_read(&call->sequence); + + _proto("Rx ACK %%%u { m=%hu f=#%u p=#%u s=%%%u r=%s n=%u }", + latest, + ntohs(ack.maxSkew), + hard, + ntohl(ack.previousPacket), + ntohl(ack.serial), + rxrpc_acks[ack.reason], + ack.nAcks); + + if (ack.reason == RXRPC_ACK_PING) { + _proto("Rx ACK %%%u PING Request", latest); + rxrpc_propose_ACK(call, RXRPC_ACK_PING_RESPONSE, + sp->hdr.serial, true); + } + + /* discard any out-of-order or duplicate ACKs */ + if (latest - call->acks_latest <= 0) { + _debug("discard ACK %d <= %d", + latest, call->acks_latest); + goto discard; + } + call->acks_latest = latest; + + if (call->state != RXRPC_CALL_CLIENT_SEND_REQUEST && + call->state != RXRPC_CALL_CLIENT_AWAIT_REPLY && + call->state != RXRPC_CALL_SERVER_SEND_REPLY && + call->state != RXRPC_CALL_SERVER_AWAIT_ACK) + goto discard; + + _debug("Tx=%d H=%u S=%d", tx, call->acks_hard, call->state); + + if (hard > 0) { + if (hard - 1 > tx) { + _debug("hard-ACK'd packet %d not transmitted" + " (%d top)", + hard - 1, tx); + goto protocol_error; + } + + if ((call->state == RXRPC_CALL_CLIENT_AWAIT_REPLY || + call->state == RXRPC_CALL_SERVER_AWAIT_ACK) && + hard > tx) + goto all_acked; + + smp_rmb(); + rxrpc_rotate_tx_window(call, hard - 1); + } + + if (ack.nAcks > 0) { + if (hard - 1 + ack.nAcks > tx) { + _debug("soft-ACK'd packet %d+%d not" + " transmitted (%d top)", + hard - 1, ack.nAcks, tx); + goto protocol_error; + } + + if (rxrpc_process_soft_ACKs(call, &ack, skb) < 0) + goto protocol_error; + } + goto discard; + + /* complete ACK to process */ + case RXRPC_PACKET_TYPE_ACKALL: + goto all_acked; + + /* abort and busy are handled elsewhere */ + case RXRPC_PACKET_TYPE_BUSY: + case RXRPC_PACKET_TYPE_ABORT: + BUG(); + + /* connection level events - also handled elsewhere */ + case RXRPC_PACKET_TYPE_CHALLENGE: + case RXRPC_PACKET_TYPE_RESPONSE: + case RXRPC_PACKET_TYPE_DEBUG: + BUG(); + } + + /* if we've had a hard ACK that covers all the packets we've sent, then + * that ends that phase of the operation */ +all_acked: + write_lock_bh(&call->state_lock); + _debug("ack all %d", call->state); + + switch (call->state) { + case RXRPC_CALL_CLIENT_AWAIT_REPLY: + call->state = RXRPC_CALL_CLIENT_RECV_REPLY; + break; + case RXRPC_CALL_SERVER_AWAIT_ACK: + _debug("srv complete"); + call->state = RXRPC_CALL_COMPLETE; + post_ACK = true; + break; + case RXRPC_CALL_CLIENT_SEND_REQUEST: + case RXRPC_CALL_SERVER_RECV_REQUEST: + goto protocol_error_unlock; /* can't occur yet */ + default: + write_unlock_bh(&call->state_lock); + goto discard; /* assume packet left over from earlier phase */ + } + + write_unlock_bh(&call->state_lock); + + /* if all the packets we sent are hard-ACK'd, then we can discard + * whatever we've got left */ + _debug("clear Tx %d", + CIRC_CNT(call->acks_head, call->acks_tail, call->acks_winsz)); + + del_timer_sync(&call->resend_timer); + clear_bit(RXRPC_CALL_RUN_RTIMER, &call->flags); + clear_bit(RXRPC_CALL_RESEND_TIMER, &call->events); + + if (call->acks_window) + rxrpc_zap_tx_window(call); + + if (post_ACK) { + /* post the final ACK message for userspace to pick up */ + _debug("post ACK"); + skb->mark = RXRPC_SKB_MARK_FINAL_ACK; + sp->call = call; + rxrpc_get_call(call); + spin_lock_bh(&call->lock); + if (rxrpc_queue_rcv_skb(call, skb, true, true) < 0) + BUG(); + spin_unlock_bh(&call->lock); + goto process_further; + } + +discard: + rxrpc_free_skb(skb); + goto process_further; + +protocol_error_unlock: + write_unlock_bh(&call->state_lock); +protocol_error: + rxrpc_free_skb(skb); + _leave(" = -EPROTO"); + return -EPROTO; +} + +/* + * post a message to the socket Rx queue for recvmsg() to pick up + */ +static int rxrpc_post_message(struct rxrpc_call *call, u32 mark, u32 error, + bool fatal) +{ + struct rxrpc_skb_priv *sp; + struct sk_buff *skb; + int ret; + + _enter("{%d,%lx},%u,%u,%d", + call->debug_id, call->flags, mark, error, fatal); + + /* remove timers and things for fatal messages */ + if (fatal) { + del_timer_sync(&call->resend_timer); + del_timer_sync(&call->ack_timer); + clear_bit(RXRPC_CALL_RUN_RTIMER, &call->flags); + } + + if (mark != RXRPC_SKB_MARK_NEW_CALL && + !test_bit(RXRPC_CALL_HAS_USERID, &call->flags)) { + _leave("[no userid]"); + return 0; + } + + if (!test_bit(RXRPC_CALL_TERMINAL_MSG, &call->flags)) { + skb = alloc_skb(0, GFP_NOFS); + if (!skb) + return -ENOMEM; + + rxrpc_new_skb(skb); + + skb->mark = mark; + + sp = rxrpc_skb(skb); + memset(sp, 0, sizeof(*sp)); + sp->error = error; + sp->call = call; + rxrpc_get_call(call); + + spin_lock_bh(&call->lock); + ret = rxrpc_queue_rcv_skb(call, skb, true, fatal); + spin_unlock_bh(&call->lock); + if (ret < 0) + BUG(); + } + + return 0; +} + +/* + * handle background processing of incoming call packets and ACK / abort + * generation + */ +void rxrpc_process_call(struct work_struct *work) +{ + struct rxrpc_call *call = + container_of(work, struct rxrpc_call, processor); + struct rxrpc_ackpacket ack; + struct rxrpc_ackinfo ackinfo; + struct rxrpc_header hdr; + struct msghdr msg; + struct kvec iov[5]; + unsigned long bits; + __be32 data; + size_t len; + int genbit, loop, nbit, ioc, ret; + u32 abort_code = RX_PROTOCOL_ERROR; + u8 *acks = NULL; + + //printk("\n--------------------\n"); + _enter("{%d,%s,%lx} [%lu]", + call->debug_id, rxrpc_call_states[call->state], call->events, + (jiffies - call->creation_jif) / (HZ / 10)); + + if (test_and_set_bit(RXRPC_CALL_PROC_BUSY, &call->flags)) { + _debug("XXXXXXXXXXXXX RUNNING ON MULTIPLE CPUS XXXXXXXXXXXXX"); + return; + } + + /* there's a good chance we're going to have to send a message, so set + * one up in advance */ + msg.msg_name = &call->conn->trans->peer->srx.transport.sin; + msg.msg_namelen = sizeof(call->conn->trans->peer->srx.transport.sin); + msg.msg_control = NULL; + msg.msg_controllen = 0; + msg.msg_flags = 0; + + hdr.epoch = call->conn->epoch; + hdr.cid = call->cid; + hdr.callNumber = call->call_id; + hdr.seq = 0; + hdr.type = RXRPC_PACKET_TYPE_ACK; + hdr.flags = call->conn->out_clientflag; + hdr.userStatus = 0; + hdr.securityIndex = call->conn->security_ix; + hdr._rsvd = 0; + hdr.serviceId = call->conn->service_id; + + memset(iov, 0, sizeof(iov)); + iov[0].iov_base = &hdr; + iov[0].iov_len = sizeof(hdr); + + /* deal with events of a final nature */ + if (test_bit(RXRPC_CALL_RELEASE, &call->events)) { + rxrpc_release_call(call); + clear_bit(RXRPC_CALL_RELEASE, &call->events); + } + + if (test_bit(RXRPC_CALL_RCVD_ERROR, &call->events)) { + int error; + + clear_bit(RXRPC_CALL_CONN_ABORT, &call->events); + clear_bit(RXRPC_CALL_REJECT_BUSY, &call->events); + clear_bit(RXRPC_CALL_ABORT, &call->events); + + error = call->conn->trans->peer->net_error; + _debug("post net error %d", error); + + if (rxrpc_post_message(call, RXRPC_SKB_MARK_NET_ERROR, + error, true) < 0) + goto no_mem; + clear_bit(RXRPC_CALL_RCVD_ERROR, &call->events); + goto kill_ACKs; + } + + if (test_bit(RXRPC_CALL_CONN_ABORT, &call->events)) { + ASSERTCMP(call->state, >, RXRPC_CALL_COMPLETE); + + clear_bit(RXRPC_CALL_REJECT_BUSY, &call->events); + clear_bit(RXRPC_CALL_ABORT, &call->events); + + _debug("post conn abort"); + + if (rxrpc_post_message(call, RXRPC_SKB_MARK_LOCAL_ERROR, + call->conn->error, true) < 0) + goto no_mem; + clear_bit(RXRPC_CALL_CONN_ABORT, &call->events); + goto kill_ACKs; + } + + if (test_bit(RXRPC_CALL_REJECT_BUSY, &call->events)) { + hdr.type = RXRPC_PACKET_TYPE_BUSY; + genbit = RXRPC_CALL_REJECT_BUSY; + goto send_message; + } + + if (test_bit(RXRPC_CALL_ABORT, &call->events)) { + ASSERTCMP(call->state, >, RXRPC_CALL_COMPLETE); + + if (rxrpc_post_message(call, RXRPC_SKB_MARK_LOCAL_ERROR, + ECONNABORTED, true) < 0) + goto no_mem; + hdr.type = RXRPC_PACKET_TYPE_ABORT; + data = htonl(call->abort_code); + iov[1].iov_base = &data; + iov[1].iov_len = sizeof(data); + genbit = RXRPC_CALL_ABORT; + goto send_message; + } + + if (test_bit(RXRPC_CALL_ACK_FINAL, &call->events)) { + hdr.type = RXRPC_PACKET_TYPE_ACKALL; + genbit = RXRPC_CALL_ACK_FINAL; + goto send_message; + } + + if (call->events & ((1 << RXRPC_CALL_RCVD_BUSY) | + (1 << RXRPC_CALL_RCVD_ABORT)) + ) { + u32 mark; + + if (test_bit(RXRPC_CALL_RCVD_ABORT, &call->events)) + mark = RXRPC_SKB_MARK_REMOTE_ABORT; + else + mark = RXRPC_SKB_MARK_BUSY; + + _debug("post abort/busy"); + rxrpc_clear_tx_window(call); + if (rxrpc_post_message(call, mark, ECONNABORTED, true) < 0) + goto no_mem; + + clear_bit(RXRPC_CALL_RCVD_BUSY, &call->events); + clear_bit(RXRPC_CALL_RCVD_ABORT, &call->events); + goto kill_ACKs; + } + + if (test_and_clear_bit(RXRPC_CALL_RCVD_ACKALL, &call->events)) { + _debug("do implicit ackall"); + rxrpc_clear_tx_window(call); + } + + if (test_bit(RXRPC_CALL_LIFE_TIMER, &call->events)) { + write_lock_bh(&call->state_lock); + if (call->state <= RXRPC_CALL_COMPLETE) { + call->state = RXRPC_CALL_LOCALLY_ABORTED; + call->abort_code = RX_CALL_TIMEOUT; + set_bit(RXRPC_CALL_ABORT, &call->events); + } + write_unlock_bh(&call->state_lock); + + _debug("post timeout"); + if (rxrpc_post_message(call, RXRPC_SKB_MARK_LOCAL_ERROR, + ETIME, true) < 0) + goto no_mem; + + clear_bit(RXRPC_CALL_LIFE_TIMER, &call->events); + goto kill_ACKs; + } + + /* deal with assorted inbound messages */ + if (!skb_queue_empty(&call->rx_queue)) { + switch (rxrpc_process_rx_queue(call, &abort_code)) { + case 0: + case -EAGAIN: + break; + case -ENOMEM: + goto no_mem; + case -EKEYEXPIRED: + case -EKEYREJECTED: + case -EPROTO: + rxrpc_abort_call(call, abort_code); + goto kill_ACKs; + } + } + + /* handle resending */ + if (test_and_clear_bit(RXRPC_CALL_RESEND_TIMER, &call->events)) + rxrpc_resend_timer(call); + if (test_and_clear_bit(RXRPC_CALL_RESEND, &call->events)) + rxrpc_resend(call); + + /* consider sending an ordinary ACK */ + if (test_bit(RXRPC_CALL_ACK, &call->events)) { + __be32 pad; + + _debug("send ACK: window: %d - %d { %lx }", + call->rx_data_eaten, call->ackr_win_top, + call->ackr_window[0]); + + if (call->state > RXRPC_CALL_SERVER_ACK_REQUEST && + call->ackr_reason != RXRPC_ACK_PING_RESPONSE) { + /* ACK by sending reply DATA packet in this state */ + clear_bit(RXRPC_CALL_ACK, &call->events); + goto maybe_reschedule; + } + + genbit = RXRPC_CALL_ACK; + + acks = kzalloc(call->ackr_win_top - call->rx_data_eaten, + GFP_NOFS); + if (!acks) + goto no_mem; + + //hdr.flags = RXRPC_SLOW_START_OK; + ack.bufferSpace = htons(8); + ack.maxSkew = 0; + ack.serial = 0; + ack.reason = 0; + + ackinfo.rxMTU = htonl(5692); +// ackinfo.rxMTU = htonl(call->conn->trans->peer->maxdata); + ackinfo.maxMTU = htonl(call->conn->trans->peer->maxdata); + ackinfo.rwind = htonl(32); + ackinfo.jumbo_max = htonl(4); + + spin_lock_bh(&call->lock); + ack.reason = call->ackr_reason; + ack.serial = call->ackr_serial; + ack.previousPacket = call->ackr_prev_seq; + ack.firstPacket = htonl(call->rx_data_eaten + 1); + + ack.nAcks = 0; + for (loop = 0; loop < RXRPC_ACKR_WINDOW_ASZ; loop++) { + nbit = loop * BITS_PER_LONG; + for (bits = call->ackr_window[loop]; bits; bits >>= 1 + ) { + _debug("- l=%d n=%d b=%lx", loop, nbit, bits); + if (bits & 1) { + acks[nbit] = RXRPC_ACK_TYPE_ACK; + ack.nAcks = nbit + 1; + } + nbit++; + } + } + call->ackr_reason = 0; + spin_unlock_bh(&call->lock); + + pad = 0; + + iov[1].iov_base = &ack; + iov[1].iov_len = sizeof(ack); + iov[2].iov_base = acks; + iov[2].iov_len = ack.nAcks; + iov[3].iov_base = &pad; + iov[3].iov_len = 3; + iov[4].iov_base = &ackinfo; + iov[4].iov_len = sizeof(ackinfo); + + switch (ack.reason) { + case RXRPC_ACK_REQUESTED: + case RXRPC_ACK_DUPLICATE: + case RXRPC_ACK_OUT_OF_SEQUENCE: + case RXRPC_ACK_EXCEEDS_WINDOW: + case RXRPC_ACK_NOSPACE: + case RXRPC_ACK_PING: + case RXRPC_ACK_PING_RESPONSE: + goto send_ACK_with_skew; + case RXRPC_ACK_DELAY: + case RXRPC_ACK_IDLE: + goto send_ACK; + } + } + + /* handle completion of security negotiations on an incoming + * connection */ + if (test_and_clear_bit(RXRPC_CALL_SECURED, &call->events)) { + _debug("secured"); + spin_lock_bh(&call->lock); + + if (call->state == RXRPC_CALL_SERVER_SECURING) { + _debug("securing"); + write_lock(&call->conn->lock); + if (!test_bit(RXRPC_CALL_RELEASED, &call->flags) && + !test_bit(RXRPC_CALL_RELEASE, &call->events)) { + _debug("not released"); + call->state = RXRPC_CALL_SERVER_ACCEPTING; + list_move_tail(&call->accept_link, + &call->socket->acceptq); + } + write_unlock(&call->conn->lock); + read_lock(&call->state_lock); + if (call->state < RXRPC_CALL_COMPLETE) + set_bit(RXRPC_CALL_POST_ACCEPT, &call->events); + read_unlock(&call->state_lock); + } + + spin_unlock_bh(&call->lock); + if (!test_bit(RXRPC_CALL_POST_ACCEPT, &call->events)) + goto maybe_reschedule; + } + + /* post a notification of an acceptable connection to the app */ + if (test_bit(RXRPC_CALL_POST_ACCEPT, &call->events)) { + _debug("post accept"); + if (rxrpc_post_message(call, RXRPC_SKB_MARK_NEW_CALL, + 0, false) < 0) + goto no_mem; + clear_bit(RXRPC_CALL_POST_ACCEPT, &call->events); + goto maybe_reschedule; + } + + /* handle incoming call acceptance */ + if (test_and_clear_bit(RXRPC_CALL_ACCEPTED, &call->events)) { + _debug("accepted"); + ASSERTCMP(call->rx_data_post, ==, 0); + call->rx_data_post = 1; + read_lock_bh(&call->state_lock); + if (call->state < RXRPC_CALL_COMPLETE) + set_bit(RXRPC_CALL_DRAIN_RX_OOS, &call->events); + read_unlock_bh(&call->state_lock); + } + + /* drain the out of sequence received packet queue into the packet Rx + * queue */ + if (test_and_clear_bit(RXRPC_CALL_DRAIN_RX_OOS, &call->events)) { + while (call->rx_data_post == call->rx_first_oos) + if (rxrpc_drain_rx_oos_queue(call) < 0) + break; + goto maybe_reschedule; + } + + /* other events may have been raised since we started checking */ + goto maybe_reschedule; + +send_ACK_with_skew: + ack.maxSkew = htons(atomic_read(&call->conn->hi_serial) - + ntohl(ack.serial)); +send_ACK: + hdr.serial = htonl(atomic_inc_return(&call->conn->serial)); + _proto("Tx ACK %%%u { m=%hu f=#%u p=#%u s=%%%u r=%s n=%u }", + ntohl(hdr.serial), + ntohs(ack.maxSkew), + ntohl(ack.firstPacket), + ntohl(ack.previousPacket), + ntohl(ack.serial), + rxrpc_acks[ack.reason], + ack.nAcks); + + del_timer_sync(&call->ack_timer); + if (ack.nAcks > 0) + set_bit(RXRPC_CALL_TX_SOFT_ACK, &call->flags); + goto send_message_2; + +send_message: + _debug("send message"); + + hdr.serial = htonl(atomic_inc_return(&call->conn->serial)); + _proto("Tx %s %%%u", rxrpc_pkts[hdr.type], ntohl(hdr.serial)); +send_message_2: + + len = iov[0].iov_len; + ioc = 1; + if (iov[4].iov_len) { + ioc = 5; + len += iov[4].iov_len; + len += iov[3].iov_len; + len += iov[2].iov_len; + len += iov[1].iov_len; + } else if (iov[3].iov_len) { + ioc = 4; + len += iov[3].iov_len; + len += iov[2].iov_len; + len += iov[1].iov_len; + } else if (iov[2].iov_len) { + ioc = 3; + len += iov[2].iov_len; + len += iov[1].iov_len; + } else if (iov[1].iov_len) { + ioc = 2; + len += iov[1].iov_len; + } + + ret = kernel_sendmsg(call->conn->trans->local->socket, + &msg, iov, ioc, len); + if (ret < 0) { + _debug("sendmsg failed: %d", ret); + read_lock_bh(&call->state_lock); + if (call->state < RXRPC_CALL_DEAD) + schedule_work(&call->processor); + read_unlock_bh(&call->state_lock); + goto error; + } + + switch (genbit) { + case RXRPC_CALL_ABORT: + clear_bit(genbit, &call->events); + clear_bit(RXRPC_CALL_RCVD_ABORT, &call->events); + goto kill_ACKs; + + case RXRPC_CALL_ACK_FINAL: + write_lock_bh(&call->state_lock); + if (call->state == RXRPC_CALL_CLIENT_FINAL_ACK) + call->state = RXRPC_CALL_COMPLETE; + write_unlock_bh(&call->state_lock); + goto kill_ACKs; + + default: + clear_bit(genbit, &call->events); + switch (call->state) { + case RXRPC_CALL_CLIENT_AWAIT_REPLY: + case RXRPC_CALL_CLIENT_RECV_REPLY: + case RXRPC_CALL_SERVER_RECV_REQUEST: + case RXRPC_CALL_SERVER_ACK_REQUEST: + _debug("start ACK timer"); + rxrpc_propose_ACK(call, RXRPC_ACK_DELAY, + call->ackr_serial, false); + default: + break; + } + goto maybe_reschedule; + } + +kill_ACKs: + del_timer_sync(&call->ack_timer); + if (test_and_clear_bit(RXRPC_CALL_ACK_FINAL, &call->events)) + rxrpc_put_call(call); + clear_bit(RXRPC_CALL_ACK, &call->events); + +maybe_reschedule: + if (call->events || !skb_queue_empty(&call->rx_queue)) { + read_lock_bh(&call->state_lock); + if (call->state < RXRPC_CALL_DEAD) + schedule_work(&call->processor); + read_unlock_bh(&call->state_lock); + } + + /* don't leave aborted connections on the accept queue */ + if (call->state >= RXRPC_CALL_COMPLETE && + !list_empty(&call->accept_link)) { + _debug("X unlinking once-pending call %p { e=%lx f=%lx c=%x }", + call, call->events, call->flags, + ntohl(call->conn->cid)); + + read_lock_bh(&call->state_lock); + if (!test_bit(RXRPC_CALL_RELEASED, &call->flags) && + !test_and_set_bit(RXRPC_CALL_RELEASE, &call->events)) + schedule_work(&call->processor); + read_unlock_bh(&call->state_lock); + } + +error: + clear_bit(RXRPC_CALL_PROC_BUSY, &call->flags); + kfree(acks); + + /* because we don't want two CPUs both processing the work item for one + * call at the same time, we use a flag to note when it's busy; however + * this means there's a race between clearing the flag and setting the + * work pending bit and the work item being processed again */ + if (call->events && !work_pending(&call->processor)) { + _debug("jumpstart %x", ntohl(call->conn->cid)); + schedule_work(&call->processor); + } + + _leave(""); + return; + +no_mem: + _debug("out of memory"); + goto maybe_reschedule; +} diff --git a/net/rxrpc/ar-call.c b/net/rxrpc/ar-call.c new file mode 100644 index 000000000000..ac31cceda2f1 --- /dev/null +++ b/net/rxrpc/ar-call.c @@ -0,0 +1,787 @@ +/* RxRPC individual remote procedure call handling + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * 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. + */ + +#include +#include +#include +#include +#include "ar-internal.h" + +struct kmem_cache *rxrpc_call_jar; +LIST_HEAD(rxrpc_calls); +DEFINE_RWLOCK(rxrpc_call_lock); +static unsigned rxrpc_call_max_lifetime = 60; +static unsigned rxrpc_dead_call_timeout = 10; + +static void rxrpc_destroy_call(struct work_struct *work); +static void rxrpc_call_life_expired(unsigned long _call); +static void rxrpc_dead_call_expired(unsigned long _call); +static void rxrpc_ack_time_expired(unsigned long _call); +static void rxrpc_resend_time_expired(unsigned long _call); + +/* + * allocate a new call + */ +static struct rxrpc_call *rxrpc_alloc_call(gfp_t gfp) +{ + struct rxrpc_call *call; + + call = kmem_cache_zalloc(rxrpc_call_jar, gfp); + if (!call) + return NULL; + + call->acks_winsz = 16; + call->acks_window = kmalloc(call->acks_winsz * sizeof(unsigned long), + gfp); + if (!call->acks_window) { + kmem_cache_free(rxrpc_call_jar, call); + return NULL; + } + + setup_timer(&call->lifetimer, &rxrpc_call_life_expired, + (unsigned long) call); + setup_timer(&call->deadspan, &rxrpc_dead_call_expired, + (unsigned long) call); + setup_timer(&call->ack_timer, &rxrpc_ack_time_expired, + (unsigned long) call); + setup_timer(&call->resend_timer, &rxrpc_resend_time_expired, + (unsigned long) call); + INIT_WORK(&call->destroyer, &rxrpc_destroy_call); + INIT_WORK(&call->processor, &rxrpc_process_call); + INIT_LIST_HEAD(&call->accept_link); + skb_queue_head_init(&call->rx_queue); + skb_queue_head_init(&call->rx_oos_queue); + init_waitqueue_head(&call->tx_waitq); + spin_lock_init(&call->lock); + rwlock_init(&call->state_lock); + atomic_set(&call->usage, 1); + call->debug_id = atomic_inc_return(&rxrpc_debug_id); + call->state = RXRPC_CALL_CLIENT_SEND_REQUEST; + + memset(&call->sock_node, 0xed, sizeof(call->sock_node)); + + call->rx_data_expect = 1; + call->rx_data_eaten = 0; + call->rx_first_oos = 0; + call->ackr_win_top = call->rx_data_eaten + 1 + RXRPC_MAXACKS; + call->creation_jif = jiffies; + return call; +} + +/* + * allocate a new client call and attempt to to get a connection slot for it + */ +static struct rxrpc_call *rxrpc_alloc_client_call( + struct rxrpc_sock *rx, + struct rxrpc_transport *trans, + struct rxrpc_conn_bundle *bundle, + gfp_t gfp) +{ + struct rxrpc_call *call; + int ret; + + _enter(""); + + ASSERT(rx != NULL); + ASSERT(trans != NULL); + ASSERT(bundle != NULL); + + call = rxrpc_alloc_call(gfp); + if (!call) + return ERR_PTR(-ENOMEM); + + sock_hold(&rx->sk); + call->socket = rx; + call->rx_data_post = 1; + + ret = rxrpc_connect_call(rx, trans, bundle, call, gfp); + if (ret < 0) { + kmem_cache_free(rxrpc_call_jar, call); + return ERR_PTR(ret); + } + + spin_lock(&call->conn->trans->peer->lock); + list_add(&call->error_link, &call->conn->trans->peer->error_targets); + spin_unlock(&call->conn->trans->peer->lock); + + call->lifetimer.expires = jiffies + rxrpc_call_max_lifetime * HZ; + add_timer(&call->lifetimer); + + _leave(" = %p", call); + return call; +} + +/* + * set up a call for the given data + * - called in process context with IRQs enabled + */ +struct rxrpc_call *rxrpc_get_client_call(struct rxrpc_sock *rx, + struct rxrpc_transport *trans, + struct rxrpc_conn_bundle *bundle, + unsigned long user_call_ID, + int create, + gfp_t gfp) +{ + struct rxrpc_call *call, *candidate; + struct rb_node *p, *parent, **pp; + + _enter("%p,%d,%d,%lx,%d", + rx, trans ? trans->debug_id : -1, bundle ? bundle->debug_id : -1, + user_call_ID, create); + + /* search the extant calls first for one that matches the specified + * user ID */ + read_lock(&rx->call_lock); + + p = rx->calls.rb_node; + while (p) { + call = rb_entry(p, struct rxrpc_call, sock_node); + + if (user_call_ID < call->user_call_ID) + p = p->rb_left; + else if (user_call_ID > call->user_call_ID) + p = p->rb_right; + else + goto found_extant_call; + } + + read_unlock(&rx->call_lock); + + if (!create || !trans) + return ERR_PTR(-EBADSLT); + + /* not yet present - create a candidate for a new record and then + * redo the search */ + candidate = rxrpc_alloc_client_call(rx, trans, bundle, gfp); + if (IS_ERR(candidate)) { + _leave(" = %ld", PTR_ERR(candidate)); + return candidate; + } + + candidate->user_call_ID = user_call_ID; + __set_bit(RXRPC_CALL_HAS_USERID, &candidate->flags); + + write_lock(&rx->call_lock); + + pp = &rx->calls.rb_node; + parent = NULL; + while (*pp) { + parent = *pp; + call = rb_entry(parent, struct rxrpc_call, sock_node); + + if (user_call_ID < call->user_call_ID) + pp = &(*pp)->rb_left; + else if (user_call_ID > call->user_call_ID) + pp = &(*pp)->rb_right; + else + goto found_extant_second; + } + + /* second search also failed; add the new call */ + call = candidate; + candidate = NULL; + rxrpc_get_call(call); + + rb_link_node(&call->sock_node, parent, pp); + rb_insert_color(&call->sock_node, &rx->calls); + write_unlock(&rx->call_lock); + + write_lock_bh(&rxrpc_call_lock); + list_add_tail(&call->link, &rxrpc_calls); + write_unlock_bh(&rxrpc_call_lock); + + _net("CALL new %d on CONN %d", call->debug_id, call->conn->debug_id); + + _leave(" = %p [new]", call); + return call; + + /* we found the call in the list immediately */ +found_extant_call: + rxrpc_get_call(call); + read_unlock(&rx->call_lock); + _leave(" = %p [extant %d]", call, atomic_read(&call->usage)); + return call; + + /* we found the call on the second time through the list */ +found_extant_second: + rxrpc_get_call(call); + write_unlock(&rx->call_lock); + rxrpc_put_call(candidate); + _leave(" = %p [second %d]", call, atomic_read(&call->usage)); + return call; +} + +/* + * set up an incoming call + * - called in process context with IRQs enabled + */ +struct rxrpc_call *rxrpc_incoming_call(struct rxrpc_sock *rx, + struct rxrpc_connection *conn, + struct rxrpc_header *hdr, + gfp_t gfp) +{ + struct rxrpc_call *call, *candidate; + struct rb_node **p, *parent; + __be32 call_id; + + _enter(",%d,,%x", conn->debug_id, gfp); + + ASSERT(rx != NULL); + + candidate = rxrpc_alloc_call(gfp); + if (!candidate) + return ERR_PTR(-EBUSY); + + candidate->socket = rx; + candidate->conn = conn; + candidate->cid = hdr->cid; + candidate->call_id = hdr->callNumber; + candidate->channel = ntohl(hdr->cid) & RXRPC_CHANNELMASK; + candidate->rx_data_post = 0; + candidate->state = RXRPC_CALL_SERVER_ACCEPTING; + if (conn->security_ix > 0) + candidate->state = RXRPC_CALL_SERVER_SECURING; + + write_lock_bh(&conn->lock); + + /* set the channel for this call */ + call = conn->channels[candidate->channel]; + _debug("channel[%u] is %p", candidate->channel, call); + if (call && call->call_id == hdr->callNumber) { + /* already set; must've been a duplicate packet */ + _debug("extant call [%d]", call->state); + ASSERTCMP(call->conn, ==, conn); + + read_lock(&call->state_lock); + switch (call->state) { + case RXRPC_CALL_LOCALLY_ABORTED: + if (!test_and_set_bit(RXRPC_CALL_ABORT, &call->events)) + schedule_work(&call->processor); + case RXRPC_CALL_REMOTELY_ABORTED: + read_unlock(&call->state_lock); + goto aborted_call; + default: + rxrpc_get_call(call); + read_unlock(&call->state_lock); + goto extant_call; + } + } + + if (call) { + /* it seems the channel is still in use from the previous call + * - ditch the old binding if its call is now complete */ + _debug("CALL: %u { %s }", + call->debug_id, rxrpc_call_states[call->state]); + + if (call->state >= RXRPC_CALL_COMPLETE) { + conn->channels[call->channel] = NULL; + } else { + write_unlock_bh(&conn->lock); + kmem_cache_free(rxrpc_call_jar, candidate); + _leave(" = -EBUSY"); + return ERR_PTR(-EBUSY); + } + } + + /* check the call number isn't duplicate */ + _debug("check dup"); + call_id = hdr->callNumber; + p = &conn->calls.rb_node; + parent = NULL; + while (*p) { + parent = *p; + call = rb_entry(parent, struct rxrpc_call, conn_node); + + if (call_id < call->call_id) + p = &(*p)->rb_left; + else if (call_id > call->call_id) + p = &(*p)->rb_right; + else + goto old_call; + } + + /* make the call available */ + _debug("new call"); + call = candidate; + candidate = NULL; + rb_link_node(&call->conn_node, parent, p); + rb_insert_color(&call->conn_node, &conn->calls); + conn->channels[call->channel] = call; + sock_hold(&rx->sk); + atomic_inc(&conn->usage); + write_unlock_bh(&conn->lock); + + spin_lock(&conn->trans->peer->lock); + list_add(&call->error_link, &conn->trans->peer->error_targets); + spin_unlock(&conn->trans->peer->lock); + + write_lock_bh(&rxrpc_call_lock); + list_add_tail(&call->link, &rxrpc_calls); + write_unlock_bh(&rxrpc_call_lock); + + _net("CALL incoming %d on CONN %d", call->debug_id, call->conn->debug_id); + + call->lifetimer.expires = jiffies + rxrpc_call_max_lifetime * HZ; + add_timer(&call->lifetimer); + _leave(" = %p {%d} [new]", call, call->debug_id); + return call; + +extant_call: + write_unlock_bh(&conn->lock); + kmem_cache_free(rxrpc_call_jar, candidate); + _leave(" = %p {%d} [extant]", call, call ? call->debug_id : -1); + return call; + +aborted_call: + write_unlock_bh(&conn->lock); + kmem_cache_free(rxrpc_call_jar, candidate); + _leave(" = -ECONNABORTED"); + return ERR_PTR(-ECONNABORTED); + +old_call: + write_unlock_bh(&conn->lock); + kmem_cache_free(rxrpc_call_jar, candidate); + _leave(" = -ECONNRESET [old]"); + return ERR_PTR(-ECONNRESET); +} + +/* + * find an extant server call + * - called in process context with IRQs enabled + */ +struct rxrpc_call *rxrpc_find_server_call(struct rxrpc_sock *rx, + unsigned long user_call_ID) +{ + struct rxrpc_call *call; + struct rb_node *p; + + _enter("%p,%lx", rx, user_call_ID); + + /* search the extant calls for one that matches the specified user + * ID */ + read_lock(&rx->call_lock); + + p = rx->calls.rb_node; + while (p) { + call = rb_entry(p, struct rxrpc_call, sock_node); + + if (user_call_ID < call->user_call_ID) + p = p->rb_left; + else if (user_call_ID > call->user_call_ID) + p = p->rb_right; + else + goto found_extant_call; + } + + read_unlock(&rx->call_lock); + _leave(" = NULL"); + return NULL; + + /* we found the call in the list immediately */ +found_extant_call: + rxrpc_get_call(call); + read_unlock(&rx->call_lock); + _leave(" = %p [%d]", call, atomic_read(&call->usage)); + return call; +} + +/* + * detach a call from a socket and set up for release + */ +void rxrpc_release_call(struct rxrpc_call *call) +{ + struct rxrpc_sock *rx = call->socket; + + _enter("{%d,%d,%d,%d}", + call->debug_id, atomic_read(&call->usage), + atomic_read(&call->ackr_not_idle), + call->rx_first_oos); + + spin_lock_bh(&call->lock); + if (test_and_set_bit(RXRPC_CALL_RELEASED, &call->flags)) + BUG(); + spin_unlock_bh(&call->lock); + + /* dissociate from the socket + * - the socket's ref on the call is passed to the death timer + */ + _debug("RELEASE CALL %p (%d CONN %p)", + call, call->debug_id, call->conn); + + write_lock_bh(&rx->call_lock); + if (!list_empty(&call->accept_link)) { + _debug("unlinking once-pending call %p { e=%lx f=%lx }", + call, call->events, call->flags); + ASSERT(!test_bit(RXRPC_CALL_HAS_USERID, &call->flags)); + list_del_init(&call->accept_link); + sk_acceptq_removed(&rx->sk); + } else if (test_bit(RXRPC_CALL_HAS_USERID, &call->flags)) { + rb_erase(&call->sock_node, &rx->calls); + memset(&call->sock_node, 0xdd, sizeof(call->sock_node)); + clear_bit(RXRPC_CALL_HAS_USERID, &call->flags); + } + write_unlock_bh(&rx->call_lock); + + if (call->conn->out_clientflag) + spin_lock(&call->conn->trans->client_lock); + write_lock_bh(&call->conn->lock); + + /* free up the channel for reuse */ + if (call->conn->out_clientflag) { + call->conn->avail_calls++; + if (call->conn->avail_calls == RXRPC_MAXCALLS) + list_move_tail(&call->conn->bundle_link, + &call->conn->bundle->unused_conns); + else if (call->conn->avail_calls == 1) + list_move_tail(&call->conn->bundle_link, + &call->conn->bundle->avail_conns); + } + + write_lock(&call->state_lock); + if (call->conn->channels[call->channel] == call) + call->conn->channels[call->channel] = NULL; + + if (call->state < RXRPC_CALL_COMPLETE && + call->state != RXRPC_CALL_CLIENT_FINAL_ACK) { + _debug("+++ ABORTING STATE %d +++\n", call->state); + call->state = RXRPC_CALL_LOCALLY_ABORTED; + call->abort_code = RX_CALL_DEAD; + set_bit(RXRPC_CALL_ABORT, &call->events); + schedule_work(&call->processor); + } + write_unlock(&call->state_lock); + write_unlock_bh(&call->conn->lock); + if (call->conn->out_clientflag) + spin_unlock(&call->conn->trans->client_lock); + + if (!skb_queue_empty(&call->rx_queue) || + !skb_queue_empty(&call->rx_oos_queue)) { + struct rxrpc_skb_priv *sp; + struct sk_buff *skb; + + _debug("purge Rx queues"); + + spin_lock_bh(&call->lock); + while ((skb = skb_dequeue(&call->rx_queue)) || + (skb = skb_dequeue(&call->rx_oos_queue))) { + sp = rxrpc_skb(skb); + if (sp->call) { + ASSERTCMP(sp->call, ==, call); + rxrpc_put_call(call); + sp->call = NULL; + } + skb->destructor = NULL; + spin_unlock_bh(&call->lock); + + _debug("- zap %s %%%u #%u", + rxrpc_pkts[sp->hdr.type], + ntohl(sp->hdr.serial), + ntohl(sp->hdr.seq)); + rxrpc_free_skb(skb); + spin_lock_bh(&call->lock); + } + spin_unlock_bh(&call->lock); + + ASSERTCMP(call->state, !=, RXRPC_CALL_COMPLETE); + } + + del_timer_sync(&call->resend_timer); + del_timer_sync(&call->ack_timer); + del_timer_sync(&call->lifetimer); + call->deadspan.expires = jiffies + rxrpc_dead_call_timeout * HZ; + add_timer(&call->deadspan); + + _leave(""); +} + +/* + * handle a dead call being ready for reaping + */ +static void rxrpc_dead_call_expired(unsigned long _call) +{ + struct rxrpc_call *call = (struct rxrpc_call *) _call; + + _enter("{%d}", call->debug_id); + + write_lock_bh(&call->state_lock); + call->state = RXRPC_CALL_DEAD; + write_unlock_bh(&call->state_lock); + rxrpc_put_call(call); +} + +/* + * mark a call as to be released, aborting it if it's still in progress + * - called with softirqs disabled + */ +static void rxrpc_mark_call_released(struct rxrpc_call *call) +{ + bool sched; + + write_lock(&call->state_lock); + if (call->state < RXRPC_CALL_DEAD) { + sched = false; + if (call->state < RXRPC_CALL_COMPLETE) { + _debug("abort call %p", call); + call->state = RXRPC_CALL_LOCALLY_ABORTED; + call->abort_code = RX_CALL_DEAD; + if (!test_and_set_bit(RXRPC_CALL_ABORT, &call->events)) + sched = true; + } + if (!test_and_set_bit(RXRPC_CALL_RELEASE, &call->events)) + sched = true; + if (sched) + schedule_work(&call->processor); + } + write_unlock(&call->state_lock); +} + +/* + * release all the calls associated with a socket + */ +void rxrpc_release_calls_on_socket(struct rxrpc_sock *rx) +{ + struct rxrpc_call *call; + struct rb_node *p; + + _enter("%p", rx); + + read_lock_bh(&rx->call_lock); + + /* mark all the calls as no longer wanting incoming packets */ + for (p = rb_first(&rx->calls); p; p = rb_next(p)) { + call = rb_entry(p, struct rxrpc_call, sock_node); + rxrpc_mark_call_released(call); + } + + /* kill the not-yet-accepted incoming calls */ + list_for_each_entry(call, &rx->secureq, accept_link) { + rxrpc_mark_call_released(call); + } + + list_for_each_entry(call, &rx->acceptq, accept_link) { + rxrpc_mark_call_released(call); + } + + read_unlock_bh(&rx->call_lock); + _leave(""); +} + +/* + * release a call + */ +void __rxrpc_put_call(struct rxrpc_call *call) +{ + ASSERT(call != NULL); + + _enter("%p{u=%d}", call, atomic_read(&call->usage)); + + ASSERTCMP(atomic_read(&call->usage), >, 0); + + if (atomic_dec_and_test(&call->usage)) { + _debug("call %d dead", call->debug_id); + ASSERTCMP(call->state, ==, RXRPC_CALL_DEAD); + schedule_work(&call->destroyer); + } + _leave(""); +} + +/* + * clean up a call + */ +static void rxrpc_cleanup_call(struct rxrpc_call *call) +{ + _net("DESTROY CALL %d", call->debug_id); + + ASSERT(call->socket); + + memset(&call->sock_node, 0xcd, sizeof(call->sock_node)); + + del_timer_sync(&call->lifetimer); + del_timer_sync(&call->deadspan); + del_timer_sync(&call->ack_timer); + del_timer_sync(&call->resend_timer); + + ASSERT(test_bit(RXRPC_CALL_RELEASED, &call->flags)); + ASSERTCMP(call->events, ==, 0); + if (work_pending(&call->processor)) { + _debug("defer destroy"); + schedule_work(&call->destroyer); + return; + } + + if (call->conn) { + spin_lock(&call->conn->trans->peer->lock); + list_del(&call->error_link); + spin_unlock(&call->conn->trans->peer->lock); + + write_lock_bh(&call->conn->lock); + rb_erase(&call->conn_node, &call->conn->calls); + write_unlock_bh(&call->conn->lock); + rxrpc_put_connection(call->conn); + } + + if (call->acks_window) { + _debug("kill Tx window %d", + CIRC_CNT(call->acks_head, call->acks_tail, + call->acks_winsz)); + smp_mb(); + while (CIRC_CNT(call->acks_head, call->acks_tail, + call->acks_winsz) > 0) { + struct rxrpc_skb_priv *sp; + unsigned long _skb; + + _skb = call->acks_window[call->acks_tail] & ~1; + sp = rxrpc_skb((struct sk_buff *) _skb); + _debug("+++ clear Tx %u", ntohl(sp->hdr.seq)); + rxrpc_free_skb((struct sk_buff *) _skb); + call->acks_tail = + (call->acks_tail + 1) & (call->acks_winsz - 1); + } + + kfree(call->acks_window); + } + + rxrpc_free_skb(call->tx_pending); + + rxrpc_purge_queue(&call->rx_queue); + ASSERT(skb_queue_empty(&call->rx_oos_queue)); + sock_put(&call->socket->sk); + kmem_cache_free(rxrpc_call_jar, call); +} + +/* + * destroy a call + */ +static void rxrpc_destroy_call(struct work_struct *work) +{ + struct rxrpc_call *call = + container_of(work, struct rxrpc_call, destroyer); + + _enter("%p{%d,%d,%p}", + call, atomic_read(&call->usage), call->channel, call->conn); + + ASSERTCMP(call->state, ==, RXRPC_CALL_DEAD); + + write_lock_bh(&rxrpc_call_lock); + list_del_init(&call->link); + write_unlock_bh(&rxrpc_call_lock); + + rxrpc_cleanup_call(call); + _leave(""); +} + +/* + * preemptively destroy all the call records from a transport endpoint rather + * than waiting for them to time out + */ +void __exit rxrpc_destroy_all_calls(void) +{ + struct rxrpc_call *call; + + _enter(""); + write_lock_bh(&rxrpc_call_lock); + + while (!list_empty(&rxrpc_calls)) { + call = list_entry(rxrpc_calls.next, struct rxrpc_call, link); + _debug("Zapping call %p", call); + + list_del_init(&call->link); + + switch (atomic_read(&call->usage)) { + case 0: + ASSERTCMP(call->state, ==, RXRPC_CALL_DEAD); + break; + case 1: + if (del_timer_sync(&call->deadspan) != 0 && + call->state != RXRPC_CALL_DEAD) + rxrpc_dead_call_expired((unsigned long) call); + if (call->state != RXRPC_CALL_DEAD) + break; + default: + printk(KERN_ERR "RXRPC:" + " Call %p still in use (%d,%d,%s,%lx,%lx)!\n", + call, atomic_read(&call->usage), + atomic_read(&call->ackr_not_idle), + rxrpc_call_states[call->state], + call->flags, call->events); + if (!skb_queue_empty(&call->rx_queue)) + printk(KERN_ERR"RXRPC: Rx queue occupied\n"); + if (!skb_queue_empty(&call->rx_oos_queue)) + printk(KERN_ERR"RXRPC: OOS queue occupied\n"); + break; + } + + write_unlock_bh(&rxrpc_call_lock); + cond_resched(); + write_lock_bh(&rxrpc_call_lock); + } + + write_unlock_bh(&rxrpc_call_lock); + _leave(""); +} + +/* + * handle call lifetime being exceeded + */ +static void rxrpc_call_life_expired(unsigned long _call) +{ + struct rxrpc_call *call = (struct rxrpc_call *) _call; + + if (call->state >= RXRPC_CALL_COMPLETE) + return; + + _enter("{%d}", call->debug_id); + read_lock_bh(&call->state_lock); + if (call->state < RXRPC_CALL_COMPLETE) { + set_bit(RXRPC_CALL_LIFE_TIMER, &call->events); + schedule_work(&call->processor); + } + read_unlock_bh(&call->state_lock); +} + +/* + * handle resend timer expiry + */ +static void rxrpc_resend_time_expired(unsigned long _call) +{ + struct rxrpc_call *call = (struct rxrpc_call *) _call; + + _enter("{%d}", call->debug_id); + + if (call->state >= RXRPC_CALL_COMPLETE) + return; + + read_lock_bh(&call->state_lock); + clear_bit(RXRPC_CALL_RUN_RTIMER, &call->flags); + if (call->state < RXRPC_CALL_COMPLETE && + !test_and_set_bit(RXRPC_CALL_RESEND_TIMER, &call->events)) + schedule_work(&call->processor); + read_unlock_bh(&call->state_lock); +} + +/* + * handle ACK timer expiry + */ +static void rxrpc_ack_time_expired(unsigned long _call) +{ + struct rxrpc_call *call = (struct rxrpc_call *) _call; + + _enter("{%d}", call->debug_id); + + if (call->state >= RXRPC_CALL_COMPLETE) + return; + + read_lock_bh(&call->state_lock); + if (call->state < RXRPC_CALL_COMPLETE && + !test_and_set_bit(RXRPC_CALL_ACK, &call->events)) + schedule_work(&call->processor); + read_unlock_bh(&call->state_lock); +} diff --git a/net/rxrpc/ar-connection.c b/net/rxrpc/ar-connection.c new file mode 100644 index 000000000000..01eb33c30571 --- /dev/null +++ b/net/rxrpc/ar-connection.c @@ -0,0 +1,895 @@ +/* RxRPC virtual connection handler + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include "ar-internal.h" + +static void rxrpc_connection_reaper(struct work_struct *work); + +LIST_HEAD(rxrpc_connections); +DEFINE_RWLOCK(rxrpc_connection_lock); +static unsigned long rxrpc_connection_timeout = 10 * 60; +static DECLARE_DELAYED_WORK(rxrpc_connection_reap, rxrpc_connection_reaper); + +/* + * allocate a new client connection bundle + */ +static struct rxrpc_conn_bundle *rxrpc_alloc_bundle(gfp_t gfp) +{ + struct rxrpc_conn_bundle *bundle; + + _enter(""); + + bundle = kzalloc(sizeof(struct rxrpc_conn_bundle), gfp); + if (bundle) { + INIT_LIST_HEAD(&bundle->unused_conns); + INIT_LIST_HEAD(&bundle->avail_conns); + INIT_LIST_HEAD(&bundle->busy_conns); + init_waitqueue_head(&bundle->chanwait); + atomic_set(&bundle->usage, 1); + } + + _leave(" = %p", bundle); + return bundle; +} + +/* + * compare bundle parameters with what we're looking for + * - return -ve, 0 or +ve + */ +static inline +int rxrpc_cmp_bundle(const struct rxrpc_conn_bundle *bundle, + struct key *key, __be16 service_id) +{ + return (bundle->service_id - service_id) ?: + ((unsigned long) bundle->key - (unsigned long) key); +} + +/* + * get bundle of client connections that a client socket can make use of + */ +struct rxrpc_conn_bundle *rxrpc_get_bundle(struct rxrpc_sock *rx, + struct rxrpc_transport *trans, + struct key *key, + __be16 service_id, + gfp_t gfp) +{ + struct rxrpc_conn_bundle *bundle, *candidate; + struct rb_node *p, *parent, **pp; + + _enter("%p{%x},%x,%hx,", + rx, key_serial(key), trans->debug_id, ntohl(service_id)); + + if (rx->trans == trans && rx->bundle) { + atomic_inc(&rx->bundle->usage); + return rx->bundle; + } + + /* search the extant bundles first for one that matches the specified + * user ID */ + spin_lock(&trans->client_lock); + + p = trans->bundles.rb_node; + while (p) { + bundle = rb_entry(p, struct rxrpc_conn_bundle, node); + + if (rxrpc_cmp_bundle(bundle, key, service_id) < 0) + p = p->rb_left; + else if (rxrpc_cmp_bundle(bundle, key, service_id) > 0) + p = p->rb_right; + else + goto found_extant_bundle; + } + + spin_unlock(&trans->client_lock); + + /* not yet present - create a candidate for a new record and then + * redo the search */ + candidate = rxrpc_alloc_bundle(gfp); + if (!candidate) { + _leave(" = -ENOMEM"); + return ERR_PTR(-ENOMEM); + } + + candidate->key = key_get(key); + candidate->service_id = service_id; + + spin_lock(&trans->client_lock); + + pp = &trans->bundles.rb_node; + parent = NULL; + while (*pp) { + parent = *pp; + bundle = rb_entry(parent, struct rxrpc_conn_bundle, node); + + if (rxrpc_cmp_bundle(bundle, key, service_id) < 0) + pp = &(*pp)->rb_left; + else if (rxrpc_cmp_bundle(bundle, key, service_id) > 0) + pp = &(*pp)->rb_right; + else + goto found_extant_second; + } + + /* second search also failed; add the new bundle */ + bundle = candidate; + candidate = NULL; + + rb_link_node(&bundle->node, parent, pp); + rb_insert_color(&bundle->node, &trans->bundles); + spin_unlock(&trans->client_lock); + _net("BUNDLE new on trans %d", trans->debug_id); + if (!rx->bundle && rx->sk.sk_state == RXRPC_CLIENT_CONNECTED) { + atomic_inc(&bundle->usage); + rx->bundle = bundle; + } + _leave(" = %p [new]", bundle); + return bundle; + + /* we found the bundle in the list immediately */ +found_extant_bundle: + atomic_inc(&bundle->usage); + spin_unlock(&trans->client_lock); + _net("BUNDLE old on trans %d", trans->debug_id); + if (!rx->bundle && rx->sk.sk_state == RXRPC_CLIENT_CONNECTED) { + atomic_inc(&bundle->usage); + rx->bundle = bundle; + } + _leave(" = %p [extant %d]", bundle, atomic_read(&bundle->usage)); + return bundle; + + /* we found the bundle on the second time through the list */ +found_extant_second: + atomic_inc(&bundle->usage); + spin_unlock(&trans->client_lock); + kfree(candidate); + _net("BUNDLE old2 on trans %d", trans->debug_id); + if (!rx->bundle && rx->sk.sk_state == RXRPC_CLIENT_CONNECTED) { + atomic_inc(&bundle->usage); + rx->bundle = bundle; + } + _leave(" = %p [second %d]", bundle, atomic_read(&bundle->usage)); + return bundle; +} + +/* + * release a bundle + */ +void rxrpc_put_bundle(struct rxrpc_transport *trans, + struct rxrpc_conn_bundle *bundle) +{ + _enter("%p,%p{%d}",trans, bundle, atomic_read(&bundle->usage)); + + if (atomic_dec_and_lock(&bundle->usage, &trans->client_lock)) { + _debug("Destroy bundle"); + rb_erase(&bundle->node, &trans->bundles); + spin_unlock(&trans->client_lock); + ASSERT(list_empty(&bundle->unused_conns)); + ASSERT(list_empty(&bundle->avail_conns)); + ASSERT(list_empty(&bundle->busy_conns)); + ASSERTCMP(bundle->num_conns, ==, 0); + key_put(bundle->key); + kfree(bundle); + } + + _leave(""); +} + +/* + * allocate a new connection + */ +static struct rxrpc_connection *rxrpc_alloc_connection(gfp_t gfp) +{ + struct rxrpc_connection *conn; + + _enter(""); + + conn = kzalloc(sizeof(struct rxrpc_connection), gfp); + if (conn) { + INIT_WORK(&conn->processor, &rxrpc_process_connection); + INIT_LIST_HEAD(&conn->bundle_link); + conn->calls = RB_ROOT; + skb_queue_head_init(&conn->rx_queue); + rwlock_init(&conn->lock); + spin_lock_init(&conn->state_lock); + atomic_set(&conn->usage, 1); + conn->debug_id = atomic_inc_return(&rxrpc_debug_id); + conn->avail_calls = RXRPC_MAXCALLS; + conn->size_align = 4; + conn->header_size = sizeof(struct rxrpc_header); + } + + _leave(" = %p{%d}", conn, conn->debug_id); + return conn; +} + +/* + * assign a connection ID to a connection and add it to the transport's + * connection lookup tree + * - called with transport client lock held + */ +static void rxrpc_assign_connection_id(struct rxrpc_connection *conn) +{ + struct rxrpc_connection *xconn; + struct rb_node *parent, **p; + __be32 epoch; + u32 real_conn_id; + + _enter(""); + + epoch = conn->epoch; + + write_lock_bh(&conn->trans->conn_lock); + + conn->trans->conn_idcounter += RXRPC_CID_INC; + if (conn->trans->conn_idcounter < RXRPC_CID_INC) + conn->trans->conn_idcounter = RXRPC_CID_INC; + real_conn_id = conn->trans->conn_idcounter; + +attempt_insertion: + parent = NULL; + p = &conn->trans->client_conns.rb_node; + + while (*p) { + parent = *p; + xconn = rb_entry(parent, struct rxrpc_connection, node); + + if (epoch < xconn->epoch) + p = &(*p)->rb_left; + else if (epoch > xconn->epoch) + p = &(*p)->rb_right; + else if (real_conn_id < xconn->real_conn_id) + p = &(*p)->rb_left; + else if (real_conn_id > xconn->real_conn_id) + p = &(*p)->rb_right; + else + goto id_exists; + } + + /* we've found a suitable hole - arrange for this connection to occupy + * it */ + rb_link_node(&conn->node, parent, p); + rb_insert_color(&conn->node, &conn->trans->client_conns); + + conn->real_conn_id = real_conn_id; + conn->cid = htonl(real_conn_id); + write_unlock_bh(&conn->trans->conn_lock); + _leave(" [CONNID %x CID %x]", real_conn_id, ntohl(conn->cid)); + return; + + /* we found a connection with the proposed ID - walk the tree from that + * point looking for the next unused ID */ +id_exists: + for (;;) { + real_conn_id += RXRPC_CID_INC; + if (real_conn_id < RXRPC_CID_INC) { + real_conn_id = RXRPC_CID_INC; + conn->trans->conn_idcounter = real_conn_id; + goto attempt_insertion; + } + + parent = rb_next(parent); + if (!parent) + goto attempt_insertion; + + xconn = rb_entry(parent, struct rxrpc_connection, node); + if (epoch < xconn->epoch || + real_conn_id < xconn->real_conn_id) + goto attempt_insertion; + } +} + +/* + * add a call to a connection's call-by-ID tree + */ +static void rxrpc_add_call_ID_to_conn(struct rxrpc_connection *conn, + struct rxrpc_call *call) +{ + struct rxrpc_call *xcall; + struct rb_node *parent, **p; + __be32 call_id; + + write_lock_bh(&conn->lock); + + call_id = call->call_id; + p = &conn->calls.rb_node; + parent = NULL; + while (*p) { + parent = *p; + xcall = rb_entry(parent, struct rxrpc_call, conn_node); + + if (call_id < xcall->call_id) + p = &(*p)->rb_left; + else if (call_id > xcall->call_id) + p = &(*p)->rb_right; + else + BUG(); + } + + rb_link_node(&call->conn_node, parent, p); + rb_insert_color(&call->conn_node, &conn->calls); + + write_unlock_bh(&conn->lock); +} + +/* + * connect a call on an exclusive connection + */ +static int rxrpc_connect_exclusive(struct rxrpc_sock *rx, + struct rxrpc_transport *trans, + __be16 service_id, + struct rxrpc_call *call, + gfp_t gfp) +{ + struct rxrpc_connection *conn; + int chan, ret; + + _enter(""); + + conn = rx->conn; + if (!conn) { + /* not yet present - create a candidate for a new connection + * and then redo the check */ + conn = rxrpc_alloc_connection(gfp); + if (IS_ERR(conn)) { + _leave(" = %ld", PTR_ERR(conn)); + return PTR_ERR(conn); + } + + conn->trans = trans; + conn->bundle = NULL; + conn->service_id = service_id; + conn->epoch = rxrpc_epoch; + conn->in_clientflag = 0; + conn->out_clientflag = RXRPC_CLIENT_INITIATED; + conn->cid = 0; + conn->state = RXRPC_CONN_CLIENT; + conn->avail_calls = RXRPC_MAXCALLS; + conn->security_level = rx->min_sec_level; + conn->key = key_get(rx->key); + + ret = rxrpc_init_client_conn_security(conn); + if (ret < 0) { + key_put(conn->key); + kfree(conn); + _leave(" = %d [key]", ret); + return ret; + } + + write_lock_bh(&rxrpc_connection_lock); + list_add_tail(&conn->link, &rxrpc_connections); + write_unlock_bh(&rxrpc_connection_lock); + + spin_lock(&trans->client_lock); + atomic_inc(&trans->usage); + + _net("CONNECT EXCL new %d on TRANS %d", + conn->debug_id, conn->trans->debug_id); + + rxrpc_assign_connection_id(conn); + rx->conn = conn; + } + + /* we've got a connection with a free channel and we can now attach the + * call to it + * - we're holding the transport's client lock + * - we're holding a reference on the connection + */ + for (chan = 0; chan < RXRPC_MAXCALLS; chan++) + if (!conn->channels[chan]) + goto found_channel; + goto no_free_channels; + +found_channel: + atomic_inc(&conn->usage); + conn->channels[chan] = call; + call->conn = conn; + call->channel = chan; + call->cid = conn->cid | htonl(chan); + call->call_id = htonl(++conn->call_counter); + + _net("CONNECT client on conn %d chan %d as call %x", + conn->debug_id, chan, ntohl(call->call_id)); + + spin_unlock(&trans->client_lock); + + rxrpc_add_call_ID_to_conn(conn, call); + _leave(" = 0"); + return 0; + +no_free_channels: + spin_unlock(&trans->client_lock); + _leave(" = -ENOSR"); + return -ENOSR; +} + +/* + * find a connection for a call + * - called in process context with IRQs enabled + */ +int rxrpc_connect_call(struct rxrpc_sock *rx, + struct rxrpc_transport *trans, + struct rxrpc_conn_bundle *bundle, + struct rxrpc_call *call, + gfp_t gfp) +{ + struct rxrpc_connection *conn, *candidate; + int chan, ret; + + DECLARE_WAITQUEUE(myself, current); + + _enter("%p,%lx,", rx, call->user_call_ID); + + if (test_bit(RXRPC_SOCK_EXCLUSIVE_CONN, &rx->flags)) + return rxrpc_connect_exclusive(rx, trans, bundle->service_id, + call, gfp); + + spin_lock(&trans->client_lock); + for (;;) { + /* see if the bundle has a call slot available */ + if (!list_empty(&bundle->avail_conns)) { + _debug("avail"); + conn = list_entry(bundle->avail_conns.next, + struct rxrpc_connection, + bundle_link); + if (--conn->avail_calls == 0) + list_move(&conn->bundle_link, + &bundle->busy_conns); + atomic_inc(&conn->usage); + break; + } + + if (!list_empty(&bundle->unused_conns)) { + _debug("unused"); + conn = list_entry(bundle->unused_conns.next, + struct rxrpc_connection, + bundle_link); + atomic_inc(&conn->usage); + list_move(&conn->bundle_link, &bundle->avail_conns); + break; + } + + /* need to allocate a new connection */ + _debug("get new conn [%d]", bundle->num_conns); + + spin_unlock(&trans->client_lock); + + if (signal_pending(current)) + goto interrupted; + + if (bundle->num_conns >= 20) { + _debug("too many conns"); + + if (!(gfp & __GFP_WAIT)) { + _leave(" = -EAGAIN"); + return -EAGAIN; + } + + add_wait_queue(&bundle->chanwait, &myself); + for (;;) { + set_current_state(TASK_INTERRUPTIBLE); + if (bundle->num_conns < 20 || + !list_empty(&bundle->unused_conns) || + !list_empty(&bundle->avail_conns)) + break; + if (signal_pending(current)) + goto interrupted_dequeue; + schedule(); + } + remove_wait_queue(&bundle->chanwait, &myself); + __set_current_state(TASK_RUNNING); + spin_lock(&trans->client_lock); + continue; + } + + /* not yet present - create a candidate for a new connection and then + * redo the check */ + candidate = rxrpc_alloc_connection(gfp); + if (IS_ERR(candidate)) { + _leave(" = %ld", PTR_ERR(candidate)); + return PTR_ERR(candidate); + } + + candidate->trans = trans; + candidate->bundle = bundle; + candidate->service_id = bundle->service_id; + candidate->epoch = rxrpc_epoch; + candidate->in_clientflag = 0; + candidate->out_clientflag = RXRPC_CLIENT_INITIATED; + candidate->cid = 0; + candidate->state = RXRPC_CONN_CLIENT; + candidate->avail_calls = RXRPC_MAXCALLS; + candidate->security_level = rx->min_sec_level; + candidate->key = key_get(rx->key); + + ret = rxrpc_init_client_conn_security(candidate); + if (ret < 0) { + key_put(candidate->key); + kfree(candidate); + _leave(" = %d [key]", ret); + return ret; + } + + write_lock_bh(&rxrpc_connection_lock); + list_add_tail(&candidate->link, &rxrpc_connections); + write_unlock_bh(&rxrpc_connection_lock); + + spin_lock(&trans->client_lock); + + list_add(&candidate->bundle_link, &bundle->unused_conns); + bundle->num_conns++; + atomic_inc(&bundle->usage); + atomic_inc(&trans->usage); + + _net("CONNECT new %d on TRANS %d", + candidate->debug_id, candidate->trans->debug_id); + + rxrpc_assign_connection_id(candidate); + if (candidate->security) + candidate->security->prime_packet_security(candidate); + + /* leave the candidate lurking in zombie mode attached to the + * bundle until we're ready for it */ + rxrpc_put_connection(candidate); + candidate = NULL; + } + + /* we've got a connection with a free channel and we can now attach the + * call to it + * - we're holding the transport's client lock + * - we're holding a reference on the connection + * - we're holding a reference on the bundle + */ + for (chan = 0; chan < RXRPC_MAXCALLS; chan++) + if (!conn->channels[chan]) + goto found_channel; + BUG(); + +found_channel: + conn->channels[chan] = call; + call->conn = conn; + call->channel = chan; + call->cid = conn->cid | htonl(chan); + call->call_id = htonl(++conn->call_counter); + + _net("CONNECT client on conn %d chan %d as call %x", + conn->debug_id, chan, ntohl(call->call_id)); + + spin_unlock(&trans->client_lock); + + rxrpc_add_call_ID_to_conn(conn, call); + + _leave(" = 0"); + return 0; + +interrupted_dequeue: + remove_wait_queue(&bundle->chanwait, &myself); + __set_current_state(TASK_RUNNING); +interrupted: + _leave(" = -ERESTARTSYS"); + return -ERESTARTSYS; +} + +/* + * get a record of an incoming connection + */ +struct rxrpc_connection * +rxrpc_incoming_connection(struct rxrpc_transport *trans, + struct rxrpc_header *hdr, + gfp_t gfp) +{ + struct rxrpc_connection *conn, *candidate = NULL; + struct rb_node *p, **pp; + const char *new = "old"; + __be32 epoch; + u32 conn_id; + + _enter(""); + + ASSERT(hdr->flags & RXRPC_CLIENT_INITIATED); + + epoch = hdr->epoch; + conn_id = ntohl(hdr->cid) & RXRPC_CIDMASK; + + /* search the connection list first */ + read_lock_bh(&trans->conn_lock); + + p = trans->server_conns.rb_node; + while (p) { + conn = rb_entry(p, struct rxrpc_connection, node); + + _debug("maybe %x", conn->real_conn_id); + + if (epoch < conn->epoch) + p = p->rb_left; + else if (epoch > conn->epoch) + p = p->rb_right; + else if (conn_id < conn->real_conn_id) + p = p->rb_left; + else if (conn_id > conn->real_conn_id) + p = p->rb_right; + else + goto found_extant_connection; + } + read_unlock_bh(&trans->conn_lock); + + /* not yet present - create a candidate for a new record and then + * redo the search */ + candidate = rxrpc_alloc_connection(gfp); + if (!candidate) { + _leave(" = -ENOMEM"); + return ERR_PTR(-ENOMEM); + } + + candidate->trans = trans; + candidate->epoch = hdr->epoch; + candidate->cid = hdr->cid & __constant_cpu_to_be32(RXRPC_CIDMASK); + candidate->service_id = hdr->serviceId; + candidate->security_ix = hdr->securityIndex; + candidate->in_clientflag = RXRPC_CLIENT_INITIATED; + candidate->out_clientflag = 0; + candidate->real_conn_id = conn_id; + candidate->state = RXRPC_CONN_SERVER; + if (candidate->service_id) + candidate->state = RXRPC_CONN_SERVER_UNSECURED; + + write_lock_bh(&trans->conn_lock); + + pp = &trans->server_conns.rb_node; + p = NULL; + while (*pp) { + p = *pp; + conn = rb_entry(p, struct rxrpc_connection, node); + + if (epoch < conn->epoch) + pp = &(*pp)->rb_left; + else if (epoch > conn->epoch) + pp = &(*pp)->rb_right; + else if (conn_id < conn->real_conn_id) + pp = &(*pp)->rb_left; + else if (conn_id > conn->real_conn_id) + pp = &(*pp)->rb_right; + else + goto found_extant_second; + } + + /* we can now add the new candidate to the list */ + conn = candidate; + candidate = NULL; + rb_link_node(&conn->node, p, pp); + rb_insert_color(&conn->node, &trans->server_conns); + atomic_inc(&conn->trans->usage); + + write_unlock_bh(&trans->conn_lock); + + write_lock_bh(&rxrpc_connection_lock); + list_add_tail(&conn->link, &rxrpc_connections); + write_unlock_bh(&rxrpc_connection_lock); + + new = "new"; + +success: + _net("CONNECTION %s %d {%x}", new, conn->debug_id, conn->real_conn_id); + + _leave(" = %p {u=%d}", conn, atomic_read(&conn->usage)); + return conn; + + /* we found the connection in the list immediately */ +found_extant_connection: + if (hdr->securityIndex != conn->security_ix) { + read_unlock_bh(&trans->conn_lock); + goto security_mismatch; + } + atomic_inc(&conn->usage); + read_unlock_bh(&trans->conn_lock); + goto success; + + /* we found the connection on the second time through the list */ +found_extant_second: + if (hdr->securityIndex != conn->security_ix) { + write_unlock_bh(&trans->conn_lock); + goto security_mismatch; + } + atomic_inc(&conn->usage); + write_unlock_bh(&trans->conn_lock); + kfree(candidate); + goto success; + +security_mismatch: + kfree(candidate); + _leave(" = -EKEYREJECTED"); + return ERR_PTR(-EKEYREJECTED); +} + +/* + * find a connection based on transport and RxRPC connection ID for an incoming + * packet + */ +struct rxrpc_connection *rxrpc_find_connection(struct rxrpc_transport *trans, + struct rxrpc_header *hdr) +{ + struct rxrpc_connection *conn; + struct rb_node *p; + __be32 epoch; + u32 conn_id; + + _enter(",{%x,%x}", ntohl(hdr->cid), hdr->flags); + + read_lock_bh(&trans->conn_lock); + + conn_id = ntohl(hdr->cid) & RXRPC_CIDMASK; + epoch = hdr->epoch; + + if (hdr->flags & RXRPC_CLIENT_INITIATED) + p = trans->server_conns.rb_node; + else + p = trans->client_conns.rb_node; + + while (p) { + conn = rb_entry(p, struct rxrpc_connection, node); + + _debug("maybe %x", conn->real_conn_id); + + if (epoch < conn->epoch) + p = p->rb_left; + else if (epoch > conn->epoch) + p = p->rb_right; + else if (conn_id < conn->real_conn_id) + p = p->rb_left; + else if (conn_id > conn->real_conn_id) + p = p->rb_right; + else + goto found; + } + + read_unlock_bh(&trans->conn_lock); + _leave(" = NULL"); + return NULL; + +found: + atomic_inc(&conn->usage); + read_unlock_bh(&trans->conn_lock); + _leave(" = %p", conn); + return conn; +} + +/* + * release a virtual connection + */ +void rxrpc_put_connection(struct rxrpc_connection *conn) +{ + _enter("%p{u=%d,d=%d}", + conn, atomic_read(&conn->usage), conn->debug_id); + + ASSERTCMP(atomic_read(&conn->usage), >, 0); + + conn->put_time = xtime.tv_sec; + if (atomic_dec_and_test(&conn->usage)) { + _debug("zombie"); + schedule_delayed_work(&rxrpc_connection_reap, 0); + } + + _leave(""); +} + +/* + * destroy a virtual connection + */ +static void rxrpc_destroy_connection(struct rxrpc_connection *conn) +{ + _enter("%p{%d}", conn, atomic_read(&conn->usage)); + + ASSERTCMP(atomic_read(&conn->usage), ==, 0); + + _net("DESTROY CONN %d", conn->debug_id); + + if (conn->bundle) + rxrpc_put_bundle(conn->trans, conn->bundle); + + ASSERT(RB_EMPTY_ROOT(&conn->calls)); + rxrpc_purge_queue(&conn->rx_queue); + + rxrpc_clear_conn_security(conn); + rxrpc_put_transport(conn->trans); + kfree(conn); + _leave(""); +} + +/* + * reap dead connections + */ +void rxrpc_connection_reaper(struct work_struct *work) +{ + struct rxrpc_connection *conn, *_p; + unsigned long now, earliest, reap_time; + + LIST_HEAD(graveyard); + + _enter(""); + + now = xtime.tv_sec; + earliest = ULONG_MAX; + + write_lock_bh(&rxrpc_connection_lock); + list_for_each_entry_safe(conn, _p, &rxrpc_connections, link) { + _debug("reap CONN %d { u=%d,t=%ld }", + conn->debug_id, atomic_read(&conn->usage), + (long) now - (long) conn->put_time); + + if (likely(atomic_read(&conn->usage) > 0)) + continue; + + spin_lock(&conn->trans->client_lock); + write_lock(&conn->trans->conn_lock); + reap_time = conn->put_time + rxrpc_connection_timeout; + + if (atomic_read(&conn->usage) > 0) { + ; + } else if (reap_time <= now) { + list_move_tail(&conn->link, &graveyard); + if (conn->out_clientflag) + rb_erase(&conn->node, + &conn->trans->client_conns); + else + rb_erase(&conn->node, + &conn->trans->server_conns); + if (conn->bundle) { + list_del_init(&conn->bundle_link); + conn->bundle->num_conns--; + } + + } else if (reap_time < earliest) { + earliest = reap_time; + } + + write_unlock(&conn->trans->conn_lock); + spin_unlock(&conn->trans->client_lock); + } + write_unlock_bh(&rxrpc_connection_lock); + + if (earliest != ULONG_MAX) { + _debug("reschedule reaper %ld", (long) earliest - now); + ASSERTCMP(earliest, >, now); + schedule_delayed_work(&rxrpc_connection_reap, + (earliest - now) * HZ); + } + + /* then destroy all those pulled out */ + while (!list_empty(&graveyard)) { + conn = list_entry(graveyard.next, struct rxrpc_connection, + link); + list_del_init(&conn->link); + + ASSERTCMP(atomic_read(&conn->usage), ==, 0); + rxrpc_destroy_connection(conn); + } + + _leave(""); +} + +/* + * preemptively destroy all the connection records rather than waiting for them + * to time out + */ +void __exit rxrpc_destroy_all_connections(void) +{ + _enter(""); + + rxrpc_connection_timeout = 0; + cancel_delayed_work(&rxrpc_connection_reap); + schedule_delayed_work(&rxrpc_connection_reap, 0); + + _leave(""); +} diff --git a/net/rxrpc/ar-connevent.c b/net/rxrpc/ar-connevent.c new file mode 100644 index 000000000000..4b02815c1ded --- /dev/null +++ b/net/rxrpc/ar-connevent.c @@ -0,0 +1,387 @@ +/* connection-level event handling + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ar-internal.h" + +/* + * pass a connection-level abort onto all calls on that connection + */ +static void rxrpc_abort_calls(struct rxrpc_connection *conn, int state, + u32 abort_code) +{ + struct rxrpc_call *call; + struct rb_node *p; + + _enter("{%d},%x", conn->debug_id, abort_code); + + read_lock_bh(&conn->lock); + + for (p = rb_first(&conn->calls); p; p = rb_next(p)) { + call = rb_entry(p, struct rxrpc_call, conn_node); + write_lock(&call->state_lock); + if (call->state <= RXRPC_CALL_COMPLETE) { + call->state = state; + call->abort_code = abort_code; + if (state == RXRPC_CALL_LOCALLY_ABORTED) + set_bit(RXRPC_CALL_CONN_ABORT, &call->events); + else + set_bit(RXRPC_CALL_RCVD_ABORT, &call->events); + schedule_work(&call->processor); + } + write_unlock(&call->state_lock); + } + + read_unlock_bh(&conn->lock); + _leave(""); +} + +/* + * generate a connection-level abort + */ +static int rxrpc_abort_connection(struct rxrpc_connection *conn, + u32 error, u32 abort_code) +{ + struct rxrpc_header hdr; + struct msghdr msg; + struct kvec iov[2]; + __be32 word; + size_t len; + int ret; + + _enter("%d,,%u,%u", conn->debug_id, error, abort_code); + + /* generate a connection-level abort */ + spin_lock_bh(&conn->state_lock); + if (conn->state < RXRPC_CONN_REMOTELY_ABORTED) { + conn->state = RXRPC_CONN_LOCALLY_ABORTED; + conn->error = error; + spin_unlock_bh(&conn->state_lock); + } else { + spin_unlock_bh(&conn->state_lock); + _leave(" = 0 [already dead]"); + return 0; + } + + rxrpc_abort_calls(conn, RXRPC_CALL_LOCALLY_ABORTED, abort_code); + + msg.msg_name = &conn->trans->peer->srx.transport.sin; + msg.msg_namelen = sizeof(conn->trans->peer->srx.transport.sin); + msg.msg_control = NULL; + msg.msg_controllen = 0; + msg.msg_flags = 0; + + hdr.epoch = conn->epoch; + hdr.cid = conn->cid; + hdr.callNumber = 0; + hdr.seq = 0; + hdr.type = RXRPC_PACKET_TYPE_ABORT; + hdr.flags = conn->out_clientflag; + hdr.userStatus = 0; + hdr.securityIndex = conn->security_ix; + hdr._rsvd = 0; + hdr.serviceId = conn->service_id; + + word = htonl(abort_code); + + iov[0].iov_base = &hdr; + iov[0].iov_len = sizeof(hdr); + iov[1].iov_base = &word; + iov[1].iov_len = sizeof(word); + + len = iov[0].iov_len + iov[1].iov_len; + + hdr.serial = htonl(atomic_inc_return(&conn->serial)); + _proto("Tx CONN ABORT %%%u { %d }", ntohl(hdr.serial), abort_code); + + ret = kernel_sendmsg(conn->trans->local->socket, &msg, iov, 2, len); + if (ret < 0) { + _debug("sendmsg failed: %d", ret); + return -EAGAIN; + } + + _leave(" = 0"); + return 0; +} + +/* + * mark a call as being on a now-secured channel + * - must be called with softirqs disabled + */ +void rxrpc_call_is_secure(struct rxrpc_call *call) +{ + _enter("%p", call); + if (call) { + read_lock(&call->state_lock); + if (call->state < RXRPC_CALL_COMPLETE && + !test_and_set_bit(RXRPC_CALL_SECURED, &call->events)) + schedule_work(&call->processor); + read_unlock(&call->state_lock); + } +} + +/* + * connection-level Rx packet processor + */ +static int rxrpc_process_event(struct rxrpc_connection *conn, + struct sk_buff *skb, + u32 *_abort_code) +{ + struct rxrpc_skb_priv *sp = rxrpc_skb(skb); + __be32 tmp; + u32 serial; + int loop, ret; + + if (conn->state >= RXRPC_CONN_REMOTELY_ABORTED) + return -ECONNABORTED; + + serial = ntohl(sp->hdr.serial); + + switch (sp->hdr.type) { + case RXRPC_PACKET_TYPE_ABORT: + if (skb_copy_bits(skb, 0, &tmp, sizeof(tmp)) < 0) + return -EPROTO; + _proto("Rx ABORT %%%u { ac=%d }", serial, ntohl(tmp)); + + conn->state = RXRPC_CONN_REMOTELY_ABORTED; + rxrpc_abort_calls(conn, RXRPC_CALL_REMOTELY_ABORTED, + ntohl(tmp)); + return -ECONNABORTED; + + case RXRPC_PACKET_TYPE_CHALLENGE: + if (conn->security) + return conn->security->respond_to_challenge( + conn, skb, _abort_code); + return -EPROTO; + + case RXRPC_PACKET_TYPE_RESPONSE: + if (!conn->security) + return -EPROTO; + + ret = conn->security->verify_response(conn, skb, _abort_code); + if (ret < 0) + return ret; + + ret = conn->security->init_connection_security(conn); + if (ret < 0) + return ret; + + conn->security->prime_packet_security(conn); + read_lock_bh(&conn->lock); + spin_lock(&conn->state_lock); + + if (conn->state == RXRPC_CONN_SERVER_CHALLENGING) { + conn->state = RXRPC_CONN_SERVER; + for (loop = 0; loop < RXRPC_MAXCALLS; loop++) + rxrpc_call_is_secure(conn->channels[loop]); + } + + spin_unlock(&conn->state_lock); + read_unlock_bh(&conn->lock); + return 0; + + default: + return -EPROTO; + } +} + +/* + * set up security and issue a challenge + */ +static void rxrpc_secure_connection(struct rxrpc_connection *conn) +{ + u32 abort_code; + int ret; + + _enter("{%d}", conn->debug_id); + + ASSERT(conn->security_ix != 0); + + if (!conn->key) { + _debug("set up security"); + ret = rxrpc_init_server_conn_security(conn); + switch (ret) { + case 0: + break; + case -ENOENT: + abort_code = RX_CALL_DEAD; + goto abort; + default: + abort_code = RXKADNOAUTH; + goto abort; + } + } + + ASSERT(conn->security != NULL); + + if (conn->security->issue_challenge(conn) < 0) { + abort_code = RX_CALL_DEAD; + ret = -ENOMEM; + goto abort; + } + + _leave(""); + return; + +abort: + _debug("abort %d, %d", ret, abort_code); + rxrpc_abort_connection(conn, -ret, abort_code); + _leave(" [aborted]"); +} + +/* + * connection-level event processor + */ +void rxrpc_process_connection(struct work_struct *work) +{ + struct rxrpc_connection *conn = + container_of(work, struct rxrpc_connection, processor); + struct rxrpc_skb_priv *sp; + struct sk_buff *skb; + u32 abort_code = RX_PROTOCOL_ERROR; + int ret; + + _enter("{%d}", conn->debug_id); + + atomic_inc(&conn->usage); + + if (test_and_clear_bit(RXRPC_CONN_CHALLENGE, &conn->events)) { + rxrpc_secure_connection(conn); + rxrpc_put_connection(conn); + } + + /* go through the conn-level event packets, releasing the ref on this + * connection that each one has when we've finished with it */ + while ((skb = skb_dequeue(&conn->rx_queue))) { + sp = rxrpc_skb(skb); + + ret = rxrpc_process_event(conn, skb, &abort_code); + switch (ret) { + case -EPROTO: + case -EKEYEXPIRED: + case -EKEYREJECTED: + goto protocol_error; + case -EAGAIN: + goto requeue_and_leave; + case -ECONNABORTED: + default: + rxrpc_put_connection(conn); + rxrpc_free_skb(skb); + break; + } + } + +out: + rxrpc_put_connection(conn); + _leave(""); + return; + +requeue_and_leave: + skb_queue_head(&conn->rx_queue, skb); + goto out; + +protocol_error: + if (rxrpc_abort_connection(conn, -ret, abort_code) < 0) + goto requeue_and_leave; + rxrpc_put_connection(conn); + rxrpc_free_skb(skb); + _leave(" [EPROTO]"); + goto out; +} + +/* + * reject packets through the local endpoint + */ +void rxrpc_reject_packets(struct work_struct *work) +{ + union { + struct sockaddr sa; + struct sockaddr_in sin; + } sa; + struct rxrpc_skb_priv *sp; + struct rxrpc_header hdr; + struct rxrpc_local *local; + struct sk_buff *skb; + struct msghdr msg; + struct kvec iov[2]; + size_t size; + __be32 code; + + local = container_of(work, struct rxrpc_local, rejecter); + rxrpc_get_local(local); + + _enter("%d", local->debug_id); + + iov[0].iov_base = &hdr; + iov[0].iov_len = sizeof(hdr); + iov[1].iov_base = &code; + iov[1].iov_len = sizeof(code); + size = sizeof(hdr) + sizeof(code); + + msg.msg_name = &sa; + msg.msg_control = NULL; + msg.msg_controllen = 0; + msg.msg_flags = 0; + + memset(&sa, 0, sizeof(sa)); + sa.sa.sa_family = local->srx.transport.family; + switch (sa.sa.sa_family) { + case AF_INET: + msg.msg_namelen = sizeof(sa.sin); + break; + default: + msg.msg_namelen = 0; + break; + } + + memset(&hdr, 0, sizeof(hdr)); + hdr.type = RXRPC_PACKET_TYPE_ABORT; + + while ((skb = skb_dequeue(&local->reject_queue))) { + sp = rxrpc_skb(skb); + switch (sa.sa.sa_family) { + case AF_INET: + sa.sin.sin_port = udp_hdr(skb)->source; + sa.sin.sin_addr.s_addr = ip_hdr(skb)->saddr; + code = htonl(skb->priority); + + hdr.epoch = sp->hdr.epoch; + hdr.cid = sp->hdr.cid; + hdr.callNumber = sp->hdr.callNumber; + hdr.serviceId = sp->hdr.serviceId; + hdr.flags = sp->hdr.flags; + hdr.flags ^= RXRPC_CLIENT_INITIATED; + hdr.flags &= RXRPC_CLIENT_INITIATED; + + kernel_sendmsg(local->socket, &msg, iov, 2, size); + break; + + default: + break; + } + + rxrpc_free_skb(skb); + rxrpc_put_local(local); + } + + rxrpc_put_local(local); + _leave(""); +} diff --git a/net/rxrpc/ar-error.c b/net/rxrpc/ar-error.c new file mode 100644 index 000000000000..f5539e2f7b58 --- /dev/null +++ b/net/rxrpc/ar-error.c @@ -0,0 +1,253 @@ +/* Error message handling (ICMP) + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ar-internal.h" + +/* + * handle an error received on the local endpoint + */ +void rxrpc_UDP_error_report(struct sock *sk) +{ + struct sock_exterr_skb *serr; + struct rxrpc_transport *trans; + struct rxrpc_local *local = sk->sk_user_data; + struct rxrpc_peer *peer; + struct sk_buff *skb; + __be32 addr; + __be16 port; + + _enter("%p{%d}", sk, local->debug_id); + + skb = skb_dequeue(&sk->sk_error_queue); + if (!skb) { + _leave("UDP socket errqueue empty"); + return; + } + + rxrpc_new_skb(skb); + + serr = SKB_EXT_ERR(skb); + addr = *(__be32 *)(skb_network_header(skb) + serr->addr_offset); + port = serr->port; + + _net("Rx UDP Error from "NIPQUAD_FMT":%hu", + NIPQUAD(addr), ntohs(port)); + _debug("Msg l:%d d:%d", skb->len, skb->data_len); + + peer = rxrpc_find_peer(local, addr, port); + if (IS_ERR(peer)) { + rxrpc_free_skb(skb); + _leave(" [no peer]"); + return; + } + + trans = rxrpc_find_transport(local, peer); + if (!trans) { + rxrpc_put_peer(peer); + rxrpc_free_skb(skb); + _leave(" [no trans]"); + return; + } + + if (serr->ee.ee_origin == SO_EE_ORIGIN_ICMP && + serr->ee.ee_type == ICMP_DEST_UNREACH && + serr->ee.ee_code == ICMP_FRAG_NEEDED + ) { + u32 mtu = serr->ee.ee_info; + + _net("Rx Received ICMP Fragmentation Needed (%d)", mtu); + + /* wind down the local interface MTU */ + if (mtu > 0 && peer->if_mtu == 65535 && mtu < peer->if_mtu) { + peer->if_mtu = mtu; + _net("I/F MTU %u", mtu); + } + + /* ip_rt_frag_needed() may have eaten the info */ + if (mtu == 0) + mtu = ntohs(icmp_hdr(skb)->un.frag.mtu); + + if (mtu == 0) { + /* they didn't give us a size, estimate one */ + if (mtu > 1500) { + mtu >>= 1; + if (mtu < 1500) + mtu = 1500; + } else { + mtu -= 100; + if (mtu < peer->hdrsize) + mtu = peer->hdrsize + 4; + } + } + + if (mtu < peer->mtu) { + peer->mtu = mtu; + peer->maxdata = peer->mtu - peer->hdrsize; + _net("Net MTU %u (maxdata %u)", + peer->mtu, peer->maxdata); + } + } + + rxrpc_put_peer(peer); + + /* pass the transport ref to error_handler to release */ + skb_queue_tail(&trans->error_queue, skb); + schedule_work(&trans->error_handler); + + /* reset and regenerate socket error */ + spin_lock_bh(&sk->sk_error_queue.lock); + sk->sk_err = 0; + skb = skb_peek(&sk->sk_error_queue); + if (skb) { + sk->sk_err = SKB_EXT_ERR(skb)->ee.ee_errno; + spin_unlock_bh(&sk->sk_error_queue.lock); + sk->sk_error_report(sk); + } else { + spin_unlock_bh(&sk->sk_error_queue.lock); + } + + _leave(""); +} + +/* + * deal with UDP error messages + */ +void rxrpc_UDP_error_handler(struct work_struct *work) +{ + struct sock_extended_err *ee; + struct sock_exterr_skb *serr; + struct rxrpc_transport *trans = + container_of(work, struct rxrpc_transport, error_handler); + struct sk_buff *skb; + int local, err; + + _enter(""); + + skb = skb_dequeue(&trans->error_queue); + if (!skb) + return; + + serr = SKB_EXT_ERR(skb); + ee = &serr->ee; + + _net("Rx Error o=%d t=%d c=%d e=%d", + ee->ee_origin, ee->ee_type, ee->ee_code, ee->ee_errno); + + err = ee->ee_errno; + + switch (ee->ee_origin) { + case SO_EE_ORIGIN_ICMP: + local = 0; + switch (ee->ee_type) { + case ICMP_DEST_UNREACH: + switch (ee->ee_code) { + case ICMP_NET_UNREACH: + _net("Rx Received ICMP Network Unreachable"); + err = ENETUNREACH; + break; + case ICMP_HOST_UNREACH: + _net("Rx Received ICMP Host Unreachable"); + err = EHOSTUNREACH; + break; + case ICMP_PORT_UNREACH: + _net("Rx Received ICMP Port Unreachable"); + err = ECONNREFUSED; + break; + case ICMP_FRAG_NEEDED: + _net("Rx Received ICMP Fragmentation Needed (%d)", + ee->ee_info); + err = 0; /* dealt with elsewhere */ + break; + case ICMP_NET_UNKNOWN: + _net("Rx Received ICMP Unknown Network"); + err = ENETUNREACH; + break; + case ICMP_HOST_UNKNOWN: + _net("Rx Received ICMP Unknown Host"); + err = EHOSTUNREACH; + break; + default: + _net("Rx Received ICMP DestUnreach code=%u", + ee->ee_code); + break; + } + break; + + case ICMP_TIME_EXCEEDED: + _net("Rx Received ICMP TTL Exceeded"); + break; + + default: + _proto("Rx Received ICMP error { type=%u code=%u }", + ee->ee_type, ee->ee_code); + break; + } + break; + + case SO_EE_ORIGIN_LOCAL: + _proto("Rx Received local error { error=%d }", + ee->ee_errno); + local = 1; + break; + + case SO_EE_ORIGIN_NONE: + case SO_EE_ORIGIN_ICMP6: + default: + _proto("Rx Received error report { orig=%u }", + ee->ee_origin); + local = 0; + break; + } + + /* terminate all the affected calls if there's an unrecoverable + * error */ + if (err) { + struct rxrpc_call *call, *_n; + + _debug("ISSUE ERROR %d", err); + + spin_lock_bh(&trans->peer->lock); + trans->peer->net_error = err; + + list_for_each_entry_safe(call, _n, &trans->peer->error_targets, + error_link) { + write_lock(&call->state_lock); + if (call->state != RXRPC_CALL_COMPLETE && + call->state < RXRPC_CALL_NETWORK_ERROR) { + call->state = RXRPC_CALL_NETWORK_ERROR; + set_bit(RXRPC_CALL_RCVD_ERROR, &call->events); + schedule_work(&call->processor); + } + write_unlock(&call->state_lock); + list_del_init(&call->error_link); + } + + spin_unlock_bh(&trans->peer->lock); + } + + if (!skb_queue_empty(&trans->error_queue)) + schedule_work(&trans->error_handler); + + rxrpc_free_skb(skb); + rxrpc_put_transport(trans); + _leave(""); +} diff --git a/net/rxrpc/ar-input.c b/net/rxrpc/ar-input.c new file mode 100644 index 000000000000..323c3454561c --- /dev/null +++ b/net/rxrpc/ar-input.c @@ -0,0 +1,791 @@ +/* RxRPC packet reception + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ar-internal.h" + +unsigned long rxrpc_ack_timeout = 1; + +const char *rxrpc_pkts[] = { + "?00", + "DATA", "ACK", "BUSY", "ABORT", "ACKALL", "CHALL", "RESP", "DEBUG", + "?09", "?10", "?11", "?12", "?13", "?14", "?15" +}; + +/* + * queue a packet for recvmsg to pass to userspace + * - the caller must hold a lock on call->lock + * - must not be called with interrupts disabled (sk_filter() disables BH's) + * - eats the packet whether successful or not + * - there must be just one reference to the packet, which the caller passes to + * this function + */ +int rxrpc_queue_rcv_skb(struct rxrpc_call *call, struct sk_buff *skb, + bool force, bool terminal) +{ + struct rxrpc_skb_priv *sp; + struct sock *sk; + int skb_len, ret; + + _enter(",,%d,%d", force, terminal); + + ASSERT(!irqs_disabled()); + + sp = rxrpc_skb(skb); + ASSERTCMP(sp->call, ==, call); + + /* if we've already posted the terminal message for a call, then we + * don't post any more */ + if (test_bit(RXRPC_CALL_TERMINAL_MSG, &call->flags)) { + _debug("already terminated"); + ASSERTCMP(call->state, >=, RXRPC_CALL_COMPLETE); + skb->destructor = NULL; + sp->call = NULL; + rxrpc_put_call(call); + rxrpc_free_skb(skb); + return 0; + } + + sk = &call->socket->sk; + + if (!force) { + /* cast skb->rcvbuf to unsigned... It's pointless, but + * reduces number of warnings when compiling with -W + * --ANK */ +// ret = -ENOBUFS; +// if (atomic_read(&sk->sk_rmem_alloc) + skb->truesize >= +// (unsigned) sk->sk_rcvbuf) +// goto out; + + ret = sk_filter(sk, skb); + if (ret < 0) + goto out; + } + + spin_lock_bh(&sk->sk_receive_queue.lock); + if (!test_bit(RXRPC_CALL_TERMINAL_MSG, &call->flags) && + !test_bit(RXRPC_CALL_RELEASED, &call->flags) && + call->socket->sk.sk_state != RXRPC_CLOSE) { + skb->destructor = rxrpc_packet_destructor; + skb->dev = NULL; + skb->sk = sk; + atomic_add(skb->truesize, &sk->sk_rmem_alloc); + + /* Cache the SKB length before we tack it onto the receive + * queue. Once it is added it no longer belongs to us and + * may be freed by other threads of control pulling packets + * from the queue. + */ + skb_len = skb->len; + + _net("post skb %p", skb); + __skb_queue_tail(&sk->sk_receive_queue, skb); + spin_unlock_bh(&sk->sk_receive_queue.lock); + + if (!sock_flag(sk, SOCK_DEAD)) + sk->sk_data_ready(sk, skb_len); + + if (terminal) { + _debug("<<<< TERMINAL MESSAGE >>>>"); + set_bit(RXRPC_CALL_TERMINAL_MSG, &call->flags); + } + + skb = NULL; + } else { + spin_unlock_bh(&sk->sk_receive_queue.lock); + } + ret = 0; + +out: + /* release the socket buffer */ + if (skb) { + skb->destructor = NULL; + sp->call = NULL; + rxrpc_put_call(call); + rxrpc_free_skb(skb); + } + + _leave(" = %d", ret); + return ret; +} + +/* + * process a DATA packet, posting the packet to the appropriate queue + * - eats the packet if successful + */ +static int rxrpc_fast_process_data(struct rxrpc_call *call, + struct sk_buff *skb, u32 seq) +{ + struct rxrpc_skb_priv *sp; + bool terminal; + int ret, ackbit, ack; + + _enter("{%u,%u},,{%u}", call->rx_data_post, call->rx_first_oos, seq); + + sp = rxrpc_skb(skb); + ASSERTCMP(sp->call, ==, NULL); + + spin_lock(&call->lock); + + if (call->state > RXRPC_CALL_COMPLETE) + goto discard; + + ASSERTCMP(call->rx_data_expect, >=, call->rx_data_post); + ASSERTCMP(call->rx_data_post, >=, call->rx_data_recv); + ASSERTCMP(call->rx_data_recv, >=, call->rx_data_eaten); + + if (seq < call->rx_data_post) { + _debug("dup #%u [-%u]", seq, call->rx_data_post); + ack = RXRPC_ACK_DUPLICATE; + ret = -ENOBUFS; + goto discard_and_ack; + } + + /* we may already have the packet in the out of sequence queue */ + ackbit = seq - (call->rx_data_eaten + 1); + ASSERTCMP(ackbit, >=, 0); + if (__test_and_set_bit(ackbit, &call->ackr_window)) { + _debug("dup oos #%u [%u,%u]", + seq, call->rx_data_eaten, call->rx_data_post); + ack = RXRPC_ACK_DUPLICATE; + goto discard_and_ack; + } + + if (seq >= call->ackr_win_top) { + _debug("exceed #%u [%u]", seq, call->ackr_win_top); + __clear_bit(ackbit, &call->ackr_window); + ack = RXRPC_ACK_EXCEEDS_WINDOW; + goto discard_and_ack; + } + + if (seq == call->rx_data_expect) { + clear_bit(RXRPC_CALL_EXPECT_OOS, &call->flags); + call->rx_data_expect++; + } else if (seq > call->rx_data_expect) { + _debug("oos #%u [%u]", seq, call->rx_data_expect); + call->rx_data_expect = seq + 1; + if (test_and_set_bit(RXRPC_CALL_EXPECT_OOS, &call->flags)) { + ack = RXRPC_ACK_OUT_OF_SEQUENCE; + goto enqueue_and_ack; + } + goto enqueue_packet; + } + + if (seq != call->rx_data_post) { + _debug("ahead #%u [%u]", seq, call->rx_data_post); + goto enqueue_packet; + } + + if (test_bit(RXRPC_CALL_RCVD_LAST, &call->flags)) + goto protocol_error; + + /* if the packet need security things doing to it, then it goes down + * the slow path */ + if (call->conn->security) + goto enqueue_packet; + + sp->call = call; + rxrpc_get_call(call); + terminal = ((sp->hdr.flags & RXRPC_LAST_PACKET) && + !(sp->hdr.flags & RXRPC_CLIENT_INITIATED)); + ret = rxrpc_queue_rcv_skb(call, skb, false, terminal); + if (ret < 0) { + if (ret == -ENOMEM || ret == -ENOBUFS) { + __clear_bit(ackbit, &call->ackr_window); + ack = RXRPC_ACK_NOSPACE; + goto discard_and_ack; + } + goto out; + } + + skb = NULL; + + _debug("post #%u", seq); + ASSERTCMP(call->rx_data_post, ==, seq); + call->rx_data_post++; + + if (sp->hdr.flags & RXRPC_LAST_PACKET) + set_bit(RXRPC_CALL_RCVD_LAST, &call->flags); + + /* if we've reached an out of sequence packet then we need to drain + * that queue into the socket Rx queue now */ + if (call->rx_data_post == call->rx_first_oos) { + _debug("drain rx oos now"); + read_lock(&call->state_lock); + if (call->state < RXRPC_CALL_COMPLETE && + !test_and_set_bit(RXRPC_CALL_DRAIN_RX_OOS, &call->events)) + schedule_work(&call->processor); + read_unlock(&call->state_lock); + } + + spin_unlock(&call->lock); + atomic_inc(&call->ackr_not_idle); + rxrpc_propose_ACK(call, RXRPC_ACK_DELAY, sp->hdr.serial, false); + _leave(" = 0 [posted]"); + return 0; + +protocol_error: + ret = -EBADMSG; +out: + spin_unlock(&call->lock); + _leave(" = %d", ret); + return ret; + +discard_and_ack: + _debug("discard and ACK packet %p", skb); + __rxrpc_propose_ACK(call, ack, sp->hdr.serial, true); +discard: + spin_unlock(&call->lock); + rxrpc_free_skb(skb); + _leave(" = 0 [discarded]"); + return 0; + +enqueue_and_ack: + __rxrpc_propose_ACK(call, ack, sp->hdr.serial, true); +enqueue_packet: + _net("defer skb %p", skb); + spin_unlock(&call->lock); + skb_queue_tail(&call->rx_queue, skb); + atomic_inc(&call->ackr_not_idle); + read_lock(&call->state_lock); + if (call->state < RXRPC_CALL_DEAD) + schedule_work(&call->processor); + read_unlock(&call->state_lock); + _leave(" = 0 [queued]"); + return 0; +} + +/* + * assume an implicit ACKALL of the transmission phase of a client socket upon + * reception of the first reply packet + */ +static void rxrpc_assume_implicit_ackall(struct rxrpc_call *call, u32 serial) +{ + write_lock_bh(&call->state_lock); + + switch (call->state) { + case RXRPC_CALL_CLIENT_AWAIT_REPLY: + call->state = RXRPC_CALL_CLIENT_RECV_REPLY; + call->acks_latest = serial; + + _debug("implicit ACKALL %%%u", call->acks_latest); + set_bit(RXRPC_CALL_RCVD_ACKALL, &call->events); + write_unlock_bh(&call->state_lock); + + if (try_to_del_timer_sync(&call->resend_timer) >= 0) { + clear_bit(RXRPC_CALL_RESEND_TIMER, &call->events); + clear_bit(RXRPC_CALL_RESEND, &call->events); + clear_bit(RXRPC_CALL_RUN_RTIMER, &call->flags); + } + break; + + default: + write_unlock_bh(&call->state_lock); + break; + } +} + +/* + * post an incoming packet to the nominated call to deal with + * - must get rid of the sk_buff, either by freeing it or by queuing it + */ +void rxrpc_fast_process_packet(struct rxrpc_call *call, struct sk_buff *skb) +{ + struct rxrpc_skb_priv *sp = rxrpc_skb(skb); + __be32 _abort_code; + u32 serial, hi_serial, seq, abort_code; + + _enter("%p,%p", call, skb); + + ASSERT(!irqs_disabled()); + +#if 0 // INJECT RX ERROR + if (sp->hdr.type == RXRPC_PACKET_TYPE_DATA) { + static int skip = 0; + if (++skip == 3) { + printk("DROPPED 3RD PACKET!!!!!!!!!!!!!\n"); + skip = 0; + goto free_packet; + } + } +#endif + + /* track the latest serial number on this connection for ACK packet + * information */ + serial = ntohl(sp->hdr.serial); + hi_serial = atomic_read(&call->conn->hi_serial); + while (serial > hi_serial) + hi_serial = atomic_cmpxchg(&call->conn->hi_serial, hi_serial, + serial); + + /* request ACK generation for any ACK or DATA packet that requests + * it */ + if (sp->hdr.flags & RXRPC_REQUEST_ACK) { + _proto("ACK Requested on %%%u", serial); + rxrpc_propose_ACK(call, RXRPC_ACK_REQUESTED, sp->hdr.serial, + !(sp->hdr.flags & RXRPC_MORE_PACKETS)); + } + + switch (sp->hdr.type) { + case RXRPC_PACKET_TYPE_ABORT: + _debug("abort"); + + if (skb_copy_bits(skb, 0, &_abort_code, + sizeof(_abort_code)) < 0) + goto protocol_error; + + abort_code = ntohl(_abort_code); + _proto("Rx ABORT %%%u { %x }", serial, abort_code); + + write_lock_bh(&call->state_lock); + if (call->state < RXRPC_CALL_COMPLETE) { + call->state = RXRPC_CALL_REMOTELY_ABORTED; + call->abort_code = abort_code; + set_bit(RXRPC_CALL_RCVD_ABORT, &call->events); + schedule_work(&call->processor); + } + goto free_packet_unlock; + + case RXRPC_PACKET_TYPE_BUSY: + _proto("Rx BUSY %%%u", serial); + + if (call->conn->out_clientflag) + goto protocol_error; + + write_lock_bh(&call->state_lock); + switch (call->state) { + case RXRPC_CALL_CLIENT_SEND_REQUEST: + call->state = RXRPC_CALL_SERVER_BUSY; + set_bit(RXRPC_CALL_RCVD_BUSY, &call->events); + schedule_work(&call->processor); + case RXRPC_CALL_SERVER_BUSY: + goto free_packet_unlock; + default: + goto protocol_error_locked; + } + + default: + _proto("Rx %s %%%u", rxrpc_pkts[sp->hdr.type], serial); + goto protocol_error; + + case RXRPC_PACKET_TYPE_DATA: + seq = ntohl(sp->hdr.seq); + + _proto("Rx DATA %%%u { #%u }", serial, seq); + + if (seq == 0) + goto protocol_error; + + call->ackr_prev_seq = sp->hdr.seq; + + /* received data implicitly ACKs all of the request packets we + * sent when we're acting as a client */ + if (call->state == RXRPC_CALL_CLIENT_AWAIT_REPLY) + rxrpc_assume_implicit_ackall(call, serial); + + switch (rxrpc_fast_process_data(call, skb, seq)) { + case 0: + skb = NULL; + goto done; + + default: + BUG(); + + /* data packet received beyond the last packet */ + case -EBADMSG: + goto protocol_error; + } + + case RXRPC_PACKET_TYPE_ACK: + /* ACK processing is done in process context */ + read_lock_bh(&call->state_lock); + if (call->state < RXRPC_CALL_DEAD) { + skb_queue_tail(&call->rx_queue, skb); + schedule_work(&call->processor); + skb = NULL; + } + read_unlock_bh(&call->state_lock); + goto free_packet; + } + +protocol_error: + _debug("protocol error"); + write_lock_bh(&call->state_lock); +protocol_error_locked: + if (call->state <= RXRPC_CALL_COMPLETE) { + call->state = RXRPC_CALL_LOCALLY_ABORTED; + call->abort_code = RX_PROTOCOL_ERROR; + set_bit(RXRPC_CALL_ABORT, &call->events); + schedule_work(&call->processor); + } +free_packet_unlock: + write_unlock_bh(&call->state_lock); +free_packet: + rxrpc_free_skb(skb); +done: + _leave(""); +} + +/* + * split up a jumbo data packet + */ +static void rxrpc_process_jumbo_packet(struct rxrpc_call *call, + struct sk_buff *jumbo) +{ + struct rxrpc_jumbo_header jhdr; + struct rxrpc_skb_priv *sp; + struct sk_buff *part; + + _enter(",{%u,%u}", jumbo->data_len, jumbo->len); + + sp = rxrpc_skb(jumbo); + + do { + sp->hdr.flags &= ~RXRPC_JUMBO_PACKET; + + /* make a clone to represent the first subpacket in what's left + * of the jumbo packet */ + part = skb_clone(jumbo, GFP_ATOMIC); + if (!part) { + /* simply ditch the tail in the event of ENOMEM */ + pskb_trim(jumbo, RXRPC_JUMBO_DATALEN); + break; + } + rxrpc_new_skb(part); + + pskb_trim(part, RXRPC_JUMBO_DATALEN); + + if (!pskb_pull(jumbo, RXRPC_JUMBO_DATALEN)) + goto protocol_error; + + if (skb_copy_bits(jumbo, 0, &jhdr, sizeof(jhdr)) < 0) + goto protocol_error; + if (!pskb_pull(jumbo, sizeof(jhdr))) + BUG(); + + sp->hdr.seq = htonl(ntohl(sp->hdr.seq) + 1); + sp->hdr.serial = htonl(ntohl(sp->hdr.serial) + 1); + sp->hdr.flags = jhdr.flags; + sp->hdr._rsvd = jhdr._rsvd; + + _proto("Rx DATA Jumbo %%%u", ntohl(sp->hdr.serial) - 1); + + rxrpc_fast_process_packet(call, part); + part = NULL; + + } while (sp->hdr.flags & RXRPC_JUMBO_PACKET); + + rxrpc_fast_process_packet(call, jumbo); + _leave(""); + return; + +protocol_error: + _debug("protocol error"); + rxrpc_free_skb(part); + rxrpc_free_skb(jumbo); + write_lock_bh(&call->state_lock); + if (call->state <= RXRPC_CALL_COMPLETE) { + call->state = RXRPC_CALL_LOCALLY_ABORTED; + call->abort_code = RX_PROTOCOL_ERROR; + set_bit(RXRPC_CALL_ABORT, &call->events); + schedule_work(&call->processor); + } + write_unlock_bh(&call->state_lock); + _leave(""); +} + +/* + * post an incoming packet to the appropriate call/socket to deal with + * - must get rid of the sk_buff, either by freeing it or by queuing it + */ +static void rxrpc_post_packet_to_call(struct rxrpc_connection *conn, + struct sk_buff *skb) +{ + struct rxrpc_skb_priv *sp; + struct rxrpc_call *call; + struct rb_node *p; + __be32 call_id; + + _enter("%p,%p", conn, skb); + + read_lock_bh(&conn->lock); + + sp = rxrpc_skb(skb); + + /* look at extant calls by channel number first */ + call = conn->channels[ntohl(sp->hdr.cid) & RXRPC_CHANNELMASK]; + if (!call || call->call_id != sp->hdr.callNumber) + goto call_not_extant; + + _debug("extant call [%d]", call->state); + ASSERTCMP(call->conn, ==, conn); + + read_lock(&call->state_lock); + switch (call->state) { + case RXRPC_CALL_LOCALLY_ABORTED: + if (!test_and_set_bit(RXRPC_CALL_ABORT, &call->events)) + schedule_work(&call->processor); + case RXRPC_CALL_REMOTELY_ABORTED: + case RXRPC_CALL_NETWORK_ERROR: + case RXRPC_CALL_DEAD: + goto free_unlock; + default: + break; + } + + read_unlock(&call->state_lock); + rxrpc_get_call(call); + read_unlock_bh(&conn->lock); + + if (sp->hdr.type == RXRPC_PACKET_TYPE_DATA && + sp->hdr.flags & RXRPC_JUMBO_PACKET) + rxrpc_process_jumbo_packet(call, skb); + else + rxrpc_fast_process_packet(call, skb); + + rxrpc_put_call(call); + goto done; + +call_not_extant: + /* search the completed calls in case what we're dealing with is + * there */ + _debug("call not extant"); + + call_id = sp->hdr.callNumber; + p = conn->calls.rb_node; + while (p) { + call = rb_entry(p, struct rxrpc_call, conn_node); + + if (call_id < call->call_id) + p = p->rb_left; + else if (call_id > call->call_id) + p = p->rb_right; + else + goto found_completed_call; + } + +dead_call: + /* it's a either a really old call that we no longer remember or its a + * new incoming call */ + read_unlock_bh(&conn->lock); + + if (sp->hdr.flags & RXRPC_CLIENT_INITIATED && + sp->hdr.seq == __constant_cpu_to_be32(1)) { + _debug("incoming call"); + skb_queue_tail(&conn->trans->local->accept_queue, skb); + schedule_work(&conn->trans->local->acceptor); + goto done; + } + + _debug("dead call"); + skb->priority = RX_CALL_DEAD; + rxrpc_reject_packet(conn->trans->local, skb); + goto done; + + /* resend last packet of a completed call + * - client calls may have been aborted or ACK'd + * - server calls may have been aborted + */ +found_completed_call: + _debug("completed call"); + + if (atomic_read(&call->usage) == 0) + goto dead_call; + + /* synchronise any state changes */ + read_lock(&call->state_lock); + ASSERTIFCMP(call->state != RXRPC_CALL_CLIENT_FINAL_ACK, + call->state, >=, RXRPC_CALL_COMPLETE); + + if (call->state == RXRPC_CALL_LOCALLY_ABORTED || + call->state == RXRPC_CALL_REMOTELY_ABORTED || + call->state == RXRPC_CALL_DEAD) { + read_unlock(&call->state_lock); + goto dead_call; + } + + if (call->conn->in_clientflag) { + read_unlock(&call->state_lock); + goto dead_call; /* complete server call */ + } + + _debug("final ack again"); + rxrpc_get_call(call); + set_bit(RXRPC_CALL_ACK_FINAL, &call->events); + schedule_work(&call->processor); + +free_unlock: + read_unlock(&call->state_lock); + read_unlock_bh(&conn->lock); + rxrpc_free_skb(skb); +done: + _leave(""); +} + +/* + * post connection-level events to the connection + * - this includes challenges, responses and some aborts + */ +static void rxrpc_post_packet_to_conn(struct rxrpc_connection *conn, + struct sk_buff *skb) +{ + _enter("%p,%p", conn, skb); + + atomic_inc(&conn->usage); + skb_queue_tail(&conn->rx_queue, skb); + schedule_work(&conn->processor); +} + +/* + * handle data received on the local endpoint + * - may be called in interrupt context + */ +void rxrpc_data_ready(struct sock *sk, int count) +{ + struct rxrpc_connection *conn; + struct rxrpc_transport *trans; + struct rxrpc_skb_priv *sp; + struct rxrpc_local *local; + struct rxrpc_peer *peer; + struct sk_buff *skb; + int ret; + + _enter("%p, %d", sk, count); + + ASSERT(!irqs_disabled()); + + read_lock_bh(&rxrpc_local_lock); + local = sk->sk_user_data; + if (local && atomic_read(&local->usage) > 0) + rxrpc_get_local(local); + else + local = NULL; + read_unlock_bh(&rxrpc_local_lock); + if (!local) { + _leave(" [local dead]"); + return; + } + + skb = skb_recv_datagram(sk, 0, 1, &ret); + if (!skb) { + rxrpc_put_local(local); + if (ret == -EAGAIN) + return; + _debug("UDP socket error %d", ret); + return; + } + + rxrpc_new_skb(skb); + + _net("recv skb %p", skb); + + /* we'll probably need to checksum it (didn't call sock_recvmsg) */ + if (skb_checksum_complete(skb)) { + rxrpc_free_skb(skb); + rxrpc_put_local(local); + _leave(" [CSUM failed]"); + return; + } + + /* the socket buffer we have is owned by UDP, with UDP's data all over + * it, but we really want our own */ + skb_orphan(skb); + sp = rxrpc_skb(skb); + memset(sp, 0, sizeof(*sp)); + + _net("Rx UDP packet from %08x:%04hu", + ntohl(ip_hdr(skb)->saddr), ntohs(udp_hdr(skb)->source)); + + /* dig out the RxRPC connection details */ + if (skb_copy_bits(skb, sizeof(struct udphdr), &sp->hdr, + sizeof(sp->hdr)) < 0) + goto bad_message; + if (!pskb_pull(skb, sizeof(struct udphdr) + sizeof(sp->hdr))) + BUG(); + + _net("Rx RxRPC %s ep=%x call=%x:%x", + sp->hdr.flags & RXRPC_CLIENT_INITIATED ? "ToServer" : "ToClient", + ntohl(sp->hdr.epoch), + ntohl(sp->hdr.cid), + ntohl(sp->hdr.callNumber)); + + if (sp->hdr.type == 0 || sp->hdr.type >= RXRPC_N_PACKET_TYPES) { + _proto("Rx Bad Packet Type %u", sp->hdr.type); + goto bad_message; + } + + if (sp->hdr.type == RXRPC_PACKET_TYPE_DATA && + (sp->hdr.callNumber == 0 || sp->hdr.seq == 0)) + goto bad_message; + + peer = rxrpc_find_peer(local, ip_hdr(skb)->saddr, udp_hdr(skb)->source); + if (IS_ERR(peer)) + goto cant_route_call; + + trans = rxrpc_find_transport(local, peer); + rxrpc_put_peer(peer); + if (!trans) + goto cant_route_call; + + conn = rxrpc_find_connection(trans, &sp->hdr); + rxrpc_put_transport(trans); + if (!conn) + goto cant_route_call; + + _debug("CONN %p {%d}", conn, conn->debug_id); + + if (sp->hdr.callNumber == 0) + rxrpc_post_packet_to_conn(conn, skb); + else + rxrpc_post_packet_to_call(conn, skb); + rxrpc_put_connection(conn); + rxrpc_put_local(local); + return; + +cant_route_call: + _debug("can't route call"); + if (sp->hdr.flags & RXRPC_CLIENT_INITIATED && + sp->hdr.type == RXRPC_PACKET_TYPE_DATA) { + if (sp->hdr.seq == __constant_cpu_to_be32(1)) { + _debug("first packet"); + skb_queue_tail(&local->accept_queue, skb); + schedule_work(&local->acceptor); + rxrpc_put_local(local); + _leave(" [incoming]"); + return; + } + skb->priority = RX_INVALID_OPERATION; + } else { + skb->priority = RX_CALL_DEAD; + } + + _debug("reject"); + rxrpc_reject_packet(local, skb); + rxrpc_put_local(local); + _leave(" [no call]"); + return; + +bad_message: + skb->priority = RX_PROTOCOL_ERROR; + rxrpc_reject_packet(local, skb); + rxrpc_put_local(local); + _leave(" [badmsg]"); +} diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h new file mode 100644 index 000000000000..7bfbf471c81e --- /dev/null +++ b/net/rxrpc/ar-internal.h @@ -0,0 +1,842 @@ +/* AF_RXRPC internal definitions + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * 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. + */ + +#include + +#if 0 +#define CHECK_SLAB_OKAY(X) \ + BUG_ON(atomic_read((X)) >> (sizeof(atomic_t) - 2) == \ + (POISON_FREE << 8 | POISON_FREE)) +#else +#define CHECK_SLAB_OKAY(X) do {} while(0) +#endif + +extern atomic_t rxrpc_n_skbs; + +#define FCRYPT_BSIZE 8 +struct rxrpc_crypt { + union { + u8 x[FCRYPT_BSIZE]; + u32 n[2]; + }; +} __attribute__((aligned(8))); + +extern __be32 rxrpc_epoch; /* local epoch for detecting local-end reset */ +extern atomic_t rxrpc_debug_id; /* current debugging ID */ + +/* + * sk_state for RxRPC sockets + */ +enum { + RXRPC_UNCONNECTED = 0, + RXRPC_CLIENT_BOUND, /* client local address bound */ + RXRPC_CLIENT_CONNECTED, /* client is connected */ + RXRPC_SERVER_BOUND, /* server local address bound */ + RXRPC_SERVER_LISTENING, /* server listening for connections */ + RXRPC_CLOSE, /* socket is being closed */ +}; + +/* + * RxRPC socket definition + */ +struct rxrpc_sock { + /* WARNING: sk has to be the first member */ + struct sock sk; + struct rxrpc_local *local; /* local endpoint */ + struct rxrpc_transport *trans; /* transport handler */ + struct rxrpc_conn_bundle *bundle; /* virtual connection bundle */ + struct rxrpc_connection *conn; /* exclusive virtual connection */ + struct list_head listen_link; /* link in the local endpoint's listen list */ + struct list_head secureq; /* calls awaiting connection security clearance */ + struct list_head acceptq; /* calls awaiting acceptance */ + struct key *key; /* security for this socket */ + struct key *securities; /* list of server security descriptors */ + struct rb_root calls; /* outstanding calls on this socket */ + unsigned long flags; +#define RXRPC_SOCK_EXCLUSIVE_CONN 1 /* exclusive connection for a client socket */ + rwlock_t call_lock; /* lock for calls */ + u32 min_sec_level; /* minimum security level */ +#define RXRPC_SECURITY_MAX RXRPC_SECURITY_ENCRYPT + struct sockaddr_rxrpc srx; /* local address */ + sa_family_t proto; /* protocol created with */ + __be16 service_id; /* service ID of local/remote service */ +}; + +#define rxrpc_sk(__sk) container_of((__sk), struct rxrpc_sock, sk) + +/* + * RxRPC socket buffer private variables + * - max 48 bytes (struct sk_buff::cb) + */ +struct rxrpc_skb_priv { + struct rxrpc_call *call; /* call with which associated */ + unsigned long resend_at; /* time in jiffies at which to resend */ + union { + unsigned offset; /* offset into buffer of next read */ + int remain; /* amount of space remaining for next write */ + u32 error; /* network error code */ + bool need_resend; /* T if needs resending */ + }; + + struct rxrpc_header hdr; /* RxRPC packet header from this packet */ +}; + +#define rxrpc_skb(__skb) ((struct rxrpc_skb_priv *) &(__skb)->cb) + +enum { + RXRPC_SKB_MARK_DATA, /* data message */ + RXRPC_SKB_MARK_FINAL_ACK, /* final ACK received message */ + RXRPC_SKB_MARK_BUSY, /* server busy message */ + RXRPC_SKB_MARK_REMOTE_ABORT, /* remote abort message */ + RXRPC_SKB_MARK_NET_ERROR, /* network error message */ + RXRPC_SKB_MARK_LOCAL_ERROR, /* local error message */ + RXRPC_SKB_MARK_NEW_CALL, /* local error message */ +}; + +enum rxrpc_command { + RXRPC_CMD_SEND_DATA, /* send data message */ + RXRPC_CMD_SEND_ABORT, /* request abort generation */ + RXRPC_CMD_ACCEPT, /* [server] accept incoming call */ + RXRPC_CMD_REJECT_BUSY, /* [server] reject a call as busy */ +}; + +/* + * RxRPC security module interface + */ +struct rxrpc_security { + struct module *owner; /* providing module */ + struct list_head link; /* link in master list */ + const char *name; /* name of this service */ + u8 security_index; /* security type provided */ + + /* initialise a connection's security */ + int (*init_connection_security)(struct rxrpc_connection *); + + /* prime a connection's packet security */ + void (*prime_packet_security)(struct rxrpc_connection *); + + /* impose security on a packet */ + int (*secure_packet)(const struct rxrpc_call *, + struct sk_buff *, + size_t, + void *); + + /* verify the security on a received packet */ + int (*verify_packet)(const struct rxrpc_call *, struct sk_buff *, + u32 *); + + /* issue a challenge */ + int (*issue_challenge)(struct rxrpc_connection *); + + /* respond to a challenge */ + int (*respond_to_challenge)(struct rxrpc_connection *, + struct sk_buff *, + u32 *); + + /* verify a response */ + int (*verify_response)(struct rxrpc_connection *, + struct sk_buff *, + u32 *); + + /* clear connection security */ + void (*clear)(struct rxrpc_connection *); +}; + +/* + * RxRPC local transport endpoint definition + * - matched by local port, address and protocol type + */ +struct rxrpc_local { + struct socket *socket; /* my UDP socket */ + struct work_struct destroyer; /* endpoint destroyer */ + struct work_struct acceptor; /* incoming call processor */ + struct work_struct rejecter; /* packet reject writer */ + struct list_head services; /* services listening on this endpoint */ + struct list_head link; /* link in endpoint list */ + struct rw_semaphore defrag_sem; /* control re-enablement of IP DF bit */ + struct sk_buff_head accept_queue; /* incoming calls awaiting acceptance */ + struct sk_buff_head reject_queue; /* packets awaiting rejection */ + spinlock_t lock; /* access lock */ + rwlock_t services_lock; /* lock for services list */ + atomic_t usage; + int debug_id; /* debug ID for printks */ + volatile char error_rcvd; /* T if received ICMP error outstanding */ + struct sockaddr_rxrpc srx; /* local address */ +}; + +/* + * RxRPC remote transport endpoint definition + * - matched by remote port, address and protocol type + * - holds the connection ID counter for connections between the two endpoints + */ +struct rxrpc_peer { + struct work_struct destroyer; /* peer destroyer */ + struct list_head link; /* link in master peer list */ + struct list_head error_targets; /* targets for net error distribution */ + spinlock_t lock; /* access lock */ + atomic_t usage; + unsigned if_mtu; /* interface MTU for this peer */ + unsigned mtu; /* network MTU for this peer */ + unsigned maxdata; /* data size (MTU - hdrsize) */ + unsigned short hdrsize; /* header size (IP + UDP + RxRPC) */ + int debug_id; /* debug ID for printks */ + int net_error; /* network error distributed */ + struct sockaddr_rxrpc srx; /* remote address */ + + /* calculated RTT cache */ +#define RXRPC_RTT_CACHE_SIZE 32 + suseconds_t rtt; /* current RTT estimate (in uS) */ + unsigned rtt_point; /* next entry at which to insert */ + unsigned rtt_usage; /* amount of cache actually used */ + suseconds_t rtt_cache[RXRPC_RTT_CACHE_SIZE]; /* calculated RTT cache */ +}; + +/* + * RxRPC point-to-point transport / connection manager definition + * - handles a bundle of connections between two endpoints + * - matched by { local, peer } + */ +struct rxrpc_transport { + struct rxrpc_local *local; /* local transport endpoint */ + struct rxrpc_peer *peer; /* remote transport endpoint */ + struct work_struct error_handler; /* network error distributor */ + struct rb_root bundles; /* client connection bundles on this transport */ + struct rb_root client_conns; /* client connections on this transport */ + struct rb_root server_conns; /* server connections on this transport */ + struct list_head link; /* link in master session list */ + struct sk_buff_head error_queue; /* error packets awaiting processing */ + time_t put_time; /* time at which to reap */ + spinlock_t client_lock; /* client connection allocation lock */ + rwlock_t conn_lock; /* lock for active/dead connections */ + atomic_t usage; + int debug_id; /* debug ID for printks */ + unsigned int conn_idcounter; /* connection ID counter (client) */ +}; + +/* + * RxRPC client connection bundle + * - matched by { transport, service_id, key } + */ +struct rxrpc_conn_bundle { + struct rb_node node; /* node in transport's lookup tree */ + struct list_head unused_conns; /* unused connections in this bundle */ + struct list_head avail_conns; /* available connections in this bundle */ + struct list_head busy_conns; /* busy connections in this bundle */ + struct key *key; /* security for this bundle */ + wait_queue_head_t chanwait; /* wait for channel to become available */ + atomic_t usage; + int debug_id; /* debug ID for printks */ + unsigned short num_conns; /* number of connections in this bundle */ + __be16 service_id; /* service ID */ + uint8_t security_ix; /* security type */ +}; + +/* + * RxRPC connection definition + * - matched by { transport, service_id, conn_id, direction, key } + * - each connection can only handle four simultaneous calls + */ +struct rxrpc_connection { + struct rxrpc_transport *trans; /* transport session */ + struct rxrpc_conn_bundle *bundle; /* connection bundle (client) */ + struct work_struct processor; /* connection event processor */ + struct rb_node node; /* node in transport's lookup tree */ + struct list_head link; /* link in master connection list */ + struct list_head bundle_link; /* link in bundle */ + struct rb_root calls; /* calls on this connection */ + struct sk_buff_head rx_queue; /* received conn-level packets */ + struct rxrpc_call *channels[RXRPC_MAXCALLS]; /* channels (active calls) */ + struct rxrpc_security *security; /* applied security module */ + struct key *key; /* security for this connection (client) */ + struct key *server_key; /* security for this service */ + struct crypto_blkcipher *cipher; /* encryption handle */ + struct rxrpc_crypt csum_iv; /* packet checksum base */ + unsigned long events; +#define RXRPC_CONN_CHALLENGE 0 /* send challenge packet */ + time_t put_time; /* time at which to reap */ + rwlock_t lock; /* access lock */ + spinlock_t state_lock; /* state-change lock */ + atomic_t usage; + u32 real_conn_id; /* connection ID (host-endian) */ + enum { /* current state of connection */ + RXRPC_CONN_UNUSED, /* - connection not yet attempted */ + RXRPC_CONN_CLIENT, /* - client connection */ + RXRPC_CONN_SERVER_UNSECURED, /* - server unsecured connection */ + RXRPC_CONN_SERVER_CHALLENGING, /* - server challenging for security */ + RXRPC_CONN_SERVER, /* - server secured connection */ + RXRPC_CONN_REMOTELY_ABORTED, /* - conn aborted by peer */ + RXRPC_CONN_LOCALLY_ABORTED, /* - conn aborted locally */ + RXRPC_CONN_NETWORK_ERROR, /* - conn terminated by network error */ + } state; + int error; /* error code for local abort */ + int debug_id; /* debug ID for printks */ + unsigned call_counter; /* call ID counter */ + atomic_t serial; /* packet serial number counter */ + atomic_t hi_serial; /* highest serial number received */ + u8 avail_calls; /* number of calls available */ + u8 size_align; /* data size alignment (for security) */ + u8 header_size; /* rxrpc + security header size */ + u8 security_size; /* security header size */ + u32 security_level; /* security level negotiated */ + u32 security_nonce; /* response re-use preventer */ + + /* the following are all in net order */ + __be32 epoch; /* epoch of this connection */ + __be32 cid; /* connection ID */ + __be16 service_id; /* service ID */ + u8 security_ix; /* security type */ + u8 in_clientflag; /* RXRPC_CLIENT_INITIATED if we are server */ + u8 out_clientflag; /* RXRPC_CLIENT_INITIATED if we are client */ +}; + +/* + * RxRPC call definition + * - matched by { connection, call_id } + */ +struct rxrpc_call { + struct rxrpc_connection *conn; /* connection carrying call */ + struct rxrpc_sock *socket; /* socket responsible */ + struct timer_list lifetimer; /* lifetime remaining on call */ + struct timer_list deadspan; /* reap timer for re-ACK'ing, etc */ + struct timer_list ack_timer; /* ACK generation timer */ + struct timer_list resend_timer; /* Tx resend timer */ + struct work_struct destroyer; /* call destroyer */ + struct work_struct processor; /* packet processor and ACK generator */ + struct list_head link; /* link in master call list */ + struct list_head error_link; /* link in error distribution list */ + struct list_head accept_link; /* calls awaiting acceptance */ + struct rb_node sock_node; /* node in socket call tree */ + struct rb_node conn_node; /* node in connection call tree */ + struct sk_buff_head rx_queue; /* received packets */ + struct sk_buff_head rx_oos_queue; /* packets received out of sequence */ + struct sk_buff *tx_pending; /* Tx socket buffer being filled */ + wait_queue_head_t tx_waitq; /* wait for Tx window space to become available */ + unsigned long user_call_ID; /* user-defined call ID */ + unsigned long creation_jif; /* time of call creation */ + unsigned long flags; +#define RXRPC_CALL_RELEASED 0 /* call has been released - no more message to userspace */ +#define RXRPC_CALL_TERMINAL_MSG 1 /* call has given the socket its final message */ +#define RXRPC_CALL_RCVD_LAST 2 /* all packets received */ +#define RXRPC_CALL_RUN_RTIMER 3 /* Tx resend timer started */ +#define RXRPC_CALL_TX_SOFT_ACK 4 /* sent some soft ACKs */ +#define RXRPC_CALL_PROC_BUSY 5 /* the processor is busy */ +#define RXRPC_CALL_INIT_ACCEPT 6 /* acceptance was initiated */ +#define RXRPC_CALL_HAS_USERID 7 /* has a user ID attached */ +#define RXRPC_CALL_EXPECT_OOS 8 /* expect out of sequence packets */ + unsigned long events; +#define RXRPC_CALL_RCVD_ACKALL 0 /* ACKALL or reply received */ +#define RXRPC_CALL_RCVD_BUSY 1 /* busy packet received */ +#define RXRPC_CALL_RCVD_ABORT 2 /* abort packet received */ +#define RXRPC_CALL_RCVD_ERROR 3 /* network error received */ +#define RXRPC_CALL_ACK_FINAL 4 /* need to generate final ACK (and release call) */ +#define RXRPC_CALL_ACK 5 /* need to generate ACK */ +#define RXRPC_CALL_REJECT_BUSY 6 /* need to generate busy message */ +#define RXRPC_CALL_ABORT 7 /* need to generate abort */ +#define RXRPC_CALL_CONN_ABORT 8 /* local connection abort generated */ +#define RXRPC_CALL_RESEND_TIMER 9 /* Tx resend timer expired */ +#define RXRPC_CALL_RESEND 10 /* Tx resend required */ +#define RXRPC_CALL_DRAIN_RX_OOS 11 /* drain the Rx out of sequence queue */ +#define RXRPC_CALL_LIFE_TIMER 12 /* call's lifetimer ran out */ +#define RXRPC_CALL_ACCEPTED 13 /* incoming call accepted by userspace app */ +#define RXRPC_CALL_SECURED 14 /* incoming call's connection is now secure */ +#define RXRPC_CALL_POST_ACCEPT 15 /* need to post an "accept?" message to the app */ +#define RXRPC_CALL_RELEASE 16 /* need to release the call's resources */ + + spinlock_t lock; + rwlock_t state_lock; /* lock for state transition */ + atomic_t usage; + atomic_t sequence; /* Tx data packet sequence counter */ + u32 abort_code; /* local/remote abort code */ + enum { /* current state of call */ + RXRPC_CALL_CLIENT_SEND_REQUEST, /* - client sending request phase */ + RXRPC_CALL_CLIENT_AWAIT_REPLY, /* - client awaiting reply */ + RXRPC_CALL_CLIENT_RECV_REPLY, /* - client receiving reply phase */ + RXRPC_CALL_CLIENT_FINAL_ACK, /* - client sending final ACK phase */ + RXRPC_CALL_SERVER_SECURING, /* - server securing request connection */ + RXRPC_CALL_SERVER_ACCEPTING, /* - server accepting request */ + RXRPC_CALL_SERVER_RECV_REQUEST, /* - server receiving request */ + RXRPC_CALL_SERVER_ACK_REQUEST, /* - server pending ACK of request */ + RXRPC_CALL_SERVER_SEND_REPLY, /* - server sending reply */ + RXRPC_CALL_SERVER_AWAIT_ACK, /* - server awaiting final ACK */ + RXRPC_CALL_COMPLETE, /* - call completed */ + RXRPC_CALL_SERVER_BUSY, /* - call rejected by busy server */ + RXRPC_CALL_REMOTELY_ABORTED, /* - call aborted by peer */ + RXRPC_CALL_LOCALLY_ABORTED, /* - call aborted locally on error or close */ + RXRPC_CALL_NETWORK_ERROR, /* - call terminated by network error */ + RXRPC_CALL_DEAD, /* - call is dead */ + } state; + int debug_id; /* debug ID for printks */ + u8 channel; /* connection channel occupied by this call */ + + /* transmission-phase ACK management */ + uint8_t acks_head; /* offset into window of first entry */ + uint8_t acks_tail; /* offset into window of last entry */ + uint8_t acks_winsz; /* size of un-ACK'd window */ + uint8_t acks_unacked; /* lowest unacked packet in last ACK received */ + int acks_latest; /* serial number of latest ACK received */ + rxrpc_seq_t acks_hard; /* highest definitively ACK'd msg seq */ + unsigned long *acks_window; /* sent packet window + * - elements are pointers with LSB set if ACK'd + */ + + /* receive-phase ACK management */ + rxrpc_seq_t rx_data_expect; /* next data seq ID expected to be received */ + rxrpc_seq_t rx_data_post; /* next data seq ID expected to be posted */ + rxrpc_seq_t rx_data_recv; /* last data seq ID encountered by recvmsg */ + rxrpc_seq_t rx_data_eaten; /* last data seq ID consumed by recvmsg */ + rxrpc_seq_t rx_first_oos; /* first packet in rx_oos_queue (or 0) */ + rxrpc_seq_t ackr_win_top; /* top of ACK window (rx_data_eaten is bottom) */ + rxrpc_seq_net_t ackr_prev_seq; /* previous sequence number received */ + uint8_t ackr_reason; /* reason to ACK */ + __be32 ackr_serial; /* serial of packet being ACK'd */ + atomic_t ackr_not_idle; /* number of packets in Rx queue */ + + /* received packet records, 1 bit per record */ +#define RXRPC_ACKR_WINDOW_ASZ DIV_ROUND_UP(RXRPC_MAXACKS, BITS_PER_LONG) + unsigned long ackr_window[RXRPC_ACKR_WINDOW_ASZ + 1]; + + /* the following should all be in net order */ + __be32 cid; /* connection ID + channel index */ + __be32 call_id; /* call ID on connection */ +}; + +/* + * RxRPC key for Kerberos (type-2 security) + */ +struct rxkad_key { + u16 security_index; /* RxRPC header security index */ + u16 ticket_len; /* length of ticket[] */ + u32 expiry; /* time at which expires */ + u32 kvno; /* key version number */ + u8 session_key[8]; /* DES session key */ + u8 ticket[0]; /* the encrypted ticket */ +}; + +struct rxrpc_key_payload { + struct rxkad_key k; +}; + +/* + * locally abort an RxRPC call + */ +static inline void rxrpc_abort_call(struct rxrpc_call *call, u32 abort_code) +{ + write_lock_bh(&call->state_lock); + if (call->state < RXRPC_CALL_COMPLETE) { + call->abort_code = abort_code; + call->state = RXRPC_CALL_LOCALLY_ABORTED; + set_bit(RXRPC_CALL_ABORT, &call->events); + } + write_unlock_bh(&call->state_lock); +} + +/* + * put a packet up for transport-level abort + */ +static inline +void rxrpc_reject_packet(struct rxrpc_local *local, struct sk_buff *skb) +{ + CHECK_SLAB_OKAY(&local->usage); + if (!atomic_inc_not_zero(&local->usage)) { + printk("resurrected on reject\n"); + BUG(); + } + skb_queue_tail(&local->reject_queue, skb); + schedule_work(&local->rejecter); +} + +/* + * ar-accept.c + */ +extern void rxrpc_accept_incoming_calls(struct work_struct *); +extern int rxrpc_accept_call(struct rxrpc_sock *, unsigned long); + +/* + * ar-ack.c + */ +extern void __rxrpc_propose_ACK(struct rxrpc_call *, uint8_t, __be32, bool); +extern void rxrpc_propose_ACK(struct rxrpc_call *, uint8_t, __be32, bool); +extern void rxrpc_process_call(struct work_struct *); + +/* + * ar-call.c + */ +extern struct kmem_cache *rxrpc_call_jar; +extern struct list_head rxrpc_calls; +extern rwlock_t rxrpc_call_lock; + +extern struct rxrpc_call *rxrpc_get_client_call(struct rxrpc_sock *, + struct rxrpc_transport *, + struct rxrpc_conn_bundle *, + unsigned long, int, gfp_t); +extern struct rxrpc_call *rxrpc_incoming_call(struct rxrpc_sock *, + struct rxrpc_connection *, + struct rxrpc_header *, gfp_t); +extern struct rxrpc_call *rxrpc_find_server_call(struct rxrpc_sock *, + unsigned long); +extern void rxrpc_release_call(struct rxrpc_call *); +extern void rxrpc_release_calls_on_socket(struct rxrpc_sock *); +extern void __rxrpc_put_call(struct rxrpc_call *); +extern void __exit rxrpc_destroy_all_calls(void); + +/* + * ar-connection.c + */ +extern struct list_head rxrpc_connections; +extern rwlock_t rxrpc_connection_lock; + +extern struct rxrpc_conn_bundle *rxrpc_get_bundle(struct rxrpc_sock *, + struct rxrpc_transport *, + struct key *, + __be16, gfp_t); +extern void rxrpc_put_bundle(struct rxrpc_transport *, + struct rxrpc_conn_bundle *); +extern int rxrpc_connect_call(struct rxrpc_sock *, struct rxrpc_transport *, + struct rxrpc_conn_bundle *, struct rxrpc_call *, + gfp_t); +extern void rxrpc_put_connection(struct rxrpc_connection *); +extern void __exit rxrpc_destroy_all_connections(void); +extern struct rxrpc_connection *rxrpc_find_connection(struct rxrpc_transport *, + struct rxrpc_header *); +extern struct rxrpc_connection * +rxrpc_incoming_connection(struct rxrpc_transport *, struct rxrpc_header *, + gfp_t); + +/* + * ar-connevent.c + */ +extern void rxrpc_process_connection(struct work_struct *); +extern void rxrpc_reject_packets(struct work_struct *); + +/* + * ar-error.c + */ +extern void rxrpc_UDP_error_report(struct sock *); +extern void rxrpc_UDP_error_handler(struct work_struct *); + +/* + * ar-input.c + */ +extern unsigned long rxrpc_ack_timeout; +extern const char *rxrpc_pkts[]; + +extern void rxrpc_data_ready(struct sock *, int); +extern int rxrpc_queue_rcv_skb(struct rxrpc_call *, struct sk_buff *, bool, + bool); +extern void rxrpc_fast_process_packet(struct rxrpc_call *, struct sk_buff *); + +/* + * ar-local.c + */ +extern rwlock_t rxrpc_local_lock; +extern struct rxrpc_local *rxrpc_lookup_local(struct sockaddr_rxrpc *); +extern void rxrpc_put_local(struct rxrpc_local *); +extern void __exit rxrpc_destroy_all_locals(void); + +/* + * ar-key.c + */ +extern struct key_type key_type_rxrpc; +extern struct key_type key_type_rxrpc_s; + +extern int rxrpc_request_key(struct rxrpc_sock *, char __user *, int); +extern int rxrpc_server_keyring(struct rxrpc_sock *, char __user *, int); +extern int rxrpc_get_server_data_key(struct rxrpc_connection *, const void *, + time_t, u32); + +/* + * ar-output.c + */ +extern int rxrpc_resend_timeout; + +extern int rxrpc_send_packet(struct rxrpc_transport *, struct sk_buff *); +extern int rxrpc_client_sendmsg(struct kiocb *, struct rxrpc_sock *, + struct rxrpc_transport *, struct msghdr *, + size_t); +extern int rxrpc_server_sendmsg(struct kiocb *, struct rxrpc_sock *, + struct msghdr *, size_t); + +/* + * ar-peer.c + */ +extern struct rxrpc_peer *rxrpc_get_peer(struct sockaddr_rxrpc *, gfp_t); +extern void rxrpc_put_peer(struct rxrpc_peer *); +extern struct rxrpc_peer *rxrpc_find_peer(struct rxrpc_local *, + __be32, __be16); +extern void __exit rxrpc_destroy_all_peers(void); + +/* + * ar-proc.c + */ +extern const char *rxrpc_call_states[]; +extern struct file_operations rxrpc_call_seq_fops; +extern struct file_operations rxrpc_connection_seq_fops; + +/* + * ar-recvmsg.c + */ +extern int rxrpc_recvmsg(struct kiocb *, struct socket *, struct msghdr *, + size_t, int); + +/* + * ar-security.c + */ +extern int rxrpc_register_security(struct rxrpc_security *); +extern void rxrpc_unregister_security(struct rxrpc_security *); +extern int rxrpc_init_client_conn_security(struct rxrpc_connection *); +extern int rxrpc_init_server_conn_security(struct rxrpc_connection *); +extern int rxrpc_secure_packet(const struct rxrpc_call *, struct sk_buff *, + size_t, void *); +extern int rxrpc_verify_packet(const struct rxrpc_call *, struct sk_buff *, + u32 *); +extern void rxrpc_clear_conn_security(struct rxrpc_connection *); + +/* + * ar-skbuff.c + */ +extern void rxrpc_packet_destructor(struct sk_buff *); + +/* + * ar-transport.c + */ +extern struct rxrpc_transport *rxrpc_get_transport(struct rxrpc_local *, + struct rxrpc_peer *, + gfp_t); +extern void rxrpc_put_transport(struct rxrpc_transport *); +extern void __exit rxrpc_destroy_all_transports(void); +extern struct rxrpc_transport *rxrpc_find_transport(struct rxrpc_local *, + struct rxrpc_peer *); + +/* + * debug tracing + */ +extern unsigned rxrpc_debug; + +#define dbgprintk(FMT,...) \ + printk("[%x%-6.6s] "FMT"\n", smp_processor_id(), current->comm ,##__VA_ARGS__) + +/* make sure we maintain the format strings, even when debugging is disabled */ +static inline __attribute__((format(printf,1,2))) +void _dbprintk(const char *fmt, ...) +{ +} + +#define kenter(FMT,...) dbgprintk("==> %s("FMT")",__FUNCTION__ ,##__VA_ARGS__) +#define kleave(FMT,...) dbgprintk("<== %s()"FMT"",__FUNCTION__ ,##__VA_ARGS__) +#define kdebug(FMT,...) dbgprintk(" "FMT ,##__VA_ARGS__) +#define kproto(FMT,...) dbgprintk("### "FMT ,##__VA_ARGS__) +#define knet(FMT,...) dbgprintk("@@@ "FMT ,##__VA_ARGS__) + + +#if defined(__KDEBUG) +#define _enter(FMT,...) kenter(FMT,##__VA_ARGS__) +#define _leave(FMT,...) kleave(FMT,##__VA_ARGS__) +#define _debug(FMT,...) kdebug(FMT,##__VA_ARGS__) +#define _proto(FMT,...) kproto(FMT,##__VA_ARGS__) +#define _net(FMT,...) knet(FMT,##__VA_ARGS__) + +#elif defined(CONFIG_AF_RXRPC_DEBUG) +#define RXRPC_DEBUG_KENTER 0x01 +#define RXRPC_DEBUG_KLEAVE 0x02 +#define RXRPC_DEBUG_KDEBUG 0x04 +#define RXRPC_DEBUG_KPROTO 0x08 +#define RXRPC_DEBUG_KNET 0x10 + +#define _enter(FMT,...) \ +do { \ + if (unlikely(rxrpc_debug & RXRPC_DEBUG_KENTER)) \ + kenter(FMT,##__VA_ARGS__); \ +} while (0) + +#define _leave(FMT,...) \ +do { \ + if (unlikely(rxrpc_debug & RXRPC_DEBUG_KLEAVE)) \ + kleave(FMT,##__VA_ARGS__); \ +} while (0) + +#define _debug(FMT,...) \ +do { \ + if (unlikely(rxrpc_debug & RXRPC_DEBUG_KDEBUG)) \ + kdebug(FMT,##__VA_ARGS__); \ +} while (0) + +#define _proto(FMT,...) \ +do { \ + if (unlikely(rxrpc_debug & RXRPC_DEBUG_KPROTO)) \ + kproto(FMT,##__VA_ARGS__); \ +} while (0) + +#define _net(FMT,...) \ +do { \ + if (unlikely(rxrpc_debug & RXRPC_DEBUG_KNET)) \ + knet(FMT,##__VA_ARGS__); \ +} while (0) + +#else +#define _enter(FMT,...) _dbprintk("==> %s("FMT")",__FUNCTION__ ,##__VA_ARGS__) +#define _leave(FMT,...) _dbprintk("<== %s()"FMT"",__FUNCTION__ ,##__VA_ARGS__) +#define _debug(FMT,...) _dbprintk(" "FMT ,##__VA_ARGS__) +#define _proto(FMT,...) _dbprintk("### "FMT ,##__VA_ARGS__) +#define _net(FMT,...) _dbprintk("@@@ "FMT ,##__VA_ARGS__) +#endif + +/* + * debug assertion checking + */ +#if 1 // defined(__KDEBUGALL) + +#define ASSERT(X) \ +do { \ + if (unlikely(!(X))) { \ + printk(KERN_ERR "\n"); \ + printk(KERN_ERR "RxRPC: Assertion failed\n"); \ + BUG(); \ + } \ +} while(0) + +#define ASSERTCMP(X, OP, Y) \ +do { \ + if (unlikely(!((X) OP (Y)))) { \ + printk(KERN_ERR "\n"); \ + printk(KERN_ERR "RxRPC: Assertion failed\n"); \ + printk(KERN_ERR "%lu " #OP " %lu is false\n", \ + (unsigned long)(X), (unsigned long)(Y)); \ + printk(KERN_ERR "0x%lx " #OP " 0x%lx is false\n", \ + (unsigned long)(X), (unsigned long)(Y)); \ + BUG(); \ + } \ +} while(0) + +#define ASSERTIF(C, X) \ +do { \ + if (unlikely((C) && !(X))) { \ + printk(KERN_ERR "\n"); \ + printk(KERN_ERR "RxRPC: Assertion failed\n"); \ + BUG(); \ + } \ +} while(0) + +#define ASSERTIFCMP(C, X, OP, Y) \ +do { \ + if (unlikely((C) && !((X) OP (Y)))) { \ + printk(KERN_ERR "\n"); \ + printk(KERN_ERR "RxRPC: Assertion failed\n"); \ + printk(KERN_ERR "%lu " #OP " %lu is false\n", \ + (unsigned long)(X), (unsigned long)(Y)); \ + printk(KERN_ERR "0x%lx " #OP " 0x%lx is false\n", \ + (unsigned long)(X), (unsigned long)(Y)); \ + BUG(); \ + } \ +} while(0) + +#else + +#define ASSERT(X) \ +do { \ +} while(0) + +#define ASSERTCMP(X, OP, Y) \ +do { \ +} while(0) + +#define ASSERTIF(C, X) \ +do { \ +} while(0) + +#define ASSERTIFCMP(C, X, OP, Y) \ +do { \ +} while(0) + +#endif /* __KDEBUGALL */ + +/* + * socket buffer accounting / leak finding + */ +static inline void __rxrpc_new_skb(struct sk_buff *skb, const char *fn) +{ + //_net("new skb %p %s [%d]", skb, fn, atomic_read(&rxrpc_n_skbs)); + //atomic_inc(&rxrpc_n_skbs); +} + +#define rxrpc_new_skb(skb) __rxrpc_new_skb((skb), __func__) + +static inline void __rxrpc_kill_skb(struct sk_buff *skb, const char *fn) +{ + //_net("kill skb %p %s [%d]", skb, fn, atomic_read(&rxrpc_n_skbs)); + //atomic_dec(&rxrpc_n_skbs); +} + +#define rxrpc_kill_skb(skb) __rxrpc_kill_skb((skb), __func__) + +static inline void __rxrpc_free_skb(struct sk_buff *skb, const char *fn) +{ + if (skb) { + CHECK_SLAB_OKAY(&skb->users); + //_net("free skb %p %s [%d]", + // skb, fn, atomic_read(&rxrpc_n_skbs)); + //atomic_dec(&rxrpc_n_skbs); + kfree_skb(skb); + } +} + +#define rxrpc_free_skb(skb) __rxrpc_free_skb((skb), __func__) + +static inline void rxrpc_purge_queue(struct sk_buff_head *list) +{ + struct sk_buff *skb; + while ((skb = skb_dequeue((list))) != NULL) + rxrpc_free_skb(skb); +} + +static inline void __rxrpc__atomic_inc(atomic_t *v) +{ + CHECK_SLAB_OKAY(v); + atomic_inc(v); +} + +#define atomic_inc(v) __rxrpc__atomic_inc((v)) + +static inline void __rxrpc__atomic_dec(atomic_t *v) +{ + CHECK_SLAB_OKAY(v); + atomic_dec(v); +} + +#define atomic_dec(v) __rxrpc__atomic_dec((v)) + +static inline int __rxrpc__atomic_dec_and_test(atomic_t *v) +{ + CHECK_SLAB_OKAY(v); + return atomic_dec_and_test(v); +} + +#define atomic_dec_and_test(v) __rxrpc__atomic_dec_and_test((v)) + +static inline void __rxrpc_get_local(struct rxrpc_local *local, const char *f) +{ + CHECK_SLAB_OKAY(&local->usage); + if (atomic_inc_return(&local->usage) == 1) + printk("resurrected (%s)\n", f); +} + +#define rxrpc_get_local(LOCAL) __rxrpc_get_local((LOCAL), __func__) + +#define rxrpc_get_call(CALL) \ +do { \ + CHECK_SLAB_OKAY(&(CALL)->usage); \ + if (atomic_inc_return(&(CALL)->usage) == 1) \ + BUG(); \ +} while(0) + +#define rxrpc_put_call(CALL) \ +do { \ + __rxrpc_put_call(CALL); \ +} while(0) diff --git a/net/rxrpc/ar-key.c b/net/rxrpc/ar-key.c new file mode 100644 index 000000000000..7e049ff6ae60 --- /dev/null +++ b/net/rxrpc/ar-key.c @@ -0,0 +1,334 @@ +/* RxRPC key management + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * 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. + * + * RxRPC keys should have a description of describing their purpose: + * "afs@CAMBRIDGE.REDHAT.COM> + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ar-internal.h" + +static int rxrpc_instantiate(struct key *, const void *, size_t); +static int rxrpc_instantiate_s(struct key *, const void *, size_t); +static void rxrpc_destroy(struct key *); +static void rxrpc_destroy_s(struct key *); +static void rxrpc_describe(const struct key *, struct seq_file *); + +/* + * rxrpc defined keys take an arbitrary string as the description and an + * arbitrary blob of data as the payload + */ +struct key_type key_type_rxrpc = { + .name = "rxrpc", + .instantiate = rxrpc_instantiate, + .match = user_match, + .destroy = rxrpc_destroy, + .describe = rxrpc_describe, +}; + +EXPORT_SYMBOL(key_type_rxrpc); + +/* + * rxrpc server defined keys take ":" as the + * description and an 8-byte decryption key as the payload + */ +struct key_type key_type_rxrpc_s = { + .name = "rxrpc_s", + .instantiate = rxrpc_instantiate_s, + .match = user_match, + .destroy = rxrpc_destroy_s, + .describe = rxrpc_describe, +}; + +/* + * instantiate an rxrpc defined key + * data should be of the form: + * OFFSET LEN CONTENT + * 0 4 key interface version number + * 4 2 security index (type) + * 6 2 ticket length + * 8 4 key expiry time (time_t) + * 12 4 kvno + * 16 8 session key + * 24 [len] ticket + * + * if no data is provided, then a no-security key is made + */ +static int rxrpc_instantiate(struct key *key, const void *data, size_t datalen) +{ + const struct rxkad_key *tsec; + struct rxrpc_key_payload *upayload; + size_t plen; + u32 kver; + int ret; + + _enter("{%x},,%zu", key_serial(key), datalen); + + /* handle a no-security key */ + if (!data && datalen == 0) + return 0; + + /* get the key interface version number */ + ret = -EINVAL; + if (datalen <= 4 || !data) + goto error; + memcpy(&kver, data, sizeof(kver)); + data += sizeof(kver); + datalen -= sizeof(kver); + + _debug("KEY I/F VERSION: %u", kver); + + ret = -EKEYREJECTED; + if (kver != 1) + goto error; + + /* deal with a version 1 key */ + ret = -EINVAL; + if (datalen < sizeof(*tsec)) + goto error; + + tsec = data; + if (datalen != sizeof(*tsec) + tsec->ticket_len) + goto error; + + _debug("SCIX: %u", tsec->security_index); + _debug("TLEN: %u", tsec->ticket_len); + _debug("EXPY: %x", tsec->expiry); + _debug("KVNO: %u", tsec->kvno); + _debug("SKEY: %02x%02x%02x%02x%02x%02x%02x%02x", + tsec->session_key[0], tsec->session_key[1], + tsec->session_key[2], tsec->session_key[3], + tsec->session_key[4], tsec->session_key[5], + tsec->session_key[6], tsec->session_key[7]); + if (tsec->ticket_len >= 8) + _debug("TCKT: %02x%02x%02x%02x%02x%02x%02x%02x", + tsec->ticket[0], tsec->ticket[1], + tsec->ticket[2], tsec->ticket[3], + tsec->ticket[4], tsec->ticket[5], + tsec->ticket[6], tsec->ticket[7]); + + ret = -EPROTONOSUPPORT; + if (tsec->security_index != 2) + goto error; + + key->type_data.x[0] = tsec->security_index; + + plen = sizeof(*upayload) + tsec->ticket_len; + ret = key_payload_reserve(key, plen); + if (ret < 0) + goto error; + + ret = -ENOMEM; + upayload = kmalloc(plen, GFP_KERNEL); + if (!upayload) + goto error; + + /* attach the data */ + memcpy(&upayload->k, tsec, sizeof(*tsec)); + memcpy(&upayload->k.ticket, (void *)tsec + sizeof(*tsec), + tsec->ticket_len); + key->payload.data = upayload; + key->expiry = tsec->expiry; + ret = 0; + +error: + return ret; +} + +/* + * instantiate a server secret key + * data should be a pointer to the 8-byte secret key + */ +static int rxrpc_instantiate_s(struct key *key, const void *data, + size_t datalen) +{ + struct crypto_blkcipher *ci; + + _enter("{%x},,%zu", key_serial(key), datalen); + + if (datalen != 8) + return -EINVAL; + + memcpy(&key->type_data, data, 8); + + ci = crypto_alloc_blkcipher("pcbc(des)", 0, CRYPTO_ALG_ASYNC); + if (IS_ERR(ci)) { + _leave(" = %ld", PTR_ERR(ci)); + return PTR_ERR(ci); + } + + if (crypto_blkcipher_setkey(ci, data, 8) < 0) + BUG(); + + key->payload.data = ci; + _leave(" = 0"); + return 0; +} + +/* + * dispose of the data dangling from the corpse of a rxrpc key + */ +static void rxrpc_destroy(struct key *key) +{ + kfree(key->payload.data); +} + +/* + * dispose of the data dangling from the corpse of a rxrpc key + */ +static void rxrpc_destroy_s(struct key *key) +{ + if (key->payload.data) { + crypto_free_blkcipher(key->payload.data); + key->payload.data = NULL; + } +} + +/* + * describe the rxrpc key + */ +static void rxrpc_describe(const struct key *key, struct seq_file *m) +{ + seq_puts(m, key->description); +} + +/* + * grab the security key for a socket + */ +int rxrpc_request_key(struct rxrpc_sock *rx, char __user *optval, int optlen) +{ + struct key *key; + char *description; + + _enter(""); + + if (optlen <= 0 || optlen > PAGE_SIZE - 1) + return -EINVAL; + + description = kmalloc(optlen + 1, GFP_KERNEL); + if (!description) + return -ENOMEM; + + if (copy_from_user(description, optval, optlen)) { + kfree(description); + return -EFAULT; + } + description[optlen] = 0; + + key = request_key(&key_type_rxrpc, description, NULL); + if (IS_ERR(key)) { + kfree(description); + _leave(" = %ld", PTR_ERR(key)); + return PTR_ERR(key); + } + + rx->key = key; + kfree(description); + _leave(" = 0 [key %x]", key->serial); + return 0; +} + +/* + * grab the security keyring for a server socket + */ +int rxrpc_server_keyring(struct rxrpc_sock *rx, char __user *optval, + int optlen) +{ + struct key *key; + char *description; + + _enter(""); + + if (optlen <= 0 || optlen > PAGE_SIZE - 1) + return -EINVAL; + + description = kmalloc(optlen + 1, GFP_KERNEL); + if (!description) + return -ENOMEM; + + if (copy_from_user(description, optval, optlen)) { + kfree(description); + return -EFAULT; + } + description[optlen] = 0; + + key = request_key(&key_type_keyring, description, NULL); + if (IS_ERR(key)) { + kfree(description); + _leave(" = %ld", PTR_ERR(key)); + return PTR_ERR(key); + } + + rx->securities = key; + kfree(description); + _leave(" = 0 [key %x]", key->serial); + return 0; +} + +/* + * generate a server data key + */ +int rxrpc_get_server_data_key(struct rxrpc_connection *conn, + const void *session_key, + time_t expiry, + u32 kvno) +{ + struct key *key; + int ret; + + struct { + u32 kver; + struct rxkad_key tsec; + } data; + + _enter(""); + + key = key_alloc(&key_type_rxrpc, "x", 0, 0, current, 0, + KEY_ALLOC_NOT_IN_QUOTA); + if (IS_ERR(key)) { + _leave(" = -ENOMEM [alloc %ld]", PTR_ERR(key)); + return -ENOMEM; + } + + _debug("key %d", key_serial(key)); + + data.kver = 1; + data.tsec.security_index = 2; + data.tsec.ticket_len = 0; + data.tsec.expiry = expiry; + data.tsec.kvno = 0; + + memcpy(&data.tsec.session_key, session_key, + sizeof(data.tsec.session_key)); + + ret = key_instantiate_and_link(key, &data, sizeof(data), NULL, NULL); + if (ret < 0) + goto error; + + conn->key = key; + _leave(" = 0 [%d]", key_serial(key)); + return 0; + +error: + key_revoke(key); + key_put(key); + _leave(" = -ENOMEM [ins %d]", ret); + return -ENOMEM; +} + +EXPORT_SYMBOL(rxrpc_get_server_data_key); diff --git a/net/rxrpc/ar-local.c b/net/rxrpc/ar-local.c new file mode 100644 index 000000000000..a20a2c0fe105 --- /dev/null +++ b/net/rxrpc/ar-local.c @@ -0,0 +1,309 @@ +/* AF_RXRPC local endpoint management + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * 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. + */ + +#include +#include +#include +#include +#include +#include "ar-internal.h" + +static LIST_HEAD(rxrpc_locals); +DEFINE_RWLOCK(rxrpc_local_lock); +static DECLARE_RWSEM(rxrpc_local_sem); +static DECLARE_WAIT_QUEUE_HEAD(rxrpc_local_wq); + +static void rxrpc_destroy_local(struct work_struct *work); + +/* + * allocate a new local + */ +static +struct rxrpc_local *rxrpc_alloc_local(struct sockaddr_rxrpc *srx) +{ + struct rxrpc_local *local; + + local = kzalloc(sizeof(struct rxrpc_local), GFP_KERNEL); + if (local) { + INIT_WORK(&local->destroyer, &rxrpc_destroy_local); + INIT_WORK(&local->acceptor, &rxrpc_accept_incoming_calls); + INIT_WORK(&local->rejecter, &rxrpc_reject_packets); + INIT_LIST_HEAD(&local->services); + INIT_LIST_HEAD(&local->link); + init_rwsem(&local->defrag_sem); + skb_queue_head_init(&local->accept_queue); + skb_queue_head_init(&local->reject_queue); + spin_lock_init(&local->lock); + rwlock_init(&local->services_lock); + atomic_set(&local->usage, 1); + local->debug_id = atomic_inc_return(&rxrpc_debug_id); + memcpy(&local->srx, srx, sizeof(*srx)); + } + + _leave(" = %p", local); + return local; +} + +/* + * create the local socket + * - must be called with rxrpc_local_sem writelocked + */ +static int rxrpc_create_local(struct rxrpc_local *local) +{ + struct sock *sock; + int ret, opt; + + _enter("%p{%d}", local, local->srx.transport_type); + + /* create a socket to represent the local endpoint */ + ret = sock_create_kern(PF_INET, local->srx.transport_type, IPPROTO_UDP, + &local->socket); + if (ret < 0) { + _leave(" = %d [socket]", ret); + return ret; + } + + /* if a local address was supplied then bind it */ + if (local->srx.transport_len > sizeof(sa_family_t)) { + _debug("bind"); + ret = kernel_bind(local->socket, + (struct sockaddr *) &local->srx.transport, + local->srx.transport_len); + if (ret < 0) { + _debug("bind failed"); + goto error; + } + } + + /* we want to receive ICMP errors */ + opt = 1; + ret = kernel_setsockopt(local->socket, SOL_IP, IP_RECVERR, + (char *) &opt, sizeof(opt)); + if (ret < 0) { + _debug("setsockopt failed"); + goto error; + } + + /* we want to set the don't fragment bit */ + opt = IP_PMTUDISC_DO; + ret = kernel_setsockopt(local->socket, SOL_IP, IP_MTU_DISCOVER, + (char *) &opt, sizeof(opt)); + if (ret < 0) { + _debug("setsockopt failed"); + goto error; + } + + write_lock_bh(&rxrpc_local_lock); + list_add(&local->link, &rxrpc_locals); + write_unlock_bh(&rxrpc_local_lock); + + /* set the socket up */ + sock = local->socket->sk; + sock->sk_user_data = local; + sock->sk_data_ready = rxrpc_data_ready; + sock->sk_error_report = rxrpc_UDP_error_report; + _leave(" = 0"); + return 0; + +error: + local->socket->ops->shutdown(local->socket, 2); + local->socket->sk->sk_user_data = NULL; + sock_release(local->socket); + local->socket = NULL; + + _leave(" = %d", ret); + return ret; +} + +/* + * create a new local endpoint using the specified UDP address + */ +struct rxrpc_local *rxrpc_lookup_local(struct sockaddr_rxrpc *srx) +{ + struct rxrpc_local *local; + int ret; + + _enter("{%d,%u,%u.%u.%u.%u+%hu}", + srx->transport_type, + srx->transport.family, + NIPQUAD(srx->transport.sin.sin_addr), + ntohs(srx->transport.sin.sin_port)); + + down_write(&rxrpc_local_sem); + + /* see if we have a suitable local local endpoint already */ + read_lock_bh(&rxrpc_local_lock); + + list_for_each_entry(local, &rxrpc_locals, link) { + _debug("CMP {%d,%u,%u.%u.%u.%u+%hu}", + local->srx.transport_type, + local->srx.transport.family, + NIPQUAD(local->srx.transport.sin.sin_addr), + ntohs(local->srx.transport.sin.sin_port)); + + if (local->srx.transport_type != srx->transport_type || + local->srx.transport.family != srx->transport.family) + continue; + + switch (srx->transport.family) { + case AF_INET: + if (local->srx.transport.sin.sin_port != + srx->transport.sin.sin_port) + continue; + if (memcmp(&local->srx.transport.sin.sin_addr, + &srx->transport.sin.sin_addr, + sizeof(struct in_addr)) != 0) + continue; + goto found_local; + + default: + BUG(); + } + } + + read_unlock_bh(&rxrpc_local_lock); + + /* we didn't find one, so we need to create one */ + local = rxrpc_alloc_local(srx); + if (!local) { + up_write(&rxrpc_local_sem); + return ERR_PTR(-ENOMEM); + } + + ret = rxrpc_create_local(local); + if (ret < 0) { + up_write(&rxrpc_local_sem); + kfree(local); + _leave(" = %d", ret); + return ERR_PTR(ret); + } + + up_write(&rxrpc_local_sem); + + _net("LOCAL new %d {%d,%u,%u.%u.%u.%u+%hu}", + local->debug_id, + local->srx.transport_type, + local->srx.transport.family, + NIPQUAD(local->srx.transport.sin.sin_addr), + ntohs(local->srx.transport.sin.sin_port)); + + _leave(" = %p [new]", local); + return local; + +found_local: + rxrpc_get_local(local); + read_unlock_bh(&rxrpc_local_lock); + up_write(&rxrpc_local_sem); + + _net("LOCAL old %d {%d,%u,%u.%u.%u.%u+%hu}", + local->debug_id, + local->srx.transport_type, + local->srx.transport.family, + NIPQUAD(local->srx.transport.sin.sin_addr), + ntohs(local->srx.transport.sin.sin_port)); + + _leave(" = %p [reuse]", local); + return local; +} + +/* + * release a local endpoint + */ +void rxrpc_put_local(struct rxrpc_local *local) +{ + _enter("%p{u=%d}", local, atomic_read(&local->usage)); + + ASSERTCMP(atomic_read(&local->usage), >, 0); + + /* to prevent a race, the decrement and the dequeue must be effectively + * atomic */ + write_lock_bh(&rxrpc_local_lock); + if (unlikely(atomic_dec_and_test(&local->usage))) { + _debug("destroy local"); + schedule_work(&local->destroyer); + } + write_unlock_bh(&rxrpc_local_lock); + _leave(""); +} + +/* + * destroy a local endpoint + */ +static void rxrpc_destroy_local(struct work_struct *work) +{ + struct rxrpc_local *local = + container_of(work, struct rxrpc_local, destroyer); + + _enter("%p{%d}", local, atomic_read(&local->usage)); + + down_write(&rxrpc_local_sem); + + write_lock_bh(&rxrpc_local_lock); + if (atomic_read(&local->usage) > 0) { + write_unlock_bh(&rxrpc_local_lock); + up_read(&rxrpc_local_sem); + _leave(" [resurrected]"); + return; + } + + list_del(&local->link); + local->socket->sk->sk_user_data = NULL; + write_unlock_bh(&rxrpc_local_lock); + + downgrade_write(&rxrpc_local_sem); + + ASSERT(list_empty(&local->services)); + ASSERT(!work_pending(&local->acceptor)); + ASSERT(!work_pending(&local->rejecter)); + + /* finish cleaning up the local descriptor */ + rxrpc_purge_queue(&local->accept_queue); + rxrpc_purge_queue(&local->reject_queue); + local->socket->ops->shutdown(local->socket, 2); + sock_release(local->socket); + + up_read(&rxrpc_local_sem); + + _net("DESTROY LOCAL %d", local->debug_id); + kfree(local); + + if (list_empty(&rxrpc_locals)) + wake_up_all(&rxrpc_local_wq); + + _leave(""); +} + +/* + * preemptively destroy all local local endpoint rather than waiting for + * them to be destroyed + */ +void __exit rxrpc_destroy_all_locals(void) +{ + DECLARE_WAITQUEUE(myself,current); + + _enter(""); + + /* we simply have to wait for them to go away */ + if (!list_empty(&rxrpc_locals)) { + set_current_state(TASK_UNINTERRUPTIBLE); + add_wait_queue(&rxrpc_local_wq, &myself); + + while (!list_empty(&rxrpc_locals)) { + schedule(); + set_current_state(TASK_UNINTERRUPTIBLE); + } + + remove_wait_queue(&rxrpc_local_wq, &myself); + set_current_state(TASK_RUNNING); + } + + _leave(""); +} diff --git a/net/rxrpc/ar-output.c b/net/rxrpc/ar-output.c new file mode 100644 index 000000000000..67aa9510f09b --- /dev/null +++ b/net/rxrpc/ar-output.c @@ -0,0 +1,658 @@ +/* RxRPC packet transmission + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * 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. + */ + +#include +#include +#include +#include +#include +#include "ar-internal.h" + +int rxrpc_resend_timeout = 4; + +static int rxrpc_send_data(struct kiocb *iocb, + struct rxrpc_sock *rx, + struct rxrpc_call *call, + struct msghdr *msg, size_t len); + +/* + * extract control messages from the sendmsg() control buffer + */ +static int rxrpc_sendmsg_cmsg(struct rxrpc_sock *rx, struct msghdr *msg, + unsigned long *user_call_ID, + enum rxrpc_command *command, + u32 *abort_code, + bool server) +{ + struct cmsghdr *cmsg; + int len; + + *command = RXRPC_CMD_SEND_DATA; + + if (msg->msg_controllen == 0) + return -EINVAL; + + for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) { + if (!CMSG_OK(msg, cmsg)) + return -EINVAL; + + len = cmsg->cmsg_len - CMSG_ALIGN(sizeof(struct cmsghdr)); + _debug("CMSG %d, %d, %d", + cmsg->cmsg_level, cmsg->cmsg_type, len); + + if (cmsg->cmsg_level != SOL_RXRPC) + continue; + + switch (cmsg->cmsg_type) { + case RXRPC_USER_CALL_ID: + if (msg->msg_flags & MSG_CMSG_COMPAT) { + if (len != sizeof(u32)) + return -EINVAL; + *user_call_ID = *(u32 *) CMSG_DATA(cmsg); + } else { + if (len != sizeof(unsigned long)) + return -EINVAL; + *user_call_ID = *(unsigned long *) + CMSG_DATA(cmsg); + } + _debug("User Call ID %lx", *user_call_ID); + break; + + case RXRPC_ABORT: + if (*command != RXRPC_CMD_SEND_DATA) + return -EINVAL; + *command = RXRPC_CMD_SEND_ABORT; + if (len != sizeof(*abort_code)) + return -EINVAL; + *abort_code = *(unsigned int *) CMSG_DATA(cmsg); + _debug("Abort %x", *abort_code); + if (*abort_code == 0) + return -EINVAL; + break; + + case RXRPC_ACCEPT: + if (*command != RXRPC_CMD_SEND_DATA) + return -EINVAL; + *command = RXRPC_CMD_ACCEPT; + if (len != 0) + return -EINVAL; + if (!server) + return -EISCONN; + break; + + default: + return -EINVAL; + } + } + + _leave(" = 0"); + return 0; +} + +/* + * abort a call, sending an ABORT packet to the peer + */ +static void rxrpc_send_abort(struct rxrpc_call *call, u32 abort_code) +{ + write_lock_bh(&call->state_lock); + + if (call->state <= RXRPC_CALL_COMPLETE) { + call->state = RXRPC_CALL_LOCALLY_ABORTED; + call->abort_code = abort_code; + set_bit(RXRPC_CALL_ABORT, &call->events); + del_timer_sync(&call->resend_timer); + del_timer_sync(&call->ack_timer); + clear_bit(RXRPC_CALL_RESEND_TIMER, &call->events); + clear_bit(RXRPC_CALL_ACK, &call->events); + clear_bit(RXRPC_CALL_RUN_RTIMER, &call->flags); + schedule_work(&call->processor); + } + + write_unlock_bh(&call->state_lock); +} + +/* + * send a message forming part of a client call through an RxRPC socket + * - caller holds the socket locked + * - the socket may be either a client socket or a server socket + */ +int rxrpc_client_sendmsg(struct kiocb *iocb, struct rxrpc_sock *rx, + struct rxrpc_transport *trans, struct msghdr *msg, + size_t len) +{ + struct rxrpc_conn_bundle *bundle; + enum rxrpc_command cmd; + struct rxrpc_call *call; + unsigned long user_call_ID = 0; + struct key *key; + __be16 service_id; + u32 abort_code = 0; + int ret; + + _enter(""); + + ASSERT(trans != NULL); + + ret = rxrpc_sendmsg_cmsg(rx, msg, &user_call_ID, &cmd, &abort_code, + false); + if (ret < 0) + return ret; + + bundle = NULL; + if (trans) { + service_id = rx->service_id; + if (msg->msg_name) { + struct sockaddr_rxrpc *srx = + (struct sockaddr_rxrpc *) msg->msg_name; + service_id = htons(srx->srx_service); + } + key = rx->key; + if (key && !rx->key->payload.data) + key = NULL; + bundle = rxrpc_get_bundle(rx, trans, key, service_id, + GFP_KERNEL); + if (IS_ERR(bundle)) + return PTR_ERR(bundle); + } + + call = rxrpc_get_client_call(rx, trans, bundle, user_call_ID, + abort_code == 0, GFP_KERNEL); + if (trans) + rxrpc_put_bundle(trans, bundle); + if (IS_ERR(call)) { + _leave(" = %ld", PTR_ERR(call)); + return PTR_ERR(call); + } + + _debug("CALL %d USR %lx ST %d on CONN %p", + call->debug_id, call->user_call_ID, call->state, call->conn); + + if (call->state >= RXRPC_CALL_COMPLETE) { + /* it's too late for this call */ + ret = -ESHUTDOWN; + } else if (cmd == RXRPC_CMD_SEND_ABORT) { + rxrpc_send_abort(call, abort_code); + } else if (cmd != RXRPC_CMD_SEND_DATA) { + ret = -EINVAL; + } else if (call->state != RXRPC_CALL_CLIENT_SEND_REQUEST) { + /* request phase complete for this client call */ + ret = -EPROTO; + } else { + ret = rxrpc_send_data(iocb, rx, call, msg, len); + } + + rxrpc_put_call(call); + _leave(" = %d", ret); + return ret; +} + +/* + * send a message through a server socket + * - caller holds the socket locked + */ +int rxrpc_server_sendmsg(struct kiocb *iocb, struct rxrpc_sock *rx, + struct msghdr *msg, size_t len) +{ + enum rxrpc_command cmd; + struct rxrpc_call *call; + unsigned long user_call_ID = 0; + u32 abort_code = 0; + int ret; + + _enter(""); + + ret = rxrpc_sendmsg_cmsg(rx, msg, &user_call_ID, &cmd, &abort_code, + true); + if (ret < 0) + return ret; + + if (cmd == RXRPC_CMD_ACCEPT) + return rxrpc_accept_call(rx, user_call_ID); + + call = rxrpc_find_server_call(rx, user_call_ID); + if (!call) + return -EBADSLT; + if (call->state >= RXRPC_CALL_COMPLETE) { + ret = -ESHUTDOWN; + goto out; + } + + switch (cmd) { + case RXRPC_CMD_SEND_DATA: + if (call->state != RXRPC_CALL_CLIENT_SEND_REQUEST && + call->state != RXRPC_CALL_SERVER_ACK_REQUEST && + call->state != RXRPC_CALL_SERVER_SEND_REPLY) { + /* Tx phase not yet begun for this call */ + ret = -EPROTO; + break; + } + + ret = rxrpc_send_data(iocb, rx, call, msg, len); + break; + + case RXRPC_CMD_SEND_ABORT: + rxrpc_send_abort(call, abort_code); + break; + default: + BUG(); + } + + out: + rxrpc_put_call(call); + _leave(" = %d", ret); + return ret; +} + +/* + * send a packet through the transport endpoint + */ +int rxrpc_send_packet(struct rxrpc_transport *trans, struct sk_buff *skb) +{ + struct kvec iov[1]; + struct msghdr msg; + int ret, opt; + + _enter(",{%d}", skb->len); + + iov[0].iov_base = skb->head; + iov[0].iov_len = skb->len; + + msg.msg_name = &trans->peer->srx.transport.sin; + msg.msg_namelen = sizeof(trans->peer->srx.transport.sin); + msg.msg_control = NULL; + msg.msg_controllen = 0; + msg.msg_flags = 0; + + /* send the packet with the don't fragment bit set if we currently + * think it's small enough */ + if (skb->len - sizeof(struct rxrpc_header) < trans->peer->maxdata) { + down_read(&trans->local->defrag_sem); + /* send the packet by UDP + * - returns -EMSGSIZE if UDP would have to fragment the packet + * to go out of the interface + * - in which case, we'll have processed the ICMP error + * message and update the peer record + */ + ret = kernel_sendmsg(trans->local->socket, &msg, iov, 1, + iov[0].iov_len); + + up_read(&trans->local->defrag_sem); + if (ret == -EMSGSIZE) + goto send_fragmentable; + + _leave(" = %d [%u]", ret, trans->peer->maxdata); + return ret; + } + +send_fragmentable: + /* attempt to send this message with fragmentation enabled */ + _debug("send fragment"); + + down_write(&trans->local->defrag_sem); + opt = IP_PMTUDISC_DONT; + ret = kernel_setsockopt(trans->local->socket, SOL_IP, IP_MTU_DISCOVER, + (char *) &opt, sizeof(opt)); + if (ret == 0) { + ret = kernel_sendmsg(trans->local->socket, &msg, iov, 1, + iov[0].iov_len); + + opt = IP_PMTUDISC_DO; + kernel_setsockopt(trans->local->socket, SOL_IP, + IP_MTU_DISCOVER, (char *) &opt, sizeof(opt)); + } + + up_write(&trans->local->defrag_sem); + _leave(" = %d [frag %u]", ret, trans->peer->maxdata); + return ret; +} + +/* + * wait for space to appear in the transmit/ACK window + * - caller holds the socket locked + */ +static int rxrpc_wait_for_tx_window(struct rxrpc_sock *rx, + struct rxrpc_call *call, + long *timeo) +{ + DECLARE_WAITQUEUE(myself, current); + int ret; + + _enter(",{%d},%ld", + CIRC_SPACE(call->acks_head, call->acks_tail, call->acks_winsz), + *timeo); + + add_wait_queue(&call->tx_waitq, &myself); + + for (;;) { + set_current_state(TASK_INTERRUPTIBLE); + ret = 0; + if (CIRC_SPACE(call->acks_head, call->acks_tail, + call->acks_winsz) > 0) + break; + if (signal_pending(current)) { + ret = sock_intr_errno(*timeo); + break; + } + + release_sock(&rx->sk); + *timeo = schedule_timeout(*timeo); + lock_sock(&rx->sk); + } + + remove_wait_queue(&call->tx_waitq, &myself); + set_current_state(TASK_RUNNING); + _leave(" = %d", ret); + return ret; +} + +/* + * attempt to schedule an instant Tx resend + */ +static inline void rxrpc_instant_resend(struct rxrpc_call *call) +{ + read_lock_bh(&call->state_lock); + if (try_to_del_timer_sync(&call->resend_timer) >= 0) { + clear_bit(RXRPC_CALL_RUN_RTIMER, &call->flags); + if (call->state < RXRPC_CALL_COMPLETE && + !test_and_set_bit(RXRPC_CALL_RESEND_TIMER, &call->events)) + schedule_work(&call->processor); + } + read_unlock_bh(&call->state_lock); +} + +/* + * queue a packet for transmission, set the resend timer and attempt + * to send the packet immediately + */ +static void rxrpc_queue_packet(struct rxrpc_call *call, struct sk_buff *skb, + bool last) +{ + struct rxrpc_skb_priv *sp = rxrpc_skb(skb); + int ret; + + _net("queue skb %p [%d]", skb, call->acks_head); + + ASSERT(call->acks_window != NULL); + call->acks_window[call->acks_head] = (unsigned long) skb; + smp_wmb(); + call->acks_head = (call->acks_head + 1) & (call->acks_winsz - 1); + + if (last || call->state == RXRPC_CALL_SERVER_ACK_REQUEST) { + _debug("________awaiting reply/ACK__________"); + write_lock_bh(&call->state_lock); + switch (call->state) { + case RXRPC_CALL_CLIENT_SEND_REQUEST: + call->state = RXRPC_CALL_CLIENT_AWAIT_REPLY; + break; + case RXRPC_CALL_SERVER_ACK_REQUEST: + call->state = RXRPC_CALL_SERVER_SEND_REPLY; + if (!last) + break; + case RXRPC_CALL_SERVER_SEND_REPLY: + call->state = RXRPC_CALL_SERVER_AWAIT_ACK; + break; + default: + break; + } + write_unlock_bh(&call->state_lock); + } + + _proto("Tx DATA %%%u { #%u }", + ntohl(sp->hdr.serial), ntohl(sp->hdr.seq)); + + sp->need_resend = 0; + sp->resend_at = jiffies + rxrpc_resend_timeout * HZ; + if (!test_and_set_bit(RXRPC_CALL_RUN_RTIMER, &call->flags)) { + _debug("run timer"); + call->resend_timer.expires = sp->resend_at; + add_timer(&call->resend_timer); + } + + /* attempt to cancel the rx-ACK timer, deferring reply transmission if + * we're ACK'ing the request phase of an incoming call */ + ret = -EAGAIN; + if (try_to_del_timer_sync(&call->ack_timer) >= 0) { + /* the packet may be freed by rxrpc_process_call() before this + * returns */ + ret = rxrpc_send_packet(call->conn->trans, skb); + _net("sent skb %p", skb); + } else { + _debug("failed to delete ACK timer"); + } + + if (ret < 0) { + _debug("need instant resend %d", ret); + sp->need_resend = 1; + rxrpc_instant_resend(call); + } + + _leave(""); +} + +/* + * send data through a socket + * - must be called in process context + * - caller holds the socket locked + */ +static int rxrpc_send_data(struct kiocb *iocb, + struct rxrpc_sock *rx, + struct rxrpc_call *call, + struct msghdr *msg, size_t len) +{ + struct rxrpc_skb_priv *sp; + unsigned char __user *from; + struct sk_buff *skb; + struct iovec *iov; + struct sock *sk = &rx->sk; + long timeo; + bool more; + int ret, ioc, segment, copied; + + _enter(",,,{%zu},%zu", msg->msg_iovlen, len); + + timeo = sock_sndtimeo(sk, msg->msg_flags & MSG_DONTWAIT); + + /* this should be in poll */ + clear_bit(SOCK_ASYNC_NOSPACE, &sk->sk_socket->flags); + + if (sk->sk_err || (sk->sk_shutdown & SEND_SHUTDOWN)) + return -EPIPE; + + iov = msg->msg_iov; + ioc = msg->msg_iovlen - 1; + from = iov->iov_base; + segment = iov->iov_len; + iov++; + more = msg->msg_flags & MSG_MORE; + + skb = call->tx_pending; + call->tx_pending = NULL; + + copied = 0; + do { + int copy; + + if (segment > len) + segment = len; + + _debug("SEGMENT %d @%p", segment, from); + + if (!skb) { + size_t size, chunk, max, space; + + _debug("alloc"); + + if (CIRC_SPACE(call->acks_head, call->acks_tail, + call->acks_winsz) <= 0) { + ret = -EAGAIN; + if (msg->msg_flags & MSG_DONTWAIT) + goto maybe_error; + ret = rxrpc_wait_for_tx_window(rx, call, + &timeo); + if (ret < 0) + goto maybe_error; + } + + max = call->conn->trans->peer->maxdata; + max -= call->conn->security_size; + max &= ~(call->conn->size_align - 1UL); + + chunk = max; + if (chunk > len) + chunk = len; + + space = chunk + call->conn->size_align; + space &= ~(call->conn->size_align - 1UL); + + size = space + call->conn->header_size; + + _debug("SIZE: %zu/%zu/%zu", chunk, space, size); + + /* create a buffer that we can retain until it's ACK'd */ + skb = sock_alloc_send_skb( + sk, size, msg->msg_flags & MSG_DONTWAIT, &ret); + if (!skb) + goto maybe_error; + + rxrpc_new_skb(skb); + + _debug("ALLOC SEND %p", skb); + + ASSERTCMP(skb->mark, ==, 0); + + _debug("HS: %u", call->conn->header_size); + skb_reserve(skb, call->conn->header_size); + skb->len += call->conn->header_size; + + sp = rxrpc_skb(skb); + sp->remain = chunk; + if (sp->remain > skb_tailroom(skb)) + sp->remain = skb_tailroom(skb); + + _net("skb: hr %d, tr %d, hl %d, rm %d", + skb_headroom(skb), + skb_tailroom(skb), + skb_headlen(skb), + sp->remain); + + skb->ip_summed = CHECKSUM_UNNECESSARY; + } + + _debug("append"); + sp = rxrpc_skb(skb); + + /* append next segment of data to the current buffer */ + copy = skb_tailroom(skb); + ASSERTCMP(copy, >, 0); + if (copy > segment) + copy = segment; + if (copy > sp->remain) + copy = sp->remain; + + _debug("add"); + ret = skb_add_data(skb, from, copy); + _debug("added"); + if (ret < 0) + goto efault; + sp->remain -= copy; + skb->mark += copy; + + len -= copy; + segment -= copy; + from += copy; + while (segment == 0 && ioc > 0) { + from = iov->iov_base; + segment = iov->iov_len; + iov++; + ioc--; + } + if (len == 0) { + segment = 0; + ioc = 0; + } + + /* check for the far side aborting the call or a network error + * occurring */ + if (call->state > RXRPC_CALL_COMPLETE) + goto call_aborted; + + /* add the packet to the send queue if it's now full */ + if (sp->remain <= 0 || (segment == 0 && !more)) { + struct rxrpc_connection *conn = call->conn; + size_t pad; + + /* pad out if we're using security */ + if (conn->security) { + pad = conn->security_size + skb->mark; + pad = conn->size_align - pad; + pad &= conn->size_align - 1; + _debug("pad %zu", pad); + if (pad) + memset(skb_put(skb, pad), 0, pad); + } + + sp->hdr.epoch = conn->epoch; + sp->hdr.cid = call->cid; + sp->hdr.callNumber = call->call_id; + sp->hdr.seq = + htonl(atomic_inc_return(&call->sequence)); + sp->hdr.serial = + htonl(atomic_inc_return(&conn->serial)); + sp->hdr.type = RXRPC_PACKET_TYPE_DATA; + sp->hdr.userStatus = 0; + sp->hdr.securityIndex = conn->security_ix; + sp->hdr._rsvd = 0; + sp->hdr.serviceId = conn->service_id; + + sp->hdr.flags = conn->out_clientflag; + if (len == 0 && !more) + sp->hdr.flags |= RXRPC_LAST_PACKET; + else if (CIRC_SPACE(call->acks_head, call->acks_tail, + call->acks_winsz) > 1) + sp->hdr.flags |= RXRPC_MORE_PACKETS; + + ret = rxrpc_secure_packet( + call, skb, skb->mark, + skb->head + sizeof(struct rxrpc_header)); + if (ret < 0) + goto out; + + memcpy(skb->head, &sp->hdr, + sizeof(struct rxrpc_header)); + rxrpc_queue_packet(call, skb, segment == 0 && !more); + skb = NULL; + } + + } while (segment > 0); + +out: + call->tx_pending = skb; + _leave(" = %d", ret); + return ret; + +call_aborted: + rxrpc_free_skb(skb); + if (call->state == RXRPC_CALL_NETWORK_ERROR) + ret = call->conn->trans->peer->net_error; + else + ret = -ECONNABORTED; + _leave(" = %d", ret); + return ret; + +maybe_error: + if (copied) + ret = copied; + goto out; + +efault: + ret = -EFAULT; + goto out; +} diff --git a/net/rxrpc/ar-peer.c b/net/rxrpc/ar-peer.c new file mode 100644 index 000000000000..69ac355546ae --- /dev/null +++ b/net/rxrpc/ar-peer.c @@ -0,0 +1,273 @@ +/* RxRPC remote transport endpoint management + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ar-internal.h" + +static LIST_HEAD(rxrpc_peers); +static DEFINE_RWLOCK(rxrpc_peer_lock); +static DECLARE_WAIT_QUEUE_HEAD(rxrpc_peer_wq); + +static void rxrpc_destroy_peer(struct work_struct *work); + +/* + * allocate a new peer + */ +static struct rxrpc_peer *rxrpc_alloc_peer(struct sockaddr_rxrpc *srx, + gfp_t gfp) +{ + struct rxrpc_peer *peer; + + _enter(""); + + peer = kzalloc(sizeof(struct rxrpc_peer), gfp); + if (peer) { + INIT_WORK(&peer->destroyer, &rxrpc_destroy_peer); + INIT_LIST_HEAD(&peer->link); + INIT_LIST_HEAD(&peer->error_targets); + spin_lock_init(&peer->lock); + atomic_set(&peer->usage, 1); + peer->debug_id = atomic_inc_return(&rxrpc_debug_id); + memcpy(&peer->srx, srx, sizeof(*srx)); + + peer->mtu = peer->if_mtu = 65535; + + if (srx->transport.family == AF_INET) { + peer->hdrsize = sizeof(struct iphdr); + switch (srx->transport_type) { + case SOCK_DGRAM: + peer->hdrsize += sizeof(struct udphdr); + break; + default: + BUG(); + break; + } + } else { + BUG(); + } + + peer->hdrsize += sizeof(struct rxrpc_header); + peer->maxdata = peer->mtu - peer->hdrsize; + } + + _leave(" = %p", peer); + return peer; +} + +/* + * obtain a remote transport endpoint for the specified address + */ +struct rxrpc_peer *rxrpc_get_peer(struct sockaddr_rxrpc *srx, gfp_t gfp) +{ + struct rxrpc_peer *peer, *candidate; + const char *new = "old"; + int usage; + + _enter("{%d,%d,%u.%u.%u.%u+%hu}", + srx->transport_type, + srx->transport_len, + NIPQUAD(srx->transport.sin.sin_addr), + ntohs(srx->transport.sin.sin_port)); + + /* search the peer list first */ + read_lock_bh(&rxrpc_peer_lock); + list_for_each_entry(peer, &rxrpc_peers, link) { + _debug("check PEER %d { u=%d t=%d l=%d }", + peer->debug_id, + atomic_read(&peer->usage), + peer->srx.transport_type, + peer->srx.transport_len); + + if (atomic_read(&peer->usage) > 0 && + peer->srx.transport_type == srx->transport_type && + peer->srx.transport_len == srx->transport_len && + memcmp(&peer->srx.transport, + &srx->transport, + srx->transport_len) == 0) + goto found_extant_peer; + } + read_unlock_bh(&rxrpc_peer_lock); + + /* not yet present - create a candidate for a new record and then + * redo the search */ + candidate = rxrpc_alloc_peer(srx, gfp); + if (!candidate) { + _leave(" = -ENOMEM"); + return ERR_PTR(-ENOMEM); + } + + write_lock_bh(&rxrpc_peer_lock); + + list_for_each_entry(peer, &rxrpc_peers, link) { + if (atomic_read(&peer->usage) > 0 && + peer->srx.transport_type == srx->transport_type && + peer->srx.transport_len == srx->transport_len && + memcmp(&peer->srx.transport, + &srx->transport, + srx->transport_len) == 0) + goto found_extant_second; + } + + /* we can now add the new candidate to the list */ + peer = candidate; + candidate = NULL; + + list_add_tail(&peer->link, &rxrpc_peers); + write_unlock_bh(&rxrpc_peer_lock); + new = "new"; + +success: + _net("PEER %s %d {%d,%u,%u.%u.%u.%u+%hu}", + new, + peer->debug_id, + peer->srx.transport_type, + peer->srx.transport.family, + NIPQUAD(peer->srx.transport.sin.sin_addr), + ntohs(peer->srx.transport.sin.sin_port)); + + _leave(" = %p {u=%d}", peer, atomic_read(&peer->usage)); + return peer; + + /* we found the peer in the list immediately */ +found_extant_peer: + usage = atomic_inc_return(&peer->usage); + read_unlock_bh(&rxrpc_peer_lock); + goto success; + + /* we found the peer on the second time through the list */ +found_extant_second: + usage = atomic_inc_return(&peer->usage); + write_unlock_bh(&rxrpc_peer_lock); + kfree(candidate); + goto success; +} + +/* + * find the peer associated with a packet + */ +struct rxrpc_peer *rxrpc_find_peer(struct rxrpc_local *local, + __be32 addr, __be16 port) +{ + struct rxrpc_peer *peer; + + _enter(""); + + /* search the peer list */ + read_lock_bh(&rxrpc_peer_lock); + + if (local->srx.transport.family == AF_INET && + local->srx.transport_type == SOCK_DGRAM + ) { + list_for_each_entry(peer, &rxrpc_peers, link) { + if (atomic_read(&peer->usage) > 0 && + peer->srx.transport_type == SOCK_DGRAM && + peer->srx.transport.family == AF_INET && + peer->srx.transport.sin.sin_port == port && + peer->srx.transport.sin.sin_addr.s_addr == addr) + goto found_UDP_peer; + } + + goto new_UDP_peer; + } + + read_unlock_bh(&rxrpc_peer_lock); + _leave(" = -EAFNOSUPPORT"); + return ERR_PTR(-EAFNOSUPPORT); + +found_UDP_peer: + _net("Rx UDP DGRAM from peer %d", peer->debug_id); + atomic_inc(&peer->usage); + read_unlock_bh(&rxrpc_peer_lock); + _leave(" = %p", peer); + return peer; + +new_UDP_peer: + _net("Rx UDP DGRAM from NEW peer %d", peer->debug_id); + read_unlock_bh(&rxrpc_peer_lock); + _leave(" = -EBUSY [new]"); + return ERR_PTR(-EBUSY); +} + +/* + * release a remote transport endpoint + */ +void rxrpc_put_peer(struct rxrpc_peer *peer) +{ + _enter("%p{u=%d}", peer, atomic_read(&peer->usage)); + + ASSERTCMP(atomic_read(&peer->usage), >, 0); + + if (likely(!atomic_dec_and_test(&peer->usage))) { + _leave(" [in use]"); + return; + } + + schedule_work(&peer->destroyer); + _leave(""); +} + +/* + * destroy a remote transport endpoint + */ +static void rxrpc_destroy_peer(struct work_struct *work) +{ + struct rxrpc_peer *peer = + container_of(work, struct rxrpc_peer, destroyer); + + _enter("%p{%d}", peer, atomic_read(&peer->usage)); + + write_lock_bh(&rxrpc_peer_lock); + list_del(&peer->link); + write_unlock_bh(&rxrpc_peer_lock); + + _net("DESTROY PEER %d", peer->debug_id); + kfree(peer); + + if (list_empty(&rxrpc_peers)) + wake_up_all(&rxrpc_peer_wq); + _leave(""); +} + +/* + * preemptively destroy all the peer records from a transport endpoint rather + * than waiting for them to time out + */ +void __exit rxrpc_destroy_all_peers(void) +{ + DECLARE_WAITQUEUE(myself,current); + + _enter(""); + + /* we simply have to wait for them to go away */ + if (!list_empty(&rxrpc_peers)) { + set_current_state(TASK_UNINTERRUPTIBLE); + add_wait_queue(&rxrpc_peer_wq, &myself); + + while (!list_empty(&rxrpc_peers)) { + schedule(); + set_current_state(TASK_UNINTERRUPTIBLE); + } + + remove_wait_queue(&rxrpc_peer_wq, &myself); + set_current_state(TASK_RUNNING); + } + + _leave(""); +} diff --git a/net/rxrpc/ar-proc.c b/net/rxrpc/ar-proc.c new file mode 100644 index 000000000000..58f4b4e5cece --- /dev/null +++ b/net/rxrpc/ar-proc.c @@ -0,0 +1,247 @@ +/* /proc/net/ support for AF_RXRPC + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * 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. + */ + +#include +#include +#include +#include "ar-internal.h" + +static const char *rxrpc_conn_states[] = { + [RXRPC_CONN_UNUSED] = "Unused ", + [RXRPC_CONN_CLIENT] = "Client ", + [RXRPC_CONN_SERVER_UNSECURED] = "SvUnsec ", + [RXRPC_CONN_SERVER_CHALLENGING] = "SvChall ", + [RXRPC_CONN_SERVER] = "SvSecure", + [RXRPC_CONN_REMOTELY_ABORTED] = "RmtAbort", + [RXRPC_CONN_LOCALLY_ABORTED] = "LocAbort", + [RXRPC_CONN_NETWORK_ERROR] = "NetError", +}; + +const char *rxrpc_call_states[] = { + [RXRPC_CALL_CLIENT_SEND_REQUEST] = "ClSndReq", + [RXRPC_CALL_CLIENT_AWAIT_REPLY] = "ClAwtRpl", + [RXRPC_CALL_CLIENT_RECV_REPLY] = "ClRcvRpl", + [RXRPC_CALL_CLIENT_FINAL_ACK] = "ClFnlACK", + [RXRPC_CALL_SERVER_SECURING] = "SvSecure", + [RXRPC_CALL_SERVER_ACCEPTING] = "SvAccept", + [RXRPC_CALL_SERVER_RECV_REQUEST] = "SvRcvReq", + [RXRPC_CALL_SERVER_ACK_REQUEST] = "SvAckReq", + [RXRPC_CALL_SERVER_SEND_REPLY] = "SvSndRpl", + [RXRPC_CALL_SERVER_AWAIT_ACK] = "SvAwtACK", + [RXRPC_CALL_COMPLETE] = "Complete", + [RXRPC_CALL_SERVER_BUSY] = "SvBusy ", + [RXRPC_CALL_REMOTELY_ABORTED] = "RmtAbort", + [RXRPC_CALL_LOCALLY_ABORTED] = "LocAbort", + [RXRPC_CALL_NETWORK_ERROR] = "NetError", + [RXRPC_CALL_DEAD] = "Dead ", +}; + +/* + * generate a list of extant and dead calls in /proc/net/rxrpc_calls + */ +static void *rxrpc_call_seq_start(struct seq_file *seq, loff_t *_pos) +{ + struct list_head *_p; + loff_t pos = *_pos; + + read_lock(&rxrpc_call_lock); + if (!pos) + return SEQ_START_TOKEN; + pos--; + + list_for_each(_p, &rxrpc_calls) + if (!pos--) + break; + + return _p != &rxrpc_calls ? _p : NULL; +} + +static void *rxrpc_call_seq_next(struct seq_file *seq, void *v, loff_t *pos) +{ + struct list_head *_p; + + (*pos)++; + + _p = v; + _p = (v == SEQ_START_TOKEN) ? rxrpc_calls.next : _p->next; + + return _p != &rxrpc_calls ? _p : NULL; +} + +static void rxrpc_call_seq_stop(struct seq_file *seq, void *v) +{ + read_unlock(&rxrpc_call_lock); +} + +static int rxrpc_call_seq_show(struct seq_file *seq, void *v) +{ + struct rxrpc_transport *trans; + struct rxrpc_call *call; + char lbuff[4 + 4 + 4 + 4 + 5 + 1], rbuff[4 + 4 + 4 + 4 + 5 + 1]; + + if (v == SEQ_START_TOKEN) { + seq_puts(seq, + "Proto Local Remote " + " SvID ConnID CallID End Use State Abort " + " UserID\n"); + return 0; + } + + call = list_entry(v, struct rxrpc_call, link); + trans = call->conn->trans; + + sprintf(lbuff, NIPQUAD_FMT":%u", + NIPQUAD(trans->local->srx.transport.sin.sin_addr), + ntohs(trans->local->srx.transport.sin.sin_port)); + + sprintf(rbuff, NIPQUAD_FMT":%u", + NIPQUAD(trans->peer->srx.transport.sin.sin_addr), + ntohs(trans->peer->srx.transport.sin.sin_port)); + + seq_printf(seq, + "UDP %-22.22s %-22.22s %4x %08x %08x %s %3u" + " %-8.8s %08x %lx\n", + lbuff, + rbuff, + ntohs(call->conn->service_id), + ntohl(call->conn->cid), + ntohl(call->call_id), + call->conn->in_clientflag ? "Svc" : "Clt", + atomic_read(&call->usage), + rxrpc_call_states[call->state], + call->abort_code, + call->user_call_ID); + + return 0; +} + +static struct seq_operations rxrpc_call_seq_ops = { + .start = rxrpc_call_seq_start, + .next = rxrpc_call_seq_next, + .stop = rxrpc_call_seq_stop, + .show = rxrpc_call_seq_show, +}; + +static int rxrpc_call_seq_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &rxrpc_call_seq_ops); +} + +struct file_operations rxrpc_call_seq_fops = { + .owner = THIS_MODULE, + .open = rxrpc_call_seq_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release_private, +}; + +/* + * generate a list of extant virtual connections in /proc/net/rxrpc_conns + */ +static void *rxrpc_connection_seq_start(struct seq_file *seq, loff_t *_pos) +{ + struct list_head *_p; + loff_t pos = *_pos; + + read_lock(&rxrpc_connection_lock); + if (!pos) + return SEQ_START_TOKEN; + pos--; + + list_for_each(_p, &rxrpc_connections) + if (!pos--) + break; + + return _p != &rxrpc_connections ? _p : NULL; +} + +static void *rxrpc_connection_seq_next(struct seq_file *seq, void *v, + loff_t *pos) +{ + struct list_head *_p; + + (*pos)++; + + _p = v; + _p = (v == SEQ_START_TOKEN) ? rxrpc_connections.next : _p->next; + + return _p != &rxrpc_connections ? _p : NULL; +} + +static void rxrpc_connection_seq_stop(struct seq_file *seq, void *v) +{ + read_unlock(&rxrpc_connection_lock); +} + +static int rxrpc_connection_seq_show(struct seq_file *seq, void *v) +{ + struct rxrpc_connection *conn; + struct rxrpc_transport *trans; + char lbuff[4 + 4 + 4 + 4 + 5 + 1], rbuff[4 + 4 + 4 + 4 + 5 + 1]; + + if (v == SEQ_START_TOKEN) { + seq_puts(seq, + "Proto Local Remote " + " SvID ConnID Calls End Use State Key " + " Serial ISerial\n" + ); + return 0; + } + + conn = list_entry(v, struct rxrpc_connection, link); + trans = conn->trans; + + sprintf(lbuff, NIPQUAD_FMT":%u", + NIPQUAD(trans->local->srx.transport.sin.sin_addr), + ntohs(trans->local->srx.transport.sin.sin_port)); + + sprintf(rbuff, NIPQUAD_FMT":%u", + NIPQUAD(trans->peer->srx.transport.sin.sin_addr), + ntohs(trans->peer->srx.transport.sin.sin_port)); + + seq_printf(seq, + "UDP %-22.22s %-22.22s %4x %08x %08x %s %3u" + " %s %08x %08x %08x\n", + lbuff, + rbuff, + ntohs(conn->service_id), + ntohl(conn->cid), + conn->call_counter, + conn->in_clientflag ? "Svc" : "Clt", + atomic_read(&conn->usage), + rxrpc_conn_states[conn->state], + key_serial(conn->key), + atomic_read(&conn->serial), + atomic_read(&conn->hi_serial)); + + return 0; +} + +static struct seq_operations rxrpc_connection_seq_ops = { + .start = rxrpc_connection_seq_start, + .next = rxrpc_connection_seq_next, + .stop = rxrpc_connection_seq_stop, + .show = rxrpc_connection_seq_show, +}; + + +static int rxrpc_connection_seq_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &rxrpc_connection_seq_ops); +} + +struct file_operations rxrpc_connection_seq_fops = { + .owner = THIS_MODULE, + .open = rxrpc_connection_seq_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release_private, +}; diff --git a/net/rxrpc/ar-recvmsg.c b/net/rxrpc/ar-recvmsg.c new file mode 100644 index 000000000000..e947d5c15900 --- /dev/null +++ b/net/rxrpc/ar-recvmsg.c @@ -0,0 +1,366 @@ +/* RxRPC recvmsg() implementation + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * 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. + */ + +#include +#include +#include +#include +#include "ar-internal.h" + +/* + * removal a call's user ID from the socket tree to make the user ID available + * again and so that it won't be seen again in association with that call + */ +static void rxrpc_remove_user_ID(struct rxrpc_sock *rx, struct rxrpc_call *call) +{ + _debug("RELEASE CALL %d", call->debug_id); + + if (test_bit(RXRPC_CALL_HAS_USERID, &call->flags)) { + write_lock_bh(&rx->call_lock); + rb_erase(&call->sock_node, &call->socket->calls); + clear_bit(RXRPC_CALL_HAS_USERID, &call->flags); + write_unlock_bh(&rx->call_lock); + } + + read_lock_bh(&call->state_lock); + if (!test_bit(RXRPC_CALL_RELEASED, &call->flags) && + !test_and_set_bit(RXRPC_CALL_RELEASE, &call->events)) + schedule_work(&call->processor); + read_unlock_bh(&call->state_lock); +} + +/* + * receive a message from an RxRPC socket + * - we need to be careful about two or more threads calling recvmsg + * simultaneously + */ +int rxrpc_recvmsg(struct kiocb *iocb, struct socket *sock, + struct msghdr *msg, size_t len, int flags) +{ + struct rxrpc_skb_priv *sp; + struct rxrpc_call *call = NULL, *continue_call = NULL; + struct rxrpc_sock *rx = rxrpc_sk(sock->sk); + struct sk_buff *skb; + long timeo; + int copy, ret, ullen, offset, copied = 0; + u32 abort_code; + + DEFINE_WAIT(wait); + + _enter(",,,%zu,%d", len, flags); + + if (flags & (MSG_OOB | MSG_TRUNC)) + return -EOPNOTSUPP; + + ullen = msg->msg_flags & MSG_CMSG_COMPAT ? 4 : sizeof(unsigned long); + + timeo = sock_rcvtimeo(&rx->sk, flags & MSG_DONTWAIT); + msg->msg_flags |= MSG_MORE; + + lock_sock(&rx->sk); + + for (;;) { + /* return immediately if a client socket has no outstanding + * calls */ + if (RB_EMPTY_ROOT(&rx->calls)) { + if (copied) + goto out; + if (rx->sk.sk_state != RXRPC_SERVER_LISTENING) { + release_sock(&rx->sk); + if (continue_call) + rxrpc_put_call(continue_call); + return -ENODATA; + } + } + + /* get the next message on the Rx queue */ + skb = skb_peek(&rx->sk.sk_receive_queue); + if (!skb) { + /* nothing remains on the queue */ + if (copied && + (msg->msg_flags & MSG_PEEK || timeo == 0)) + goto out; + + /* wait for a message to turn up */ + release_sock(&rx->sk); + prepare_to_wait_exclusive(rx->sk.sk_sleep, &wait, + TASK_INTERRUPTIBLE); + ret = sock_error(&rx->sk); + if (ret) + goto wait_error; + + if (skb_queue_empty(&rx->sk.sk_receive_queue)) { + if (signal_pending(current)) + goto wait_interrupted; + timeo = schedule_timeout(timeo); + } + finish_wait(rx->sk.sk_sleep, &wait); + lock_sock(&rx->sk); + continue; + } + + peek_next_packet: + sp = rxrpc_skb(skb); + call = sp->call; + ASSERT(call != NULL); + + _debug("next pkt %s", rxrpc_pkts[sp->hdr.type]); + + /* make sure we wait for the state to be updated in this call */ + spin_lock_bh(&call->lock); + spin_unlock_bh(&call->lock); + + if (test_bit(RXRPC_CALL_RELEASED, &call->flags)) { + _debug("packet from released call"); + if (skb_dequeue(&rx->sk.sk_receive_queue) != skb) + BUG(); + rxrpc_free_skb(skb); + continue; + } + + /* determine whether to continue last data receive */ + if (continue_call) { + _debug("maybe cont"); + if (call != continue_call || + skb->mark != RXRPC_SKB_MARK_DATA) { + release_sock(&rx->sk); + rxrpc_put_call(continue_call); + _leave(" = %d [noncont]", copied); + return copied; + } + } + + rxrpc_get_call(call); + + /* copy the peer address and timestamp */ + if (!continue_call) { + if (msg->msg_name && msg->msg_namelen > 0) + memcpy(&msg->msg_name, &call->conn->trans->peer->srx, + sizeof(call->conn->trans->peer->srx)); + sock_recv_timestamp(msg, &rx->sk, skb); + } + + /* receive the message */ + if (skb->mark != RXRPC_SKB_MARK_DATA) + goto receive_non_data_message; + + _debug("recvmsg DATA #%u { %d, %d }", + ntohl(sp->hdr.seq), skb->len, sp->offset); + + if (!continue_call) { + /* only set the control data once per recvmsg() */ + ret = put_cmsg(msg, SOL_RXRPC, RXRPC_USER_CALL_ID, + ullen, &call->user_call_ID); + if (ret < 0) + goto copy_error; + ASSERT(test_bit(RXRPC_CALL_HAS_USERID, &call->flags)); + } + + ASSERTCMP(ntohl(sp->hdr.seq), >=, call->rx_data_recv); + ASSERTCMP(ntohl(sp->hdr.seq), <=, call->rx_data_recv + 1); + call->rx_data_recv = ntohl(sp->hdr.seq); + + ASSERTCMP(ntohl(sp->hdr.seq), >, call->rx_data_eaten); + + offset = sp->offset; + copy = skb->len - offset; + if (copy > len - copied) + copy = len - copied; + + if (skb->ip_summed == CHECKSUM_UNNECESSARY) { + ret = skb_copy_datagram_iovec(skb, offset, + msg->msg_iov, copy); + } else { + ret = skb_copy_and_csum_datagram_iovec(skb, offset, + msg->msg_iov); + if (ret == -EINVAL) + goto csum_copy_error; + } + + if (ret < 0) + goto copy_error; + + /* handle piecemeal consumption of data packets */ + _debug("copied %d+%d", copy, copied); + + offset += copy; + copied += copy; + + if (!(flags & MSG_PEEK)) + sp->offset = offset; + + if (sp->offset < skb->len) { + _debug("buffer full"); + ASSERTCMP(copied, ==, len); + break; + } + + /* we transferred the whole data packet */ + if (sp->hdr.flags & RXRPC_LAST_PACKET) { + _debug("last"); + if (call->conn->out_clientflag) { + /* last byte of reply received */ + ret = copied; + goto terminal_message; + } + + /* last bit of request received */ + if (!(flags & MSG_PEEK)) { + _debug("eat packet"); + if (skb_dequeue(&rx->sk.sk_receive_queue) != + skb) + BUG(); + rxrpc_free_skb(skb); + } + msg->msg_flags &= ~MSG_MORE; + break; + } + + /* move on to the next data message */ + _debug("next"); + if (!continue_call) + continue_call = sp->call; + else + rxrpc_put_call(call); + call = NULL; + + if (flags & MSG_PEEK) { + _debug("peek next"); + skb = skb->next; + if (skb == (struct sk_buff *) &rx->sk.sk_receive_queue) + break; + goto peek_next_packet; + } + + _debug("eat packet"); + if (skb_dequeue(&rx->sk.sk_receive_queue) != skb) + BUG(); + rxrpc_free_skb(skb); + } + + /* end of non-terminal data packet reception for the moment */ + _debug("end rcv data"); +out: + release_sock(&rx->sk); + if (call) + rxrpc_put_call(call); + if (continue_call) + rxrpc_put_call(continue_call); + _leave(" = %d [data]", copied); + return copied; + + /* handle non-DATA messages such as aborts, incoming connections and + * final ACKs */ +receive_non_data_message: + _debug("non-data"); + + if (skb->mark == RXRPC_SKB_MARK_NEW_CALL) { + _debug("RECV NEW CALL"); + ret = put_cmsg(msg, SOL_RXRPC, RXRPC_NEW_CALL, 0, &abort_code); + if (ret < 0) + goto copy_error; + if (!(flags & MSG_PEEK)) { + if (skb_dequeue(&rx->sk.sk_receive_queue) != skb) + BUG(); + rxrpc_free_skb(skb); + } + goto out; + } + + ret = put_cmsg(msg, SOL_RXRPC, RXRPC_USER_CALL_ID, + ullen, &call->user_call_ID); + if (ret < 0) + goto copy_error; + ASSERT(test_bit(RXRPC_CALL_HAS_USERID, &call->flags)); + + switch (skb->mark) { + case RXRPC_SKB_MARK_DATA: + BUG(); + case RXRPC_SKB_MARK_FINAL_ACK: + ret = put_cmsg(msg, SOL_RXRPC, RXRPC_ACK, 0, &abort_code); + break; + case RXRPC_SKB_MARK_BUSY: + ret = put_cmsg(msg, SOL_RXRPC, RXRPC_BUSY, 0, &abort_code); + break; + case RXRPC_SKB_MARK_REMOTE_ABORT: + abort_code = call->abort_code; + ret = put_cmsg(msg, SOL_RXRPC, RXRPC_ABORT, 4, &abort_code); + break; + case RXRPC_SKB_MARK_NET_ERROR: + _debug("RECV NET ERROR %d", sp->error); + abort_code = sp->error; + ret = put_cmsg(msg, SOL_RXRPC, RXRPC_NET_ERROR, 4, &abort_code); + break; + case RXRPC_SKB_MARK_LOCAL_ERROR: + _debug("RECV LOCAL ERROR %d", sp->error); + abort_code = sp->error; + ret = put_cmsg(msg, SOL_RXRPC, RXRPC_LOCAL_ERROR, 4, + &abort_code); + break; + default: + BUG(); + break; + } + + if (ret < 0) + goto copy_error; + +terminal_message: + _debug("terminal"); + msg->msg_flags &= ~MSG_MORE; + msg->msg_flags |= MSG_EOR; + + if (!(flags & MSG_PEEK)) { + _net("free terminal skb %p", skb); + if (skb_dequeue(&rx->sk.sk_receive_queue) != skb) + BUG(); + rxrpc_free_skb(skb); + rxrpc_remove_user_ID(rx, call); + } + + release_sock(&rx->sk); + rxrpc_put_call(call); + if (continue_call) + rxrpc_put_call(continue_call); + _leave(" = %d", ret); + return ret; + +copy_error: + _debug("copy error"); + release_sock(&rx->sk); + rxrpc_put_call(call); + if (continue_call) + rxrpc_put_call(continue_call); + _leave(" = %d", ret); + return ret; + +csum_copy_error: + _debug("csum error"); + release_sock(&rx->sk); + if (continue_call) + rxrpc_put_call(continue_call); + rxrpc_kill_skb(skb); + skb_kill_datagram(&rx->sk, skb, flags); + rxrpc_put_call(call); + return -EAGAIN; + +wait_interrupted: + ret = sock_intr_errno(timeo); +wait_error: + finish_wait(rx->sk.sk_sleep, &wait); + if (continue_call) + rxrpc_put_call(continue_call); + if (copied) + copied = ret; + _leave(" = %d [waitfail %d]", copied, ret); + return copied; + +} diff --git a/net/rxrpc/ar-security.c b/net/rxrpc/ar-security.c new file mode 100644 index 000000000000..60d1d364430a --- /dev/null +++ b/net/rxrpc/ar-security.c @@ -0,0 +1,258 @@ +/* RxRPC security handling + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "ar-internal.h" + +static LIST_HEAD(rxrpc_security_methods); +static DECLARE_RWSEM(rxrpc_security_sem); + +/* + * get an RxRPC security module + */ +static struct rxrpc_security *rxrpc_security_get(struct rxrpc_security *sec) +{ + return try_module_get(sec->owner) ? sec : NULL; +} + +/* + * release an RxRPC security module + */ +static void rxrpc_security_put(struct rxrpc_security *sec) +{ + module_put(sec->owner); +} + +/* + * look up an rxrpc security module + */ +struct rxrpc_security *rxrpc_security_lookup(u8 security_index) +{ + struct rxrpc_security *sec = NULL; + + _enter(""); + + down_read(&rxrpc_security_sem); + + list_for_each_entry(sec, &rxrpc_security_methods, link) { + if (sec->security_index == security_index) { + if (unlikely(!rxrpc_security_get(sec))) + break; + goto out; + } + } + + sec = NULL; +out: + up_read(&rxrpc_security_sem); + _leave(" = %p [%s]", sec, sec ? sec->name : ""); + return sec; +} + +/** + * rxrpc_register_security - register an RxRPC security handler + * @sec: security module + * + * register an RxRPC security handler for use by RxRPC + */ +int rxrpc_register_security(struct rxrpc_security *sec) +{ + struct rxrpc_security *psec; + int ret; + + _enter(""); + down_write(&rxrpc_security_sem); + + ret = -EEXIST; + list_for_each_entry(psec, &rxrpc_security_methods, link) { + if (psec->security_index == sec->security_index) + goto out; + } + + list_add(&sec->link, &rxrpc_security_methods); + + printk(KERN_NOTICE "RxRPC: Registered security type %d '%s'\n", + sec->security_index, sec->name); + ret = 0; + +out: + up_write(&rxrpc_security_sem); + _leave(" = %d", ret); + return ret; +} + +EXPORT_SYMBOL_GPL(rxrpc_register_security); + +/** + * rxrpc_unregister_security - unregister an RxRPC security handler + * @sec: security module + * + * unregister an RxRPC security handler + */ +void rxrpc_unregister_security(struct rxrpc_security *sec) +{ + + _enter(""); + down_write(&rxrpc_security_sem); + list_del_init(&sec->link); + up_write(&rxrpc_security_sem); + + printk(KERN_NOTICE "RxRPC: Unregistered security type %d '%s'\n", + sec->security_index, sec->name); +} + +EXPORT_SYMBOL_GPL(rxrpc_unregister_security); + +/* + * initialise the security on a client connection + */ +int rxrpc_init_client_conn_security(struct rxrpc_connection *conn) +{ + struct rxrpc_security *sec; + struct key *key = conn->key; + int ret; + + _enter("{%d},{%x}", conn->debug_id, key_serial(key)); + + if (!key) + return 0; + + ret = key_validate(key); + if (ret < 0) + return ret; + + sec = rxrpc_security_lookup(key->type_data.x[0]); + if (!sec) + return -EKEYREJECTED; + conn->security = sec; + + ret = conn->security->init_connection_security(conn); + if (ret < 0) { + rxrpc_security_put(conn->security); + conn->security = NULL; + return ret; + } + + _leave(" = 0"); + return 0; +} + +/* + * initialise the security on a server connection + */ +int rxrpc_init_server_conn_security(struct rxrpc_connection *conn) +{ + struct rxrpc_security *sec; + struct rxrpc_local *local = conn->trans->local; + struct rxrpc_sock *rx; + struct key *key; + key_ref_t kref; + char kdesc[5+1+3+1]; + + _enter(""); + + sprintf(kdesc, "%u:%u", ntohs(conn->service_id), conn->security_ix); + + sec = rxrpc_security_lookup(conn->security_ix); + if (!sec) { + _leave(" = -ENOKEY [lookup]"); + return -ENOKEY; + } + + /* find the service */ + read_lock_bh(&local->services_lock); + list_for_each_entry(rx, &local->services, listen_link) { + if (rx->service_id == conn->service_id) + goto found_service; + } + + /* the service appears to have died */ + read_unlock_bh(&local->services_lock); + rxrpc_security_put(sec); + _leave(" = -ENOENT"); + return -ENOENT; + +found_service: + if (!rx->securities) { + read_unlock_bh(&local->services_lock); + rxrpc_security_put(sec); + _leave(" = -ENOKEY"); + return -ENOKEY; + } + + /* look through the service's keyring */ + kref = keyring_search(make_key_ref(rx->securities, 1UL), + &key_type_rxrpc_s, kdesc); + if (IS_ERR(kref)) { + read_unlock_bh(&local->services_lock); + rxrpc_security_put(sec); + _leave(" = %ld [search]", PTR_ERR(kref)); + return PTR_ERR(kref); + } + + key = key_ref_to_ptr(kref); + read_unlock_bh(&local->services_lock); + + conn->server_key = key; + conn->security = sec; + + _leave(" = 0"); + return 0; +} + +/* + * secure a packet prior to transmission + */ +int rxrpc_secure_packet(const struct rxrpc_call *call, + struct sk_buff *skb, + size_t data_size, + void *sechdr) +{ + if (call->conn->security) + return call->conn->security->secure_packet( + call, skb, data_size, sechdr); + return 0; +} + +/* + * secure a packet prior to transmission + */ +int rxrpc_verify_packet(const struct rxrpc_call *call, struct sk_buff *skb, + u32 *_abort_code) +{ + if (call->conn->security) + return call->conn->security->verify_packet( + call, skb, _abort_code); + return 0; +} + +/* + * clear connection security + */ +void rxrpc_clear_conn_security(struct rxrpc_connection *conn) +{ + _enter("{%d}", conn->debug_id); + + if (conn->security) { + conn->security->clear(conn); + rxrpc_security_put(conn->security); + conn->security = NULL; + } + + key_put(conn->key); + key_put(conn->server_key); +} diff --git a/net/rxrpc/ar-skbuff.c b/net/rxrpc/ar-skbuff.c new file mode 100644 index 000000000000..d73f6fc76011 --- /dev/null +++ b/net/rxrpc/ar-skbuff.c @@ -0,0 +1,118 @@ +/* ar-skbuff.c: socket buffer destruction handling + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * 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. + */ + +#include +#include +#include +#include +#include +#include "ar-internal.h" + +/* + * set up for the ACK at the end of the receive phase when we discard the final + * receive phase data packet + * - called with softirqs disabled + */ +static void rxrpc_request_final_ACK(struct rxrpc_call *call) +{ + /* the call may be aborted before we have a chance to ACK it */ + write_lock(&call->state_lock); + + switch (call->state) { + case RXRPC_CALL_CLIENT_RECV_REPLY: + call->state = RXRPC_CALL_CLIENT_FINAL_ACK; + _debug("request final ACK"); + + /* get an extra ref on the call for the final-ACK generator to + * release */ + rxrpc_get_call(call); + set_bit(RXRPC_CALL_ACK_FINAL, &call->events); + if (try_to_del_timer_sync(&call->ack_timer) >= 0) + schedule_work(&call->processor); + break; + + case RXRPC_CALL_SERVER_RECV_REQUEST: + call->state = RXRPC_CALL_SERVER_ACK_REQUEST; + default: + break; + } + + write_unlock(&call->state_lock); +} + +/* + * drop the bottom ACK off of the call ACK window and advance the window + */ +static void rxrpc_hard_ACK_data(struct rxrpc_call *call, + struct rxrpc_skb_priv *sp) +{ + int loop; + u32 seq; + + spin_lock_bh(&call->lock); + + _debug("hard ACK #%u", ntohl(sp->hdr.seq)); + + for (loop = 0; loop < RXRPC_ACKR_WINDOW_ASZ; loop++) { + call->ackr_window[loop] >>= 1; + call->ackr_window[loop] |= + call->ackr_window[loop + 1] << (BITS_PER_LONG - 1); + } + + seq = ntohl(sp->hdr.seq); + ASSERTCMP(seq, ==, call->rx_data_eaten + 1); + call->rx_data_eaten = seq; + + if (call->ackr_win_top < UINT_MAX) + call->ackr_win_top++; + + ASSERTIFCMP(call->state <= RXRPC_CALL_COMPLETE, + call->rx_data_post, >=, call->rx_data_recv); + ASSERTIFCMP(call->state <= RXRPC_CALL_COMPLETE, + call->rx_data_recv, >=, call->rx_data_eaten); + + if (sp->hdr.flags & RXRPC_LAST_PACKET) { + rxrpc_request_final_ACK(call); + } else if (atomic_dec_and_test(&call->ackr_not_idle) && + test_and_clear_bit(RXRPC_CALL_TX_SOFT_ACK, &call->flags)) { + _debug("send Rx idle ACK"); + __rxrpc_propose_ACK(call, RXRPC_ACK_IDLE, sp->hdr.serial, + true); + } + + spin_unlock_bh(&call->lock); +} + +/* + * destroy a packet that has an RxRPC control buffer + * - advance the hard-ACK state of the parent call (done here in case something + * in the kernel bypasses recvmsg() and steals the packet directly off of the + * socket receive queue) + */ +void rxrpc_packet_destructor(struct sk_buff *skb) +{ + struct rxrpc_skb_priv *sp = rxrpc_skb(skb); + struct rxrpc_call *call = sp->call; + + _enter("%p{%p}", skb, call); + + if (call) { + /* send the final ACK on a client call */ + if (sp->hdr.type == RXRPC_PACKET_TYPE_DATA) + rxrpc_hard_ACK_data(call, sp); + rxrpc_put_call(call); + sp->call = NULL; + } + + if (skb->sk) + sock_rfree(skb); + _leave(""); +} diff --git a/net/rxrpc/ar-transport.c b/net/rxrpc/ar-transport.c new file mode 100644 index 000000000000..9b4e5cb545d2 --- /dev/null +++ b/net/rxrpc/ar-transport.c @@ -0,0 +1,276 @@ +/* RxRPC point-to-point transport session management + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * 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. + */ + +#include +#include +#include +#include +#include +#include "ar-internal.h" + +static void rxrpc_transport_reaper(struct work_struct *work); + +static LIST_HEAD(rxrpc_transports); +static DEFINE_RWLOCK(rxrpc_transport_lock); +static unsigned long rxrpc_transport_timeout = 3600 * 24; +static DECLARE_DELAYED_WORK(rxrpc_transport_reap, rxrpc_transport_reaper); + +/* + * allocate a new transport session manager + */ +static struct rxrpc_transport *rxrpc_alloc_transport(struct rxrpc_local *local, + struct rxrpc_peer *peer, + gfp_t gfp) +{ + struct rxrpc_transport *trans; + + _enter(""); + + trans = kzalloc(sizeof(struct rxrpc_transport), gfp); + if (trans) { + trans->local = local; + trans->peer = peer; + INIT_LIST_HEAD(&trans->link); + trans->bundles = RB_ROOT; + trans->client_conns = RB_ROOT; + trans->server_conns = RB_ROOT; + skb_queue_head_init(&trans->error_queue); + spin_lock_init(&trans->client_lock); + rwlock_init(&trans->conn_lock); + atomic_set(&trans->usage, 1); + trans->debug_id = atomic_inc_return(&rxrpc_debug_id); + + if (peer->srx.transport.family == AF_INET) { + switch (peer->srx.transport_type) { + case SOCK_DGRAM: + INIT_WORK(&trans->error_handler, + rxrpc_UDP_error_handler); + break; + default: + BUG(); + break; + } + } else { + BUG(); + } + } + + _leave(" = %p", trans); + return trans; +} + +/* + * obtain a transport session for the nominated endpoints + */ +struct rxrpc_transport *rxrpc_get_transport(struct rxrpc_local *local, + struct rxrpc_peer *peer, + gfp_t gfp) +{ + struct rxrpc_transport *trans, *candidate; + const char *new = "old"; + int usage; + + _enter("{%u.%u.%u.%u+%hu},{%u.%u.%u.%u+%hu},", + NIPQUAD(local->srx.transport.sin.sin_addr), + ntohs(local->srx.transport.sin.sin_port), + NIPQUAD(peer->srx.transport.sin.sin_addr), + ntohs(peer->srx.transport.sin.sin_port)); + + /* search the transport list first */ + read_lock_bh(&rxrpc_transport_lock); + list_for_each_entry(trans, &rxrpc_transports, link) { + if (trans->local == local && trans->peer == peer) + goto found_extant_transport; + } + read_unlock_bh(&rxrpc_transport_lock); + + /* not yet present - create a candidate for a new record and then + * redo the search */ + candidate = rxrpc_alloc_transport(local, peer, gfp); + if (!candidate) { + _leave(" = -ENOMEM"); + return ERR_PTR(-ENOMEM); + } + + write_lock_bh(&rxrpc_transport_lock); + + list_for_each_entry(trans, &rxrpc_transports, link) { + if (trans->local == local && trans->peer == peer) + goto found_extant_second; + } + + /* we can now add the new candidate to the list */ + trans = candidate; + candidate = NULL; + + rxrpc_get_local(trans->local); + atomic_inc(&trans->peer->usage); + list_add_tail(&trans->link, &rxrpc_transports); + write_unlock_bh(&rxrpc_transport_lock); + new = "new"; + +success: + _net("TRANSPORT %s %d local %d -> peer %d", + new, + trans->debug_id, + trans->local->debug_id, + trans->peer->debug_id); + + _leave(" = %p {u=%d}", trans, atomic_read(&trans->usage)); + return trans; + + /* we found the transport in the list immediately */ +found_extant_transport: + usage = atomic_inc_return(&trans->usage); + read_unlock_bh(&rxrpc_transport_lock); + goto success; + + /* we found the transport on the second time through the list */ +found_extant_second: + usage = atomic_inc_return(&trans->usage); + write_unlock_bh(&rxrpc_transport_lock); + kfree(candidate); + goto success; +} + +/* + * find the transport connecting two endpoints + */ +struct rxrpc_transport *rxrpc_find_transport(struct rxrpc_local *local, + struct rxrpc_peer *peer) +{ + struct rxrpc_transport *trans; + + _enter("{%u.%u.%u.%u+%hu},{%u.%u.%u.%u+%hu},", + NIPQUAD(local->srx.transport.sin.sin_addr), + ntohs(local->srx.transport.sin.sin_port), + NIPQUAD(peer->srx.transport.sin.sin_addr), + ntohs(peer->srx.transport.sin.sin_port)); + + /* search the transport list */ + read_lock_bh(&rxrpc_transport_lock); + + list_for_each_entry(trans, &rxrpc_transports, link) { + if (trans->local == local && trans->peer == peer) + goto found_extant_transport; + } + + read_unlock_bh(&rxrpc_transport_lock); + _leave(" = NULL"); + return NULL; + +found_extant_transport: + atomic_inc(&trans->usage); + read_unlock_bh(&rxrpc_transport_lock); + _leave(" = %p", trans); + return trans; +} + +/* + * release a transport session + */ +void rxrpc_put_transport(struct rxrpc_transport *trans) +{ + _enter("%p{u=%d}", trans, atomic_read(&trans->usage)); + + ASSERTCMP(atomic_read(&trans->usage), >, 0); + + trans->put_time = xtime.tv_sec; + if (unlikely(atomic_dec_and_test(&trans->usage))) + _debug("zombie"); + /* let the reaper determine the timeout to avoid a race with + * overextending the timeout if the reaper is running at the + * same time */ + schedule_delayed_work(&rxrpc_transport_reap, 0); + _leave(""); +} + +/* + * clean up a transport session + */ +static void rxrpc_cleanup_transport(struct rxrpc_transport *trans) +{ + _net("DESTROY TRANS %d", trans->debug_id); + + rxrpc_purge_queue(&trans->error_queue); + + rxrpc_put_local(trans->local); + rxrpc_put_peer(trans->peer); + kfree(trans); +} + +/* + * reap dead transports that have passed their expiry date + */ +static void rxrpc_transport_reaper(struct work_struct *work) +{ + struct rxrpc_transport *trans, *_p; + unsigned long now, earliest, reap_time; + + LIST_HEAD(graveyard); + + _enter(""); + + now = xtime.tv_sec; + earliest = ULONG_MAX; + + /* extract all the transports that have been dead too long */ + write_lock_bh(&rxrpc_transport_lock); + list_for_each_entry_safe(trans, _p, &rxrpc_transports, link) { + _debug("reap TRANS %d { u=%d t=%ld }", + trans->debug_id, atomic_read(&trans->usage), + (long) now - (long) trans->put_time); + + if (likely(atomic_read(&trans->usage) > 0)) + continue; + + reap_time = trans->put_time + rxrpc_transport_timeout; + if (reap_time <= now) + list_move_tail(&trans->link, &graveyard); + else if (reap_time < earliest) + earliest = reap_time; + } + write_unlock_bh(&rxrpc_transport_lock); + + if (earliest != ULONG_MAX) { + _debug("reschedule reaper %ld", (long) earliest - now); + ASSERTCMP(earliest, >, now); + schedule_delayed_work(&rxrpc_transport_reap, + (earliest - now) * HZ); + } + + /* then destroy all those pulled out */ + while (!list_empty(&graveyard)) { + trans = list_entry(graveyard.next, struct rxrpc_transport, + link); + list_del_init(&trans->link); + + ASSERTCMP(atomic_read(&trans->usage), ==, 0); + rxrpc_cleanup_transport(trans); + } + + _leave(""); +} + +/* + * preemptively destroy all the transport session records rather than waiting + * for them to time out + */ +void __exit rxrpc_destroy_all_transports(void) +{ + _enter(""); + + rxrpc_transport_timeout = 0; + cancel_delayed_work(&rxrpc_transport_reap); + schedule_delayed_work(&rxrpc_transport_reap, 0); + + _leave(""); +} diff --git a/net/rxrpc/rxkad.c b/net/rxrpc/rxkad.c new file mode 100644 index 000000000000..1eaf529efac1 --- /dev/null +++ b/net/rxrpc/rxkad.c @@ -0,0 +1,1153 @@ +/* Kerberos-based RxRPC security + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ar-internal.h" + +#define RXKAD_VERSION 2 +#define MAXKRB5TICKETLEN 1024 +#define RXKAD_TKT_TYPE_KERBEROS_V5 256 +#define ANAME_SZ 40 /* size of authentication name */ +#define INST_SZ 40 /* size of principal's instance */ +#define REALM_SZ 40 /* size of principal's auth domain */ +#define SNAME_SZ 40 /* size of service name */ + +unsigned rxrpc_debug; +module_param_named(debug, rxrpc_debug, uint, S_IWUSR | S_IRUGO); +MODULE_PARM_DESC(rxrpc_debug, "rxkad debugging mask"); + +struct rxkad_level1_hdr { + __be32 data_size; /* true data size (excluding padding) */ +}; + +struct rxkad_level2_hdr { + __be32 data_size; /* true data size (excluding padding) */ + __be32 checksum; /* decrypted data checksum */ +}; + +MODULE_DESCRIPTION("RxRPC network protocol type-2 security (Kerberos)"); +MODULE_AUTHOR("Red Hat, Inc."); +MODULE_LICENSE("GPL"); + +/* + * this holds a pinned cipher so that keventd doesn't get called by the cipher + * alloc routine, but since we have it to hand, we use it to decrypt RESPONSE + * packets + */ +static struct crypto_blkcipher *rxkad_ci; +static DEFINE_MUTEX(rxkad_ci_mutex); + +/* + * initialise connection security + */ +static int rxkad_init_connection_security(struct rxrpc_connection *conn) +{ + struct rxrpc_key_payload *payload; + struct crypto_blkcipher *ci; + int ret; + + _enter("{%d},{%x}", conn->debug_id, key_serial(conn->key)); + + payload = conn->key->payload.data; + conn->security_ix = payload->k.security_index; + + ci = crypto_alloc_blkcipher("pcbc(fcrypt)", 0, CRYPTO_ALG_ASYNC); + if (IS_ERR(ci)) { + _debug("no cipher"); + ret = PTR_ERR(ci); + goto error; + } + + if (crypto_blkcipher_setkey(ci, payload->k.session_key, + sizeof(payload->k.session_key)) < 0) + BUG(); + + switch (conn->security_level) { + case RXRPC_SECURITY_PLAIN: + break; + case RXRPC_SECURITY_AUTH: + conn->size_align = 8; + conn->security_size = sizeof(struct rxkad_level1_hdr); + conn->header_size += sizeof(struct rxkad_level1_hdr); + break; + case RXRPC_SECURITY_ENCRYPT: + conn->size_align = 8; + conn->security_size = sizeof(struct rxkad_level2_hdr); + conn->header_size += sizeof(struct rxkad_level2_hdr); + break; + default: + ret = -EKEYREJECTED; + goto error; + } + + conn->cipher = ci; + ret = 0; +error: + _leave(" = %d", ret); + return ret; +} + +/* + * prime the encryption state with the invariant parts of a connection's + * description + */ +static void rxkad_prime_packet_security(struct rxrpc_connection *conn) +{ + struct rxrpc_key_payload *payload; + struct blkcipher_desc desc; + struct scatterlist sg[2]; + struct rxrpc_crypt iv; + struct { + __be32 x[4]; + } tmpbuf __attribute__((aligned(16))); /* must all be in same page */ + + _enter(""); + + if (!conn->key) + return; + + payload = conn->key->payload.data; + memcpy(&iv, payload->k.session_key, sizeof(iv)); + + desc.tfm = conn->cipher; + desc.info = iv.x; + desc.flags = 0; + + tmpbuf.x[0] = conn->epoch; + tmpbuf.x[1] = conn->cid; + tmpbuf.x[2] = 0; + tmpbuf.x[3] = htonl(conn->security_ix); + + memset(sg, 0, sizeof(sg)); + sg_set_buf(&sg[0], &tmpbuf, sizeof(tmpbuf)); + sg_set_buf(&sg[1], &tmpbuf, sizeof(tmpbuf)); + crypto_blkcipher_encrypt_iv(&desc, &sg[0], &sg[1], sizeof(tmpbuf)); + + memcpy(&conn->csum_iv, &tmpbuf.x[2], sizeof(conn->csum_iv)); + ASSERTCMP(conn->csum_iv.n[0], ==, tmpbuf.x[2]); + + _leave(""); +} + +/* + * partially encrypt a packet (level 1 security) + */ +static int rxkad_secure_packet_auth(const struct rxrpc_call *call, + struct sk_buff *skb, + u32 data_size, + void *sechdr) +{ + struct rxrpc_skb_priv *sp; + struct blkcipher_desc desc; + struct rxrpc_crypt iv; + struct scatterlist sg[2]; + struct { + struct rxkad_level1_hdr hdr; + __be32 first; /* first four bytes of data and padding */ + } tmpbuf __attribute__((aligned(8))); /* must all be in same page */ + u16 check; + + sp = rxrpc_skb(skb); + + _enter(""); + + check = ntohl(sp->hdr.seq ^ sp->hdr.callNumber); + data_size |= (u32) check << 16; + + tmpbuf.hdr.data_size = htonl(data_size); + memcpy(&tmpbuf.first, sechdr + 4, sizeof(tmpbuf.first)); + + /* start the encryption afresh */ + memset(&iv, 0, sizeof(iv)); + desc.tfm = call->conn->cipher; + desc.info = iv.x; + desc.flags = 0; + + memset(sg, 0, sizeof(sg)); + sg_set_buf(&sg[0], &tmpbuf, sizeof(tmpbuf)); + sg_set_buf(&sg[1], &tmpbuf, sizeof(tmpbuf)); + crypto_blkcipher_encrypt_iv(&desc, &sg[0], &sg[1], sizeof(tmpbuf)); + + memcpy(sechdr, &tmpbuf, sizeof(tmpbuf)); + + _leave(" = 0"); + return 0; +} + +/* + * wholly encrypt a packet (level 2 security) + */ +static int rxkad_secure_packet_encrypt(const struct rxrpc_call *call, + struct sk_buff *skb, + u32 data_size, + void *sechdr) +{ + const struct rxrpc_key_payload *payload; + struct rxkad_level2_hdr rxkhdr + __attribute__((aligned(8))); /* must be all on one page */ + struct rxrpc_skb_priv *sp; + struct blkcipher_desc desc; + struct rxrpc_crypt iv; + struct scatterlist sg[16]; + struct sk_buff *trailer; + unsigned len; + u16 check; + int nsg; + + sp = rxrpc_skb(skb); + + _enter(""); + + check = ntohl(sp->hdr.seq ^ sp->hdr.callNumber); + + rxkhdr.data_size = htonl(data_size | (u32) check << 16); + rxkhdr.checksum = 0; + + /* encrypt from the session key */ + payload = call->conn->key->payload.data; + memcpy(&iv, payload->k.session_key, sizeof(iv)); + desc.tfm = call->conn->cipher; + desc.info = iv.x; + desc.flags = 0; + + memset(sg, 0, sizeof(sg[0]) * 2); + sg_set_buf(&sg[0], sechdr, sizeof(rxkhdr)); + sg_set_buf(&sg[1], &rxkhdr, sizeof(rxkhdr)); + crypto_blkcipher_encrypt_iv(&desc, &sg[0], &sg[1], sizeof(rxkhdr)); + + /* we want to encrypt the skbuff in-place */ + nsg = skb_cow_data(skb, 0, &trailer); + if (nsg < 0 || nsg > 16) + return -ENOMEM; + + len = data_size + call->conn->size_align - 1; + len &= ~(call->conn->size_align - 1); + + skb_to_sgvec(skb, sg, 0, len); + crypto_blkcipher_encrypt_iv(&desc, sg, sg, len); + + _leave(" = 0"); + return 0; +} + +/* + * checksum an RxRPC packet header + */ +static int rxkad_secure_packet(const struct rxrpc_call *call, + struct sk_buff *skb, + size_t data_size, + void *sechdr) +{ + struct rxrpc_skb_priv *sp; + struct blkcipher_desc desc; + struct rxrpc_crypt iv; + struct scatterlist sg[2]; + struct { + __be32 x[2]; + } tmpbuf __attribute__((aligned(8))); /* must all be in same page */ + __be32 x; + int ret; + + sp = rxrpc_skb(skb); + + _enter("{%d{%x}},{#%u},%zu,", + call->debug_id, key_serial(call->conn->key), ntohl(sp->hdr.seq), + data_size); + + if (!call->conn->cipher) + return 0; + + ret = key_validate(call->conn->key); + if (ret < 0) + return ret; + + /* continue encrypting from where we left off */ + memcpy(&iv, call->conn->csum_iv.x, sizeof(iv)); + desc.tfm = call->conn->cipher; + desc.info = iv.x; + desc.flags = 0; + + /* calculate the security checksum */ + x = htonl(call->channel << (32 - RXRPC_CIDSHIFT)); + x |= sp->hdr.seq & __constant_cpu_to_be32(0x3fffffff); + tmpbuf.x[0] = sp->hdr.callNumber; + tmpbuf.x[1] = x; + + memset(&sg, 0, sizeof(sg)); + sg_set_buf(&sg[0], &tmpbuf, sizeof(tmpbuf)); + sg_set_buf(&sg[1], &tmpbuf, sizeof(tmpbuf)); + crypto_blkcipher_encrypt_iv(&desc, &sg[0], &sg[1], sizeof(tmpbuf)); + + x = ntohl(tmpbuf.x[1]); + x = (x >> 16) & 0xffff; + if (x == 0) + x = 1; /* zero checksums are not permitted */ + sp->hdr.cksum = htons(x); + + switch (call->conn->security_level) { + case RXRPC_SECURITY_PLAIN: + ret = 0; + break; + case RXRPC_SECURITY_AUTH: + ret = rxkad_secure_packet_auth(call, skb, data_size, sechdr); + break; + case RXRPC_SECURITY_ENCRYPT: + ret = rxkad_secure_packet_encrypt(call, skb, data_size, + sechdr); + break; + default: + ret = -EPERM; + break; + } + + _leave(" = %d [set %hx]", ret, x); + return ret; +} + +/* + * decrypt partial encryption on a packet (level 1 security) + */ +static int rxkad_verify_packet_auth(const struct rxrpc_call *call, + struct sk_buff *skb, + u32 *_abort_code) +{ + struct rxkad_level1_hdr sechdr; + struct rxrpc_skb_priv *sp; + struct blkcipher_desc desc; + struct rxrpc_crypt iv; + struct scatterlist sg[2]; + struct sk_buff *trailer; + u32 data_size, buf; + u16 check; + + _enter(""); + + sp = rxrpc_skb(skb); + + /* we want to decrypt the skbuff in-place */ + if (skb_cow_data(skb, 0, &trailer) < 0) + goto nomem; + + skb_to_sgvec(skb, sg, 0, 8); + + /* start the decryption afresh */ + memset(&iv, 0, sizeof(iv)); + desc.tfm = call->conn->cipher; + desc.info = iv.x; + desc.flags = 0; + + crypto_blkcipher_decrypt_iv(&desc, sg, sg, 8); + + /* remove the decrypted packet length */ + if (skb_copy_bits(skb, 0, &sechdr, sizeof(sechdr)) < 0) + goto datalen_error; + if (!skb_pull(skb, sizeof(sechdr))) + BUG(); + + buf = ntohl(sechdr.data_size); + data_size = buf & 0xffff; + + check = buf >> 16; + check ^= ntohl(sp->hdr.seq ^ sp->hdr.callNumber); + check &= 0xffff; + if (check != 0) { + *_abort_code = RXKADSEALEDINCON; + goto protocol_error; + } + + /* shorten the packet to remove the padding */ + if (data_size > skb->len) + goto datalen_error; + else if (data_size < skb->len) + skb->len = data_size; + + _leave(" = 0 [dlen=%x]", data_size); + return 0; + +datalen_error: + *_abort_code = RXKADDATALEN; +protocol_error: + _leave(" = -EPROTO"); + return -EPROTO; + +nomem: + _leave(" = -ENOMEM"); + return -ENOMEM; +} + +/* + * wholly decrypt a packet (level 2 security) + */ +static int rxkad_verify_packet_encrypt(const struct rxrpc_call *call, + struct sk_buff *skb, + u32 *_abort_code) +{ + const struct rxrpc_key_payload *payload; + struct rxkad_level2_hdr sechdr; + struct rxrpc_skb_priv *sp; + struct blkcipher_desc desc; + struct rxrpc_crypt iv; + struct scatterlist _sg[4], *sg; + struct sk_buff *trailer; + u32 data_size, buf; + u16 check; + int nsg; + + _enter(",{%d}", skb->len); + + sp = rxrpc_skb(skb); + + /* we want to decrypt the skbuff in-place */ + nsg = skb_cow_data(skb, 0, &trailer); + if (nsg < 0) + goto nomem; + + sg = _sg; + if (unlikely(nsg > 4)) { + sg = kmalloc(sizeof(*sg) * nsg, GFP_NOIO); + if (!sg) + goto nomem; + } + + skb_to_sgvec(skb, sg, 0, skb->len); + + /* decrypt from the session key */ + payload = call->conn->key->payload.data; + memcpy(&iv, payload->k.session_key, sizeof(iv)); + desc.tfm = call->conn->cipher; + desc.info = iv.x; + desc.flags = 0; + + crypto_blkcipher_decrypt_iv(&desc, sg, sg, skb->len); + if (sg != _sg) + kfree(sg); + + /* remove the decrypted packet length */ + if (skb_copy_bits(skb, 0, &sechdr, sizeof(sechdr)) < 0) + goto datalen_error; + if (!skb_pull(skb, sizeof(sechdr))) + BUG(); + + buf = ntohl(sechdr.data_size); + data_size = buf & 0xffff; + + check = buf >> 16; + check ^= ntohl(sp->hdr.seq ^ sp->hdr.callNumber); + check &= 0xffff; + if (check != 0) { + *_abort_code = RXKADSEALEDINCON; + goto protocol_error; + } + + /* shorten the packet to remove the padding */ + if (data_size > skb->len) + goto datalen_error; + else if (data_size < skb->len) + skb->len = data_size; + + _leave(" = 0 [dlen=%x]", data_size); + return 0; + +datalen_error: + *_abort_code = RXKADDATALEN; +protocol_error: + _leave(" = -EPROTO"); + return -EPROTO; + +nomem: + _leave(" = -ENOMEM"); + return -ENOMEM; +} + +/* + * verify the security on a received packet + */ +static int rxkad_verify_packet(const struct rxrpc_call *call, + struct sk_buff *skb, + u32 *_abort_code) +{ + struct blkcipher_desc desc; + struct rxrpc_skb_priv *sp; + struct rxrpc_crypt iv; + struct scatterlist sg[2]; + struct { + __be32 x[2]; + } tmpbuf __attribute__((aligned(8))); /* must all be in same page */ + __be32 x; + __be16 cksum; + int ret; + + sp = rxrpc_skb(skb); + + _enter("{%d{%x}},{#%u}", + call->debug_id, key_serial(call->conn->key), + ntohl(sp->hdr.seq)); + + if (!call->conn->cipher) + return 0; + + if (sp->hdr.securityIndex != 2) { + *_abort_code = RXKADINCONSISTENCY; + _leave(" = -EPROTO [not rxkad]"); + return -EPROTO; + } + + /* continue encrypting from where we left off */ + memcpy(&iv, call->conn->csum_iv.x, sizeof(iv)); + desc.tfm = call->conn->cipher; + desc.info = iv.x; + desc.flags = 0; + + /* validate the security checksum */ + x = htonl(call->channel << (32 - RXRPC_CIDSHIFT)); + x |= sp->hdr.seq & __constant_cpu_to_be32(0x3fffffff); + tmpbuf.x[0] = call->call_id; + tmpbuf.x[1] = x; + + memset(&sg, 0, sizeof(sg)); + sg_set_buf(&sg[0], &tmpbuf, sizeof(tmpbuf)); + sg_set_buf(&sg[1], &tmpbuf, sizeof(tmpbuf)); + crypto_blkcipher_encrypt_iv(&desc, &sg[0], &sg[1], sizeof(tmpbuf)); + + x = ntohl(tmpbuf.x[1]); + x = (x >> 16) & 0xffff; + if (x == 0) + x = 1; /* zero checksums are not permitted */ + + cksum = htons(x); + if (sp->hdr.cksum != cksum) { + *_abort_code = RXKADSEALEDINCON; + _leave(" = -EPROTO [csum failed]"); + return -EPROTO; + } + + switch (call->conn->security_level) { + case RXRPC_SECURITY_PLAIN: + ret = 0; + break; + case RXRPC_SECURITY_AUTH: + ret = rxkad_verify_packet_auth(call, skb, _abort_code); + break; + case RXRPC_SECURITY_ENCRYPT: + ret = rxkad_verify_packet_encrypt(call, skb, _abort_code); + break; + default: + ret = -ENOANO; + break; + } + + _leave(" = %d", ret); + return ret; +} + +/* + * issue a challenge + */ +static int rxkad_issue_challenge(struct rxrpc_connection *conn) +{ + struct rxkad_challenge challenge; + struct rxrpc_header hdr; + struct msghdr msg; + struct kvec iov[2]; + size_t len; + int ret; + + _enter("{%d,%x}", conn->debug_id, key_serial(conn->key)); + + ret = key_validate(conn->key); + if (ret < 0) + return ret; + + get_random_bytes(&conn->security_nonce, sizeof(conn->security_nonce)); + + challenge.version = htonl(2); + challenge.nonce = htonl(conn->security_nonce); + challenge.min_level = htonl(0); + challenge.__padding = 0; + + msg.msg_name = &conn->trans->peer->srx.transport.sin; + msg.msg_namelen = sizeof(conn->trans->peer->srx.transport.sin); + msg.msg_control = NULL; + msg.msg_controllen = 0; + msg.msg_flags = 0; + + hdr.epoch = conn->epoch; + hdr.cid = conn->cid; + hdr.callNumber = 0; + hdr.seq = 0; + hdr.type = RXRPC_PACKET_TYPE_CHALLENGE; + hdr.flags = conn->out_clientflag; + hdr.userStatus = 0; + hdr.securityIndex = conn->security_ix; + hdr._rsvd = 0; + hdr.serviceId = conn->service_id; + + iov[0].iov_base = &hdr; + iov[0].iov_len = sizeof(hdr); + iov[1].iov_base = &challenge; + iov[1].iov_len = sizeof(challenge); + + len = iov[0].iov_len + iov[1].iov_len; + + hdr.serial = htonl(atomic_inc_return(&conn->serial)); + _proto("Tx CHALLENGE %%%u", ntohl(hdr.serial)); + + ret = kernel_sendmsg(conn->trans->local->socket, &msg, iov, 2, len); + if (ret < 0) { + _debug("sendmsg failed: %d", ret); + return -EAGAIN; + } + + _leave(" = 0"); + return 0; +} + +/* + * send a Kerberos security response + */ +static int rxkad_send_response(struct rxrpc_connection *conn, + struct rxrpc_header *hdr, + struct rxkad_response *resp, + const struct rxkad_key *s2) +{ + struct msghdr msg; + struct kvec iov[3]; + size_t len; + int ret; + + _enter(""); + + msg.msg_name = &conn->trans->peer->srx.transport.sin; + msg.msg_namelen = sizeof(conn->trans->peer->srx.transport.sin); + msg.msg_control = NULL; + msg.msg_controllen = 0; + msg.msg_flags = 0; + + hdr->epoch = conn->epoch; + hdr->seq = 0; + hdr->type = RXRPC_PACKET_TYPE_RESPONSE; + hdr->flags = conn->out_clientflag; + hdr->userStatus = 0; + hdr->_rsvd = 0; + + iov[0].iov_base = hdr; + iov[0].iov_len = sizeof(*hdr); + iov[1].iov_base = resp; + iov[1].iov_len = sizeof(*resp); + iov[2].iov_base = (void *) s2->ticket; + iov[2].iov_len = s2->ticket_len; + + len = iov[0].iov_len + iov[1].iov_len + iov[2].iov_len; + + hdr->serial = htonl(atomic_inc_return(&conn->serial)); + _proto("Tx RESPONSE %%%u", ntohl(hdr->serial)); + + ret = kernel_sendmsg(conn->trans->local->socket, &msg, iov, 3, len); + if (ret < 0) { + _debug("sendmsg failed: %d", ret); + return -EAGAIN; + } + + _leave(" = 0"); + return 0; +} + +/* + * calculate the response checksum + */ +static void rxkad_calc_response_checksum(struct rxkad_response *response) +{ + u32 csum = 1000003; + int loop; + u8 *p = (u8 *) response; + + for (loop = sizeof(*response); loop > 0; loop--) + csum = csum * 0x10204081 + *p++; + + response->encrypted.checksum = htonl(csum); +} + +/* + * load a scatterlist with a potentially split-page buffer + */ +static void rxkad_sg_set_buf2(struct scatterlist sg[2], + void *buf, size_t buflen) +{ + + memset(sg, 0, sizeof(sg)); + + sg_set_buf(&sg[0], buf, buflen); + if (sg[0].offset + buflen > PAGE_SIZE) { + /* the buffer was split over two pages */ + sg[0].length = PAGE_SIZE - sg[0].offset; + sg_set_buf(&sg[1], buf + sg[0].length, buflen - sg[0].length); + } + + ASSERTCMP(sg[0].length + sg[1].length, ==, buflen); +} + +/* + * encrypt the response packet + */ +static void rxkad_encrypt_response(struct rxrpc_connection *conn, + struct rxkad_response *resp, + const struct rxkad_key *s2) +{ + struct blkcipher_desc desc; + struct rxrpc_crypt iv; + struct scatterlist ssg[2], dsg[2]; + + /* continue encrypting from where we left off */ + memcpy(&iv, s2->session_key, sizeof(iv)); + desc.tfm = conn->cipher; + desc.info = iv.x; + desc.flags = 0; + + rxkad_sg_set_buf2(ssg, &resp->encrypted, sizeof(resp->encrypted)); + memcpy(dsg, ssg, sizeof(dsg)); + crypto_blkcipher_encrypt_iv(&desc, dsg, ssg, sizeof(resp->encrypted)); +} + +/* + * respond to a challenge packet + */ +static int rxkad_respond_to_challenge(struct rxrpc_connection *conn, + struct sk_buff *skb, + u32 *_abort_code) +{ + const struct rxrpc_key_payload *payload; + struct rxkad_challenge challenge; + struct rxkad_response resp + __attribute__((aligned(8))); /* must be aligned for crypto */ + struct rxrpc_skb_priv *sp; + u32 version, nonce, min_level, abort_code; + int ret; + + _enter("{%d,%x}", conn->debug_id, key_serial(conn->key)); + + if (!conn->key) { + _leave(" = -EPROTO [no key]"); + return -EPROTO; + } + + ret = key_validate(conn->key); + if (ret < 0) { + *_abort_code = RXKADEXPIRED; + return ret; + } + + abort_code = RXKADPACKETSHORT; + sp = rxrpc_skb(skb); + if (skb_copy_bits(skb, 0, &challenge, sizeof(challenge)) < 0) + goto protocol_error; + + version = ntohl(challenge.version); + nonce = ntohl(challenge.nonce); + min_level = ntohl(challenge.min_level); + + _proto("Rx CHALLENGE %%%u { v=%u n=%u ml=%u }", + ntohl(sp->hdr.serial), version, nonce, min_level); + + abort_code = RXKADINCONSISTENCY; + if (version != RXKAD_VERSION) + goto protocol_error; + + abort_code = RXKADLEVELFAIL; + if (conn->security_level < min_level) + goto protocol_error; + + payload = conn->key->payload.data; + + /* build the response packet */ + memset(&resp, 0, sizeof(resp)); + + resp.version = RXKAD_VERSION; + resp.encrypted.epoch = conn->epoch; + resp.encrypted.cid = conn->cid; + resp.encrypted.securityIndex = htonl(conn->security_ix); + resp.encrypted.call_id[0] = + (conn->channels[0] ? conn->channels[0]->call_id : 0); + resp.encrypted.call_id[1] = + (conn->channels[1] ? conn->channels[1]->call_id : 0); + resp.encrypted.call_id[2] = + (conn->channels[2] ? conn->channels[2]->call_id : 0); + resp.encrypted.call_id[3] = + (conn->channels[3] ? conn->channels[3]->call_id : 0); + resp.encrypted.inc_nonce = htonl(nonce + 1); + resp.encrypted.level = htonl(conn->security_level); + resp.kvno = htonl(payload->k.kvno); + resp.ticket_len = htonl(payload->k.ticket_len); + + /* calculate the response checksum and then do the encryption */ + rxkad_calc_response_checksum(&resp); + rxkad_encrypt_response(conn, &resp, &payload->k); + return rxkad_send_response(conn, &sp->hdr, &resp, &payload->k); + +protocol_error: + *_abort_code = abort_code; + _leave(" = -EPROTO [%d]", abort_code); + return -EPROTO; +} + +/* + * decrypt the kerberos IV ticket in the response + */ +static int rxkad_decrypt_ticket(struct rxrpc_connection *conn, + void *ticket, size_t ticket_len, + struct rxrpc_crypt *_session_key, + time_t *_expiry, + u32 *_abort_code) +{ + struct blkcipher_desc desc; + struct rxrpc_crypt iv, key; + struct scatterlist ssg[1], dsg[1]; + struct in_addr addr; + unsigned life; + time_t issue, now; + bool little_endian; + int ret; + u8 *p, *q, *name, *end; + + _enter("{%d},{%x}", conn->debug_id, key_serial(conn->server_key)); + + *_expiry = 0; + + ret = key_validate(conn->server_key); + if (ret < 0) { + switch (ret) { + case -EKEYEXPIRED: + *_abort_code = RXKADEXPIRED; + goto error; + default: + *_abort_code = RXKADNOAUTH; + goto error; + } + } + + ASSERT(conn->server_key->payload.data != NULL); + ASSERTCMP((unsigned long) ticket & 7UL, ==, 0); + + memcpy(&iv, &conn->server_key->type_data, sizeof(iv)); + + desc.tfm = conn->server_key->payload.data; + desc.info = iv.x; + desc.flags = 0; + + sg_init_one(&ssg[0], ticket, ticket_len); + memcpy(dsg, ssg, sizeof(dsg)); + crypto_blkcipher_decrypt_iv(&desc, dsg, ssg, ticket_len); + + p = ticket; + end = p + ticket_len; + +#define Z(size) \ + ({ \ + u8 *__str = p; \ + q = memchr(p, 0, end - p); \ + if (!q || q - p > (size)) \ + goto bad_ticket; \ + for (; p < q; p++) \ + if (!isprint(*p)) \ + goto bad_ticket; \ + p++; \ + __str; \ + }) + + /* extract the ticket flags */ + _debug("KIV FLAGS: %x", *p); + little_endian = *p & 1; + p++; + + /* extract the authentication name */ + name = Z(ANAME_SZ); + _debug("KIV ANAME: %s", name); + + /* extract the principal's instance */ + name = Z(INST_SZ); + _debug("KIV INST : %s", name); + + /* extract the principal's authentication domain */ + name = Z(REALM_SZ); + _debug("KIV REALM: %s", name); + + if (end - p < 4 + 8 + 4 + 2) + goto bad_ticket; + + /* get the IPv4 address of the entity that requested the ticket */ + memcpy(&addr, p, sizeof(addr)); + p += 4; + _debug("KIV ADDR : "NIPQUAD_FMT, NIPQUAD(addr)); + + /* get the session key from the ticket */ + memcpy(&key, p, sizeof(key)); + p += 8; + _debug("KIV KEY : %08x %08x", ntohl(key.n[0]), ntohl(key.n[1])); + memcpy(_session_key, &key, sizeof(key)); + + /* get the ticket's lifetime */ + life = *p++ * 5 * 60; + _debug("KIV LIFE : %u", life); + + /* get the issue time of the ticket */ + if (little_endian) { + __le32 stamp; + memcpy(&stamp, p, 4); + issue = le32_to_cpu(stamp); + } else { + __be32 stamp; + memcpy(&stamp, p, 4); + issue = be32_to_cpu(stamp); + } + p += 4; + now = xtime.tv_sec; + _debug("KIV ISSUE: %lx [%lx]", issue, now); + + /* check the ticket is in date */ + if (issue > now) { + *_abort_code = RXKADNOAUTH; + ret = -EKEYREJECTED; + goto error; + } + + if (issue < now - life) { + *_abort_code = RXKADEXPIRED; + ret = -EKEYEXPIRED; + goto error; + } + + *_expiry = issue + life; + + /* get the service name */ + name = Z(SNAME_SZ); + _debug("KIV SNAME: %s", name); + + /* get the service instance name */ + name = Z(INST_SZ); + _debug("KIV SINST: %s", name); + + ret = 0; +error: + _leave(" = %d", ret); + return ret; + +bad_ticket: + *_abort_code = RXKADBADTICKET; + ret = -EBADMSG; + goto error; +} + +/* + * decrypt the response packet + */ +static void rxkad_decrypt_response(struct rxrpc_connection *conn, + struct rxkad_response *resp, + const struct rxrpc_crypt *session_key) +{ + struct blkcipher_desc desc; + struct scatterlist ssg[2], dsg[2]; + struct rxrpc_crypt iv; + + _enter(",,%08x%08x", + ntohl(session_key->n[0]), ntohl(session_key->n[1])); + + ASSERT(rxkad_ci != NULL); + + mutex_lock(&rxkad_ci_mutex); + if (crypto_blkcipher_setkey(rxkad_ci, session_key->x, + sizeof(*session_key)) < 0) + BUG(); + + memcpy(&iv, session_key, sizeof(iv)); + desc.tfm = rxkad_ci; + desc.info = iv.x; + desc.flags = 0; + + rxkad_sg_set_buf2(ssg, &resp->encrypted, sizeof(resp->encrypted)); + memcpy(dsg, ssg, sizeof(dsg)); + crypto_blkcipher_decrypt_iv(&desc, dsg, ssg, sizeof(resp->encrypted)); + mutex_unlock(&rxkad_ci_mutex); + + _leave(""); +} + +/* + * verify a response + */ +static int rxkad_verify_response(struct rxrpc_connection *conn, + struct sk_buff *skb, + u32 *_abort_code) +{ + struct rxkad_response response + __attribute__((aligned(8))); /* must be aligned for crypto */ + struct rxrpc_skb_priv *sp; + struct rxrpc_crypt session_key; + time_t expiry; + void *ticket; + u32 abort_code, version, kvno, ticket_len, csum, level; + int ret; + + _enter("{%d,%x}", conn->debug_id, key_serial(conn->server_key)); + + abort_code = RXKADPACKETSHORT; + if (skb_copy_bits(skb, 0, &response, sizeof(response)) < 0) + goto protocol_error; + if (!pskb_pull(skb, sizeof(response))) + BUG(); + + version = ntohl(response.version); + ticket_len = ntohl(response.ticket_len); + kvno = ntohl(response.kvno); + sp = rxrpc_skb(skb); + _proto("Rx RESPONSE %%%u { v=%u kv=%u tl=%u }", + ntohl(sp->hdr.serial), version, kvno, ticket_len); + + abort_code = RXKADINCONSISTENCY; + if (version != RXKAD_VERSION) + + abort_code = RXKADTICKETLEN; + if (ticket_len < 4 || ticket_len > MAXKRB5TICKETLEN) + goto protocol_error; + + abort_code = RXKADUNKNOWNKEY; + if (kvno >= RXKAD_TKT_TYPE_KERBEROS_V5) + goto protocol_error; + + /* extract the kerberos ticket and decrypt and decode it */ + ticket = kmalloc(ticket_len, GFP_NOFS); + if (!ticket) + return -ENOMEM; + + abort_code = RXKADPACKETSHORT; + if (skb_copy_bits(skb, 0, ticket, ticket_len) < 0) + goto protocol_error_free; + + ret = rxkad_decrypt_ticket(conn, ticket, ticket_len, &session_key, + &expiry, &abort_code); + if (ret < 0) { + *_abort_code = abort_code; + kfree(ticket); + return ret; + } + + /* use the session key from inside the ticket to decrypt the + * response */ + rxkad_decrypt_response(conn, &response, &session_key); + + abort_code = RXKADSEALEDINCON; + if (response.encrypted.epoch != conn->epoch) + goto protocol_error_free; + if (response.encrypted.cid != conn->cid) + goto protocol_error_free; + if (ntohl(response.encrypted.securityIndex) != conn->security_ix) + goto protocol_error_free; + csum = response.encrypted.checksum; + response.encrypted.checksum = 0; + rxkad_calc_response_checksum(&response); + if (response.encrypted.checksum != csum) + goto protocol_error_free; + + if (ntohl(response.encrypted.call_id[0]) > INT_MAX || + ntohl(response.encrypted.call_id[1]) > INT_MAX || + ntohl(response.encrypted.call_id[2]) > INT_MAX || + ntohl(response.encrypted.call_id[3]) > INT_MAX) + goto protocol_error_free; + + abort_code = RXKADOUTOFSEQUENCE; + if (response.encrypted.inc_nonce != htonl(conn->security_nonce + 1)) + goto protocol_error_free; + + abort_code = RXKADLEVELFAIL; + level = ntohl(response.encrypted.level); + if (level > RXRPC_SECURITY_ENCRYPT) + goto protocol_error_free; + conn->security_level = level; + + /* create a key to hold the security data and expiration time - after + * this the connection security can be handled in exactly the same way + * as for a client connection */ + ret = rxrpc_get_server_data_key(conn, &session_key, expiry, kvno); + if (ret < 0) { + kfree(ticket); + return ret; + } + + kfree(ticket); + _leave(" = 0"); + return 0; + +protocol_error_free: + kfree(ticket); +protocol_error: + *_abort_code = abort_code; + _leave(" = -EPROTO [%d]", abort_code); + return -EPROTO; +} + +/* + * clear the connection security + */ +static void rxkad_clear(struct rxrpc_connection *conn) +{ + _enter(""); + + if (conn->cipher) + crypto_free_blkcipher(conn->cipher); +} + +/* + * RxRPC Kerberos-based security + */ +static struct rxrpc_security rxkad = { + .owner = THIS_MODULE, + .name = "rxkad", + .security_index = RXKAD_VERSION, + .init_connection_security = rxkad_init_connection_security, + .prime_packet_security = rxkad_prime_packet_security, + .secure_packet = rxkad_secure_packet, + .verify_packet = rxkad_verify_packet, + .issue_challenge = rxkad_issue_challenge, + .respond_to_challenge = rxkad_respond_to_challenge, + .verify_response = rxkad_verify_response, + .clear = rxkad_clear, +}; + +static __init int rxkad_init(void) +{ + _enter(""); + + /* pin the cipher we need so that the crypto layer doesn't invoke + * keventd to go get it */ + rxkad_ci = crypto_alloc_blkcipher("pcbc(fcrypt)", 0, CRYPTO_ALG_ASYNC); + if (IS_ERR(rxkad_ci)) + return PTR_ERR(rxkad_ci); + + return rxrpc_register_security(&rxkad); +} + +module_init(rxkad_init); + +static __exit void rxkad_exit(void) +{ + _enter(""); + + rxrpc_unregister_security(&rxkad); + crypto_free_blkcipher(rxkad_ci); +} + +module_exit(rxkad_exit); -- cgit v1.2.3 From 651350d10f93bed7003c9a66e24cf25e0f8eed3d Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 26 Apr 2007 15:50:17 -0700 Subject: [AF_RXRPC]: Add an interface to the AF_RXRPC module for the AFS filesystem to use Add an interface to the AF_RXRPC module so that the AFS filesystem module can more easily make use of the services available. AFS still opens a socket but then uses the action functions in lieu of sendmsg() and registers an intercept functions to grab messages before they're queued on the socket Rx queue. This permits AFS (or whatever) to: (1) Avoid the overhead of using the recvmsg() call. (2) Use different keys directly on individual client calls on one socket rather than having to open a whole slew of sockets, one for each key it might want to use. (3) Avoid calling request_key() at the point of issue of a call or opening of a socket. This is done instead by AFS at the point of open(), unlink() or other VFS operation and the key handed through. (4) Request the use of something other than GFP_KERNEL to allocate memory. Furthermore: (*) The socket buffer markings used by RxRPC are made available for AFS so that it can interpret the cooked RxRPC messages itself. (*) rxgen (un)marshalling abort codes are made available. The following documentation for the kernel interface is added to Documentation/networking/rxrpc.txt: ========================= AF_RXRPC KERNEL INTERFACE ========================= The AF_RXRPC module also provides an interface for use by in-kernel utilities such as the AFS filesystem. This permits such a utility to: (1) Use different keys directly on individual client calls on one socket rather than having to open a whole slew of sockets, one for each key it might want to use. (2) Avoid having RxRPC call request_key() at the point of issue of a call or opening of a socket. Instead the utility is responsible for requesting a key at the appropriate point. AFS, for instance, would do this during VFS operations such as open() or unlink(). The key is then handed through when the call is initiated. (3) Request the use of something other than GFP_KERNEL to allocate memory. (4) Avoid the overhead of using the recvmsg() call. RxRPC messages can be intercepted before they get put into the socket Rx queue and the socket buffers manipulated directly. To use the RxRPC facility, a kernel utility must still open an AF_RXRPC socket, bind an addess as appropriate and listen if it's to be a server socket, but then it passes this to the kernel interface functions. The kernel interface functions are as follows: (*) Begin a new client call. struct rxrpc_call * rxrpc_kernel_begin_call(struct socket *sock, struct sockaddr_rxrpc *srx, struct key *key, unsigned long user_call_ID, gfp_t gfp); This allocates the infrastructure to make a new RxRPC call and assigns call and connection numbers. The call will be made on the UDP port that the socket is bound to. The call will go to the destination address of a connected client socket unless an alternative is supplied (srx is non-NULL). If a key is supplied then this will be used to secure the call instead of the key bound to the socket with the RXRPC_SECURITY_KEY sockopt. Calls secured in this way will still share connections if at all possible. The user_call_ID is equivalent to that supplied to sendmsg() in the control data buffer. It is entirely feasible to use this to point to a kernel data structure. If this function is successful, an opaque reference to the RxRPC call is returned. The caller now holds a reference on this and it must be properly ended. (*) End a client call. void rxrpc_kernel_end_call(struct rxrpc_call *call); This is used to end a previously begun call. The user_call_ID is expunged from AF_RXRPC's knowledge and will not be seen again in association with the specified call. (*) Send data through a call. int rxrpc_kernel_send_data(struct rxrpc_call *call, struct msghdr *msg, size_t len); This is used to supply either the request part of a client call or the reply part of a server call. msg.msg_iovlen and msg.msg_iov specify the data buffers to be used. msg_iov may not be NULL and must point exclusively to in-kernel virtual addresses. msg.msg_flags may be given MSG_MORE if there will be subsequent data sends for this call. The msg must not specify a destination address, control data or any flags other than MSG_MORE. len is the total amount of data to transmit. (*) Abort a call. void rxrpc_kernel_abort_call(struct rxrpc_call *call, u32 abort_code); This is used to abort a call if it's still in an abortable state. The abort code specified will be placed in the ABORT message sent. (*) Intercept received RxRPC messages. typedef void (*rxrpc_interceptor_t)(struct sock *sk, unsigned long user_call_ID, struct sk_buff *skb); void rxrpc_kernel_intercept_rx_messages(struct socket *sock, rxrpc_interceptor_t interceptor); This installs an interceptor function on the specified AF_RXRPC socket. All messages that would otherwise wind up in the socket's Rx queue are then diverted to this function. Note that care must be taken to process the messages in the right order to maintain DATA message sequentiality. The interceptor function itself is provided with the address of the socket and handling the incoming message, the ID assigned by the kernel utility to the call and the socket buffer containing the message. The skb->mark field indicates the type of message: MARK MEANING =============================== ======================================= RXRPC_SKB_MARK_DATA Data message RXRPC_SKB_MARK_FINAL_ACK Final ACK received for an incoming call RXRPC_SKB_MARK_BUSY Client call rejected as server busy RXRPC_SKB_MARK_REMOTE_ABORT Call aborted by peer RXRPC_SKB_MARK_NET_ERROR Network error detected RXRPC_SKB_MARK_LOCAL_ERROR Local error encountered RXRPC_SKB_MARK_NEW_CALL New incoming call awaiting acceptance The remote abort message can be probed with rxrpc_kernel_get_abort_code(). The two error messages can be probed with rxrpc_kernel_get_error_number(). A new call can be accepted with rxrpc_kernel_accept_call(). Data messages can have their contents extracted with the usual bunch of socket buffer manipulation functions. A data message can be determined to be the last one in a sequence with rxrpc_kernel_is_data_last(). When a data message has been used up, rxrpc_kernel_data_delivered() should be called on it.. Non-data messages should be handled to rxrpc_kernel_free_skb() to dispose of. It is possible to get extra refs on all types of message for later freeing, but this may pin the state of a call until the message is finally freed. (*) Accept an incoming call. struct rxrpc_call * rxrpc_kernel_accept_call(struct socket *sock, unsigned long user_call_ID); This is used to accept an incoming call and to assign it a call ID. This function is similar to rxrpc_kernel_begin_call() and calls accepted must be ended in the same way. If this function is successful, an opaque reference to the RxRPC call is returned. The caller now holds a reference on this and it must be properly ended. (*) Reject an incoming call. int rxrpc_kernel_reject_call(struct socket *sock); This is used to reject the first incoming call on the socket's queue with a BUSY message. -ENODATA is returned if there were no incoming calls. Other errors may be returned if the call had been aborted (-ECONNABORTED) or had timed out (-ETIME). (*) Record the delivery of a data message and free it. void rxrpc_kernel_data_delivered(struct sk_buff *skb); This is used to record a data message as having been delivered and to update the ACK state for the call. The socket buffer will be freed. (*) Free a message. void rxrpc_kernel_free_skb(struct sk_buff *skb); This is used to free a non-DATA socket buffer intercepted from an AF_RXRPC socket. (*) Determine if a data message is the last one on a call. bool rxrpc_kernel_is_data_last(struct sk_buff *skb); This is used to determine if a socket buffer holds the last data message to be received for a call (true will be returned if it does, false if not). The data message will be part of the reply on a client call and the request on an incoming call. In the latter case there will be more messages, but in the former case there will not. (*) Get the abort code from an abort message. u32 rxrpc_kernel_get_abort_code(struct sk_buff *skb); This is used to extract the abort code from a remote abort message. (*) Get the error number from a local or network error message. int rxrpc_kernel_get_error_number(struct sk_buff *skb); This is used to extract the error number from a message indicating either a local error occurred or a network error occurred. Signed-off-by: David Howells Signed-off-by: David S. Miller --- Documentation/networking/rxrpc.txt | 196 +++++++++++++++++++++++++++++++++++++ include/net/af_rxrpc.h | 44 ++++++++- include/rxrpc/packet.h | 12 +++ net/rxrpc/af_rxrpc.c | 141 ++++++++++++++++++++++++-- net/rxrpc/ar-accept.c | 119 ++++++++++++++++++++-- net/rxrpc/ar-ack.c | 10 +- net/rxrpc/ar-call.c | 75 ++++++++------ net/rxrpc/ar-connection.c | 28 ++++-- net/rxrpc/ar-connevent.c | 20 +++- net/rxrpc/ar-error.c | 6 +- net/rxrpc/ar-input.c | 60 +++++++----- net/rxrpc/ar-internal.h | 44 ++++----- net/rxrpc/ar-local.c | 2 +- net/rxrpc/ar-output.c | 84 +++++++++++++++- net/rxrpc/ar-peer.c | 2 +- net/rxrpc/ar-recvmsg.c | 75 +++++++++++++- net/rxrpc/ar-skbuff.c | 16 ++- net/rxrpc/ar-transport.c | 8 +- 18 files changed, 813 insertions(+), 129 deletions(-) (limited to 'Documentation') diff --git a/Documentation/networking/rxrpc.txt b/Documentation/networking/rxrpc.txt index fb809b738a0d..cae231b1c134 100644 --- a/Documentation/networking/rxrpc.txt +++ b/Documentation/networking/rxrpc.txt @@ -25,6 +25,8 @@ Contents of this document: (*) Example server usage. + (*) AF_RXRPC kernel interface. + ======== OVERVIEW @@ -661,3 +663,197 @@ A server would be set up to accept operations in the following manner: Note that all the communications for a particular service take place through the one server socket, using control messages on sendmsg() and recvmsg() to determine the call affected. + + +========================= +AF_RXRPC KERNEL INTERFACE +========================= + +The AF_RXRPC module also provides an interface for use by in-kernel utilities +such as the AFS filesystem. This permits such a utility to: + + (1) Use different keys directly on individual client calls on one socket + rather than having to open a whole slew of sockets, one for each key it + might want to use. + + (2) Avoid having RxRPC call request_key() at the point of issue of a call or + opening of a socket. Instead the utility is responsible for requesting a + key at the appropriate point. AFS, for instance, would do this during VFS + operations such as open() or unlink(). The key is then handed through + when the call is initiated. + + (3) Request the use of something other than GFP_KERNEL to allocate memory. + + (4) Avoid the overhead of using the recvmsg() call. RxRPC messages can be + intercepted before they get put into the socket Rx queue and the socket + buffers manipulated directly. + +To use the RxRPC facility, a kernel utility must still open an AF_RXRPC socket, +bind an addess as appropriate and listen if it's to be a server socket, but +then it passes this to the kernel interface functions. + +The kernel interface functions are as follows: + + (*) Begin a new client call. + + struct rxrpc_call * + rxrpc_kernel_begin_call(struct socket *sock, + struct sockaddr_rxrpc *srx, + struct key *key, + unsigned long user_call_ID, + gfp_t gfp); + + This allocates the infrastructure to make a new RxRPC call and assigns + call and connection numbers. The call will be made on the UDP port that + the socket is bound to. The call will go to the destination address of a + connected client socket unless an alternative is supplied (srx is + non-NULL). + + If a key is supplied then this will be used to secure the call instead of + the key bound to the socket with the RXRPC_SECURITY_KEY sockopt. Calls + secured in this way will still share connections if at all possible. + + The user_call_ID is equivalent to that supplied to sendmsg() in the + control data buffer. It is entirely feasible to use this to point to a + kernel data structure. + + If this function is successful, an opaque reference to the RxRPC call is + returned. The caller now holds a reference on this and it must be + properly ended. + + (*) End a client call. + + void rxrpc_kernel_end_call(struct rxrpc_call *call); + + This is used to end a previously begun call. The user_call_ID is expunged + from AF_RXRPC's knowledge and will not be seen again in association with + the specified call. + + (*) Send data through a call. + + int rxrpc_kernel_send_data(struct rxrpc_call *call, struct msghdr *msg, + size_t len); + + This is used to supply either the request part of a client call or the + reply part of a server call. msg.msg_iovlen and msg.msg_iov specify the + data buffers to be used. msg_iov may not be NULL and must point + exclusively to in-kernel virtual addresses. msg.msg_flags may be given + MSG_MORE if there will be subsequent data sends for this call. + + The msg must not specify a destination address, control data or any flags + other than MSG_MORE. len is the total amount of data to transmit. + + (*) Abort a call. + + void rxrpc_kernel_abort_call(struct rxrpc_call *call, u32 abort_code); + + This is used to abort a call if it's still in an abortable state. The + abort code specified will be placed in the ABORT message sent. + + (*) Intercept received RxRPC messages. + + typedef void (*rxrpc_interceptor_t)(struct sock *sk, + unsigned long user_call_ID, + struct sk_buff *skb); + + void + rxrpc_kernel_intercept_rx_messages(struct socket *sock, + rxrpc_interceptor_t interceptor); + + This installs an interceptor function on the specified AF_RXRPC socket. + All messages that would otherwise wind up in the socket's Rx queue are + then diverted to this function. Note that care must be taken to process + the messages in the right order to maintain DATA message sequentiality. + + The interceptor function itself is provided with the address of the socket + and handling the incoming message, the ID assigned by the kernel utility + to the call and the socket buffer containing the message. + + The skb->mark field indicates the type of message: + + MARK MEANING + =============================== ======================================= + RXRPC_SKB_MARK_DATA Data message + RXRPC_SKB_MARK_FINAL_ACK Final ACK received for an incoming call + RXRPC_SKB_MARK_BUSY Client call rejected as server busy + RXRPC_SKB_MARK_REMOTE_ABORT Call aborted by peer + RXRPC_SKB_MARK_NET_ERROR Network error detected + RXRPC_SKB_MARK_LOCAL_ERROR Local error encountered + RXRPC_SKB_MARK_NEW_CALL New incoming call awaiting acceptance + + The remote abort message can be probed with rxrpc_kernel_get_abort_code(). + The two error messages can be probed with rxrpc_kernel_get_error_number(). + A new call can be accepted with rxrpc_kernel_accept_call(). + + Data messages can have their contents extracted with the usual bunch of + socket buffer manipulation functions. A data message can be determined to + be the last one in a sequence with rxrpc_kernel_is_data_last(). When a + data message has been used up, rxrpc_kernel_data_delivered() should be + called on it.. + + Non-data messages should be handled to rxrpc_kernel_free_skb() to dispose + of. It is possible to get extra refs on all types of message for later + freeing, but this may pin the state of a call until the message is finally + freed. + + (*) Accept an incoming call. + + struct rxrpc_call * + rxrpc_kernel_accept_call(struct socket *sock, + unsigned long user_call_ID); + + This is used to accept an incoming call and to assign it a call ID. This + function is similar to rxrpc_kernel_begin_call() and calls accepted must + be ended in the same way. + + If this function is successful, an opaque reference to the RxRPC call is + returned. The caller now holds a reference on this and it must be + properly ended. + + (*) Reject an incoming call. + + int rxrpc_kernel_reject_call(struct socket *sock); + + This is used to reject the first incoming call on the socket's queue with + a BUSY message. -ENODATA is returned if there were no incoming calls. + Other errors may be returned if the call had been aborted (-ECONNABORTED) + or had timed out (-ETIME). + + (*) Record the delivery of a data message and free it. + + void rxrpc_kernel_data_delivered(struct sk_buff *skb); + + This is used to record a data message as having been delivered and to + update the ACK state for the call. The socket buffer will be freed. + + (*) Free a message. + + void rxrpc_kernel_free_skb(struct sk_buff *skb); + + This is used to free a non-DATA socket buffer intercepted from an AF_RXRPC + socket. + + (*) Determine if a data message is the last one on a call. + + bool rxrpc_kernel_is_data_last(struct sk_buff *skb); + + This is used to determine if a socket buffer holds the last data message + to be received for a call (true will be returned if it does, false + if not). + + The data message will be part of the reply on a client call and the + request on an incoming call. In the latter case there will be more + messages, but in the former case there will not. + + (*) Get the abort code from an abort message. + + u32 rxrpc_kernel_get_abort_code(struct sk_buff *skb); + + This is used to extract the abort code from a remote abort message. + + (*) Get the error number from a local or network error message. + + int rxrpc_kernel_get_error_number(struct sk_buff *skb); + + This is used to extract the error number from a message indicating either + a local error occurred or a network error occurred. diff --git a/include/net/af_rxrpc.h b/include/net/af_rxrpc.h index b01ca2589d69..00c2eaa07c25 100644 --- a/include/net/af_rxrpc.h +++ b/include/net/af_rxrpc.h @@ -1,6 +1,6 @@ -/* RxRPC definitions +/* RxRPC kernel service interface definitions * - * Copyright (C) 2006 Red Hat, Inc. All Rights Reserved. + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. * Written by David Howells (dhowells@redhat.com) * * This program is free software; you can redistribute it and/or @@ -12,6 +12,46 @@ #ifndef _NET_RXRPC_H #define _NET_RXRPC_H +#ifdef __KERNEL__ + #include +struct rxrpc_call; + +/* + * the mark applied to socket buffers that may be intercepted + */ +enum { + RXRPC_SKB_MARK_DATA, /* data message */ + RXRPC_SKB_MARK_FINAL_ACK, /* final ACK received message */ + RXRPC_SKB_MARK_BUSY, /* server busy message */ + RXRPC_SKB_MARK_REMOTE_ABORT, /* remote abort message */ + RXRPC_SKB_MARK_NET_ERROR, /* network error message */ + RXRPC_SKB_MARK_LOCAL_ERROR, /* local error message */ + RXRPC_SKB_MARK_NEW_CALL, /* local error message */ +}; + +typedef void (*rxrpc_interceptor_t)(struct sock *, unsigned long, + struct sk_buff *); +extern void rxrpc_kernel_intercept_rx_messages(struct socket *, + rxrpc_interceptor_t); +extern struct rxrpc_call *rxrpc_kernel_begin_call(struct socket *, + struct sockaddr_rxrpc *, + struct key *, + unsigned long, + gfp_t); +extern int rxrpc_kernel_send_data(struct rxrpc_call *, struct msghdr *, + size_t); +extern void rxrpc_kernel_abort_call(struct rxrpc_call *, u32); +extern void rxrpc_kernel_end_call(struct rxrpc_call *); +extern bool rxrpc_kernel_is_data_last(struct sk_buff *); +extern u32 rxrpc_kernel_get_abort_code(struct sk_buff *); +extern int rxrpc_kernel_get_error_number(struct sk_buff *); +extern void rxrpc_kernel_data_delivered(struct sk_buff *); +extern void rxrpc_kernel_free_skb(struct sk_buff *); +extern struct rxrpc_call *rxrpc_kernel_accept_call(struct socket *, + unsigned long); +extern int rxrpc_kernel_reject_call(struct socket *); + +#endif /* __KERNEL__ */ #endif /* _NET_RXRPC_H */ diff --git a/include/rxrpc/packet.h b/include/rxrpc/packet.h index 452a9bb02d48..09b11a1e8d46 100644 --- a/include/rxrpc/packet.h +++ b/include/rxrpc/packet.h @@ -185,6 +185,18 @@ struct rxkad_response { #define RX_ADDRINUSE -7 /* UDP port in use */ #define RX_DEBUGI_BADTYPE -8 /* bad debugging packet type */ +/* + * (un)marshalling abort codes (rxgen) + */ +#define RXGEN_CC_MARSHAL -450 +#define RXGEN_CC_UNMARSHAL -451 +#define RXGEN_SS_MARSHAL -452 +#define RXGEN_SS_UNMARSHAL -453 +#define RXGEN_DECODE -454 +#define RXGEN_OPCODE -455 +#define RXGEN_SS_XDRFREE -456 +#define RXGEN_CC_XDRFREE -457 + /* * Rx kerberos security abort codes * - unfortunately we have no generalised security abort codes to say things diff --git a/net/rxrpc/af_rxrpc.c b/net/rxrpc/af_rxrpc.c index bfa8822e2286..2c57df9c131b 100644 --- a/net/rxrpc/af_rxrpc.c +++ b/net/rxrpc/af_rxrpc.c @@ -41,6 +41,8 @@ atomic_t rxrpc_debug_id; /* count of skbs currently in use */ atomic_t rxrpc_n_skbs; +struct workqueue_struct *rxrpc_workqueue; + static void rxrpc_sock_destructor(struct sock *); /* @@ -214,7 +216,8 @@ static int rxrpc_listen(struct socket *sock, int backlog) */ static struct rxrpc_transport *rxrpc_name_to_transport(struct socket *sock, struct sockaddr *addr, - int addr_len, int flags) + int addr_len, int flags, + gfp_t gfp) { struct sockaddr_rxrpc *srx = (struct sockaddr_rxrpc *) addr; struct rxrpc_transport *trans; @@ -232,17 +235,129 @@ static struct rxrpc_transport *rxrpc_name_to_transport(struct socket *sock, return ERR_PTR(-EAFNOSUPPORT); /* find a remote transport endpoint from the local one */ - peer = rxrpc_get_peer(srx, GFP_KERNEL); + peer = rxrpc_get_peer(srx, gfp); if (IS_ERR(peer)) return ERR_PTR(PTR_ERR(peer)); /* find a transport */ - trans = rxrpc_get_transport(rx->local, peer, GFP_KERNEL); + trans = rxrpc_get_transport(rx->local, peer, gfp); rxrpc_put_peer(peer); _leave(" = %p", trans); return trans; } +/** + * rxrpc_kernel_begin_call - Allow a kernel service to begin a call + * @sock: The socket on which to make the call + * @srx: The address of the peer to contact (defaults to socket setting) + * @key: The security context to use (defaults to socket setting) + * @user_call_ID: The ID to use + * + * Allow a kernel service to begin a call on the nominated socket. This just + * sets up all the internal tracking structures and allocates connection and + * call IDs as appropriate. The call to be used is returned. + * + * The default socket destination address and security may be overridden by + * supplying @srx and @key. + */ +struct rxrpc_call *rxrpc_kernel_begin_call(struct socket *sock, + struct sockaddr_rxrpc *srx, + struct key *key, + unsigned long user_call_ID, + gfp_t gfp) +{ + struct rxrpc_conn_bundle *bundle; + struct rxrpc_transport *trans; + struct rxrpc_call *call; + struct rxrpc_sock *rx = rxrpc_sk(sock->sk); + __be16 service_id; + + _enter(",,%x,%lx", key_serial(key), user_call_ID); + + lock_sock(&rx->sk); + + if (srx) { + trans = rxrpc_name_to_transport(sock, (struct sockaddr *) srx, + sizeof(*srx), 0, gfp); + if (IS_ERR(trans)) { + call = ERR_PTR(PTR_ERR(trans)); + trans = NULL; + goto out; + } + } else { + trans = rx->trans; + if (!trans) { + call = ERR_PTR(-ENOTCONN); + goto out; + } + atomic_inc(&trans->usage); + } + + service_id = rx->service_id; + if (srx) + service_id = htons(srx->srx_service); + + if (!key) + key = rx->key; + if (key && !key->payload.data) + key = NULL; /* a no-security key */ + + bundle = rxrpc_get_bundle(rx, trans, key, service_id, gfp); + if (IS_ERR(bundle)) { + call = ERR_PTR(PTR_ERR(bundle)); + goto out; + } + + call = rxrpc_get_client_call(rx, trans, bundle, user_call_ID, true, + gfp); + rxrpc_put_bundle(trans, bundle); +out: + rxrpc_put_transport(trans); + release_sock(&rx->sk); + _leave(" = %p", call); + return call; +} + +EXPORT_SYMBOL(rxrpc_kernel_begin_call); + +/** + * rxrpc_kernel_end_call - Allow a kernel service to end a call it was using + * @call: The call to end + * + * Allow a kernel service to end a call it was using. The call must be + * complete before this is called (the call should be aborted if necessary). + */ +void rxrpc_kernel_end_call(struct rxrpc_call *call) +{ + _enter("%d{%d}", call->debug_id, atomic_read(&call->usage)); + rxrpc_remove_user_ID(call->socket, call); + rxrpc_put_call(call); +} + +EXPORT_SYMBOL(rxrpc_kernel_end_call); + +/** + * rxrpc_kernel_intercept_rx_messages - Intercept received RxRPC messages + * @sock: The socket to intercept received messages on + * @interceptor: The function to pass the messages to + * + * Allow a kernel service to intercept messages heading for the Rx queue on an + * RxRPC socket. They get passed to the specified function instead. + * @interceptor should free the socket buffers it is given. @interceptor is + * called with the socket receive queue spinlock held and softirqs disabled - + * this ensures that the messages will be delivered in the right order. + */ +void rxrpc_kernel_intercept_rx_messages(struct socket *sock, + rxrpc_interceptor_t interceptor) +{ + struct rxrpc_sock *rx = rxrpc_sk(sock->sk); + + _enter(""); + rx->interceptor = interceptor; +} + +EXPORT_SYMBOL(rxrpc_kernel_intercept_rx_messages); + /* * connect an RxRPC socket * - this just targets it at a specific destination; no actual connection @@ -294,7 +409,8 @@ static int rxrpc_connect(struct socket *sock, struct sockaddr *addr, return -EBUSY; /* server sockets can't connect as well */ } - trans = rxrpc_name_to_transport(sock, addr, addr_len, flags); + trans = rxrpc_name_to_transport(sock, addr, addr_len, flags, + GFP_KERNEL); if (IS_ERR(trans)) { release_sock(&rx->sk); _leave(" = %ld", PTR_ERR(trans)); @@ -344,7 +460,7 @@ static int rxrpc_sendmsg(struct kiocb *iocb, struct socket *sock, if (m->msg_name) { ret = -EISCONN; trans = rxrpc_name_to_transport(sock, m->msg_name, - m->msg_namelen, 0); + m->msg_namelen, 0, GFP_KERNEL); if (IS_ERR(trans)) { ret = PTR_ERR(trans); trans = NULL; @@ -576,7 +692,7 @@ static int rxrpc_release_sock(struct sock *sk) /* try to flush out this socket */ rxrpc_release_calls_on_socket(rx); - flush_scheduled_work(); + flush_workqueue(rxrpc_workqueue); rxrpc_purge_queue(&sk->sk_receive_queue); if (rx->conn) { @@ -673,15 +789,21 @@ static int __init af_rxrpc_init(void) rxrpc_epoch = htonl(xtime.tv_sec); + ret = -ENOMEM; rxrpc_call_jar = kmem_cache_create( "rxrpc_call_jar", sizeof(struct rxrpc_call), 0, SLAB_HWCACHE_ALIGN, NULL, NULL); if (!rxrpc_call_jar) { printk(KERN_NOTICE "RxRPC: Failed to allocate call jar\n"); - ret = -ENOMEM; goto error_call_jar; } + rxrpc_workqueue = create_workqueue("krxrpcd"); + if (!rxrpc_workqueue) { + printk(KERN_NOTICE "RxRPC: Failed to allocate work queue\n"); + goto error_work_queue; + } + ret = proto_register(&rxrpc_proto, 1); if (ret < 0) { printk(KERN_CRIT "RxRPC: Cannot register protocol\n"); @@ -719,6 +841,8 @@ error_key_type: error_sock: proto_unregister(&rxrpc_proto); error_proto: + destroy_workqueue(rxrpc_workqueue); +error_work_queue: kmem_cache_destroy(rxrpc_call_jar); error_call_jar: return ret; @@ -743,9 +867,10 @@ static void __exit af_rxrpc_exit(void) ASSERTCMP(atomic_read(&rxrpc_n_skbs), ==, 0); _debug("flush scheduled work"); - flush_scheduled_work(); + flush_workqueue(rxrpc_workqueue); proc_net_remove("rxrpc_conns"); proc_net_remove("rxrpc_calls"); + destroy_workqueue(rxrpc_workqueue); kmem_cache_destroy(rxrpc_call_jar); _leave(""); } diff --git a/net/rxrpc/ar-accept.c b/net/rxrpc/ar-accept.c index e7af780cd6f9..92a87fde8bfe 100644 --- a/net/rxrpc/ar-accept.c +++ b/net/rxrpc/ar-accept.c @@ -139,7 +139,7 @@ static int rxrpc_accept_incoming_call(struct rxrpc_local *local, call->conn->state = RXRPC_CONN_SERVER_CHALLENGING; atomic_inc(&call->conn->usage); set_bit(RXRPC_CONN_CHALLENGE, &call->conn->events); - schedule_work(&call->conn->processor); + rxrpc_queue_conn(call->conn); } else { _debug("conn ready"); call->state = RXRPC_CALL_SERVER_ACCEPTING; @@ -183,7 +183,7 @@ invalid_service: if (!test_bit(RXRPC_CALL_RELEASE, &call->flags) && !test_and_set_bit(RXRPC_CALL_RELEASE, &call->events)) { rxrpc_get_call(call); - schedule_work(&call->processor); + rxrpc_queue_call(call); } read_unlock_bh(&call->state_lock); rxrpc_put_call(call); @@ -310,7 +310,8 @@ security_mismatch: * handle acceptance of a call by userspace * - assign the user call ID to the call at the front of the queue */ -int rxrpc_accept_call(struct rxrpc_sock *rx, unsigned long user_call_ID) +struct rxrpc_call *rxrpc_accept_call(struct rxrpc_sock *rx, + unsigned long user_call_ID) { struct rxrpc_call *call; struct rb_node *parent, **pp; @@ -374,12 +375,76 @@ int rxrpc_accept_call(struct rxrpc_sock *rx, unsigned long user_call_ID) BUG(); if (test_and_set_bit(RXRPC_CALL_ACCEPTED, &call->events)) BUG(); - schedule_work(&call->processor); + rxrpc_queue_call(call); + rxrpc_get_call(call); write_unlock_bh(&call->state_lock); write_unlock(&rx->call_lock); - _leave(" = 0"); - return 0; + _leave(" = %p{%d}", call, call->debug_id); + return call; + + /* if the call is already dying or dead, then we leave the socket's ref + * on it to be released by rxrpc_dead_call_expired() as induced by + * rxrpc_release_call() */ +out_release: + _debug("release %p", call); + if (!test_bit(RXRPC_CALL_RELEASED, &call->flags) && + !test_and_set_bit(RXRPC_CALL_RELEASE, &call->events)) + rxrpc_queue_call(call); +out_discard: + write_unlock_bh(&call->state_lock); + _debug("discard %p", call); +out: + write_unlock(&rx->call_lock); + _leave(" = %d", ret); + return ERR_PTR(ret); +} + +/* + * handle rejectance of a call by userspace + * - reject the call at the front of the queue + */ +int rxrpc_reject_call(struct rxrpc_sock *rx) +{ + struct rxrpc_call *call; + int ret; + + _enter(""); + + ASSERT(!irqs_disabled()); + + write_lock(&rx->call_lock); + + ret = -ENODATA; + if (list_empty(&rx->acceptq)) + goto out; + + /* dequeue the first call and check it's still valid */ + call = list_entry(rx->acceptq.next, struct rxrpc_call, accept_link); + list_del_init(&call->accept_link); + sk_acceptq_removed(&rx->sk); + + write_lock_bh(&call->state_lock); + switch (call->state) { + case RXRPC_CALL_SERVER_ACCEPTING: + call->state = RXRPC_CALL_SERVER_BUSY; + if (test_and_set_bit(RXRPC_CALL_REJECT_BUSY, &call->events)) + rxrpc_queue_call(call); + ret = 0; + goto out_release; + case RXRPC_CALL_REMOTELY_ABORTED: + case RXRPC_CALL_LOCALLY_ABORTED: + ret = -ECONNABORTED; + goto out_release; + case RXRPC_CALL_NETWORK_ERROR: + ret = call->conn->error; + goto out_release; + case RXRPC_CALL_DEAD: + ret = -ETIME; + goto out_discard; + default: + BUG(); + } /* if the call is already dying or dead, then we leave the socket's ref * on it to be released by rxrpc_dead_call_expired() as induced by @@ -388,7 +453,7 @@ out_release: _debug("release %p", call); if (!test_bit(RXRPC_CALL_RELEASED, &call->flags) && !test_and_set_bit(RXRPC_CALL_RELEASE, &call->events)) - schedule_work(&call->processor); + rxrpc_queue_call(call); out_discard: write_unlock_bh(&call->state_lock); _debug("discard %p", call); @@ -397,3 +462,43 @@ out: _leave(" = %d", ret); return ret; } + +/** + * rxrpc_kernel_accept_call - Allow a kernel service to accept an incoming call + * @sock: The socket on which the impending call is waiting + * @user_call_ID: The tag to attach to the call + * + * Allow a kernel service to accept an incoming call, assuming the incoming + * call is still valid. + */ +struct rxrpc_call *rxrpc_kernel_accept_call(struct socket *sock, + unsigned long user_call_ID) +{ + struct rxrpc_call *call; + + _enter(",%lx", user_call_ID); + call = rxrpc_accept_call(rxrpc_sk(sock->sk), user_call_ID); + _leave(" = %p", call); + return call; +} + +EXPORT_SYMBOL(rxrpc_kernel_accept_call); + +/** + * rxrpc_kernel_reject_call - Allow a kernel service to reject an incoming call + * @sock: The socket on which the impending call is waiting + * + * Allow a kernel service to reject an incoming call with a BUSY message, + * assuming the incoming call is still valid. + */ +int rxrpc_kernel_reject_call(struct socket *sock) +{ + int ret; + + _enter(""); + ret = rxrpc_reject_call(rxrpc_sk(sock->sk)); + _leave(" = %d", ret); + return ret; +} + +EXPORT_SYMBOL(rxrpc_kernel_reject_call); diff --git a/net/rxrpc/ar-ack.c b/net/rxrpc/ar-ack.c index 8f7764eca96c..fc07a926df56 100644 --- a/net/rxrpc/ar-ack.c +++ b/net/rxrpc/ar-ack.c @@ -113,7 +113,7 @@ cancel_timer: read_lock_bh(&call->state_lock); if (call->state <= RXRPC_CALL_COMPLETE && !test_and_set_bit(RXRPC_CALL_ACK, &call->events)) - schedule_work(&call->processor); + rxrpc_queue_call(call); read_unlock_bh(&call->state_lock); } @@ -1166,7 +1166,7 @@ send_message_2: _debug("sendmsg failed: %d", ret); read_lock_bh(&call->state_lock); if (call->state < RXRPC_CALL_DEAD) - schedule_work(&call->processor); + rxrpc_queue_call(call); read_unlock_bh(&call->state_lock); goto error; } @@ -1210,7 +1210,7 @@ maybe_reschedule: if (call->events || !skb_queue_empty(&call->rx_queue)) { read_lock_bh(&call->state_lock); if (call->state < RXRPC_CALL_DEAD) - schedule_work(&call->processor); + rxrpc_queue_call(call); read_unlock_bh(&call->state_lock); } @@ -1224,7 +1224,7 @@ maybe_reschedule: read_lock_bh(&call->state_lock); if (!test_bit(RXRPC_CALL_RELEASED, &call->flags) && !test_and_set_bit(RXRPC_CALL_RELEASE, &call->events)) - schedule_work(&call->processor); + rxrpc_queue_call(call); read_unlock_bh(&call->state_lock); } @@ -1238,7 +1238,7 @@ error: * work pending bit and the work item being processed again */ if (call->events && !work_pending(&call->processor)) { _debug("jumpstart %x", ntohl(call->conn->cid)); - schedule_work(&call->processor); + rxrpc_queue_call(call); } _leave(""); diff --git a/net/rxrpc/ar-call.c b/net/rxrpc/ar-call.c index ac31cceda2f1..4d92d88ff1fc 100644 --- a/net/rxrpc/ar-call.c +++ b/net/rxrpc/ar-call.c @@ -19,7 +19,7 @@ struct kmem_cache *rxrpc_call_jar; LIST_HEAD(rxrpc_calls); DEFINE_RWLOCK(rxrpc_call_lock); static unsigned rxrpc_call_max_lifetime = 60; -static unsigned rxrpc_dead_call_timeout = 10; +static unsigned rxrpc_dead_call_timeout = 2; static void rxrpc_destroy_call(struct work_struct *work); static void rxrpc_call_life_expired(unsigned long _call); @@ -264,7 +264,7 @@ struct rxrpc_call *rxrpc_incoming_call(struct rxrpc_sock *rx, switch (call->state) { case RXRPC_CALL_LOCALLY_ABORTED: if (!test_and_set_bit(RXRPC_CALL_ABORT, &call->events)) - schedule_work(&call->processor); + rxrpc_queue_call(call); case RXRPC_CALL_REMOTELY_ABORTED: read_unlock(&call->state_lock); goto aborted_call; @@ -398,6 +398,7 @@ found_extant_call: */ void rxrpc_release_call(struct rxrpc_call *call) { + struct rxrpc_connection *conn = call->conn; struct rxrpc_sock *rx = call->socket; _enter("{%d,%d,%d,%d}", @@ -413,8 +414,7 @@ void rxrpc_release_call(struct rxrpc_call *call) /* dissociate from the socket * - the socket's ref on the call is passed to the death timer */ - _debug("RELEASE CALL %p (%d CONN %p)", - call, call->debug_id, call->conn); + _debug("RELEASE CALL %p (%d CONN %p)", call, call->debug_id, conn); write_lock_bh(&rx->call_lock); if (!list_empty(&call->accept_link)) { @@ -430,24 +430,42 @@ void rxrpc_release_call(struct rxrpc_call *call) } write_unlock_bh(&rx->call_lock); - if (call->conn->out_clientflag) - spin_lock(&call->conn->trans->client_lock); - write_lock_bh(&call->conn->lock); - /* free up the channel for reuse */ - if (call->conn->out_clientflag) { - call->conn->avail_calls++; - if (call->conn->avail_calls == RXRPC_MAXCALLS) - list_move_tail(&call->conn->bundle_link, - &call->conn->bundle->unused_conns); - else if (call->conn->avail_calls == 1) - list_move_tail(&call->conn->bundle_link, - &call->conn->bundle->avail_conns); + spin_lock(&conn->trans->client_lock); + write_lock_bh(&conn->lock); + write_lock(&call->state_lock); + + if (conn->channels[call->channel] == call) + conn->channels[call->channel] = NULL; + + if (conn->out_clientflag && conn->bundle) { + conn->avail_calls++; + switch (conn->avail_calls) { + case 1: + list_move_tail(&conn->bundle_link, + &conn->bundle->avail_conns); + case 2 ... RXRPC_MAXCALLS - 1: + ASSERT(conn->channels[0] == NULL || + conn->channels[1] == NULL || + conn->channels[2] == NULL || + conn->channels[3] == NULL); + break; + case RXRPC_MAXCALLS: + list_move_tail(&conn->bundle_link, + &conn->bundle->unused_conns); + ASSERT(conn->channels[0] == NULL && + conn->channels[1] == NULL && + conn->channels[2] == NULL && + conn->channels[3] == NULL); + break; + default: + printk(KERN_ERR "RxRPC: conn->avail_calls=%d\n", + conn->avail_calls); + BUG(); + } } - write_lock(&call->state_lock); - if (call->conn->channels[call->channel] == call) - call->conn->channels[call->channel] = NULL; + spin_unlock(&conn->trans->client_lock); if (call->state < RXRPC_CALL_COMPLETE && call->state != RXRPC_CALL_CLIENT_FINAL_ACK) { @@ -455,13 +473,12 @@ void rxrpc_release_call(struct rxrpc_call *call) call->state = RXRPC_CALL_LOCALLY_ABORTED; call->abort_code = RX_CALL_DEAD; set_bit(RXRPC_CALL_ABORT, &call->events); - schedule_work(&call->processor); + rxrpc_queue_call(call); } write_unlock(&call->state_lock); - write_unlock_bh(&call->conn->lock); - if (call->conn->out_clientflag) - spin_unlock(&call->conn->trans->client_lock); + write_unlock_bh(&conn->lock); + /* clean up the Rx queue */ if (!skb_queue_empty(&call->rx_queue) || !skb_queue_empty(&call->rx_oos_queue)) { struct rxrpc_skb_priv *sp; @@ -538,7 +555,7 @@ static void rxrpc_mark_call_released(struct rxrpc_call *call) if (!test_and_set_bit(RXRPC_CALL_RELEASE, &call->events)) sched = true; if (sched) - schedule_work(&call->processor); + rxrpc_queue_call(call); } write_unlock(&call->state_lock); } @@ -588,7 +605,7 @@ void __rxrpc_put_call(struct rxrpc_call *call) if (atomic_dec_and_test(&call->usage)) { _debug("call %d dead", call->debug_id); ASSERTCMP(call->state, ==, RXRPC_CALL_DEAD); - schedule_work(&call->destroyer); + rxrpc_queue_work(&call->destroyer); } _leave(""); } @@ -613,7 +630,7 @@ static void rxrpc_cleanup_call(struct rxrpc_call *call) ASSERTCMP(call->events, ==, 0); if (work_pending(&call->processor)) { _debug("defer destroy"); - schedule_work(&call->destroyer); + rxrpc_queue_work(&call->destroyer); return; } @@ -742,7 +759,7 @@ static void rxrpc_call_life_expired(unsigned long _call) read_lock_bh(&call->state_lock); if (call->state < RXRPC_CALL_COMPLETE) { set_bit(RXRPC_CALL_LIFE_TIMER, &call->events); - schedule_work(&call->processor); + rxrpc_queue_call(call); } read_unlock_bh(&call->state_lock); } @@ -763,7 +780,7 @@ static void rxrpc_resend_time_expired(unsigned long _call) clear_bit(RXRPC_CALL_RUN_RTIMER, &call->flags); if (call->state < RXRPC_CALL_COMPLETE && !test_and_set_bit(RXRPC_CALL_RESEND_TIMER, &call->events)) - schedule_work(&call->processor); + rxrpc_queue_call(call); read_unlock_bh(&call->state_lock); } @@ -782,6 +799,6 @@ static void rxrpc_ack_time_expired(unsigned long _call) read_lock_bh(&call->state_lock); if (call->state < RXRPC_CALL_COMPLETE && !test_and_set_bit(RXRPC_CALL_ACK, &call->events)) - schedule_work(&call->processor); + rxrpc_queue_call(call); read_unlock_bh(&call->state_lock); } diff --git a/net/rxrpc/ar-connection.c b/net/rxrpc/ar-connection.c index 01eb33c30571..43cb3e051ece 100644 --- a/net/rxrpc/ar-connection.c +++ b/net/rxrpc/ar-connection.c @@ -356,7 +356,7 @@ static int rxrpc_connect_exclusive(struct rxrpc_sock *rx, conn->out_clientflag = RXRPC_CLIENT_INITIATED; conn->cid = 0; conn->state = RXRPC_CONN_CLIENT; - conn->avail_calls = RXRPC_MAXCALLS; + conn->avail_calls = RXRPC_MAXCALLS - 1; conn->security_level = rx->min_sec_level; conn->key = key_get(rx->key); @@ -447,6 +447,11 @@ int rxrpc_connect_call(struct rxrpc_sock *rx, if (--conn->avail_calls == 0) list_move(&conn->bundle_link, &bundle->busy_conns); + ASSERTCMP(conn->avail_calls, <, RXRPC_MAXCALLS); + ASSERT(conn->channels[0] == NULL || + conn->channels[1] == NULL || + conn->channels[2] == NULL || + conn->channels[3] == NULL); atomic_inc(&conn->usage); break; } @@ -456,6 +461,12 @@ int rxrpc_connect_call(struct rxrpc_sock *rx, conn = list_entry(bundle->unused_conns.next, struct rxrpc_connection, bundle_link); + ASSERTCMP(conn->avail_calls, ==, RXRPC_MAXCALLS); + conn->avail_calls = RXRPC_MAXCALLS - 1; + ASSERT(conn->channels[0] == NULL && + conn->channels[1] == NULL && + conn->channels[2] == NULL && + conn->channels[3] == NULL); atomic_inc(&conn->usage); list_move(&conn->bundle_link, &bundle->avail_conns); break; @@ -512,7 +523,7 @@ int rxrpc_connect_call(struct rxrpc_sock *rx, candidate->state = RXRPC_CONN_CLIENT; candidate->avail_calls = RXRPC_MAXCALLS; candidate->security_level = rx->min_sec_level; - candidate->key = key_get(rx->key); + candidate->key = key_get(bundle->key); ret = rxrpc_init_client_conn_security(candidate); if (ret < 0) { @@ -555,6 +566,10 @@ int rxrpc_connect_call(struct rxrpc_sock *rx, for (chan = 0; chan < RXRPC_MAXCALLS; chan++) if (!conn->channels[chan]) goto found_channel; + ASSERT(conn->channels[0] == NULL || + conn->channels[1] == NULL || + conn->channels[2] == NULL || + conn->channels[3] == NULL); BUG(); found_channel: @@ -567,6 +582,7 @@ found_channel: _net("CONNECT client on conn %d chan %d as call %x", conn->debug_id, chan, ntohl(call->call_id)); + ASSERTCMP(conn->avail_calls, <, RXRPC_MAXCALLS); spin_unlock(&trans->client_lock); rxrpc_add_call_ID_to_conn(conn, call); @@ -778,7 +794,7 @@ void rxrpc_put_connection(struct rxrpc_connection *conn) conn->put_time = xtime.tv_sec; if (atomic_dec_and_test(&conn->usage)) { _debug("zombie"); - schedule_delayed_work(&rxrpc_connection_reap, 0); + rxrpc_queue_delayed_work(&rxrpc_connection_reap, 0); } _leave(""); @@ -862,8 +878,8 @@ void rxrpc_connection_reaper(struct work_struct *work) if (earliest != ULONG_MAX) { _debug("reschedule reaper %ld", (long) earliest - now); ASSERTCMP(earliest, >, now); - schedule_delayed_work(&rxrpc_connection_reap, - (earliest - now) * HZ); + rxrpc_queue_delayed_work(&rxrpc_connection_reap, + (earliest - now) * HZ); } /* then destroy all those pulled out */ @@ -889,7 +905,7 @@ void __exit rxrpc_destroy_all_connections(void) rxrpc_connection_timeout = 0; cancel_delayed_work(&rxrpc_connection_reap); - schedule_delayed_work(&rxrpc_connection_reap, 0); + rxrpc_queue_delayed_work(&rxrpc_connection_reap, 0); _leave(""); } diff --git a/net/rxrpc/ar-connevent.c b/net/rxrpc/ar-connevent.c index 4b02815c1ded..1ada43d51165 100644 --- a/net/rxrpc/ar-connevent.c +++ b/net/rxrpc/ar-connevent.c @@ -45,7 +45,7 @@ static void rxrpc_abort_calls(struct rxrpc_connection *conn, int state, set_bit(RXRPC_CALL_CONN_ABORT, &call->events); else set_bit(RXRPC_CALL_RCVD_ABORT, &call->events); - schedule_work(&call->processor); + rxrpc_queue_call(call); } write_unlock(&call->state_lock); } @@ -133,7 +133,7 @@ void rxrpc_call_is_secure(struct rxrpc_call *call) read_lock(&call->state_lock); if (call->state < RXRPC_CALL_COMPLETE && !test_and_set_bit(RXRPC_CALL_SECURED, &call->events)) - schedule_work(&call->processor); + rxrpc_queue_call(call); read_unlock(&call->state_lock); } } @@ -307,6 +307,22 @@ protocol_error: goto out; } +/* + * put a packet up for transport-level abort + */ +void rxrpc_reject_packet(struct rxrpc_local *local, struct sk_buff *skb) +{ + CHECK_SLAB_OKAY(&local->usage); + + if (!atomic_inc_not_zero(&local->usage)) { + printk("resurrected on reject\n"); + BUG(); + } + + skb_queue_tail(&local->reject_queue, skb); + rxrpc_queue_work(&local->rejecter); +} + /* * reject packets through the local endpoint */ diff --git a/net/rxrpc/ar-error.c b/net/rxrpc/ar-error.c index f5539e2f7b58..2c27df1ffa17 100644 --- a/net/rxrpc/ar-error.c +++ b/net/rxrpc/ar-error.c @@ -111,7 +111,7 @@ void rxrpc_UDP_error_report(struct sock *sk) /* pass the transport ref to error_handler to release */ skb_queue_tail(&trans->error_queue, skb); - schedule_work(&trans->error_handler); + rxrpc_queue_work(&trans->error_handler); /* reset and regenerate socket error */ spin_lock_bh(&sk->sk_error_queue.lock); @@ -235,7 +235,7 @@ void rxrpc_UDP_error_handler(struct work_struct *work) call->state < RXRPC_CALL_NETWORK_ERROR) { call->state = RXRPC_CALL_NETWORK_ERROR; set_bit(RXRPC_CALL_RCVD_ERROR, &call->events); - schedule_work(&call->processor); + rxrpc_queue_call(call); } write_unlock(&call->state_lock); list_del_init(&call->error_link); @@ -245,7 +245,7 @@ void rxrpc_UDP_error_handler(struct work_struct *work) } if (!skb_queue_empty(&trans->error_queue)) - schedule_work(&trans->error_handler); + rxrpc_queue_work(&trans->error_handler); rxrpc_free_skb(skb); rxrpc_put_transport(trans); diff --git a/net/rxrpc/ar-input.c b/net/rxrpc/ar-input.c index 323c3454561c..ceb5d619a1d4 100644 --- a/net/rxrpc/ar-input.c +++ b/net/rxrpc/ar-input.c @@ -42,6 +42,7 @@ int rxrpc_queue_rcv_skb(struct rxrpc_call *call, struct sk_buff *skb, bool force, bool terminal) { struct rxrpc_skb_priv *sp; + struct rxrpc_sock *rx = call->socket; struct sock *sk; int skb_len, ret; @@ -64,7 +65,7 @@ int rxrpc_queue_rcv_skb(struct rxrpc_call *call, struct sk_buff *skb, return 0; } - sk = &call->socket->sk; + sk = &rx->sk; if (!force) { /* cast skb->rcvbuf to unsigned... It's pointless, but @@ -89,25 +90,30 @@ int rxrpc_queue_rcv_skb(struct rxrpc_call *call, struct sk_buff *skb, skb->sk = sk; atomic_add(skb->truesize, &sk->sk_rmem_alloc); - /* Cache the SKB length before we tack it onto the receive - * queue. Once it is added it no longer belongs to us and - * may be freed by other threads of control pulling packets - * from the queue. - */ - skb_len = skb->len; - - _net("post skb %p", skb); - __skb_queue_tail(&sk->sk_receive_queue, skb); - spin_unlock_bh(&sk->sk_receive_queue.lock); - - if (!sock_flag(sk, SOCK_DEAD)) - sk->sk_data_ready(sk, skb_len); - if (terminal) { _debug("<<<< TERMINAL MESSAGE >>>>"); set_bit(RXRPC_CALL_TERMINAL_MSG, &call->flags); } + /* allow interception by a kernel service */ + if (rx->interceptor) { + rx->interceptor(sk, call->user_call_ID, skb); + spin_unlock_bh(&sk->sk_receive_queue.lock); + } else { + + /* Cache the SKB length before we tack it onto the + * receive queue. Once it is added it no longer + * belongs to us and may be freed by other threads of + * control pulling packets from the queue */ + skb_len = skb->len; + + _net("post skb %p", skb); + __skb_queue_tail(&sk->sk_receive_queue, skb); + spin_unlock_bh(&sk->sk_receive_queue.lock); + + if (!sock_flag(sk, SOCK_DEAD)) + sk->sk_data_ready(sk, skb_len); + } skb = NULL; } else { spin_unlock_bh(&sk->sk_receive_queue.lock); @@ -232,7 +238,7 @@ static int rxrpc_fast_process_data(struct rxrpc_call *call, read_lock(&call->state_lock); if (call->state < RXRPC_CALL_COMPLETE && !test_and_set_bit(RXRPC_CALL_DRAIN_RX_OOS, &call->events)) - schedule_work(&call->processor); + rxrpc_queue_call(call); read_unlock(&call->state_lock); } @@ -267,7 +273,7 @@ enqueue_packet: atomic_inc(&call->ackr_not_idle); read_lock(&call->state_lock); if (call->state < RXRPC_CALL_DEAD) - schedule_work(&call->processor); + rxrpc_queue_call(call); read_unlock(&call->state_lock); _leave(" = 0 [queued]"); return 0; @@ -360,7 +366,7 @@ void rxrpc_fast_process_packet(struct rxrpc_call *call, struct sk_buff *skb) call->state = RXRPC_CALL_REMOTELY_ABORTED; call->abort_code = abort_code; set_bit(RXRPC_CALL_RCVD_ABORT, &call->events); - schedule_work(&call->processor); + rxrpc_queue_call(call); } goto free_packet_unlock; @@ -375,7 +381,7 @@ void rxrpc_fast_process_packet(struct rxrpc_call *call, struct sk_buff *skb) case RXRPC_CALL_CLIENT_SEND_REQUEST: call->state = RXRPC_CALL_SERVER_BUSY; set_bit(RXRPC_CALL_RCVD_BUSY, &call->events); - schedule_work(&call->processor); + rxrpc_queue_call(call); case RXRPC_CALL_SERVER_BUSY: goto free_packet_unlock; default: @@ -419,7 +425,7 @@ void rxrpc_fast_process_packet(struct rxrpc_call *call, struct sk_buff *skb) read_lock_bh(&call->state_lock); if (call->state < RXRPC_CALL_DEAD) { skb_queue_tail(&call->rx_queue, skb); - schedule_work(&call->processor); + rxrpc_queue_call(call); skb = NULL; } read_unlock_bh(&call->state_lock); @@ -434,7 +440,7 @@ protocol_error_locked: call->state = RXRPC_CALL_LOCALLY_ABORTED; call->abort_code = RX_PROTOCOL_ERROR; set_bit(RXRPC_CALL_ABORT, &call->events); - schedule_work(&call->processor); + rxrpc_queue_call(call); } free_packet_unlock: write_unlock_bh(&call->state_lock); @@ -506,7 +512,7 @@ protocol_error: call->state = RXRPC_CALL_LOCALLY_ABORTED; call->abort_code = RX_PROTOCOL_ERROR; set_bit(RXRPC_CALL_ABORT, &call->events); - schedule_work(&call->processor); + rxrpc_queue_call(call); } write_unlock_bh(&call->state_lock); _leave(""); @@ -542,7 +548,7 @@ static void rxrpc_post_packet_to_call(struct rxrpc_connection *conn, switch (call->state) { case RXRPC_CALL_LOCALLY_ABORTED: if (!test_and_set_bit(RXRPC_CALL_ABORT, &call->events)) - schedule_work(&call->processor); + rxrpc_queue_call(call); case RXRPC_CALL_REMOTELY_ABORTED: case RXRPC_CALL_NETWORK_ERROR: case RXRPC_CALL_DEAD: @@ -591,7 +597,7 @@ dead_call: sp->hdr.seq == __constant_cpu_to_be32(1)) { _debug("incoming call"); skb_queue_tail(&conn->trans->local->accept_queue, skb); - schedule_work(&conn->trans->local->acceptor); + rxrpc_queue_work(&conn->trans->local->acceptor); goto done; } @@ -630,7 +636,7 @@ found_completed_call: _debug("final ack again"); rxrpc_get_call(call); set_bit(RXRPC_CALL_ACK_FINAL, &call->events); - schedule_work(&call->processor); + rxrpc_queue_call(call); free_unlock: read_unlock(&call->state_lock); @@ -651,7 +657,7 @@ static void rxrpc_post_packet_to_conn(struct rxrpc_connection *conn, atomic_inc(&conn->usage); skb_queue_tail(&conn->rx_queue, skb); - schedule_work(&conn->processor); + rxrpc_queue_conn(conn); } /* @@ -767,7 +773,7 @@ cant_route_call: if (sp->hdr.seq == __constant_cpu_to_be32(1)) { _debug("first packet"); skb_queue_tail(&local->accept_queue, skb); - schedule_work(&local->acceptor); + rxrpc_queue_work(&local->acceptor); rxrpc_put_local(local); _leave(" [incoming]"); return; diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h index 7bfbf471c81e..cb1eb492ee48 100644 --- a/net/rxrpc/ar-internal.h +++ b/net/rxrpc/ar-internal.h @@ -19,8 +19,6 @@ #define CHECK_SLAB_OKAY(X) do {} while(0) #endif -extern atomic_t rxrpc_n_skbs; - #define FCRYPT_BSIZE 8 struct rxrpc_crypt { union { @@ -29,8 +27,12 @@ struct rxrpc_crypt { }; } __attribute__((aligned(8))); -extern __be32 rxrpc_epoch; /* local epoch for detecting local-end reset */ -extern atomic_t rxrpc_debug_id; /* current debugging ID */ +#define rxrpc_queue_work(WS) queue_work(rxrpc_workqueue, (WS)) +#define rxrpc_queue_delayed_work(WS,D) \ + queue_delayed_work(rxrpc_workqueue, (WS), (D)) + +#define rxrpc_queue_call(CALL) rxrpc_queue_work(&(CALL)->processor) +#define rxrpc_queue_conn(CONN) rxrpc_queue_work(&(CONN)->processor) /* * sk_state for RxRPC sockets @@ -50,6 +52,7 @@ enum { struct rxrpc_sock { /* WARNING: sk has to be the first member */ struct sock sk; + rxrpc_interceptor_t interceptor; /* kernel service Rx interceptor function */ struct rxrpc_local *local; /* local endpoint */ struct rxrpc_transport *trans; /* transport handler */ struct rxrpc_conn_bundle *bundle; /* virtual connection bundle */ @@ -91,16 +94,6 @@ struct rxrpc_skb_priv { #define rxrpc_skb(__skb) ((struct rxrpc_skb_priv *) &(__skb)->cb) -enum { - RXRPC_SKB_MARK_DATA, /* data message */ - RXRPC_SKB_MARK_FINAL_ACK, /* final ACK received message */ - RXRPC_SKB_MARK_BUSY, /* server busy message */ - RXRPC_SKB_MARK_REMOTE_ABORT, /* remote abort message */ - RXRPC_SKB_MARK_NET_ERROR, /* network error message */ - RXRPC_SKB_MARK_LOCAL_ERROR, /* local error message */ - RXRPC_SKB_MARK_NEW_CALL, /* local error message */ -}; - enum rxrpc_command { RXRPC_CMD_SEND_DATA, /* send data message */ RXRPC_CMD_SEND_ABORT, /* request abort generation */ @@ -439,25 +432,20 @@ static inline void rxrpc_abort_call(struct rxrpc_call *call, u32 abort_code) } /* - * put a packet up for transport-level abort + * af_rxrpc.c */ -static inline -void rxrpc_reject_packet(struct rxrpc_local *local, struct sk_buff *skb) -{ - CHECK_SLAB_OKAY(&local->usage); - if (!atomic_inc_not_zero(&local->usage)) { - printk("resurrected on reject\n"); - BUG(); - } - skb_queue_tail(&local->reject_queue, skb); - schedule_work(&local->rejecter); -} +extern atomic_t rxrpc_n_skbs; +extern __be32 rxrpc_epoch; +extern atomic_t rxrpc_debug_id; +extern struct workqueue_struct *rxrpc_workqueue; /* * ar-accept.c */ extern void rxrpc_accept_incoming_calls(struct work_struct *); -extern int rxrpc_accept_call(struct rxrpc_sock *, unsigned long); +extern struct rxrpc_call *rxrpc_accept_call(struct rxrpc_sock *, + unsigned long); +extern int rxrpc_reject_call(struct rxrpc_sock *); /* * ar-ack.c @@ -514,6 +502,7 @@ rxrpc_incoming_connection(struct rxrpc_transport *, struct rxrpc_header *, * ar-connevent.c */ extern void rxrpc_process_connection(struct work_struct *); +extern void rxrpc_reject_packet(struct rxrpc_local *, struct sk_buff *); extern void rxrpc_reject_packets(struct work_struct *); /* @@ -583,6 +572,7 @@ extern struct file_operations rxrpc_connection_seq_fops; /* * ar-recvmsg.c */ +extern void rxrpc_remove_user_ID(struct rxrpc_sock *, struct rxrpc_call *); extern int rxrpc_recvmsg(struct kiocb *, struct socket *, struct msghdr *, size_t, int); diff --git a/net/rxrpc/ar-local.c b/net/rxrpc/ar-local.c index a20a2c0fe105..fe03f71f17da 100644 --- a/net/rxrpc/ar-local.c +++ b/net/rxrpc/ar-local.c @@ -228,7 +228,7 @@ void rxrpc_put_local(struct rxrpc_local *local) write_lock_bh(&rxrpc_local_lock); if (unlikely(atomic_dec_and_test(&local->usage))) { _debug("destroy local"); - schedule_work(&local->destroyer); + rxrpc_queue_work(&local->destroyer); } write_unlock_bh(&rxrpc_local_lock); _leave(""); diff --git a/net/rxrpc/ar-output.c b/net/rxrpc/ar-output.c index 67aa9510f09b..5cdde4a48ed1 100644 --- a/net/rxrpc/ar-output.c +++ b/net/rxrpc/ar-output.c @@ -113,7 +113,7 @@ static void rxrpc_send_abort(struct rxrpc_call *call, u32 abort_code) clear_bit(RXRPC_CALL_RESEND_TIMER, &call->events); clear_bit(RXRPC_CALL_ACK, &call->events); clear_bit(RXRPC_CALL_RUN_RTIMER, &call->flags); - schedule_work(&call->processor); + rxrpc_queue_call(call); } write_unlock_bh(&call->state_lock); @@ -194,6 +194,77 @@ int rxrpc_client_sendmsg(struct kiocb *iocb, struct rxrpc_sock *rx, return ret; } +/** + * rxrpc_kernel_send_data - Allow a kernel service to send data on a call + * @call: The call to send data through + * @msg: The data to send + * @len: The amount of data to send + * + * Allow a kernel service to send data on a call. The call must be in an state + * appropriate to sending data. No control data should be supplied in @msg, + * nor should an address be supplied. MSG_MORE should be flagged if there's + * more data to come, otherwise this data will end the transmission phase. + */ +int rxrpc_kernel_send_data(struct rxrpc_call *call, struct msghdr *msg, + size_t len) +{ + int ret; + + _enter("{%d,%s},", call->debug_id, rxrpc_call_states[call->state]); + + ASSERTCMP(msg->msg_name, ==, NULL); + ASSERTCMP(msg->msg_control, ==, NULL); + + lock_sock(&call->socket->sk); + + _debug("CALL %d USR %lx ST %d on CONN %p", + call->debug_id, call->user_call_ID, call->state, call->conn); + + if (call->state >= RXRPC_CALL_COMPLETE) { + ret = -ESHUTDOWN; /* it's too late for this call */ + } else if (call->state != RXRPC_CALL_CLIENT_SEND_REQUEST && + call->state != RXRPC_CALL_SERVER_ACK_REQUEST && + call->state != RXRPC_CALL_SERVER_SEND_REPLY) { + ret = -EPROTO; /* request phase complete for this client call */ + } else { + mm_segment_t oldfs = get_fs(); + set_fs(KERNEL_DS); + ret = rxrpc_send_data(NULL, call->socket, call, msg, len); + set_fs(oldfs); + } + + release_sock(&call->socket->sk); + _leave(" = %d", ret); + return ret; +} + +EXPORT_SYMBOL(rxrpc_kernel_send_data); + +/* + * rxrpc_kernel_abort_call - Allow a kernel service to abort a call + * @call: The call to be aborted + * @abort_code: The abort code to stick into the ABORT packet + * + * Allow a kernel service to abort a call, if it's still in an abortable state. + */ +void rxrpc_kernel_abort_call(struct rxrpc_call *call, u32 abort_code) +{ + _enter("{%d},%d", call->debug_id, abort_code); + + lock_sock(&call->socket->sk); + + _debug("CALL %d USR %lx ST %d on CONN %p", + call->debug_id, call->user_call_ID, call->state, call->conn); + + if (call->state < RXRPC_CALL_COMPLETE) + rxrpc_send_abort(call, abort_code); + + release_sock(&call->socket->sk); + _leave(""); +} + +EXPORT_SYMBOL(rxrpc_kernel_abort_call); + /* * send a message through a server socket * - caller holds the socket locked @@ -214,8 +285,13 @@ int rxrpc_server_sendmsg(struct kiocb *iocb, struct rxrpc_sock *rx, if (ret < 0) return ret; - if (cmd == RXRPC_CMD_ACCEPT) - return rxrpc_accept_call(rx, user_call_ID); + if (cmd == RXRPC_CMD_ACCEPT) { + call = rxrpc_accept_call(rx, user_call_ID); + if (IS_ERR(call)) + return PTR_ERR(call); + rxrpc_put_call(call); + return 0; + } call = rxrpc_find_server_call(rx, user_call_ID); if (!call) @@ -363,7 +439,7 @@ static inline void rxrpc_instant_resend(struct rxrpc_call *call) clear_bit(RXRPC_CALL_RUN_RTIMER, &call->flags); if (call->state < RXRPC_CALL_COMPLETE && !test_and_set_bit(RXRPC_CALL_RESEND_TIMER, &call->events)) - schedule_work(&call->processor); + rxrpc_queue_call(call); } read_unlock_bh(&call->state_lock); } diff --git a/net/rxrpc/ar-peer.c b/net/rxrpc/ar-peer.c index 69ac355546ae..d399de4a7fe2 100644 --- a/net/rxrpc/ar-peer.c +++ b/net/rxrpc/ar-peer.c @@ -219,7 +219,7 @@ void rxrpc_put_peer(struct rxrpc_peer *peer) return; } - schedule_work(&peer->destroyer); + rxrpc_queue_work(&peer->destroyer); _leave(""); } diff --git a/net/rxrpc/ar-recvmsg.c b/net/rxrpc/ar-recvmsg.c index e947d5c15900..f19121d4795b 100644 --- a/net/rxrpc/ar-recvmsg.c +++ b/net/rxrpc/ar-recvmsg.c @@ -19,7 +19,7 @@ * removal a call's user ID from the socket tree to make the user ID available * again and so that it won't be seen again in association with that call */ -static void rxrpc_remove_user_ID(struct rxrpc_sock *rx, struct rxrpc_call *call) +void rxrpc_remove_user_ID(struct rxrpc_sock *rx, struct rxrpc_call *call) { _debug("RELEASE CALL %d", call->debug_id); @@ -33,7 +33,7 @@ static void rxrpc_remove_user_ID(struct rxrpc_sock *rx, struct rxrpc_call *call) read_lock_bh(&call->state_lock); if (!test_bit(RXRPC_CALL_RELEASED, &call->flags) && !test_and_set_bit(RXRPC_CALL_RELEASE, &call->events)) - schedule_work(&call->processor); + rxrpc_queue_call(call); read_unlock_bh(&call->state_lock); } @@ -364,3 +364,74 @@ wait_error: return copied; } + +/** + * rxrpc_kernel_data_delivered - Record delivery of data message + * @skb: Message holding data + * + * Record the delivery of a data message. This permits RxRPC to keep its + * tracking correct. The socket buffer will be deleted. + */ +void rxrpc_kernel_data_delivered(struct sk_buff *skb) +{ + struct rxrpc_skb_priv *sp = rxrpc_skb(skb); + struct rxrpc_call *call = sp->call; + + ASSERTCMP(ntohl(sp->hdr.seq), >=, call->rx_data_recv); + ASSERTCMP(ntohl(sp->hdr.seq), <=, call->rx_data_recv + 1); + call->rx_data_recv = ntohl(sp->hdr.seq); + + ASSERTCMP(ntohl(sp->hdr.seq), >, call->rx_data_eaten); + rxrpc_free_skb(skb); +} + +EXPORT_SYMBOL(rxrpc_kernel_data_delivered); + +/** + * rxrpc_kernel_is_data_last - Determine if data message is last one + * @skb: Message holding data + * + * Determine if data message is last one for the parent call. + */ +bool rxrpc_kernel_is_data_last(struct sk_buff *skb) +{ + struct rxrpc_skb_priv *sp = rxrpc_skb(skb); + + ASSERTCMP(skb->mark, ==, RXRPC_SKB_MARK_DATA); + + return sp->hdr.flags & RXRPC_LAST_PACKET; +} + +EXPORT_SYMBOL(rxrpc_kernel_is_data_last); + +/** + * rxrpc_kernel_get_abort_code - Get the abort code from an RxRPC abort message + * @skb: Message indicating an abort + * + * Get the abort code from an RxRPC abort message. + */ +u32 rxrpc_kernel_get_abort_code(struct sk_buff *skb) +{ + struct rxrpc_skb_priv *sp = rxrpc_skb(skb); + + ASSERTCMP(skb->mark, ==, RXRPC_SKB_MARK_REMOTE_ABORT); + + return sp->call->abort_code; +} + +EXPORT_SYMBOL(rxrpc_kernel_get_abort_code); + +/** + * rxrpc_kernel_get_error - Get the error number from an RxRPC error message + * @skb: Message indicating an error + * + * Get the error number from an RxRPC error message. + */ +int rxrpc_kernel_get_error_number(struct sk_buff *skb) +{ + struct rxrpc_skb_priv *sp = rxrpc_skb(skb); + + return sp->error; +} + +EXPORT_SYMBOL(rxrpc_kernel_get_error_number); diff --git a/net/rxrpc/ar-skbuff.c b/net/rxrpc/ar-skbuff.c index d73f6fc76011..de755e04d29c 100644 --- a/net/rxrpc/ar-skbuff.c +++ b/net/rxrpc/ar-skbuff.c @@ -36,7 +36,7 @@ static void rxrpc_request_final_ACK(struct rxrpc_call *call) rxrpc_get_call(call); set_bit(RXRPC_CALL_ACK_FINAL, &call->events); if (try_to_del_timer_sync(&call->ack_timer) >= 0) - schedule_work(&call->processor); + rxrpc_queue_call(call); break; case RXRPC_CALL_SERVER_RECV_REQUEST: @@ -116,3 +116,17 @@ void rxrpc_packet_destructor(struct sk_buff *skb) sock_rfree(skb); _leave(""); } + +/** + * rxrpc_kernel_free_skb - Free an RxRPC socket buffer + * @skb: The socket buffer to be freed + * + * Let RxRPC free its own socket buffer, permitting it to maintain debug + * accounting. + */ +void rxrpc_kernel_free_skb(struct sk_buff *skb) +{ + rxrpc_free_skb(skb); +} + +EXPORT_SYMBOL(rxrpc_kernel_free_skb); diff --git a/net/rxrpc/ar-transport.c b/net/rxrpc/ar-transport.c index 9b4e5cb545d2..d43d78f19302 100644 --- a/net/rxrpc/ar-transport.c +++ b/net/rxrpc/ar-transport.c @@ -189,7 +189,7 @@ void rxrpc_put_transport(struct rxrpc_transport *trans) /* let the reaper determine the timeout to avoid a race with * overextending the timeout if the reaper is running at the * same time */ - schedule_delayed_work(&rxrpc_transport_reap, 0); + rxrpc_queue_delayed_work(&rxrpc_transport_reap, 0); _leave(""); } @@ -243,8 +243,8 @@ static void rxrpc_transport_reaper(struct work_struct *work) if (earliest != ULONG_MAX) { _debug("reschedule reaper %ld", (long) earliest - now); ASSERTCMP(earliest, >, now); - schedule_delayed_work(&rxrpc_transport_reap, - (earliest - now) * HZ); + rxrpc_queue_delayed_work(&rxrpc_transport_reap, + (earliest - now) * HZ); } /* then destroy all those pulled out */ @@ -270,7 +270,7 @@ void __exit rxrpc_destroy_all_transports(void) rxrpc_transport_timeout = 0; cancel_delayed_work(&rxrpc_transport_reap); - schedule_delayed_work(&rxrpc_transport_reap, 0); + rxrpc_queue_delayed_work(&rxrpc_transport_reap, 0); _leave(""); } -- cgit v1.2.3 From 0795e7c031c4bda46fbdde678adf29de19bef7f4 Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 26 Apr 2007 15:57:43 -0700 Subject: [AFS]: Update the AFS fs documentation. Update the AFS fs documentation. Signed-off-by: David Howells Signed-off-by: David S. Miller --- Documentation/filesystems/afs.txt | 214 +++++++++++++++++++++++++++----------- 1 file changed, 154 insertions(+), 60 deletions(-) (limited to 'Documentation') diff --git a/Documentation/filesystems/afs.txt b/Documentation/filesystems/afs.txt index 2f4237dfb8c7..12ad6c7f4e50 100644 --- a/Documentation/filesystems/afs.txt +++ b/Documentation/filesystems/afs.txt @@ -1,31 +1,82 @@ + ==================== kAFS: AFS FILESYSTEM ==================== -ABOUT -===== +Contents: + + - Overview. + - Usage. + - Mountpoints. + - Proc filesystem. + - The cell database. + - Security. + - Examples. + + +======== +OVERVIEW +======== -This filesystem provides a fairly simple AFS filesystem driver. It is under -development and only provides very basic facilities. It does not yet support -the following AFS features: +This filesystem provides a fairly simple secure AFS filesystem driver. It is +under development and does not yet provide the full feature set. The features +it does support include: - (*) Write support. - (*) Communications security. - (*) Local caching. - (*) pioctl() system call. - (*) Automatic mounting of embedded mountpoints. + (*) Security (currently only AFS kaserver and KerberosIV tickets). + (*) File reading. + (*) Automounting. + +It does not yet support the following AFS features: + + (*) Write support. + + (*) Local caching. + + (*) pioctl() system call. + + +=========== +COMPILATION +=========== + +The filesystem should be enabled by turning on the kernel configuration +options: + + CONFIG_AF_RXRPC - The RxRPC protocol transport + CONFIG_RXKAD - The RxRPC Kerberos security handler + CONFIG_AFS - The AFS filesystem + +Additionally, the following can be turned on to aid debugging: + + CONFIG_AF_RXRPC_DEBUG - Permit AF_RXRPC debugging to be enabled + CONFIG_AFS_DEBUG - Permit AFS debugging to be enabled + +They permit the debugging messages to be turned on dynamically by manipulating +the masks in the following files: + + /sys/module/af_rxrpc/parameters/debug + /sys/module/afs/parameters/debug + + +===== USAGE ===== When inserting the driver modules the root cell must be specified along with a list of volume location server IP addresses: - insmod rxrpc.o + insmod af_rxrpc.o + insmod rxkad.o insmod kafs.o rootcell=cambridge.redhat.com:172.16.18.73:172.16.18.91 -The first module is a driver for the RxRPC remote operation protocol, and the -second is the actual filesystem driver for the AFS filesystem. +The first module is the AF_RXRPC network protocol driver. This provides the +RxRPC remote operation protocol and may also be accessed from userspace. See: + + Documentation/networking/rxrpc.txt + +The second module is the kerberos RxRPC security driver, and the third module +is the actual filesystem driver for the AFS filesystem. Once the module has been loaded, more modules can be added by the following procedure: @@ -33,7 +84,7 @@ procedure: echo add grand.central.org 18.7.14.88:128.2.191.224 >/proc/fs/afs/cells Where the parameters to the "add" command are the name of a cell and a list of -volume location servers within that cell. +volume location servers within that cell, with the latter separated by colons. Filesystems can be mounted anywhere by commands similar to the following: @@ -42,11 +93,6 @@ Filesystems can be mounted anywhere by commands similar to the following: mount -t afs "#root.afs." /afs mount -t afs "#root.cell." /afs/cambridge - NB: When using this on Linux 2.4, the mount command has to be different, - since the filesystem doesn't have access to the device name argument: - - mount -t afs none /afs -ovol="#root.afs." - Where the initial character is either a hash or a percent symbol depending on whether you definitely want a R/W volume (hash) or whether you'd prefer a R/O volume, but are willing to use a R/W volume instead (percent). @@ -60,55 +106,66 @@ named volume will be looked up in the cell specified during insmod. Additional cells can be added through /proc (see later section). +=========== MOUNTPOINTS =========== -AFS has a concept of mountpoints. These are specially formatted symbolic links -(of the same form as the "device name" passed to mount). kAFS presents these -to the user as directories that have special properties: +AFS has a concept of mountpoints. In AFS terms, these are specially formatted +symbolic links (of the same form as the "device name" passed to mount). kAFS +presents these to the user as directories that have a follow-link capability +(ie: symbolic link semantics). If anyone attempts to access them, they will +automatically cause the target volume to be mounted (if possible) on that site. - (*) They cannot be listed. Running a program like "ls" on them will incur an - EREMOTE error (Object is remote). +Automatically mounted filesystems will be automatically unmounted approximately +twenty minutes after they were last used. Alternatively they can be unmounted +directly with the umount() system call. - (*) Other objects can't be looked up inside of them. This also incurs an - EREMOTE error. +Manually unmounting an AFS volume will cause any idle submounts upon it to be +culled first. If all are culled, then the requested volume will also be +unmounted, otherwise error EBUSY will be returned. - (*) They can be queried with the readlink() system call, which will return - the name of the mountpoint to which they point. The "readlink" program - will also work. +This can be used by the administrator to attempt to unmount the whole AFS tree +mounted on /afs in one go by doing: - (*) They can be mounted on (which symbolic links can't). + umount /afs +=============== PROC FILESYSTEM =============== -The rxrpc module creates a number of files in various places in the /proc -filesystem: - - (*) Firstly, some information files are made available in a directory called - "/proc/net/rxrpc/". These list the extant transport endpoint, peer, - connection and call records. - - (*) Secondly, some control files are made available in a directory called - "/proc/sys/rxrpc/". Currently, all these files can be used for is to - turn on various levels of tracing. - The AFS modules creates a "/proc/fs/afs/" directory and populates it: - (*) A "cells" file that lists cells currently known to the afs module. + (*) A "cells" file that lists cells currently known to the afs module and + their usage counts: + + [root@andromeda ~]# cat /proc/fs/afs/cells + USE NAME + 3 cambridge.redhat.com (*) A directory per cell that contains files that list volume location servers, volumes, and active servers known within that cell. + [root@andromeda ~]# cat /proc/fs/afs/cambridge.redhat.com/servers + USE ADDR STATE + 4 172.16.18.91 0 + [root@andromeda ~]# cat /proc/fs/afs/cambridge.redhat.com/vlservers + ADDRESS + 172.16.18.91 + [root@andromeda ~]# cat /proc/fs/afs/cambridge.redhat.com/volumes + USE STT VLID[0] VLID[1] VLID[2] NAME + 1 Val 20000000 20000001 20000002 root.afs + +================= THE CELL DATABASE ================= -The filesystem maintains an internal database of all the cells it knows and -the IP addresses of the volume location servers for those cells. The cell to -which the computer belongs is added to the database when insmod is performed -by the "rootcell=" argument. +The filesystem maintains an internal database of all the cells it knows and the +IP addresses of the volume location servers for those cells. The cell to which +the system belongs is added to the database when insmod is performed by the +"rootcell=" argument or, if compiled in, using a "kafs.rootcell=" argument on +the kernel command line. Further cells can be added by commands similar to the following: @@ -118,20 +175,65 @@ Further cells can be added by commands similar to the following: No other cell database operations are available at this time. +======== +SECURITY +======== + +Secure operations are initiated by acquiring a key using the klog program. A +very primitive klog program is available at: + + http://people.redhat.com/~dhowells/rxrpc/klog.c + +This should be compiled by: + + make klog LDLIBS="-lcrypto -lcrypt -lkrb4 -lkeyutils" + +And then run as: + + ./klog + +Assuming it's successful, this adds a key of type RxRPC, named for the service +and cell, eg: "afs@". This can be viewed with the keyctl program or +by cat'ing /proc/keys: + + [root@andromeda ~]# keyctl show + Session Keyring + -3 --alswrv 0 0 keyring: _ses.3268 + 2 --alswrv 0 0 \_ keyring: _uid.0 + 111416553 --als--v 0 0 \_ rxrpc: afs@CAMBRIDGE.REDHAT.COM + +Currently the username, realm, password and proposed ticket lifetime are +compiled in to the program. + +It is not required to acquire a key before using AFS facilities, but if one is +not acquired then all operations will be governed by the anonymous user parts +of the ACLs. + +If a key is acquired, then all AFS operations, including mounts and automounts, +made by a possessor of that key will be secured with that key. + +If a file is opened with a particular key and then the file descriptor is +passed to a process that doesn't have that key (perhaps over an AF_UNIX +socket), then the operations on the file will be made with key that was used to +open the file. + + +======== EXAMPLES ======== -Here's what I use to test this. Some of the names and IP addresses are local -to my internal DNS. My "root.afs" partition has a mount point within it for +Here's what I use to test this. Some of the names and IP addresses are local +to my internal DNS. My "root.afs" partition has a mount point within it for some public volumes volumes. -insmod -S /tmp/rxrpc.o -insmod -S /tmp/kafs.o rootcell=cambridge.redhat.com:172.16.18.73:172.16.18.91 +insmod /tmp/rxrpc.o +insmod /tmp/rxkad.o +insmod /tmp/kafs.o rootcell=cambridge.redhat.com:172.16.18.91 mount -t afs \%root.afs. /afs mount -t afs \%cambridge.redhat.com:root.cell. /afs/cambridge.redhat.com/ -echo add grand.central.org 18.7.14.88:128.2.191.224 > /proc/fs/afs/cells +echo add grand.central.org 18.7.14.88:128.2.191.224 > /proc/fs/afs/cells mount -t afs "#grand.central.org:root.cell." /afs/grand.central.org/ mount -t afs "#grand.central.org:root.archive." /afs/grand.central.org/archive mount -t afs "#grand.central.org:root.contrib." /afs/grand.central.org/contrib @@ -141,15 +243,7 @@ mount -t afs "#grand.central.org:root.service." /afs/grand.central.org/service mount -t afs "#grand.central.org:root.software." /afs/grand.central.org/software mount -t afs "#grand.central.org:root.user." /afs/grand.central.org/user -umount /afs/grand.central.org/user -umount /afs/grand.central.org/software -umount /afs/grand.central.org/service -umount /afs/grand.central.org/project -umount /afs/grand.central.org/doc -umount /afs/grand.central.org/contrib -umount /afs/grand.central.org/archive -umount /afs/grand.central.org -umount /afs/cambridge.redhat.com umount /afs rmmod kafs +rmmod rxkad rmmod rxrpc -- cgit v1.2.3 From 49e9f70f8e7a4df00a5185e7f5c91e3c583847db Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Fri, 27 Apr 2007 01:04:23 -0700 Subject: [IPV4]: Add multipath cached to feature-removal-schedule.txt Signed-off-by: David S. Miller --- Documentation/feature-removal-schedule.txt | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'Documentation') diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt index 976c8a1bd7cd..6da663607f7b 100644 --- a/Documentation/feature-removal-schedule.txt +++ b/Documentation/feature-removal-schedule.txt @@ -292,3 +292,22 @@ Why: the i8xx_tco watchdog driver has been replaced by the iTCO_wdt Who: Wim Van Sebroeck --------------------------- + +What: Multipath cached routing support in ipv4 +When: in 2.6.23 +Why: Code was merged, then submitter immediately disappeared leaving + us with no maintainer and lots of bugs. The code should not have + been merged in the first place, and many aspects of it's + implementation are blocking more critical core networking + development. It's marked EXPERIMENTAL and no distribution + enables it because it cause obscure crashes due to unfixable bugs + (interfaces don't return errors so memory allocation can't be + handled, calling contexts of these interfaces make handling + errors impossible too because they get called after we've + totally commited to creating a route object, for example). + This problem has existed for years and no forward progress + has ever been made, and nobody steps up to try and salvage + this code, so we're going to finally just get rid of it. +Who: David S. Miller + +--------------------------- -- cgit v1.2.3 From 5dd60166c22dbbb7a8684c119d28cb88b6087784 Mon Sep 17 00:00:00 2001 From: Domen Puncer Date: Fri, 2 Mar 2007 21:44:45 +1100 Subject: [POWERPC] Fix typos in booting-without-of.txt Fix typos + some cosmetic changes. Signed-off-by: Domen Puncer Signed-off-by: Paul Mackerras --- Documentation/powerpc/booting-without-of.txt | 80 ++++++++++++++-------------- 1 file changed, 39 insertions(+), 41 deletions(-) (limited to 'Documentation') diff --git a/Documentation/powerpc/booting-without-of.txt b/Documentation/powerpc/booting-without-of.txt index 88cdb592fdf9..033a3f3b3ab7 100644 --- a/Documentation/powerpc/booting-without-of.txt +++ b/Documentation/powerpc/booting-without-of.txt @@ -39,7 +39,7 @@ and property data. The old style variable alignment would make it impossible to do "simple" insertion of properties using - memove (thanks Milton for + memmove (thanks Milton for noticing). Updated kernel patch as well - Correct a few more alignment constraints - Add a chapter about the device-tree @@ -55,7 +55,7 @@ ToDo: - Add some definitions of interrupt tree (simple/complex) - - Add some definitions for pci host bridges + - Add some definitions for PCI host bridges - Add some common address format examples - Add definitions for standard properties and "compatible" names for cells that are not already defined by the existing @@ -114,7 +114,7 @@ it with special cases. forth words isn't required), you can enter the kernel with: r5 : OF callback pointer as defined by IEEE 1275 - bindings to powerpc. Only the 32 bit client interface + bindings to powerpc. Only the 32-bit client interface is currently supported r3, r4 : address & length of an initrd if any or 0 @@ -194,7 +194,7 @@ it with special cases. for this is to keep kernels on embedded systems small and efficient; part of this is due to the fact the code is already that way. In the future, a kernel may support multiple platforms, but only if the - platforms feature the same core architectire. A single kernel build + platforms feature the same core architecture. A single kernel build cannot support both configurations with Book E and configurations with classic Powerpc architectures. @@ -215,7 +215,7 @@ of the boot sequences.... someone speak up if this is wrong! enable another config option to select the specific board supported. -NOTE: If ben doesn't merge the setup files, may need to change this to +NOTE: If Ben doesn't merge the setup files, may need to change this to point to setup_32.c @@ -256,7 +256,7 @@ struct boot_param_header { u32 off_dt_struct; /* offset to structure */ u32 off_dt_strings; /* offset to strings */ u32 off_mem_rsvmap; /* offset to memory reserve map -*/ + */ u32 version; /* format version */ u32 last_comp_version; /* last compatible version */ @@ -276,7 +276,7 @@ struct boot_param_header { #define OF_DT_HEADER 0xd00dfeed /* 4: version, 4: total size */ #define OF_DT_BEGIN_NODE 0x1 /* Start node: full name -*/ + */ #define OF_DT_END_NODE 0x2 /* End node */ #define OF_DT_PROP 0x3 /* Property: name off, size, content */ @@ -313,9 +313,8 @@ struct boot_param_header { - off_mem_rsvmap This is an offset from the beginning of the header to the start - of the reserved memory map. This map is a list of pairs of 64 + of the reserved memory map. This map is a list of pairs of 64- bit integers. Each pair is a physical address and a size. The - list is terminated by an entry of size 0. This map provides the kernel with a list of physical memory areas that are "reserved" and thus not to be used for memory allocations, especially during @@ -328,7 +327,7 @@ struct boot_param_header { contain _at least_ this DT block itself (header,total_size). If you are passing an initrd to the kernel, you should reserve it as well. You do not need to reserve the kernel image itself. The map - should be 64 bit aligned. + should be 64-bit aligned. - version @@ -478,7 +477,7 @@ referencing another node via "phandle" is when laying out the interrupt tree which will be described in a further version of this document. -This "linux, phandle" property is a 32 bit value that uniquely +This "linux, phandle" property is a 32-bit value that uniquely identifies a node. You are free to use whatever values or system of values, internal pointers, or whatever to generate these, the only requirement is that every node for which you provide that property has @@ -488,7 +487,7 @@ Here is an example of a simple device-tree. In this example, an "o" designates a node followed by the node unit name. Properties are presented with their name followed by their content. "content" represents an ASCII string (zero terminated) value, while -represents a 32 bit hexadecimal value. The various nodes in this +represents a 32-bit hexadecimal value. The various nodes in this example will be discussed in a later chapter. At this point, it is only meant to give you a idea of what a device-tree looks like. I have purposefully kept the "name" and "linux,phandle" properties which @@ -560,15 +559,15 @@ Here's the basic structure of a single node: * [align gap to next 4 bytes boundary] * for each property: * token OF_DT_PROP (that is 0x00000003) - * 32 bit value of property value size in bytes (or 0 of no - * value) - * 32 bit value of offset in string block of property name + * 32-bit value of property value size in bytes (or 0 if no + value) + * 32-bit value of offset in string block of property name * property value data if any * [align gap to next 4 bytes boundary] * [child nodes if any] * token OF_DT_END_NODE (that is 0x00000002) -So the node content can be summarised as a start token, a full path, +So the node content can be summarized as a start token, a full path, a list of properties, a list of child nodes, and an end token. Every child node is a full node structure itself as defined above. @@ -600,7 +599,7 @@ provide those properties yourself. ---------------------------------------------- The general rule is documented in the various Open Firmware -documentations. If you chose to describe a bus with the device-tree +documentations. If you choose to describe a bus with the device-tree and there exist an OF bus binding, then you should follow the specification. However, the kernel does not require every single device or bus to be described by the device tree. @@ -613,9 +612,9 @@ those properties defining addresses format for devices directly mapped on the processor bus. Those 2 properties define 'cells' for representing an address and a -size. A "cell" is a 32 bit number. For example, if both contain 2 +size. A "cell" is a 32-bit number. For example, if both contain 2 like the example tree given above, then an address and a size are both -composed of 2 cells, and each is a 64 bit number (cells are +composed of 2 cells, and each is a 64-bit number (cells are concatenated and expected to be in big endian format). Another example is the way Apple firmware defines them, with 2 cells for an address and one cell for a size. Most 32-bit implementations should define @@ -649,7 +648,7 @@ prom_parse.c file of the recent kernels for your bus type. The "reg" property only defines addresses and sizes (if #size-cells is non-0) within a given bus. In order to translate addresses upward -(that is into parent bus addresses, and possibly into cpu physical +(that is into parent bus addresses, and possibly into CPU physical addresses), all busses must contain a "ranges" property. If the "ranges" property is missing at a given level, it's assumed that translation isn't possible. The format of the "ranges" property for a @@ -665,9 +664,9 @@ example, for a PCI host controller, that would be a CPU address. For a PCI<->ISA bridge, that would be a PCI address. It defines the base address in the parent bus where the beginning of that range is mapped. -For a new 64 bit powerpc board, I recommend either the 2/2 format or +For a new 64-bit powerpc board, I recommend either the 2/2 format or Apple's 2/1 format which is slightly more compact since sizes usually -fit in a single 32 bit word. New 32 bit powerpc boards should use a +fit in a single 32-bit word. New 32-bit powerpc boards should use a 1/1 format, unless the processor supports physical addresses greater than 32-bits, in which case a 2/1 format is recommended. @@ -781,7 +780,7 @@ address which can extend beyond that limit. Required properties: - device_type : has to be "cpu" - - reg : This is the physical cpu number, it's a single 32 bit cell + - reg : This is the physical CPU number, it's a single 32-bit cell and is also used as-is as the unit number for constructing the unit name in the full path. For example, with 2 CPUs, you would have the full path: @@ -802,7 +801,7 @@ address which can extend beyond that limit. the kernel timebase/decrementer calibration based on this value. - clock-frequency : a cell indicating the CPU core clock frequency - in Hz. A new property will be defined for 64 bit values, but if + in Hz. A new property will be defined for 64-bit values, but if your frequency is < 4Ghz, one cell is enough. Here as well as for the above, the common code doesn't use that property, but you are welcome to re-use the pSeries or Maple one. A future @@ -924,8 +923,7 @@ address which can extend beyond that limit. The SOC node may contain child nodes for each SOC device that the platform uses. Nodes should not be created for devices which exist on the SOC but are not used by a particular platform. See chapter VI - for more information on how to specify devices that are part of an -SOC. + for more information on how to specify devices that are part of a SOC. Example SOC node for the MPC8540: @@ -988,7 +986,7 @@ The syntax of the dtc tool is [-o output-filename] [-V output_version] input_filename -The "output_version" defines what versio of the "blob" format will be +The "output_version" defines what version of the "blob" format will be generated. Supported versions are 1,2,3 and 16. The default is currently version 3 but that may change in the future to version 16. @@ -1010,12 +1008,12 @@ supported currently at the toplevel. */ property2 = <1234abcd>; /* define a property containing a - * numerical 32 bits value (hexadecimal) + * numerical 32-bit value (hexadecimal) */ property3 = <12345678 12345678 deadbeef>; /* define a property containing 3 - * numerical 32 bits values (cells) in + * numerical 32-bit values (cells) in * hexadecimal */ property4 = [0a 0b 0c 0d de ea ad be ef]; @@ -1084,7 +1082,7 @@ while all this has been defined and implemented. its usage in early_init_devtree(), and the corresponding various early_init_dt_scan_*() callbacks. That code can be re-used in a GPL bootloader, and as the author of that code, I would be happy - to discuss possible free licencing to any vendor who wishes to + to discuss possible free licensing to any vendor who wishes to integrate all or part of this code into a non-GPL bootloader. @@ -1093,7 +1091,7 @@ VI - System-on-a-chip devices and nodes ======================================= Many companies are now starting to develop system-on-a-chip -processors, where the processor core (cpu) and many peripheral devices +processors, where the processor core (CPU) and many peripheral devices exist on a single piece of silicon. For these SOCs, an SOC node should be used that defines child nodes for the devices that make up the SOC. While platforms are not required to use this model in @@ -1300,10 +1298,10 @@ platforms are moved over to use the flattened-device-tree model. and additions : Required properties : - - compatible : Should be "fsl-usb2-mph" for multi port host usb - controllers, or "fsl-usb2-dr" for dual role usb controllers - - phy_type : For multi port host usb controllers, should be one of - "ulpi", or "serial". For dual role usb controllers, should be + - compatible : Should be "fsl-usb2-mph" for multi port host USB + controllers, or "fsl-usb2-dr" for dual role USB controllers + - phy_type : For multi port host USB controllers, should be one of + "ulpi", or "serial". For dual role USB controllers, should be one of "ulpi", "utmi", "utmi_wide", or "serial". - reg : Offset and length of the register set for the device - port0 : boolean; if defined, indicates port0 is connected for @@ -1327,7 +1325,7 @@ platforms are moved over to use the flattened-device-tree model. - interrupt-parent : the phandle for the interrupt controller that services interrupts for this device. - Example multi port host usb controller device node : + Example multi port host USB controller device node : usb@22000 { device_type = "usb"; compatible = "fsl-usb2-mph"; @@ -1341,7 +1339,7 @@ platforms are moved over to use the flattened-device-tree model. port1; }; - Example dual role usb controller device node : + Example dual role USB controller device node : usb@23000 { device_type = "usb"; compatible = "fsl-usb2-dr"; @@ -1375,7 +1373,7 @@ platforms are moved over to use the flattened-device-tree model. - channel-fifo-len : An integer representing the number of descriptor pointers each channel fetch fifo can hold. - exec-units-mask : The bitmask representing what execution units - (EUs) are available. It's a single 32 bit cell. EU information + (EUs) are available. It's a single 32-bit cell. EU information should be encoded following the SEC's Descriptor Header Dword EU_SEL0 field documentation, i.e. as follows: @@ -1391,7 +1389,7 @@ platforms are moved over to use the flattened-device-tree model. bits 8 through 31 are reserved for future SEC EUs. - descriptor-types-mask : The bitmask representing what descriptors - are available. It's a single 32 bit cell. Descriptor type + are available. It's a single 32-bit cell. Descriptor type information should be encoded following the SEC's Descriptor Header Dword DESC_TYPE field documentation, i.e. as follows: @@ -1480,7 +1478,7 @@ platforms are moved over to use the flattened-device-tree model. Required properties: - device_type : should be "spi". - compatible : should be "fsl_spi". - - mode : the spi operation mode, it can be "cpu" or "qe". + - mode : the SPI operation mode, it can be "cpu" or "qe". - reg : Offset and length of the register set for the device - interrupts : where a is the interrupt number and b is a field that represents an encoding of the sense and level @@ -1706,7 +1704,7 @@ platforms are moved over to use the flattened-device-tree model. - partitions : Several pairs of 32-bit values where the first value is partition's offset from the start of the device and the second one is partition size in bytes with LSB used to signify a read only - partition (so, the parition size should always be an even number). + partition (so, the partition size should always be an even number). - partition-names : The list of concatenated zero terminated strings representing the partition names. - probe-type : The type of probe which should be done for the chip -- cgit v1.2.3 From 411ed3225733dbd83b4cbaaa992ef80d6ec1534e Mon Sep 17 00:00:00 2001 From: Michael Holzheu Date: Fri, 27 Apr 2007 16:01:49 +0200 Subject: [S390] zfcpdump support. s390 machines provide hardware support for creating Linux dumps on SCSI disks. For creating a dump a special purpose dump Linux is used. The first 32 MB of memory are saved by the hardware before the dump Linux is booted. Via an SCLP interface, the saved memory can be accessed from Linux. This patch exports memory and registers of the crashed Linux to userspace via a debugfs file. For more information refer to Documentation/s390/zfcpdump.txt, which is included in this patch. Signed-off-by: Michael Holzheu Signed-off-by: Martin Schwidefsky Signed-off-by: Heiko Carstens --- Documentation/s390/zfcpdump.txt | 87 ++++++ arch/s390/Kconfig | 8 + arch/s390/Makefile | 3 + arch/s390/defconfig | 1 + arch/s390/kernel/head64.S | 72 ++++- arch/s390/kernel/ipl.c | 232 +++++++++----- arch/s390/kernel/setup.c | 38 ++- arch/s390/kernel/smp.c | 62 ++++ drivers/s390/char/Makefile | 3 + drivers/s390/char/sclp.h | 2 + drivers/s390/char/sclp_sdias.c | 255 ++++++++++++++++ drivers/s390/char/zcore.c | 651 ++++++++++++++++++++++++++++++++++++++++ include/asm-s390/ipl.h | 32 ++ include/asm-s390/lowcore.h | 46 +++ include/asm-s390/sclp.h | 2 + include/asm-s390/setup.h | 2 + include/asm-s390/smp.h | 1 + 17 files changed, 1411 insertions(+), 86 deletions(-) create mode 100644 Documentation/s390/zfcpdump.txt create mode 100644 drivers/s390/char/sclp_sdias.c create mode 100644 drivers/s390/char/zcore.c (limited to 'Documentation') diff --git a/Documentation/s390/zfcpdump.txt b/Documentation/s390/zfcpdump.txt new file mode 100644 index 000000000000..cf45d27c4608 --- /dev/null +++ b/Documentation/s390/zfcpdump.txt @@ -0,0 +1,87 @@ +s390 SCSI dump tool (zfcpdump) + +System z machines (z900 or higher) provide hardware support for creating system +dumps on SCSI disks. The dump process is initiated by booting a dump tool, which +has to create a dump of the current (probably crashed) Linux image. In order to +not overwrite memory of the crashed Linux with data of the dump tool, the +hardware saves some memory plus the register sets of the boot cpu before the +dump tool is loaded. There exists an SCLP hardware interface to obtain the saved +memory afterwards. Currently 32 MB are saved. + +This zfcpdump implementation consists of a Linux dump kernel together with +a userspace dump tool, which are loaded together into the saved memory region +below 32 MB. zfcpdump is installed on a SCSI disk using zipl (as contained in +the s390-tools package) to make the device bootable. The operator of a Linux +system can then trigger a SCSI dump by booting the SCSI disk, where zfcpdump +resides on. + +The kernel part of zfcpdump is implemented as a debugfs file under "zcore/mem", +which exports memory and registers of the crashed Linux in an s390 +standalone dump format. It can be used in the same way as e.g. /dev/mem. The +dump format defines a 4K header followed by plain uncompressed memory. The +register sets are stored in the prefix pages of the respective cpus. To build a +dump enabled kernel with the zcore driver, the kernel config option +CONFIG_ZFCPDUMP has to be set. When reading from "zcore/mem", the part of +memory, which has been saved by hardware is read by the driver via the SCLP +hardware interface. The second part is just copied from the non overwritten real +memory. + +The userspace application of zfcpdump can reside e.g. in an intitramfs or an +initrd. It reads from zcore/mem and writes the system dump to a file on a +SCSI disk. + +To build a zfcpdump kernel use the following settings in your kernel +configuration: + * CONFIG_ZFCPDUMP=y + * Enable ZFCP driver + * Enable SCSI driver + * Enable ext2 and ext3 filesystems + * Disable as many features as possible to keep the kernel small. + E.g. network support is not needed at all. + +To use the zfcpdump userspace application in an initramfs you have to do the +following: + + * Copy the zfcpdump executable somewhere into your Linux tree. + E.g. to "arch/s390/boot/zfcpdump. If you do not want to include + shared libraries, compile the tool with the "-static" gcc option. + * If you want to include e2fsck, add it to your source tree, too. The zfcpdump + application attempts to start /sbin/e2fsck from the ramdisk. + * Use an initramfs config file like the following: + + dir /dev 755 0 0 + nod /dev/console 644 0 0 c 5 1 + nod /dev/null 644 0 0 c 1 3 + nod /dev/sda1 644 0 0 b 8 1 + nod /dev/sda2 644 0 0 b 8 2 + nod /dev/sda3 644 0 0 b 8 3 + nod /dev/sda4 644 0 0 b 8 4 + nod /dev/sda5 644 0 0 b 8 5 + nod /dev/sda6 644 0 0 b 8 6 + nod /dev/sda7 644 0 0 b 8 7 + nod /dev/sda8 644 0 0 b 8 8 + nod /dev/sda9 644 0 0 b 8 9 + nod /dev/sda10 644 0 0 b 8 10 + nod /dev/sda11 644 0 0 b 8 11 + nod /dev/sda12 644 0 0 b 8 12 + nod /dev/sda13 644 0 0 b 8 13 + nod /dev/sda14 644 0 0 b 8 14 + nod /dev/sda15 644 0 0 b 8 15 + file /init arch/s390/boot/zfcpdump 755 0 0 + file /sbin/e2fsck arch/s390/boot/e2fsck 755 0 0 + dir /proc 755 0 0 + dir /sys 755 0 0 + dir /mnt 755 0 0 + dir /sbin 755 0 0 + + * Issue "make image" to build the zfcpdump image with initramfs. + +In a Linux distribution the zfcpdump enabled kernel image must be copied to +/usr/share/zfcpdump/zfcpdump.image, where the s390 zipl tool is looking for the +dump kernel when preparing a SCSI dump disk. + +If you use a ramdisk copy it to "/usr/share/zfcpdump/zfcpdump.rd". + +For more information on how to use zfcpdump refer to the s390 'Using the Dump +Tools book', which is available from +http://www.ibm.com/developerworks/linux/linux390. diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig index 0f9517bc8e70..e6ec418093e5 100644 --- a/arch/s390/Kconfig +++ b/arch/s390/Kconfig @@ -519,6 +519,14 @@ config KEXEC current kernel, and to start another kernel. It is like a reboot but is independent of hardware/microcode support. +config ZFCPDUMP + tristate "zfcpdump support" + select SMP + default n + help + Select this option if you want to build an zfcpdump enabled kernel. + Refer to "Documentation/s390/zfcpdump.txt" for more details on this. + endmenu source "net/Kconfig" diff --git a/arch/s390/Makefile b/arch/s390/Makefile index ece5adc05606..68441e0e74b6 100644 --- a/arch/s390/Makefile +++ b/arch/s390/Makefile @@ -105,6 +105,9 @@ install: vmlinux image: vmlinux $(Q)$(MAKE) $(build)=$(boot) $(boot)/$@ +zfcpdump: + $(Q)$(MAKE) $(build)=$(boot) $(boot)/$@ + archclean: $(Q)$(MAKE) $(clean)=$(boot) diff --git a/arch/s390/defconfig b/arch/s390/defconfig index 80046d9e2a3b..0e4da8a7d826 100644 --- a/arch/s390/defconfig +++ b/arch/s390/defconfig @@ -167,6 +167,7 @@ CONFIG_NO_IDLE_HZ=y CONFIG_NO_IDLE_HZ_INIT=y CONFIG_S390_HYPFS_FS=y CONFIG_KEXEC=y +# CONFIG_ZFCPDUMP is not set # # Networking diff --git a/arch/s390/kernel/head64.S b/arch/s390/kernel/head64.S index 37010709fe68..a87b1976d409 100644 --- a/arch/s390/kernel/head64.S +++ b/arch/s390/kernel/head64.S @@ -39,7 +39,69 @@ startup_continue: basr %r13,0 # get base .LPG1: sll %r13,1 # remove high order bit srl %r13,1 - lhi %r1,1 # mode 1 = esame + +#ifdef CONFIG_ZFCPDUMP + + # check if we have been ipled using zfcp dump: + + tm 0xb9,0x01 # test if subchannel is enabled + jno .nodump # subchannel disabled + l %r1,0xb8 + la %r5,.Lipl_schib-.LPG1(%r13) + stsch 0(%r5) # get schib of subchannel + jne .nodump # schib not available + tm 5(%r5),0x01 # devno valid? + jno .nodump + tm 4(%r5),0x80 # qdio capable device? + jno .nodump + l %r2,20(%r0) # address of ipl parameter block + lhi %r3,0 + ic %r3,0x148(%r2) # get opt field + chi %r3,0x20 # load with dump? + jne .nodump + + # store all prefix registers in case of load with dump: + + la %r7,0 # base register for 0 page + la %r8,0 # first cpu + l %r11,.Lpref_arr_ptr-.LPG1(%r13) # address of prefix array + ahi %r11,4 # skip boot cpu + lr %r12,%r11 + ahi %r12,(CONFIG_NR_CPUS*4) # end of prefix array + stap .Lcurrent_cpu+2-.LPG1(%r13) # store current cpu addr +1: + cl %r8,.Lcurrent_cpu-.LPG1(%r13) # is ipl cpu ? + je 4f # if yes get next cpu +2: + lr %r9,%r7 + sigp %r9,%r8,0x9 # stop & store status of cpu + brc 8,3f # accepted + brc 4,4f # status stored: next cpu + brc 2,2b # busy: try again + brc 1,4f # not op: next cpu +3: + mvc 0(4,%r11),264(%r7) # copy prefix register to prefix array + ahi %r11,4 # next element in prefix array + clr %r11,%r12 + je 5f # no more space in prefix array +4: + ahi %r8,1 # next cpu (r8 += 1) + cl %r8,.Llast_cpu-.LPG1(%r13) # is last possible cpu ? + jl 1b # jump if not last cpu +5: + lhi %r1,2 # mode 2 = esame (dump) + j 6f + .align 4 +.Lipl_schib: + .rept 13 + .long 0 + .endr +.nodump: + lhi %r1,1 # mode 1 = esame (normal ipl) +6: +#else + lhi %r1,1 # mode 1 = esame (normal ipl) +#endif /* CONFIG_ZFCPDUMP */ mvi __LC_AR_MODE_ID,1 # set esame flag slr %r0,%r0 # set cpuid to zero sigp %r1,%r0,0x12 # switch to esame mode @@ -149,6 +211,14 @@ startup_continue: .L4malign:.quad 0xffffffffffc00000 .Lscan2g:.quad 0x80000000 + 0x20000 - 8 # 2GB + 128K - 8 .Lnop: .long 0x07000700 +#ifdef CONFIG_ZFCPDUMP +.Lcurrent_cpu: + .long 0x0 +.Llast_cpu: + .long 0x0000ffff +.Lpref_arr_ptr: + .long zfcpdump_prefix_array +#endif /* CONFIG_ZFCPDUMP */ .Lparmaddr: .quad PARMAREA .align 64 diff --git a/arch/s390/kernel/ipl.c b/arch/s390/kernel/ipl.c index a83cf1fdd8f5..06833ac2b115 100644 --- a/arch/s390/kernel/ipl.c +++ b/arch/s390/kernel/ipl.c @@ -29,36 +29,21 @@ #define SCCB_LOADPARM (&s390_readinfo_sccb.loadparm) #define SCCB_FLAG (s390_readinfo_sccb.flags) -enum ipl_type { - IPL_TYPE_NONE = 1, - IPL_TYPE_UNKNOWN = 2, - IPL_TYPE_CCW = 4, - IPL_TYPE_FCP = 8, - IPL_TYPE_NSS = 16, -}; - -#define IPL_NONE_STR "none" -#define IPL_UNKNOWN_STR "unknown" -#define IPL_CCW_STR "ccw" -#define IPL_FCP_STR "fcp" -#define IPL_NSS_STR "nss" - -/* - * Must be in data section since the bss section - * is not cleared when these are accessed. - */ -static u16 ipl_devno __attribute__((__section__(".data"))) = 0; -u32 ipl_flags __attribute__((__section__(".data"))) = 0; +#define IPL_UNKNOWN_STR "unknown" +#define IPL_CCW_STR "ccw" +#define IPL_FCP_STR "fcp" +#define IPL_FCP_DUMP_STR "fcp_dump" +#define IPL_NSS_STR "nss" static char *ipl_type_str(enum ipl_type type) { switch (type) { - case IPL_TYPE_NONE: - return IPL_NONE_STR; case IPL_TYPE_CCW: return IPL_CCW_STR; case IPL_TYPE_FCP: return IPL_FCP_STR; + case IPL_TYPE_FCP_DUMP: + return IPL_FCP_DUMP_STR; case IPL_TYPE_NSS: return IPL_NSS_STR; case IPL_TYPE_UNKNOWN: @@ -67,15 +52,55 @@ static char *ipl_type_str(enum ipl_type type) } } +enum dump_type { + DUMP_TYPE_NONE = 1, + DUMP_TYPE_CCW = 2, + DUMP_TYPE_FCP = 4, +}; + +#define DUMP_NONE_STR "none" +#define DUMP_CCW_STR "ccw" +#define DUMP_FCP_STR "fcp" + +static char *dump_type_str(enum dump_type type) +{ + switch (type) { + case DUMP_TYPE_NONE: + return DUMP_NONE_STR; + case DUMP_TYPE_CCW: + return DUMP_CCW_STR; + case DUMP_TYPE_FCP: + return DUMP_FCP_STR; + default: + return NULL; + } +} + +/* + * Must be in data section since the bss section + * is not cleared when these are accessed. + */ +static u16 ipl_devno __attribute__((__section__(".data"))) = 0; +u32 ipl_flags __attribute__((__section__(".data"))) = 0; + enum ipl_method { - IPL_METHOD_NONE, - IPL_METHOD_CCW_CIO, - IPL_METHOD_CCW_DIAG, - IPL_METHOD_CCW_VM, - IPL_METHOD_FCP_RO_DIAG, - IPL_METHOD_FCP_RW_DIAG, - IPL_METHOD_FCP_RO_VM, - IPL_METHOD_NSS, + REIPL_METHOD_CCW_CIO, + REIPL_METHOD_CCW_DIAG, + REIPL_METHOD_CCW_VM, + REIPL_METHOD_FCP_RO_DIAG, + REIPL_METHOD_FCP_RW_DIAG, + REIPL_METHOD_FCP_RO_VM, + REIPL_METHOD_FCP_DUMP, + REIPL_METHOD_NSS, + REIPL_METHOD_DEFAULT, +}; + +enum dump_method { + DUMP_METHOD_NONE, + DUMP_METHOD_CCW_CIO, + DUMP_METHOD_CCW_DIAG, + DUMP_METHOD_CCW_VM, + DUMP_METHOD_FCP_DIAG, }; enum shutdown_action { @@ -107,15 +132,15 @@ static int diag308_set_works = 0; static int reipl_capabilities = IPL_TYPE_UNKNOWN; static enum ipl_type reipl_type = IPL_TYPE_UNKNOWN; -static enum ipl_method reipl_method = IPL_METHOD_NONE; +static enum ipl_method reipl_method = REIPL_METHOD_DEFAULT; static struct ipl_parameter_block *reipl_block_fcp; static struct ipl_parameter_block *reipl_block_ccw; static char reipl_nss_name[NSS_NAME_SIZE + 1]; -static int dump_capabilities = IPL_TYPE_NONE; -static enum ipl_type dump_type = IPL_TYPE_NONE; -static enum ipl_method dump_method = IPL_METHOD_NONE; +static int dump_capabilities = DUMP_TYPE_NONE; +static enum dump_type dump_type = DUMP_TYPE_NONE; +static enum dump_method dump_method = DUMP_METHOD_NONE; static struct ipl_parameter_block *dump_block_fcp; static struct ipl_parameter_block *dump_block_ccw; @@ -134,6 +159,7 @@ int diag308(unsigned long subcode, void *addr) : "d" (subcode) : "cc", "memory"); return _rc; } +EXPORT_SYMBOL_GPL(diag308); /* SYSFS */ @@ -197,7 +223,7 @@ static void make_attrs_ro(struct attribute **attrs) * ipl section */ -static enum ipl_type ipl_get_type(void) +static __init enum ipl_type get_ipl_type(void) { struct ipl_parameter_block *ipl = IPL_PARMBLOCK_START; @@ -211,12 +237,44 @@ static enum ipl_type ipl_get_type(void) return IPL_TYPE_UNKNOWN; if (ipl->hdr.pbt != DIAG308_IPL_TYPE_FCP) return IPL_TYPE_UNKNOWN; + if (ipl->ipl_info.fcp.opt == DIAG308_IPL_OPT_DUMP) + return IPL_TYPE_FCP_DUMP; return IPL_TYPE_FCP; } +void __init setup_ipl_info(void) +{ + ipl_info.type = get_ipl_type(); + switch (ipl_info.type) { + case IPL_TYPE_CCW: + ipl_info.data.ccw.dev_id.devno = ipl_devno; + ipl_info.data.ccw.dev_id.ssid = 0; + break; + case IPL_TYPE_FCP: + case IPL_TYPE_FCP_DUMP: + ipl_info.data.fcp.dev_id.devno = + IPL_PARMBLOCK_START->ipl_info.fcp.devno; + ipl_info.data.fcp.dev_id.ssid = 0; + ipl_info.data.fcp.wwpn = IPL_PARMBLOCK_START->ipl_info.fcp.wwpn; + ipl_info.data.fcp.lun = IPL_PARMBLOCK_START->ipl_info.fcp.lun; + break; + case IPL_TYPE_NSS: + strncpy(ipl_info.data.nss.name, kernel_nss_name, + sizeof(ipl_info.data.nss.name)); + break; + case IPL_TYPE_UNKNOWN: + default: + /* We have no info to copy */ + break; + } +} + +struct ipl_info ipl_info; +EXPORT_SYMBOL_GPL(ipl_info); + static ssize_t ipl_type_show(struct subsystem *subsys, char *page) { - return sprintf(page, "%s\n", ipl_type_str(ipl_get_type())); + return sprintf(page, "%s\n", ipl_type_str(ipl_info.type)); } static struct subsys_attribute sys_ipl_type_attr = __ATTR_RO(ipl_type); @@ -225,10 +283,11 @@ static ssize_t sys_ipl_device_show(struct subsystem *subsys, char *page) { struct ipl_parameter_block *ipl = IPL_PARMBLOCK_START; - switch (ipl_get_type()) { + switch (ipl_info.type) { case IPL_TYPE_CCW: return sprintf(page, "0.0.%04x\n", ipl_devno); case IPL_TYPE_FCP: + case IPL_TYPE_FCP_DUMP: return sprintf(page, "0.0.%04x\n", ipl->ipl_info.fcp.devno); default: return 0; @@ -485,23 +544,29 @@ static int reipl_set_type(enum ipl_type type) switch(type) { case IPL_TYPE_CCW: if (MACHINE_IS_VM) - reipl_method = IPL_METHOD_CCW_VM; + reipl_method = REIPL_METHOD_CCW_VM; else - reipl_method = IPL_METHOD_CCW_CIO; + reipl_method = REIPL_METHOD_CCW_CIO; break; case IPL_TYPE_FCP: if (diag308_set_works) - reipl_method = IPL_METHOD_FCP_RW_DIAG; + reipl_method = REIPL_METHOD_FCP_RW_DIAG; else if (MACHINE_IS_VM) - reipl_method = IPL_METHOD_FCP_RO_VM; + reipl_method = REIPL_METHOD_FCP_RO_VM; else - reipl_method = IPL_METHOD_FCP_RO_DIAG; + reipl_method = REIPL_METHOD_FCP_RO_DIAG; + break; + case IPL_TYPE_FCP_DUMP: + reipl_method = REIPL_METHOD_FCP_DUMP; break; case IPL_TYPE_NSS: - reipl_method = IPL_METHOD_NSS; + reipl_method = REIPL_METHOD_NSS; + break; + case IPL_TYPE_UNKNOWN: + reipl_method = REIPL_METHOD_DEFAULT; break; default: - reipl_method = IPL_METHOD_NONE; + BUG(); } reipl_type = type; return 0; @@ -579,22 +644,22 @@ static struct attribute_group dump_ccw_attr_group = { /* dump type */ -static int dump_set_type(enum ipl_type type) +static int dump_set_type(enum dump_type type) { if (!(dump_capabilities & type)) return -EINVAL; switch(type) { - case IPL_TYPE_CCW: + case DUMP_TYPE_CCW: if (MACHINE_IS_VM) - dump_method = IPL_METHOD_CCW_VM; + dump_method = DUMP_METHOD_CCW_VM; else - dump_method = IPL_METHOD_CCW_CIO; + dump_method = DUMP_METHOD_CCW_CIO; break; - case IPL_TYPE_FCP: - dump_method = IPL_METHOD_FCP_RW_DIAG; + case DUMP_TYPE_FCP: + dump_method = DUMP_METHOD_FCP_DIAG; break; default: - dump_method = IPL_METHOD_NONE; + dump_method = DUMP_METHOD_NONE; } dump_type = type; return 0; @@ -602,7 +667,7 @@ static int dump_set_type(enum ipl_type type) static ssize_t dump_type_show(struct subsystem *subsys, char *page) { - return sprintf(page, "%s\n", ipl_type_str(dump_type)); + return sprintf(page, "%s\n", dump_type_str(dump_type)); } static ssize_t dump_type_store(struct subsystem *subsys, const char *buf, @@ -610,12 +675,12 @@ static ssize_t dump_type_store(struct subsystem *subsys, const char *buf, { int rc = -EINVAL; - if (strncmp(buf, IPL_NONE_STR, strlen(IPL_NONE_STR)) == 0) - rc = dump_set_type(IPL_TYPE_NONE); - else if (strncmp(buf, IPL_CCW_STR, strlen(IPL_CCW_STR)) == 0) - rc = dump_set_type(IPL_TYPE_CCW); - else if (strncmp(buf, IPL_FCP_STR, strlen(IPL_FCP_STR)) == 0) - rc = dump_set_type(IPL_TYPE_FCP); + if (strncmp(buf, DUMP_NONE_STR, strlen(DUMP_NONE_STR)) == 0) + rc = dump_set_type(DUMP_TYPE_NONE); + else if (strncmp(buf, DUMP_CCW_STR, strlen(DUMP_CCW_STR)) == 0) + rc = dump_set_type(DUMP_TYPE_CCW); + else if (strncmp(buf, DUMP_FCP_STR, strlen(DUMP_FCP_STR)) == 0) + rc = dump_set_type(DUMP_TYPE_FCP); return (rc != 0) ? rc : len; } @@ -664,14 +729,14 @@ void do_reipl(void) char loadparm[LOADPARM_LEN + 1]; switch (reipl_method) { - case IPL_METHOD_CCW_CIO: + case REIPL_METHOD_CCW_CIO: devid.devno = reipl_block_ccw->ipl_info.ccw.devno; - if (ipl_get_type() == IPL_TYPE_CCW && devid.devno == ipl_devno) + if (ipl_info.type == IPL_TYPE_CCW && devid.devno == ipl_devno) diag308(DIAG308_IPL, NULL); devid.ssid = 0; reipl_ccw_dev(&devid); break; - case IPL_METHOD_CCW_VM: + case REIPL_METHOD_CCW_VM: reipl_get_ascii_loadparm(loadparm); if (strlen(loadparm) == 0) sprintf(buf, "IPL %X", @@ -681,30 +746,32 @@ void do_reipl(void) reipl_block_ccw->ipl_info.ccw.devno, loadparm); __cpcmd(buf, NULL, 0, NULL); break; - case IPL_METHOD_CCW_DIAG: + case REIPL_METHOD_CCW_DIAG: diag308(DIAG308_SET, reipl_block_ccw); diag308(DIAG308_IPL, NULL); break; - case IPL_METHOD_FCP_RW_DIAG: + case REIPL_METHOD_FCP_RW_DIAG: diag308(DIAG308_SET, reipl_block_fcp); diag308(DIAG308_IPL, NULL); break; - case IPL_METHOD_FCP_RO_DIAG: + case REIPL_METHOD_FCP_RO_DIAG: diag308(DIAG308_IPL, NULL); break; - case IPL_METHOD_FCP_RO_VM: + case REIPL_METHOD_FCP_RO_VM: __cpcmd("IPL", NULL, 0, NULL); break; - case IPL_METHOD_NSS: + case REIPL_METHOD_NSS: sprintf(buf, "IPL %s", reipl_nss_name); __cpcmd(buf, NULL, 0, NULL); break; - case IPL_METHOD_NONE: - default: + case REIPL_METHOD_DEFAULT: if (MACHINE_IS_VM) __cpcmd("IPL", NULL, 0, NULL); diag308(DIAG308_IPL, NULL); break; + case REIPL_METHOD_FCP_DUMP: + default: + break; } signal_processor(smp_processor_id(), sigp_stop_and_store_status); } @@ -715,28 +782,28 @@ static void do_dump(void) static char buf[100]; switch (dump_method) { - case IPL_METHOD_CCW_CIO: + case DUMP_METHOD_CCW_CIO: smp_send_stop(); devid.devno = dump_block_ccw->ipl_info.ccw.devno; devid.ssid = 0; reipl_ccw_dev(&devid); break; - case IPL_METHOD_CCW_VM: + case DUMP_METHOD_CCW_VM: smp_send_stop(); sprintf(buf, "STORE STATUS"); __cpcmd(buf, NULL, 0, NULL); sprintf(buf, "IPL %X", dump_block_ccw->ipl_info.ccw.devno); __cpcmd(buf, NULL, 0, NULL); break; - case IPL_METHOD_CCW_DIAG: + case DUMP_METHOD_CCW_DIAG: diag308(DIAG308_SET, dump_block_ccw); diag308(DIAG308_DUMP, NULL); break; - case IPL_METHOD_FCP_RW_DIAG: + case DUMP_METHOD_FCP_DIAG: diag308(DIAG308_SET, dump_block_fcp); diag308(DIAG308_DUMP, NULL); break; - case IPL_METHOD_NONE: + case DUMP_METHOD_NONE: default: return; } @@ -777,12 +844,13 @@ static int __init ipl_init(void) rc = firmware_register(&ipl_subsys); if (rc) return rc; - switch (ipl_get_type()) { + switch (ipl_info.type) { case IPL_TYPE_CCW: rc = sysfs_create_group(&ipl_subsys.kset.kobj, &ipl_ccw_attr_group); break; case IPL_TYPE_FCP: + case IPL_TYPE_FCP_DUMP: rc = ipl_register_fcp_files(); break; case IPL_TYPE_NSS: @@ -852,7 +920,7 @@ static int __init reipl_ccw_init(void) /* FIXME: check for diag308_set_works when enabling diag ccw reipl */ if (!MACHINE_IS_VM) sys_reipl_ccw_loadparm_attr.attr.mode = S_IRUGO; - if (ipl_get_type() == IPL_TYPE_CCW) + if (ipl_info.type == IPL_TYPE_CCW) reipl_block_ccw->ipl_info.ccw.devno = ipl_devno; reipl_capabilities |= IPL_TYPE_CCW; return 0; @@ -862,9 +930,9 @@ static int __init reipl_fcp_init(void) { int rc; - if ((!diag308_set_works) && (ipl_get_type() != IPL_TYPE_FCP)) + if ((!diag308_set_works) && (ipl_info.type != IPL_TYPE_FCP)) return 0; - if ((!diag308_set_works) && (ipl_get_type() == IPL_TYPE_FCP)) + if ((!diag308_set_works) && (ipl_info.type == IPL_TYPE_FCP)) make_attrs_ro(reipl_fcp_attrs); reipl_block_fcp = (void *) get_zeroed_page(GFP_KERNEL); @@ -875,7 +943,7 @@ static int __init reipl_fcp_init(void) free_page((unsigned long)reipl_block_fcp); return rc; } - if (ipl_get_type() == IPL_TYPE_FCP) { + if (ipl_info.type == IPL_TYPE_FCP) { memcpy(reipl_block_fcp, IPL_PARMBLOCK_START, PAGE_SIZE); } else { reipl_block_fcp->hdr.len = IPL_PARM_BLK_FCP_LEN; @@ -909,7 +977,7 @@ static int __init reipl_init(void) rc = reipl_nss_init(); if (rc) return rc; - rc = reipl_set_type(ipl_get_type()); + rc = reipl_set_type(ipl_info.type); if (rc) return rc; return 0; @@ -931,7 +999,7 @@ static int __init dump_ccw_init(void) dump_block_ccw->hdr.version = IPL_PARM_BLOCK_VERSION; dump_block_ccw->hdr.blk0_len = IPL_PARM_BLK0_CCW_LEN; dump_block_ccw->hdr.pbt = DIAG308_IPL_TYPE_CCW; - dump_capabilities |= IPL_TYPE_CCW; + dump_capabilities |= DUMP_TYPE_CCW; return 0; } @@ -956,7 +1024,7 @@ static int __init dump_fcp_init(void) dump_block_fcp->hdr.blk0_len = IPL_PARM_BLK0_FCP_LEN; dump_block_fcp->hdr.pbt = DIAG308_IPL_TYPE_FCP; dump_block_fcp->ipl_info.fcp.opt = DIAG308_IPL_OPT_DUMP; - dump_capabilities |= IPL_TYPE_FCP; + dump_capabilities |= DUMP_TYPE_FCP; return 0; } @@ -995,7 +1063,7 @@ static int __init dump_init(void) rc = dump_fcp_init(); if (rc) return rc; - dump_set_type(IPL_TYPE_NONE); + dump_set_type(DUMP_TYPE_NONE); return 0; } diff --git a/arch/s390/kernel/setup.c b/arch/s390/kernel/setup.c index 863c8d08c026..3dfd0985861c 100644 --- a/arch/s390/kernel/setup.c +++ b/arch/s390/kernel/setup.c @@ -285,6 +285,26 @@ static void __init conmode_default(void) } } +#if defined(CONFIG_ZFCPDUMP) || defined(CONFIG_ZFCPDUMP_MODULE) +static void __init setup_zfcpdump(unsigned int console_devno) +{ + static char str[64]; + + if (ipl_info.type != IPL_TYPE_FCP_DUMP) + return; + if (console_devno != -1) + sprintf(str, "cio_ignore=all,!0.0.%04x,!0.0.%04x", + ipl_info.data.fcp.dev_id.devno, console_devno); + else + sprintf(str, "cio_ignore=all,!0.0.%04x", + ipl_info.data.fcp.dev_id.devno); + strcat(COMMAND_LINE, str); + console_loglevel = 2; +} +#else +static inline void setup_zfcpdump(unsigned int console_devno) {} +#endif /* CONFIG_ZFCPDUMP */ + #ifdef CONFIG_SMP void (*_machine_restart)(char *command) = machine_restart_smp; void (*_machine_halt)(void) = machine_halt_smp; @@ -586,13 +606,20 @@ setup_resources(void) } } +unsigned long real_memory_size; +EXPORT_SYMBOL_GPL(real_memory_size); + static void __init setup_memory_end(void) { - unsigned long real_size, memory_size; + unsigned long memory_size; unsigned long max_mem, max_phys; int i; - memory_size = real_size = 0; +#if defined(CONFIG_ZFCPDUMP) || defined(CONFIG_ZFCPDUMP_MODULE) + if (ipl_info.type == IPL_TYPE_FCP_DUMP) + memory_end = ZFCPDUMP_HSA_SIZE; +#endif + memory_size = 0; max_phys = VMALLOC_END_INIT - VMALLOC_MIN_SIZE; memory_end &= PAGE_MASK; @@ -601,7 +628,8 @@ static void __init setup_memory_end(void) for (i = 0; i < MEMORY_CHUNKS; i++) { struct mem_chunk *chunk = &memory_chunk[i]; - real_size = max(real_size, chunk->addr + chunk->size); + real_memory_size = max(real_memory_size, + chunk->addr + chunk->size); if (chunk->addr >= max_mem) { memset(chunk, 0, sizeof(*chunk)); continue; @@ -765,6 +793,7 @@ setup_arch(char **cmdline_p) parse_early_param(); + setup_ipl_info(); setup_memory_end(); setup_addressing_mode(); setup_memory(); @@ -782,6 +811,9 @@ setup_arch(char **cmdline_p) /* Setup default console */ conmode_default(); + + /* Setup zfcpdump support */ + setup_zfcpdump(console_devno); } void print_cpu_info(struct cpuinfo_S390 *cpuinfo) diff --git a/arch/s390/kernel/smp.c b/arch/s390/kernel/smp.c index 97764f710bb7..7c0143fdf710 100644 --- a/arch/s390/kernel/smp.c +++ b/arch/s390/kernel/smp.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -40,6 +41,7 @@ #include #include #include +#include extern volatile int __cpu_logical_map[]; @@ -395,6 +397,65 @@ void smp_ctl_clear_bit(int cr, int bit) on_each_cpu(smp_ctl_bit_callback, &parms, 0, 1); } +#if defined(CONFIG_ZFCPDUMP) || defined(CONFIG_ZFCPDUMP_MODULE) + +/* + * zfcpdump_prefix_array holds prefix registers for the following scenario: + * 64 bit zfcpdump kernel and 31 bit kernel which is to be dumped. We have to + * save its prefix registers, since they get lost, when switching from 31 bit + * to 64 bit. + */ +unsigned int zfcpdump_prefix_array[NR_CPUS + 1] \ + __attribute__((__section__(".data"))); + +static void __init smp_get_save_areas(void) +{ + unsigned int cpu, cpu_num, rc; + __u16 boot_cpu_addr; + + if (ipl_info.type != IPL_TYPE_FCP_DUMP) + return; + boot_cpu_addr = S390_lowcore.cpu_data.cpu_addr; + cpu_num = 1; + for (cpu = 0; cpu <= 65535; cpu++) { + if ((u16) cpu == boot_cpu_addr) + continue; + __cpu_logical_map[1] = (__u16) cpu; + if (signal_processor(1, sigp_sense) == sigp_not_operational) + continue; + if (cpu_num >= NR_CPUS) { + printk("WARNING: Registers for cpu %i are not " + "saved, since dump kernel was compiled with" + "NR_CPUS=%i!\n", cpu_num, NR_CPUS); + continue; + } + zfcpdump_save_areas[cpu_num] = + alloc_bootmem(sizeof(union save_area)); + while (1) { + rc = signal_processor(1, sigp_stop_and_store_status); + if (rc != sigp_busy) + break; + cpu_relax(); + } + memcpy(zfcpdump_save_areas[cpu_num], + (void *)(unsigned long) store_prefix() + + SAVE_AREA_BASE, SAVE_AREA_SIZE); +#ifdef __s390x__ + /* copy original prefix register */ + zfcpdump_save_areas[cpu_num]->s390x.pref_reg = + zfcpdump_prefix_array[cpu_num]; +#endif + cpu_num++; + } +} + +union save_area *zfcpdump_save_areas[NR_CPUS + 1]; +EXPORT_SYMBOL_GPL(zfcpdump_save_areas); + +#else +#define smp_get_save_areas() do { } while (0) +#endif + /* * Lets check how many CPUs we have. */ @@ -589,6 +650,7 @@ void __init smp_setup_cpu_possible_map(void) { unsigned int phy_cpus, pos_cpus, cpu; + smp_get_save_areas(); phy_cpus = smp_count_cpus(); pos_cpus = min(phy_cpus + additional_cpus, (unsigned int) NR_CPUS); diff --git a/drivers/s390/char/Makefile b/drivers/s390/char/Makefile index 5fd581c22db3..a0f6db21855a 100644 --- a/drivers/s390/char/Makefile +++ b/drivers/s390/char/Makefile @@ -29,3 +29,6 @@ obj-$(CONFIG_S390_TAPE_34XX) += tape_34xx.o obj-$(CONFIG_S390_TAPE_3590) += tape_3590.o obj-$(CONFIG_MONREADER) += monreader.o obj-$(CONFIG_MONWRITER) += monwriter.o + +zcore_mod-objs := sclp_sdias.o zcore.o +obj-$(CONFIG_ZFCPDUMP) += zcore_mod.o diff --git a/drivers/s390/char/sclp.h b/drivers/s390/char/sclp.h index 7d29ab45a6ed..6402e943436f 100644 --- a/drivers/s390/char/sclp.h +++ b/drivers/s390/char/sclp.h @@ -27,6 +27,7 @@ #define EvTyp_CntlProgIdent 0x0B #define EvTyp_SigQuiesce 0x1D #define EvTyp_VT220Msg 0x1A +#define EvTyp_SDIAS 0x1C #define EvTyp_OpCmd_Mask 0x80000000 #define EvTyp_Msg_Mask 0x40000000 @@ -36,6 +37,7 @@ #define EvTyp_CtlProgIdent_Mask 0x00200000 #define EvTyp_SigQuiesce_Mask 0x00000008 #define EvTyp_VT220Msg_Mask 0x00000040 +#define EvTyp_SDIAS_Mask 0x00000010 #define GnrlMsgFlgs_DOM 0x8000 #define GnrlMsgFlgs_SndAlrm 0x4000 diff --git a/drivers/s390/char/sclp_sdias.c b/drivers/s390/char/sclp_sdias.c new file mode 100644 index 000000000000..06a4d0897232 --- /dev/null +++ b/drivers/s390/char/sclp_sdias.c @@ -0,0 +1,255 @@ +/* + * Sclp "store data in absolut storage" + * + * Copyright IBM Corp. 2003,2007 + * Author(s): Michael Holzheu + */ + +#include +#include +#include +#include +#include "sclp.h" +#include "sclp_rw.h" + +#define TRACE(x...) debug_sprintf_event(sdias_dbf, 1, x) +#define ERROR_MSG(x...) printk ( KERN_ALERT "SDIAS: " x ) + +#define SDIAS_RETRIES 300 +#define SDIAS_SLEEP_TICKS 50 + +#define EQ_STORE_DATA 0x0 +#define EQ_SIZE 0x1 +#define DI_FCP_DUMP 0x0 +#define ASA_SIZE_32 0x0 +#define ASA_SIZE_64 0x1 +#define EVSTATE_ALL_STORED 0x0 +#define EVSTATE_NO_DATA 0x3 +#define EVSTATE_PART_STORED 0x10 + +static struct debug_info *sdias_dbf; + +static struct sclp_register sclp_sdias_register = { + .send_mask = EvTyp_SDIAS_Mask, +}; + +struct sdias_evbuf { + struct evbuf_header hdr; + u8 event_qual; + u8 data_id; + u64 reserved2; + u32 event_id; + u16 reserved3; + u8 asa_size; + u8 event_status; + u32 reserved4; + u32 blk_cnt; + u64 asa; + u32 reserved5; + u32 fbn; + u32 reserved6; + u32 lbn; + u16 reserved7; + u16 dbs; +} __attribute__((packed)); + +struct sdias_sccb { + struct sccb_header hdr; + struct sdias_evbuf evbuf; +} __attribute__((packed)); + +static struct sdias_sccb sccb __attribute__((aligned(4096))); + +static int sclp_req_done; +static wait_queue_head_t sdias_wq; +static DEFINE_MUTEX(sdias_mutex); + +static void sdias_callback(struct sclp_req *request, void *data) +{ + struct sdias_sccb *sccb; + + sccb = (struct sdias_sccb *) request->sccb; + sclp_req_done = 1; + wake_up(&sdias_wq); /* Inform caller, that request is complete */ + TRACE("callback done\n"); +} + +static int sdias_sclp_send(struct sclp_req *req) +{ + int retries; + int rc; + + for (retries = SDIAS_RETRIES; retries; retries--) { + sclp_req_done = 0; + TRACE("add request\n"); + rc = sclp_add_request(req); + if (rc) { + /* not initiated, wait some time and retry */ + set_current_state(TASK_INTERRUPTIBLE); + TRACE("add request failed: rc = %i\n",rc); + schedule_timeout(SDIAS_SLEEP_TICKS); + continue; + } + /* initiated, wait for completion of service call */ + wait_event(sdias_wq, (sclp_req_done == 1)); + if (req->status == SCLP_REQ_FAILED) { + TRACE("sclp request failed\n"); + rc = -EIO; + continue; + } + TRACE("request done\n"); + break; + } + return rc; +} + +/* + * Get number of blocks (4K) available in the HSA + */ +int sclp_sdias_blk_count(void) +{ + struct sclp_req request; + int rc; + + mutex_lock(&sdias_mutex); + + memset(&sccb, 0, sizeof(sccb)); + memset(&request, 0, sizeof(request)); + + sccb.hdr.length = sizeof(sccb); + sccb.evbuf.hdr.length = sizeof(struct sdias_evbuf); + sccb.evbuf.hdr.type = EvTyp_SDIAS; + sccb.evbuf.event_qual = EQ_SIZE; + sccb.evbuf.data_id = DI_FCP_DUMP; + sccb.evbuf.event_id = 4712; + sccb.evbuf.dbs = 1; + + request.sccb = &sccb; + request.command = SCLP_CMDW_WRITE_EVENT_DATA; + request.status = SCLP_REQ_FILLED; + request.callback = sdias_callback; + + rc = sdias_sclp_send(&request); + if (rc) { + ERROR_MSG("sclp_send failed for get_nr_blocks\n"); + goto out; + } + if (sccb.hdr.response_code != 0x0020) { + TRACE("send failed: %x\n", sccb.hdr.response_code); + rc = -EIO; + goto out; + } + + switch (sccb.evbuf.event_status) { + case 0: + rc = sccb.evbuf.blk_cnt; + break; + default: + ERROR_MSG("SCLP error: %x\n", sccb.evbuf.event_status); + rc = -EIO; + goto out; + } + TRACE("%i blocks\n", rc); +out: + mutex_unlock(&sdias_mutex); + return rc; +} + +/* + * Copy from HSA to absolute storage (not reentrant): + * + * @dest : Address of buffer where data should be copied + * @start_blk: Start Block (beginning with 1) + * @nr_blks : Number of 4K blocks to copy + * + * Return Value: 0 : Requested 'number' of blocks of data copied + * <0: ERROR - negative event status + */ +int sclp_sdias_copy(void *dest, int start_blk, int nr_blks) +{ + struct sclp_req request; + int rc; + + mutex_lock(&sdias_mutex); + + memset(&sccb, 0, sizeof(sccb)); + memset(&request, 0, sizeof(request)); + + sccb.hdr.length = sizeof(sccb); + sccb.evbuf.hdr.length = sizeof(struct sdias_evbuf); + sccb.evbuf.hdr.type = EvTyp_SDIAS; + sccb.evbuf.hdr.flags = 0; + sccb.evbuf.event_qual = EQ_STORE_DATA; + sccb.evbuf.data_id = DI_FCP_DUMP; + sccb.evbuf.event_id = 4712; +#ifdef __s390x__ + sccb.evbuf.asa_size = ASA_SIZE_64; +#else + sccb.evbuf.asa_size = ASA_SIZE_32; +#endif + sccb.evbuf.event_status = 0; + sccb.evbuf.blk_cnt = nr_blks; + sccb.evbuf.asa = (unsigned long)dest; + sccb.evbuf.fbn = start_blk; + sccb.evbuf.lbn = 0; + sccb.evbuf.dbs = 1; + + request.sccb = &sccb; + request.command = SCLP_CMDW_WRITE_EVENT_DATA; + request.status = SCLP_REQ_FILLED; + request.callback = sdias_callback; + + rc = sdias_sclp_send(&request); + if (rc) { + ERROR_MSG("sclp_send failed: %x\n", rc); + goto out; + } + if (sccb.hdr.response_code != 0x0020) { + TRACE("copy failed: %x\n", sccb.hdr.response_code); + rc = -EIO; + goto out; + } + + switch (sccb.evbuf.event_status) { + case EVSTATE_ALL_STORED: + TRACE("all stored\n"); + case EVSTATE_PART_STORED: + TRACE("part stored: %i\n", sccb.evbuf.blk_cnt); + break; + case EVSTATE_NO_DATA: + TRACE("no data\n"); + default: + ERROR_MSG("Error from SCLP while copying hsa. " + "Event status = %x\n", + sccb.evbuf.event_status); + rc = -EIO; + } +out: + mutex_unlock(&sdias_mutex); + return rc; +} + +int __init sdias_init(void) +{ + int rc; + + if (ipl_info.type != IPL_TYPE_FCP_DUMP) + return 0; + sdias_dbf = debug_register("dump_sdias", 4, 1, 4 * sizeof(long)); + debug_register_view(sdias_dbf, &debug_sprintf_view); + debug_set_level(sdias_dbf, 6); + rc = sclp_register(&sclp_sdias_register); + if (rc) { + ERROR_MSG("sclp register failed\n"); + return rc; + } + init_waitqueue_head(&sdias_wq); + TRACE("init done\n"); + return 0; +} + +void __exit sdias_exit(void) +{ + debug_unregister(sdias_dbf); + sclp_unregister(&sclp_sdias_register); +} diff --git a/drivers/s390/char/zcore.c b/drivers/s390/char/zcore.c new file mode 100644 index 000000000000..89d439316a53 --- /dev/null +++ b/drivers/s390/char/zcore.c @@ -0,0 +1,651 @@ +/* + * zcore module to export memory content and register sets for creating system + * dumps on SCSI disks (zfcpdump). The "zcore/mem" debugfs file shows the same + * dump format as s390 standalone dumps. + * + * For more information please refer to Documentation/s390/zfcpdump.txt + * + * Copyright IBM Corp. 2003,2007 + * Author(s): Michael Holzheu + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define TRACE(x...) debug_sprintf_event(zcore_dbf, 1, x) +#define MSG(x...) printk( KERN_ALERT x ) +#define ERROR_MSG(x...) printk ( KERN_ALERT "DUMP: " x ) + +#define TO_USER 0 +#define TO_KERNEL 1 + +enum arch_id { + ARCH_S390 = 0, + ARCH_S390X = 1, +}; + +/* dump system info */ + +struct sys_info { + enum arch_id arch; + unsigned long sa_base; + u32 sa_size; + int cpu_map[NR_CPUS]; + unsigned long mem_size; + union save_area lc_mask; +}; + +static struct sys_info sys_info; +static struct debug_info *zcore_dbf; +static int hsa_available; +static struct dentry *zcore_dir; +static struct dentry *zcore_file; + +/* + * Copy memory from HSA to kernel or user memory (not reentrant): + * + * @dest: Kernel or user buffer where memory should be copied to + * @src: Start address within HSA where data should be copied + * @count: Size of buffer, which should be copied + * @mode: Either TO_KERNEL or TO_USER + */ +static int memcpy_hsa(void *dest, unsigned long src, size_t count, int mode) +{ + int offs, blk_num; + static char buf[PAGE_SIZE] __attribute__((__aligned__(PAGE_SIZE))); + + if (count == 0) + return 0; + + /* copy first block */ + offs = 0; + if ((src % PAGE_SIZE) != 0) { + blk_num = src / PAGE_SIZE + 2; + if (sclp_sdias_copy(buf, blk_num, 1)) { + TRACE("sclp_sdias_copy() failed\n"); + return -EIO; + } + offs = min((PAGE_SIZE - (src % PAGE_SIZE)), count); + if (mode == TO_USER) { + if (copy_to_user((__force __user void*) dest, + buf + (src % PAGE_SIZE), offs)) + return -EFAULT; + } else + memcpy(dest, buf + (src % PAGE_SIZE), offs); + } + if (offs == count) + goto out; + + /* copy middle */ + for (; (offs + PAGE_SIZE) <= count; offs += PAGE_SIZE) { + blk_num = (src + offs) / PAGE_SIZE + 2; + if (sclp_sdias_copy(buf, blk_num, 1)) { + TRACE("sclp_sdias_copy() failed\n"); + return -EIO; + } + if (mode == TO_USER) { + if (copy_to_user((__force __user void*) dest + offs, + buf, PAGE_SIZE)) + return -EFAULT; + } else + memcpy(dest + offs, buf, PAGE_SIZE); + } + if (offs == count) + goto out; + + /* copy last block */ + blk_num = (src + offs) / PAGE_SIZE + 2; + if (sclp_sdias_copy(buf, blk_num, 1)) { + TRACE("sclp_sdias_copy() failed\n"); + return -EIO; + } + if (mode == TO_USER) { + if (copy_to_user((__force __user void*) dest + offs, buf, + PAGE_SIZE)) + return -EFAULT; + } else + memcpy(dest + offs, buf, count - offs); +out: + return 0; +} + +static int memcpy_hsa_user(void __user *dest, unsigned long src, size_t count) +{ + return memcpy_hsa((void __force *) dest, src, count, TO_USER); +} + +static int memcpy_hsa_kernel(void *dest, unsigned long src, size_t count) +{ + return memcpy_hsa(dest, src, count, TO_KERNEL); +} + +static int memcpy_real(void *dest, unsigned long src, size_t count) +{ + unsigned long flags; + int rc = -EFAULT; + register unsigned long _dest asm("2") = (unsigned long) dest; + register unsigned long _len1 asm("3") = (unsigned long) count; + register unsigned long _src asm("4") = src; + register unsigned long _len2 asm("5") = (unsigned long) count; + + if (count == 0) + return 0; + flags = __raw_local_irq_stnsm(0xf8); /* switch to real mode */ + asm volatile ( + "0: mvcle %1,%2,0x0\n" + "1: jo 0b\n" + " lhi %0,0x0\n" + "2:\n" + EX_TABLE(1b,2b) + : "+d" (rc) + : "d" (_dest), "d" (_src), "d" (_len1), "d" (_len2) + : "cc", "memory"); + __raw_local_irq_ssm(flags); + + return rc; +} + +static int memcpy_real_user(__user void *dest, unsigned long src, size_t count) +{ + static char buf[4096]; + int offs = 0, size; + + while (offs < count) { + size = min(sizeof(buf), count - offs); + if (memcpy_real(buf, src + offs, size)) + return -EFAULT; + if (copy_to_user(dest + offs, buf, size)) + return -EFAULT; + offs += size; + } + return 0; +} + +#ifdef __s390x__ +/* + * Convert s390x (64 bit) cpu info to s390 (32 bit) cpu info + */ +static void __init s390x_to_s390_regs(union save_area *out, union save_area *in, + int cpu) +{ + int i; + + for (i = 0; i < 16; i++) { + out->s390.gp_regs[i] = in->s390x.gp_regs[i] & 0x00000000ffffffff; + out->s390.acc_regs[i] = in->s390x.acc_regs[i]; + out->s390.ctrl_regs[i] = + in->s390x.ctrl_regs[i] & 0x00000000ffffffff; + } + /* locore for 31 bit has only space for fpregs 0,2,4,6 */ + out->s390.fp_regs[0] = in->s390x.fp_regs[0]; + out->s390.fp_regs[1] = in->s390x.fp_regs[2]; + out->s390.fp_regs[2] = in->s390x.fp_regs[4]; + out->s390.fp_regs[3] = in->s390x.fp_regs[6]; + memcpy(&(out->s390.psw[0]), &(in->s390x.psw[0]), 4); + out->s390.psw[1] |= 0x8; /* set bit 12 */ + memcpy(&(out->s390.psw[4]),&(in->s390x.psw[12]), 4); + out->s390.psw[4] |= 0x80; /* set (31bit) addressing bit */ + out->s390.pref_reg = in->s390x.pref_reg; + out->s390.timer = in->s390x.timer; + out->s390.clk_cmp = in->s390x.clk_cmp; +} + +static void __init s390x_to_s390_save_areas(void) +{ + int i = 1; + static union save_area tmp; + + while (zfcpdump_save_areas[i]) { + s390x_to_s390_regs(&tmp, zfcpdump_save_areas[i], i); + memcpy(zfcpdump_save_areas[i], &tmp, sizeof(tmp)); + i++; + } +} + +#endif /* __s390x__ */ + +static int __init init_cpu_info(enum arch_id arch) +{ + union save_area *sa; + + /* get info for boot cpu from lowcore, stored in the HSA */ + + sa = kmalloc(sizeof(*sa), GFP_KERNEL); + if (!sa) { + ERROR_MSG("kmalloc failed: %s: %i\n",__FUNCTION__, __LINE__); + return -ENOMEM; + } + if (memcpy_hsa_kernel(sa, sys_info.sa_base, sys_info.sa_size) < 0) { + ERROR_MSG("could not copy from HSA\n"); + kfree(sa); + return -EIO; + } + zfcpdump_save_areas[0] = sa; + +#ifdef __s390x__ + /* convert s390x regs to s390, if we are dumping an s390 Linux */ + + if (arch == ARCH_S390) + s390x_to_s390_save_areas(); +#endif + + return 0; +} + +static DEFINE_MUTEX(zcore_mutex); + +#define DUMP_VERSION 0x3 +#define DUMP_MAGIC 0xa8190173618f23fdULL +#define DUMP_ARCH_S390X 2 +#define DUMP_ARCH_S390 1 +#define HEADER_SIZE 4096 + +/* dump header dumped according to s390 crash dump format */ + +struct zcore_header { + u64 magic; + u32 version; + u32 header_size; + u32 dump_level; + u32 page_size; + u64 mem_size; + u64 mem_start; + u64 mem_end; + u32 num_pages; + u32 pad1; + u64 tod; + cpuid_t cpu_id; + u32 arch_id; + u32 build_arch; + char pad2[4016]; +} __attribute__((packed,__aligned__(16))); + +static struct zcore_header zcore_header = { + .magic = DUMP_MAGIC, + .version = DUMP_VERSION, + .header_size = 4096, + .dump_level = 0, + .page_size = PAGE_SIZE, + .mem_start = 0, +#ifdef __s390x__ + .build_arch = DUMP_ARCH_S390X, +#else + .build_arch = DUMP_ARCH_S390, +#endif +}; + +/* + * Copy lowcore info to buffer. Use map in order to copy only register parts. + * + * @buf: User buffer + * @sa: Pointer to save area + * @sa_off: Offset in save area to copy + * @len: Number of bytes to copy + */ +static int copy_lc(void __user *buf, void *sa, int sa_off, int len) +{ + int i; + char *lc_mask = (char*)&sys_info.lc_mask; + + for (i = 0; i < len; i++) { + if (!lc_mask[i + sa_off]) + continue; + if (copy_to_user(buf + i, sa + sa_off + i, 1)) + return -EFAULT; + } + return 0; +} + +/* + * Copy lowcores info to memory, if necessary + * + * @buf: User buffer + * @addr: Start address of buffer in dump memory + * @count: Size of buffer + */ +static int zcore_add_lc(char __user *buf, unsigned long start, size_t count) +{ + unsigned long end; + int i = 0; + + if (count == 0) + return 0; + + end = start + count; + while (zfcpdump_save_areas[i]) { + unsigned long cp_start, cp_end; /* copy range */ + unsigned long sa_start, sa_end; /* save area range */ + unsigned long prefix; + unsigned long sa_off, len, buf_off; + + if (sys_info.arch == ARCH_S390) + prefix = zfcpdump_save_areas[i]->s390.pref_reg; + else + prefix = zfcpdump_save_areas[i]->s390x.pref_reg; + + sa_start = prefix + sys_info.sa_base; + sa_end = prefix + sys_info.sa_base + sys_info.sa_size; + + if ((end < sa_start) || (start > sa_end)) + goto next; + cp_start = max(start, sa_start); + cp_end = min(end, sa_end); + + buf_off = cp_start - start; + sa_off = cp_start - sa_start; + len = cp_end - cp_start; + + TRACE("copy_lc for: %lx\n", start); + if (copy_lc(buf + buf_off, zfcpdump_save_areas[i], sa_off, len)) + return -EFAULT; +next: + i++; + } + return 0; +} + +/* + * Read routine for zcore character device + * First 4K are dump header + * Next 32MB are HSA Memory + * Rest is read from absolute Memory + */ +static ssize_t zcore_read(struct file *file, char __user *buf, size_t count, + loff_t *ppos) +{ + unsigned long mem_start; /* Start address in memory */ + size_t mem_offs; /* Offset in dump memory */ + size_t hdr_count; /* Size of header part of output buffer */ + size_t size; + int rc; + + mutex_lock(&zcore_mutex); + + if (*ppos > (sys_info.mem_size + HEADER_SIZE)) { + rc = -EINVAL; + goto fail; + } + + count = min(count, (size_t) (sys_info.mem_size + HEADER_SIZE - *ppos)); + + /* Copy dump header */ + if (*ppos < HEADER_SIZE) { + size = min(count, (size_t) (HEADER_SIZE - *ppos)); + if (copy_to_user(buf, &zcore_header + *ppos, size)) { + rc = -EFAULT; + goto fail; + } + hdr_count = size; + mem_start = 0; + } else { + hdr_count = 0; + mem_start = *ppos - HEADER_SIZE; + } + + mem_offs = 0; + + /* Copy from HSA data */ + if (*ppos < (ZFCPDUMP_HSA_SIZE + HEADER_SIZE)) { + size = min((count - hdr_count), (size_t) (ZFCPDUMP_HSA_SIZE + - mem_start)); + rc = memcpy_hsa_user(buf + hdr_count, mem_start, size); + if (rc) + goto fail; + + mem_offs += size; + } + + /* Copy from real mem */ + size = count - mem_offs - hdr_count; + rc = memcpy_real_user(buf + hdr_count + mem_offs, mem_start + mem_offs, + size); + if (rc) + goto fail; + + /* + * Since s390 dump analysis tools like lcrash or crash + * expect register sets in the prefix pages of the cpus, + * we copy them into the read buffer, if necessary. + * buf + hdr_count: Start of memory part of output buffer + * mem_start: Start memory address to copy from + * count - hdr_count: Size of memory area to copy + */ + if (zcore_add_lc(buf + hdr_count, mem_start, count - hdr_count)) { + rc = -EFAULT; + goto fail; + } + *ppos += count; +fail: + mutex_unlock(&zcore_mutex); + return (rc < 0) ? rc : count; +} + +static int zcore_open(struct inode *inode, struct file *filp) +{ + if (!hsa_available) + return -ENODATA; + else + return capable(CAP_SYS_RAWIO) ? 0 : -EPERM; +} + +static int zcore_release(struct inode *inode, struct file *filep) +{ + diag308(DIAG308_REL_HSA, NULL); + hsa_available = 0; + return 0; +} + +static loff_t zcore_lseek(struct file *file, loff_t offset, int orig) +{ + loff_t rc; + + mutex_lock(&zcore_mutex); + switch (orig) { + case 0: + file->f_pos = offset; + rc = file->f_pos; + break; + case 1: + file->f_pos += offset; + rc = file->f_pos; + break; + default: + rc = -EINVAL; + } + mutex_unlock(&zcore_mutex); + return rc; +} + +static struct file_operations zcore_fops = { + .owner = THIS_MODULE, + .llseek = zcore_lseek, + .read = zcore_read, + .open = zcore_open, + .release = zcore_release, +}; + + +static void __init set_s390_lc_mask(union save_area *map) +{ + memset(&map->s390.ext_save, 0xff, sizeof(map->s390.ext_save)); + memset(&map->s390.timer, 0xff, sizeof(map->s390.timer)); + memset(&map->s390.clk_cmp, 0xff, sizeof(map->s390.clk_cmp)); + memset(&map->s390.psw, 0xff, sizeof(map->s390.psw)); + memset(&map->s390.pref_reg, 0xff, sizeof(map->s390.pref_reg)); + memset(&map->s390.acc_regs, 0xff, sizeof(map->s390.acc_regs)); + memset(&map->s390.fp_regs, 0xff, sizeof(map->s390.fp_regs)); + memset(&map->s390.gp_regs, 0xff, sizeof(map->s390.gp_regs)); + memset(&map->s390.ctrl_regs, 0xff, sizeof(map->s390.ctrl_regs)); +} + +static void __init set_s390x_lc_mask(union save_area *map) +{ + memset(&map->s390x.fp_regs, 0xff, sizeof(map->s390x.fp_regs)); + memset(&map->s390x.gp_regs, 0xff, sizeof(map->s390x.gp_regs)); + memset(&map->s390x.psw, 0xff, sizeof(map->s390x.psw)); + memset(&map->s390x.pref_reg, 0xff, sizeof(map->s390x.pref_reg)); + memset(&map->s390x.fp_ctrl_reg, 0xff, sizeof(map->s390x.fp_ctrl_reg)); + memset(&map->s390x.tod_reg, 0xff, sizeof(map->s390x.tod_reg)); + memset(&map->s390x.timer, 0xff, sizeof(map->s390x.timer)); + memset(&map->s390x.clk_cmp, 0xff, sizeof(map->s390x.clk_cmp)); + memset(&map->s390x.acc_regs, 0xff, sizeof(map->s390x.acc_regs)); + memset(&map->s390x.ctrl_regs, 0xff, sizeof(map->s390x.ctrl_regs)); +} + +/* + * Initialize dump globals for a given architecture + */ +static int __init sys_info_init(enum arch_id arch) +{ + switch (arch) { + case ARCH_S390X: + MSG("DETECTED 'S390X (64 bit) OS'\n"); + sys_info.sa_base = SAVE_AREA_BASE_S390X; + sys_info.sa_size = sizeof(struct save_area_s390x); + set_s390x_lc_mask(&sys_info.lc_mask); + break; + case ARCH_S390: + MSG("DETECTED 'S390 (32 bit) OS'\n"); + sys_info.sa_base = SAVE_AREA_BASE_S390; + sys_info.sa_size = sizeof(struct save_area_s390); + set_s390_lc_mask(&sys_info.lc_mask); + break; + default: + ERROR_MSG("unknown architecture 0x%x.\n",arch); + return -EINVAL; + } + sys_info.arch = arch; + if (init_cpu_info(arch)) { + ERROR_MSG("get cpu info failed\n"); + return -ENOMEM; + } + sys_info.mem_size = real_memory_size; + + return 0; +} + +static int __init check_sdias(void) +{ + int rc, act_hsa_size; + + rc = sclp_sdias_blk_count(); + if (rc < 0) { + ERROR_MSG("Could not determine HSA size\n"); + return rc; + } + act_hsa_size = (rc - 1) * PAGE_SIZE; + if (act_hsa_size < ZFCPDUMP_HSA_SIZE) { + ERROR_MSG("HSA size too small: %i\n", act_hsa_size); + return -EINVAL; + } + return 0; +} + +static void __init zcore_header_init(int arch, struct zcore_header *hdr) +{ + if (arch == ARCH_S390X) + hdr->arch_id = DUMP_ARCH_S390X; + else + hdr->arch_id = DUMP_ARCH_S390; + hdr->mem_size = sys_info.mem_size; + hdr->mem_end = sys_info.mem_size; + hdr->num_pages = sys_info.mem_size / PAGE_SIZE; + hdr->tod = get_clock(); + get_cpu_id(&hdr->cpu_id); +} + +extern int sdias_init(void); + +static int __init zcore_init(void) +{ + unsigned char arch; + int rc; + + if (ipl_info.type != IPL_TYPE_FCP_DUMP) + return -ENODATA; + + zcore_dbf = debug_register("zcore", 4, 1, 4 * sizeof(long)); + debug_register_view(zcore_dbf, &debug_sprintf_view); + debug_set_level(zcore_dbf, 6); + + TRACE("devno: %x\n", ipl_info.data.fcp.dev_id.devno); + TRACE("wwpn: %llx\n", (unsigned long long) ipl_info.data.fcp.wwpn); + TRACE("lun: %llx\n", (unsigned long long) ipl_info.data.fcp.lun); + + rc = sdias_init(); + if (rc) + goto fail; + + rc = check_sdias(); + if (rc) { + ERROR_MSG("Dump initialization failed\n"); + goto fail; + } + + rc = memcpy_hsa_kernel(&arch, __LC_AR_MODE_ID, 1); + if (rc) { + ERROR_MSG("sdial memcpy for arch id failed\n"); + goto fail; + } + +#ifndef __s390x__ + if (arch == ARCH_S390X) { + ERROR_MSG("32 bit dumper can't dump 64 bit system!\n"); + rc = -EINVAL; + goto fail; + } +#endif + + rc = sys_info_init(arch); + if (rc) { + ERROR_MSG("arch init failed\n"); + goto fail; + } + + zcore_header_init(arch, &zcore_header); + + zcore_dir = debugfs_create_dir("zcore" , NULL); + if (!zcore_dir) { + rc = -ENOMEM; + goto fail; + } + zcore_file = debugfs_create_file("mem", S_IRUSR, zcore_dir, NULL, + &zcore_fops); + if (!zcore_file) { + debugfs_remove(zcore_dir); + rc = -ENOMEM; + goto fail; + } + hsa_available = 1; + return 0; + +fail: + diag308(DIAG308_REL_HSA, NULL); + return rc; +} + +extern void sdias_exit(void); + +static void __exit zcore_exit(void) +{ + debug_unregister(zcore_dbf); + sdias_exit(); + diag308(DIAG308_REL_HSA, NULL); +} + +MODULE_AUTHOR("Copyright IBM Corp. 2003,2007"); +MODULE_DESCRIPTION("zcore module for zfcpdump support"); +MODULE_LICENSE("GPL"); + +subsys_initcall(zcore_init); +module_exit(zcore_exit); diff --git a/include/asm-s390/ipl.h b/include/asm-s390/ipl.h index 15bb0b529551..bdcd448d43fb 100644 --- a/include/asm-s390/ipl.h +++ b/include/asm-s390/ipl.h @@ -8,6 +8,8 @@ #define _ASM_S390_IPL_H #include +#include +#include #define IPL_PARMBLOCK_ORIGIN 0x2000 @@ -79,6 +81,7 @@ struct ipl_parameter_block { extern u32 ipl_flags; extern u32 dump_prefix_page; + extern void do_reipl(void); extern void ipl_save_parameters(void); @@ -88,6 +91,35 @@ enum { IPL_NSS_VALID = 4, }; +enum ipl_type { + IPL_TYPE_UNKNOWN = 1, + IPL_TYPE_CCW = 2, + IPL_TYPE_FCP = 4, + IPL_TYPE_FCP_DUMP = 8, + IPL_TYPE_NSS = 16, +}; + +struct ipl_info +{ + enum ipl_type type; + union { + struct { + struct ccw_dev_id dev_id; + } ccw; + struct { + struct ccw_dev_id dev_id; + u64 wwpn; + u64 lun; + } fcp; + struct { + char name[NSS_NAME_SIZE + 1]; + } nss; + } data; +}; + +extern struct ipl_info ipl_info; +extern void setup_ipl_info(void); + /* * DIAG 308 support */ diff --git a/include/asm-s390/lowcore.h b/include/asm-s390/lowcore.h index 4a31d0a7ee83..ffc9788a21a7 100644 --- a/include/asm-s390/lowcore.h +++ b/include/asm-s390/lowcore.h @@ -147,6 +147,52 @@ void pgm_check_handler(void); void mcck_int_handler(void); void io_int_handler(void); +struct save_area_s390 { + u32 ext_save; + u64 timer; + u64 clk_cmp; + u8 pad1[24]; + u8 psw[8]; + u32 pref_reg; + u8 pad2[20]; + u32 acc_regs[16]; + u64 fp_regs[4]; + u32 gp_regs[16]; + u32 ctrl_regs[16]; +} __attribute__((packed)); + +struct save_area_s390x { + u64 fp_regs[16]; + u64 gp_regs[16]; + u8 psw[16]; + u8 pad1[8]; + u32 pref_reg; + u32 fp_ctrl_reg; + u8 pad2[4]; + u32 tod_reg; + u64 timer; + u64 clk_cmp; + u8 pad3[8]; + u32 acc_regs[16]; + u64 ctrl_regs[16]; +} __attribute__((packed)); + +union save_area { + struct save_area_s390 s390; + struct save_area_s390x s390x; +}; + +#define SAVE_AREA_BASE_S390 0xd4 +#define SAVE_AREA_BASE_S390X 0x1200 + +#ifndef __s390x__ +#define SAVE_AREA_SIZE sizeof(struct save_area_s390) +#define SAVE_AREA_BASE SAVE_AREA_BASE_S390 +#else +#define SAVE_AREA_SIZE sizeof(struct save_area_s390x) +#define SAVE_AREA_BASE SAVE_AREA_BASE_S390X +#endif + struct _lowcore { #ifndef __s390x__ diff --git a/include/asm-s390/sclp.h b/include/asm-s390/sclp.h index 3996daaa8f54..21ed64773210 100644 --- a/include/asm-s390/sclp.h +++ b/include/asm-s390/sclp.h @@ -44,6 +44,8 @@ struct sclp_chp_info { extern struct sclp_readinfo_sccb s390_readinfo_sccb; extern void sclp_readinfo_early(void); +extern int sclp_sdias_blk_count(void); +extern int sclp_sdias_copy(void *dest, int blk_num, int nr_blks); extern int sclp_chp_configure(struct chp_id chpid); extern int sclp_chp_deconfigure(struct chp_id chpid); extern int sclp_chp_read_info(struct sclp_chp_info *info); diff --git a/include/asm-s390/setup.h b/include/asm-s390/setup.h index 44c7aee2bd34..a76a6b8fd887 100644 --- a/include/asm-s390/setup.h +++ b/include/asm-s390/setup.h @@ -40,6 +40,7 @@ struct mem_chunk { }; extern struct mem_chunk memory_chunk[]; +extern unsigned long real_memory_size; #ifdef CONFIG_S390_SWITCH_AMODE extern unsigned int switch_amode; @@ -77,6 +78,7 @@ extern unsigned long machine_flags; #endif /* __s390x__ */ #define MACHINE_HAS_SCLP (!MACHINE_IS_P390) +#define ZFCPDUMP_HSA_SIZE (32UL<<20) /* * Console mode. Override with conmode= diff --git a/include/asm-s390/smp.h b/include/asm-s390/smp.h index b957e4cda464..676e94ee15f0 100644 --- a/include/asm-s390/smp.h +++ b/include/asm-s390/smp.h @@ -119,4 +119,5 @@ static inline void smp_send_stop(void) #define smp_setup_cpu_possible_map() do { } while (0) #endif +extern union save_area *zfcpdump_save_areas[NR_CPUS + 1]; #endif -- cgit v1.2.3 From 131a395c18af43d824841642038e5cc0d48f0bd2 Mon Sep 17 00:00:00 2001 From: Jan Glauber Date: Fri, 27 Apr 2007 16:01:54 +0200 Subject: [S390] crypto: cleanup. Cleanup code and remove obsolete documentation. Signed-off-by: Jan Glauber Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky --- Documentation/s390/crypto/crypto-API.txt | 83 -------------------- arch/s390/crypto/sha1_s390.c | 129 ++++++++++++++++--------------- arch/s390/crypto/sha256_s390.c | 38 ++++----- 3 files changed, 81 insertions(+), 169 deletions(-) delete mode 100644 Documentation/s390/crypto/crypto-API.txt (limited to 'Documentation') diff --git a/Documentation/s390/crypto/crypto-API.txt b/Documentation/s390/crypto/crypto-API.txt deleted file mode 100644 index 71ae6ca9f2c2..000000000000 --- a/Documentation/s390/crypto/crypto-API.txt +++ /dev/null @@ -1,83 +0,0 @@ -crypto-API support for z990 Message Security Assist (MSA) instructions -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -AUTHOR: Thomas Spatzier (tspat@de.ibm.com) - - -1. Introduction crypto-API -~~~~~~~~~~~~~~~~~~~~~~~~~~ -See Documentation/crypto/api-intro.txt for an introduction/description of the -kernel crypto API. -According to api-intro.txt support for z990 crypto instructions has been added -in the algorithm api layer of the crypto API. Several files containing z990 -optimized implementations of crypto algorithms are placed in the -arch/s390/crypto directory. - - -2. Probing for availability of MSA -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -It should be possible to use Kernels with the z990 crypto implementations both -on machines with MSA available and on those without MSA (pre z990 or z990 -without MSA). Therefore a simple probing mechanism has been implemented: -In the init function of each crypto module the availability of MSA and of the -respective crypto algorithm in particular will be tested. If the algorithm is -available the module will load and register its algorithm with the crypto API. - -If the respective crypto algorithm is not available, the init function will -return -ENOSYS. In that case a fallback to the standard software implementation -of the crypto algorithm must be taken ( -> the standard crypto modules are -also built when compiling the kernel). - - -3. Ensuring z990 crypto module preference -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -If z990 crypto instructions are available the optimized modules should be -preferred instead of standard modules. - -3.1. compiled-in modules -~~~~~~~~~~~~~~~~~~~~~~~~ -For compiled-in modules it has to be ensured that the z990 modules are linked -before the standard crypto modules. Then, on system startup the init functions -of z990 crypto modules will be called first and query for availability of z990 -crypto instructions. If instruction is available, the z990 module will register -its crypto algorithm implementation -> the load of the standard module will fail -since the algorithm is already registered. -If z990 crypto instruction is not available the load of the z990 module will -fail -> the standard module will load and register its algorithm. - -3.2. dynamic modules -~~~~~~~~~~~~~~~~~~~~ -A system administrator has to take care of giving preference to z990 crypto -modules. If MSA is available appropriate lines have to be added to -/etc/modprobe.conf. - -Example: z990 crypto instruction for SHA1 algorithm is available - - add the following line to /etc/modprobe.conf (assuming the - z990 crypto modules for SHA1 is called sha1_z990): - - alias sha1 sha1_z990 - - -> when the sha1 algorithm is requested through the crypto API - (which has a module autoloader) the z990 module will be loaded. - -TBD: a userspace module probing mechanism - something like 'probe sha1 sha1_z990 sha1' in modprobe.conf - -> try module sha1_z990, if it fails to load standard module sha1 - the 'probe' statement is currently not supported in modprobe.conf - - -4. Currently implemented z990 crypto algorithms -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The following crypto algorithms with z990 MSA support are currently implemented. -The name of each algorithm under which it is registered in crypto API and the -name of the respective module is given in square brackets. - -- SHA1 Digest Algorithm [sha1 -> sha1_z990] -- DES Encrypt/Decrypt Algorithm (64bit key) [des -> des_z990] -- Triple DES Encrypt/Decrypt Algorithm (128bit key) [des3_ede128 -> des_z990] -- Triple DES Encrypt/Decrypt Algorithm (192bit key) [des3_ede -> des_z990] - -In order to load, for example, the sha1_z990 module when the sha1 algorithm is -requested (see 3.2.) add 'alias sha1 sha1_z990' to /etc/modprobe.conf. - diff --git a/arch/s390/crypto/sha1_s390.c b/arch/s390/crypto/sha1_s390.c index 969639f31977..af4460ec381f 100644 --- a/arch/s390/crypto/sha1_s390.c +++ b/arch/s390/crypto/sha1_s390.c @@ -25,99 +25,100 @@ */ #include #include -#include #include -#include -#include + #include "crypt_s390.h" #define SHA1_DIGEST_SIZE 20 #define SHA1_BLOCK_SIZE 64 -struct crypt_s390_sha1_ctx { - u64 count; +struct s390_sha1_ctx { + u64 count; /* message length */ u32 state[5]; - u32 buf_len; - u8 buffer[2 * SHA1_BLOCK_SIZE]; + u8 buf[2 * SHA1_BLOCK_SIZE]; }; static void sha1_init(struct crypto_tfm *tfm) { - struct crypt_s390_sha1_ctx *ctx = crypto_tfm_ctx(tfm); - - ctx->state[0] = 0x67452301; - ctx->state[1] = 0xEFCDAB89; - ctx->state[2] = 0x98BADCFE; - ctx->state[3] = 0x10325476; - ctx->state[4] = 0xC3D2E1F0; - - ctx->count = 0; - ctx->buf_len = 0; + struct s390_sha1_ctx *sctx = crypto_tfm_ctx(tfm); + + sctx->state[0] = 0x67452301; + sctx->state[1] = 0xEFCDAB89; + sctx->state[2] = 0x98BADCFE; + sctx->state[3] = 0x10325476; + sctx->state[4] = 0xC3D2E1F0; + sctx->count = 0; } static void sha1_update(struct crypto_tfm *tfm, const u8 *data, unsigned int len) { - struct crypt_s390_sha1_ctx *sctx; - long imd_len; - - sctx = crypto_tfm_ctx(tfm); - sctx->count += len * 8; /* message bit length */ - - /* anything in buffer yet? -> must be completed */ - if (sctx->buf_len && (sctx->buf_len + len) >= SHA1_BLOCK_SIZE) { - /* complete full block and hash */ - memcpy(sctx->buffer + sctx->buf_len, data, - SHA1_BLOCK_SIZE - sctx->buf_len); - crypt_s390_kimd(KIMD_SHA_1, sctx->state, sctx->buffer, - SHA1_BLOCK_SIZE); - data += SHA1_BLOCK_SIZE - sctx->buf_len; - len -= SHA1_BLOCK_SIZE - sctx->buf_len; - sctx->buf_len = 0; + struct s390_sha1_ctx *sctx = crypto_tfm_ctx(tfm); + unsigned int index; + int ret; + + /* how much is already in the buffer? */ + index = sctx->count & 0x3f; + + sctx->count += len; + + if (index + len < SHA1_BLOCK_SIZE) + goto store; + + /* process one stored block */ + if (index) { + memcpy(sctx->buf + index, data, SHA1_BLOCK_SIZE - index); + ret = crypt_s390_kimd(KIMD_SHA_1, sctx->state, sctx->buf, + SHA1_BLOCK_SIZE); + BUG_ON(ret != SHA1_BLOCK_SIZE); + data += SHA1_BLOCK_SIZE - index; + len -= SHA1_BLOCK_SIZE - index; } - /* rest of data contains full blocks? */ - imd_len = len & ~0x3ful; - if (imd_len) { - crypt_s390_kimd(KIMD_SHA_1, sctx->state, data, imd_len); - data += imd_len; - len -= imd_len; + /* process as many blocks as possible */ + if (len >= SHA1_BLOCK_SIZE) { + ret = crypt_s390_kimd(KIMD_SHA_1, sctx->state, data, + len & ~(SHA1_BLOCK_SIZE - 1)); + BUG_ON(ret != (len & ~(SHA1_BLOCK_SIZE - 1))); + data += ret; + len -= ret; } - /* anything left? store in buffer */ - if (len) { - memcpy(sctx->buffer + sctx->buf_len , data, len); - sctx->buf_len += len; - } -} +store: + /* anything left? */ + if (len) + memcpy(sctx->buf + index , data, len); +} -static void pad_message(struct crypt_s390_sha1_ctx* sctx) +/* Add padding and return the message digest. */ +static void sha1_final(struct crypto_tfm *tfm, u8 *out) { - int index; + struct s390_sha1_ctx *sctx = crypto_tfm_ctx(tfm); + u64 bits; + unsigned int index, end; + int ret; + + /* must perform manual padding */ + index = sctx->count & 0x3f; + end = (index < 56) ? SHA1_BLOCK_SIZE : (2 * SHA1_BLOCK_SIZE); - index = sctx->buf_len; - sctx->buf_len = (sctx->buf_len < 56) ? - SHA1_BLOCK_SIZE:2 * SHA1_BLOCK_SIZE; /* start pad with 1 */ - sctx->buffer[index] = 0x80; + sctx->buf[index] = 0x80; + /* pad with zeros */ index++; - memset(sctx->buffer + index, 0x00, sctx->buf_len - index); - /* append length */ - memcpy(sctx->buffer + sctx->buf_len - 8, &sctx->count, - sizeof sctx->count); -} + memset(sctx->buf + index, 0x00, end - index - 8); -/* Add padding and return the message digest. */ -static void sha1_final(struct crypto_tfm *tfm, u8 *out) -{ - struct crypt_s390_sha1_ctx *sctx = crypto_tfm_ctx(tfm); + /* append message length */ + bits = sctx->count * 8; + memcpy(sctx->buf + end - 8, &bits, sizeof(bits)); + + ret = crypt_s390_kimd(KIMD_SHA_1, sctx->state, sctx->buf, end); + BUG_ON(ret != end); - /* must perform manual padding */ - pad_message(sctx); - crypt_s390_kimd(KIMD_SHA_1, sctx->state, sctx->buffer, sctx->buf_len); /* copy digest to out */ memcpy(out, sctx->state, SHA1_DIGEST_SIZE); + /* wipe context */ memset(sctx, 0, sizeof *sctx); } @@ -128,7 +129,7 @@ static struct crypto_alg alg = { .cra_priority = CRYPT_S390_PRIORITY, .cra_flags = CRYPTO_ALG_TYPE_DIGEST, .cra_blocksize = SHA1_BLOCK_SIZE, - .cra_ctxsize = sizeof(struct crypt_s390_sha1_ctx), + .cra_ctxsize = sizeof(struct s390_sha1_ctx), .cra_module = THIS_MODULE, .cra_list = LIST_HEAD_INIT(alg.cra_list), .cra_u = { .digest = { diff --git a/arch/s390/crypto/sha256_s390.c b/arch/s390/crypto/sha256_s390.c index 78436c696d37..2ced3330bce0 100644 --- a/arch/s390/crypto/sha256_s390.c +++ b/arch/s390/crypto/sha256_s390.c @@ -26,7 +26,7 @@ #define SHA256_BLOCK_SIZE 64 struct s390_sha256_ctx { - u64 count; + u64 count; /* message length */ u32 state[8]; u8 buf[2 * SHA256_BLOCK_SIZE]; }; @@ -54,10 +54,9 @@ static void sha256_update(struct crypto_tfm *tfm, const u8 *data, int ret; /* how much is already in the buffer? */ - index = sctx->count / 8 & 0x3f; + index = sctx->count & 0x3f; - /* update message bit length */ - sctx->count += len * 8; + sctx->count += len; if ((index + len) < SHA256_BLOCK_SIZE) goto store; @@ -87,12 +86,17 @@ store: memcpy(sctx->buf + index , data, len); } -static void pad_message(struct s390_sha256_ctx* sctx) +/* Add padding and return the message digest */ +static void sha256_final(struct crypto_tfm *tfm, u8 *out) { - int index, end; + struct s390_sha256_ctx *sctx = crypto_tfm_ctx(tfm); + u64 bits; + unsigned int index, end; + int ret; - index = sctx->count / 8 & 0x3f; - end = index < 56 ? SHA256_BLOCK_SIZE : 2 * SHA256_BLOCK_SIZE; + /* must perform manual padding */ + index = sctx->count & 0x3f; + end = (index < 56) ? SHA256_BLOCK_SIZE : (2 * SHA256_BLOCK_SIZE); /* start pad with 1 */ sctx->buf[index] = 0x80; @@ -102,21 +106,11 @@ static void pad_message(struct s390_sha256_ctx* sctx) memset(sctx->buf + index, 0x00, end - index - 8); /* append message length */ - memcpy(sctx->buf + end - 8, &sctx->count, sizeof sctx->count); - - sctx->count = end * 8; -} - -/* Add padding and return the message digest */ -static void sha256_final(struct crypto_tfm *tfm, u8 *out) -{ - struct s390_sha256_ctx *sctx = crypto_tfm_ctx(tfm); - - /* must perform manual padding */ - pad_message(sctx); + bits = sctx->count * 8; + memcpy(sctx->buf + end - 8, &bits, sizeof(bits)); - crypt_s390_kimd(KIMD_SHA_256, sctx->state, sctx->buf, - sctx->count / 8); + ret = crypt_s390_kimd(KIMD_SHA_256, sctx->state, sctx->buf, end); + BUG_ON(ret != end); /* copy digest to out */ memcpy(out, sctx->state, SHA256_DIGEST_SIZE); -- cgit v1.2.3 From 3106d46f51a1a72fdbf071ebc0800a9bcfcbc544 Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Fri, 6 Apr 2007 12:21:45 +0200 Subject: the overdue removal of the mount/umount uevents This patch contains the overdue removal of the mount/umount uevents. Signed-off-by: Adrian Bunk Signed-off-by: Greg Kroah-Hartman --- Documentation/feature-removal-schedule.txt | 9 --------- fs/super.c | 12 ------------ include/linux/kobject.h | 8 +++----- lib/kobject_uevent.c | 4 ---- 4 files changed, 3 insertions(+), 30 deletions(-) (limited to 'Documentation') diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt index 6da663607f7b..ec0b4843b1cb 100644 --- a/Documentation/feature-removal-schedule.txt +++ b/Documentation/feature-removal-schedule.txt @@ -134,15 +134,6 @@ Who: Arjan van de Ven --------------------------- -What: mount/umount uevents -When: February 2007 -Why: These events are not correct, and do not properly let userspace know - when a file system has been mounted or unmounted. Userspace should - poll the /proc/mounts file instead to detect this properly. -Who: Greg Kroah-Hartman - ---------------------------- - What: USB driver API moves to EXPORT_SYMBOL_GPL When: February 2008 Files: include/linux/usb.h, drivers/usb/core/driver.c diff --git a/fs/super.c b/fs/super.c index 60b1e50cbf53..8341e4e1d738 100644 --- a/fs/super.c +++ b/fs/super.c @@ -725,16 +725,6 @@ static int test_bdev_super(struct super_block *s, void *data) return (void *)s->s_bdev == data; } -static void bdev_uevent(struct block_device *bdev, enum kobject_action action) -{ - if (bdev->bd_disk) { - if (bdev->bd_part) - kobject_uevent(&bdev->bd_part->kobj, action); - else - kobject_uevent(&bdev->bd_disk->kobj, action); - } -} - int get_sb_bdev(struct file_system_type *fs_type, int flags, const char *dev_name, void *data, int (*fill_super)(struct super_block *, void *, int), @@ -782,7 +772,6 @@ int get_sb_bdev(struct file_system_type *fs_type, } s->s_flags |= MS_ACTIVE; - bdev_uevent(bdev, KOBJ_MOUNT); } return simple_set_mnt(mnt, s); @@ -801,7 +790,6 @@ void kill_block_super(struct super_block *sb) { struct block_device *bdev = sb->s_bdev; - bdev_uevent(bdev, KOBJ_UMOUNT); generic_shutdown_super(sb); sync_blockdev(bdev); close_bdev_excl(bdev); diff --git a/include/linux/kobject.h b/include/linux/kobject.h index a659a97eccf1..eb0e63ef297f 100644 --- a/include/linux/kobject.h +++ b/include/linux/kobject.h @@ -42,11 +42,9 @@ enum kobject_action { KOBJ_ADD = (__force kobject_action_t) 0x01, /* exclusive to core */ KOBJ_REMOVE = (__force kobject_action_t) 0x02, /* exclusive to core */ KOBJ_CHANGE = (__force kobject_action_t) 0x03, /* device state change */ - KOBJ_MOUNT = (__force kobject_action_t) 0x04, /* mount event for block devices (broken) */ - KOBJ_UMOUNT = (__force kobject_action_t) 0x05, /* umount event for block devices (broken) */ - KOBJ_OFFLINE = (__force kobject_action_t) 0x06, /* device offline */ - KOBJ_ONLINE = (__force kobject_action_t) 0x07, /* device online */ - KOBJ_MOVE = (__force kobject_action_t) 0x08, /* device move */ + KOBJ_OFFLINE = (__force kobject_action_t) 0x04, /* device offline */ + KOBJ_ONLINE = (__force kobject_action_t) 0x05, /* device online */ + KOBJ_MOVE = (__force kobject_action_t) 0x06, /* device move */ }; struct kobject { diff --git a/lib/kobject_uevent.c b/lib/kobject_uevent.c index d9a3510ed2e2..12e311dc664c 100644 --- a/lib/kobject_uevent.c +++ b/lib/kobject_uevent.c @@ -42,10 +42,6 @@ static char *action_to_string(enum kobject_action action) return "remove"; case KOBJ_CHANGE: return "change"; - case KOBJ_MOUNT: - return "mount"; - case KOBJ_UMOUNT: - return "umount"; case KOBJ_OFFLINE: return "offline"; case KOBJ_ONLINE: -- cgit v1.2.3 From b7eee616ad8db5db5441a7d82083003df3ab6d3b Mon Sep 17 00:00:00 2001 From: Antoine Jacquet Date: Fri, 27 Apr 2007 12:30:59 -0300 Subject: V4L/DVB (5257): USB: add zr364xx V4L2 driver This patch adds a V4L2 driver giving support for USB webcams based on the zr364xx chipsets. Signed-off-by: Antoine Jacquet Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/zr364xx.txt | 65 +++ MAINTAINERS | 32 +- drivers/media/video/Kconfig | 12 + drivers/media/video/Makefile | 1 + drivers/media/video/zr364xx.c | 929 ++++++++++++++++++++++++++++++++++ 5 files changed, 1027 insertions(+), 12 deletions(-) create mode 100644 Documentation/video4linux/zr364xx.txt create mode 100644 drivers/media/video/zr364xx.c (limited to 'Documentation') diff --git a/Documentation/video4linux/zr364xx.txt b/Documentation/video4linux/zr364xx.txt new file mode 100644 index 000000000000..c76992d0ff4d --- /dev/null +++ b/Documentation/video4linux/zr364xx.txt @@ -0,0 +1,65 @@ +Zoran 364xx based USB webcam module version 0.72 +site: http://royale.zerezo.com/zr364xx/ +mail: royale@zerezo.com + +introduction: +This brings support under Linux for the Aiptek PocketDV 3300 in webcam mode. +If you just want to get on your PC the pictures and movies on the camera, you should use the usb-storage module instead. +The driver works with several other cameras in webcam mode (see the list below). +Maybe this code can work for other JPEG/USB cams based on the Coach chips from Zoran? +Possible chipsets are : ZR36430 (ZR36430BGC) and maybe ZR36431, ZR36440, ZR36442... +You can try the experience changing the vendor/product ID values (look at the source code). +You can get these values by looking at /var/log/messages when you plug your camera, or by typing : cat /proc/bus/usb/devices. +If you manage to use your cam with this code, you can send me a mail (royale@zerezo.com) with the name of your cam and a patch if needed. +This is a beta release of the driver. +Since version 0.70, this driver is only compatible with V4L2 API and 2.6.x kernels. +If you need V4L1 or 2.4x kernels support, please use an older version, but the code is not maintained anymore. +Good luck! + +install: +In order to use this driver, you must compile it with your kernel. +Location: Device Drivers -> Multimedia devices -> Video For Linux -> Video Capture Adapters -> V4L USB devices + +usage: +modprobe zr364xx debug=X mode=Y + - debug : set to 1 to enable verbose debug messages + - mode : 0 = 320x240, 1 = 160x120, 2 = 640x480 +You can then use the camera with V4L2 compatible applications, for example Ekiga. +To capture a single image, try this: dd if=/dev/video0 of=test.jpg bs=1 count=1 + +links : +http://mxhaard.free.fr/ (support for many others cams including some Aiptek PocketDV) +http://www.harmwal.nl/pccam880/ (this project also supports cameras based on this chipset) + +supported devices: +------ ------- ----------- ----- +Vendor Product Distributor Model +------ ------- ----------- ----- +0x08ca 0x0109 Aiptek PocketDV 3300 +0x08ca 0x0109 Maxell Maxcam PRO DV3 +0x041e 0x4024 Creative PC-CAM 880 +0x0d64 0x0108 Aiptek Fidelity 3200 +0x0d64 0x0108 Praktica DCZ 1.3 S +0x0d64 0x0108 Genius Digital Camera (?) +0x0d64 0x0108 DXG Technology Fashion Cam +0x0546 0x3187 Polaroid iON 230 +0x0d64 0x3108 Praktica Exakta DC 2200 +0x0d64 0x3108 Genius G-Shot D211 +0x0595 0x4343 Concord Eye-Q Duo 1300 +0x0595 0x4343 Concord Eye-Q Duo 2000 +0x0595 0x4343 Fujifilm EX-10 +0x0595 0x4343 Ricoh RDC-6000 +0x0595 0x4343 Digitrex DSC 1300 +0x0595 0x4343 Firstline FDC 2000 +0x0bb0 0x500d Concord EyeQ Go Wireless +0x0feb 0x2004 CRS Electronic 3.3 Digital Camera +0x0feb 0x2004 Packard Bell DSC-300 +0x055f 0xb500 Mustek MDC 3000 +0x08ca 0x2062 Aiptek PocketDV 5700 +0x052b 0x1a18 Chiphead Megapix V12 +0x04c8 0x0729 Konica Revio 2 +0x04f2 0xa208 Creative PC-CAM 850 +0x0784 0x0040 Traveler Slimline X5 +0x06d6 0x0034 Trust Powerc@m 750 +0x0a17 0x0062 Pentax Optio 50L + diff --git a/MAINTAINERS b/MAINTAINERS index 5519d257b556..456a3dbcc447 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -55,7 +55,7 @@ trivial patch so apply some common sense. 8. Happy hacking. - ----------------------------------- + ----------------------------------- Maintainers List (try to look for most precise areas first) @@ -907,7 +907,7 @@ L: linux-cifs-client@lists.samba.org L: samba-technical@lists.samba.org W: http://us1.samba.org/samba/Linux_CIFS_client.html T: git kernel.org:/pub/scm/linux/kernel/git/sfrench/cifs-2.6.git -S: Supported +S: Supported CONFIGFS P: Joel Becker @@ -1549,19 +1549,19 @@ P: Chirag Kantharia M: chirag.kantharia@hp.com L: iss_storagedev@hp.com S: Maintained - + HEWLETT-PACKARD SMART2 RAID DRIVER P: Chirag Kantharia M: chirag.kantharia@hp.com L: iss_storagedev@hp.com S: Maintained - + HEWLETT-PACKARD SMART CISS RAID DRIVER (cciss) P: Mike Miller M: mike.miller@hp.com L: iss_storagedev@hp.com S: Supported - + HOST AP DRIVER P: Jouni Malinen M: jkmaline@cc.hut.fi @@ -1673,7 +1673,7 @@ P: Jack Hammer P: Dave Jeffery M: ipslinux@adaptec.com W: http://www.developer.ibm.com/welcome/netfinity/serveraid.html -S: Supported +S: Supported IDE SUBSYSTEM P: Bartlomiej Zolnierkiewicz @@ -1975,7 +1975,7 @@ M: kai@germaschewski.name P: Sam Ravnborg M: sam@ravnborg.org T: git kernel.org:/pub/scm/linux/kernel/git/sam/kbuild.git -S: Maintained +S: Maintained KERNEL JANITORS P: Several @@ -2155,7 +2155,7 @@ S: Maintained LOGICAL DISK MANAGER SUPPORT (LDM, Windows 2000/XP Dynamic Disks) P: Richard Russon (FlatCap) M: ldm@flatcap.org -L: ldm-devel@lists.sourceforge.net +L: ldm-devel@lists.sourceforge.net W: http://ldm.sourceforge.net S: Maintained @@ -2504,13 +2504,13 @@ P: Kurt Hackel M: kurt.hackel@oracle.com L: ocfs2-devel@oss.oracle.com W: http://oss.oracle.com/projects/ocfs2/ -S: Supported +S: Supported OLYMPIC NETWORK DRIVER P: Peter De Shrijver M: p2@ace.ulyssis.student.kuleuven.ac.be P: Mike Phillips -M: mikep@linuxtr.net +M: mikep@linuxtr.net L: netdev@vger.kernel.org L: linux-tr@linuxtr.net W: http://www.linuxtr.net @@ -3045,7 +3045,7 @@ SIS FRAMEBUFFER DRIVER P: Thomas Winischhofer M: thomas@winischhofer.net W: http://www.winischhofer.net/linuxsisvga.shtml -S: Maintained +S: Maintained SIS USB2VGA DRIVER P: Thomas Winischhofer @@ -3649,6 +3649,14 @@ L: linux-usb-devel@lists.sourceforge.net W: http://linux-lc100020.sourceforge.net S: Maintained +USB ZR364XX DRIVER +P: Antoine Jacquet +M: royale@zerezo.com +L: linux-usb-devel@lists.sourceforge.net +L: video4linux-list@redhat.com +W: http://royale.zerezo.com/zr364xx/ +S: Maintained + USER-MODE LINUX P: Jeff Dike M: jdike@karaya.com @@ -3656,7 +3664,7 @@ L: user-mode-linux-devel@lists.sourceforge.net L: user-mode-linux-user@lists.sourceforge.net W: http://user-mode-linux.sourceforge.net S: Maintained - + FAT/VFAT/MSDOS FILESYSTEM: P: OGAWA Hirofumi M: hirofumi@mail.parknet.co.jp diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index 7a6105153f23..fa0a87679190 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -761,6 +761,18 @@ source "drivers/media/video/zc0301/Kconfig" source "drivers/media/video/pwc/Kconfig" +config USB_ZR364XX + tristate "USB ZR364XX Camera support" + depends on USB && VIDEO_V4L2 + ---help--- + Say Y here if you want to connect this type of camera to your + computer's USB port. + See for more info + and list of supported cameras. + + To compile this driver as a module, choose M here: the + module will be called zr364xx. + endmenu # V4L USB devices endmenu diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile index 44ccaed40b49..384f01c133c5 100644 --- a/drivers/media/video/Makefile +++ b/drivers/media/video/Makefile @@ -99,6 +99,7 @@ obj-$(CONFIG_USB_OV511) += ov511.o obj-$(CONFIG_USB_SE401) += se401.o obj-$(CONFIG_USB_STV680) += stv680.o obj-$(CONFIG_USB_W9968CF) += w9968cf.o +obj-$(CONFIG_USB_ZR364XX) += zr364xx.o obj-$(CONFIG_USB_SN9C102) += sn9c102/ obj-$(CONFIG_USB_ET61X251) += et61x251/ diff --git a/drivers/media/video/zr364xx.c b/drivers/media/video/zr364xx.c new file mode 100644 index 000000000000..c055cf017cf5 --- /dev/null +++ b/drivers/media/video/zr364xx.c @@ -0,0 +1,929 @@ +/* + * Zoran 364xx based USB webcam module version 0.72 + * + * Allows you to use your USB webcam with V4L2 applications + * This is still in heavy developpement ! + * + * Copyright (C) 2004 Antoine Jacquet + * http://royale.zerezo.com/zr364xx/ + * + * Heavily inspired by usb-skeleton.c, vicam.c, cpia.c and spca50x.c drivers + * V4L2 version inspired by meye.c driver + * + * 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 + */ + + +#include +#include +#include +#include +#include +#include +#include +#include + + +/* Version Information */ +#define DRIVER_VERSION "v0.72" +#define DRIVER_AUTHOR "Antoine Jacquet, http://royale.zerezo.com/" +#define DRIVER_DESC "Zoran 364xx" + + +/* Camera */ +#define FRAMES 2 +#define MAX_FRAME_SIZE 100000 +#define BUFFER_SIZE 0x1000 +#define CTRL_TIMEOUT 500 + + +/* Debug macro */ +#define DBG(x...) if (debug) info(x) + + +/* Init methods, need to find nicer names for these + * the exact names of the chipsets would be the best if someone finds it */ +#define METHOD0 0 +#define METHOD1 1 +#define METHOD2 2 + + +/* Module parameters */ +static int debug = 0; +static int mode = 0; + + +/* Module parameters interface */ +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Debug level"); +module_param(mode, int, 0644); +MODULE_PARM_DESC(mode, "0 = 320x240, 1 = 160x120, 2 = 640x480"); + + +/* Devices supported by this driver + * .driver_info contains the init method used by the camera */ +static struct usb_device_id device_table[] = { + {USB_DEVICE(0x08ca, 0x0109), .driver_info = METHOD0 }, + {USB_DEVICE(0x041e, 0x4024), .driver_info = METHOD0 }, + {USB_DEVICE(0x0d64, 0x0108), .driver_info = METHOD0 }, + {USB_DEVICE(0x0546, 0x3187), .driver_info = METHOD0 }, + {USB_DEVICE(0x0d64, 0x3108), .driver_info = METHOD0 }, + {USB_DEVICE(0x0595, 0x4343), .driver_info = METHOD0 }, + {USB_DEVICE(0x0bb0, 0x500d), .driver_info = METHOD0 }, + {USB_DEVICE(0x0feb, 0x2004), .driver_info = METHOD0 }, + {USB_DEVICE(0x055f, 0xb500), .driver_info = METHOD0 }, + {USB_DEVICE(0x08ca, 0x2062), .driver_info = METHOD2 }, + {USB_DEVICE(0x052b, 0x1a18), .driver_info = METHOD1 }, + {USB_DEVICE(0x04c8, 0x0729), .driver_info = METHOD0 }, + {USB_DEVICE(0x04f2, 0xa208), .driver_info = METHOD0 }, + {USB_DEVICE(0x0784, 0x0040), .driver_info = METHOD1 }, + {USB_DEVICE(0x06d6, 0x0034), .driver_info = METHOD0 }, + {USB_DEVICE(0x0a17, 0x0062), .driver_info = METHOD2 }, + {} /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(usb, device_table); + + +/* Camera stuff */ +struct zr364xx_camera { + struct usb_device *udev; /* save off the usb device pointer */ + struct usb_interface *interface;/* the interface for this device */ + struct video_device *vdev; /* v4l video device */ + u8 *framebuf; + int nb; + unsigned char *buffer; + int skip; + int brightness; + int width; + int height; + int method; + struct mutex lock; +}; + + +/* function used to send initialisation commands to the camera */ +static int send_control_msg(struct usb_device *udev, u8 request, u16 value, + u16 index, unsigned char *cp, u16 size) +{ + int status; + + unsigned char *transfer_buffer = kmalloc(size, GFP_KERNEL); + if (!transfer_buffer) { + info("kmalloc(%d) failed", size); + return -ENOMEM; + } + + memcpy(transfer_buffer, cp, size); + + status = usb_control_msg(udev, + usb_sndctrlpipe(udev, 0), + request, + USB_DIR_OUT | USB_TYPE_VENDOR | + USB_RECIP_DEVICE, value, index, + transfer_buffer, size, CTRL_TIMEOUT); + + kfree(transfer_buffer); + + if (status < 0) + info("Failed sending control message, error %d.", status); + + return status; +} + + +/* Control messages sent to the camera to initialize it + * and launch the capture */ +typedef struct { + unsigned int value; + unsigned int size; + unsigned char *bytes; +} message; + +/* method 0 */ +static unsigned char m0d1[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; +static unsigned char m0d2[] = { 0, 0, 0, 0, 0, 0 }; +static unsigned char m0d3[] = { 0, 0 }; +static message m0[] = { + {0x1f30, 0, NULL}, + {0xd000, 0, NULL}, + {0x3370, sizeof(m0d1), m0d1}, + {0x2000, 0, NULL}, + {0x2f0f, 0, NULL}, + {0x2610, sizeof(m0d2), m0d2}, + {0xe107, 0, NULL}, + {0x2502, 0, NULL}, + {0x1f70, 0, NULL}, + {0xd000, 0, NULL}, + {0x9a01, sizeof(m0d3), m0d3}, + {-1, -1, NULL} +}; + +/* method 1 */ +static unsigned char m1d1[] = { 0xff, 0xff }; +static unsigned char m1d2[] = { 0x00, 0x00 }; +static message m1[] = { + {0x1f30, 0, NULL}, + {0xd000, 0, NULL}, + {0xf000, 0, NULL}, + {0x2000, 0, NULL}, + {0x2f0f, 0, NULL}, + {0x2650, 0, NULL}, + {0xe107, 0, NULL}, + {0x2502, sizeof(m1d1), m1d1}, + {0x1f70, 0, NULL}, + {0xd000, 0, NULL}, + {0xd000, 0, NULL}, + {0xd000, 0, NULL}, + {0x9a01, sizeof(m1d2), m1d2}, + {-1, -1, NULL} +}; + +/* method 2 */ +static unsigned char m2d1[] = { 0xff, 0xff }; +static message m2[] = { + {0x1f30, 0, NULL}, + {0xf000, 0, NULL}, + {0x2000, 0, NULL}, + {0x2f0f, 0, NULL}, + {0x2650, 0, NULL}, + {0xe107, 0, NULL}, + {0x2502, sizeof(m2d1), m2d1}, + {0x1f70, 0, NULL}, + {-1, -1, NULL} +}; + +/* init table */ +static message *init[3] = { m0, m1, m2 }; + + +/* JPEG static data in header (Huffman table, etc) */ +static unsigned char header1[] = { + 0xFF, 0xD8, + /* + 0xFF, 0xE0, 0x00, 0x10, 'J', 'F', 'I', 'F', + 0x00, 0x01, 0x01, 0x00, 0x33, 0x8A, 0x00, 0x00, 0x33, 0x88, + */ + 0xFF, 0xDB, 0x00, 0x84 +}; +static unsigned char header2[] = { + 0xFF, 0xC4, 0x00, 0x1F, 0x00, 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, + 0xFF, 0xC4, 0x00, 0xB5, 0x10, 0x00, 0x02, 0x01, 0x03, 0x03, 0x02, + 0x04, 0x03, 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01, 0x7D, 0x01, + 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, + 0x13, 0x51, 0x61, 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xA1, + 0x08, 0x23, 0x42, 0xB1, 0xC1, 0x15, 0x52, 0xD1, 0xF0, 0x24, 0x33, + 0x62, 0x72, 0x82, 0x09, 0x0A, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x25, + 0x26, 0x27, 0x28, 0x29, 0x2A, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, + 0x3A, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, + 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, + 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, + 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, + 0xA7, 0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, + 0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, + 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xE1, 0xE2, + 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xF1, 0xF2, 0xF3, + 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFF, 0xC4, 0x00, 0x1F, + 0x01, 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, + 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0xFF, 0xC4, 0x00, 0xB5, + 0x11, 0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, 0x07, 0x05, + 0x04, 0x04, 0x00, 0x01, 0x02, 0x77, 0x00, 0x01, 0x02, 0x03, 0x11, + 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, + 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, 0xA1, 0xB1, 0xC1, + 0x09, 0x23, 0x33, 0x52, 0xF0, 0x15, 0x62, 0x72, 0xD1, 0x0A, 0x16, + 0x24, 0x34, 0xE1, 0x25, 0xF1, 0x17, 0x18, 0x19, 0x1A, 0x26, 0x27, + 0x28, 0x29, 0x2A, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, + 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56, 0x57, + 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, + 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x82, 0x83, 0x84, + 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, + 0x97, 0x98, 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, + 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, + 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, + 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xE2, 0xE3, 0xE4, 0xE5, + 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, + 0xF8, 0xF9, 0xFA, 0xFF, 0xC0, 0x00, 0x11, 0x08, 0x00, 0xF0, 0x01, + 0x40, 0x03, 0x01, 0x21, 0x00, 0x02, 0x11, 0x01, 0x03, 0x11, 0x01, + 0xFF, 0xDA, 0x00, 0x0C, 0x03, 0x01, 0x00, 0x02, 0x11, 0x03, 0x11, + 0x00, 0x3F, 0x00 +}; +static unsigned char header3; + + + +/********************/ +/* V4L2 integration */ +/********************/ + +/* this function reads a full JPEG picture synchronously + * TODO: do it asynchronously... */ +static int read_frame(struct zr364xx_camera *cam, int framenum) +{ + int i, n, temp, head, size, actual_length; + unsigned char *ptr = NULL, *jpeg, swap; + + redo: + /* hardware brightness */ + n = send_control_msg(cam->udev, 1, 0x2001, 0, NULL, 0); + temp = (0x60 << 8) + 127 - cam->brightness; + n = send_control_msg(cam->udev, 1, temp, 0, NULL, 0); + + /* during the first loop we are going to insert JPEG header */ + head = 0; + /* this is the place in memory where we are going to build + * the JPEG image */ + jpeg = cam->framebuf + framenum * MAX_FRAME_SIZE; + /* read data... */ + do { + n = usb_bulk_msg(cam->udev, + usb_rcvbulkpipe(cam->udev, 0x81), + cam->buffer, BUFFER_SIZE, &actual_length, + CTRL_TIMEOUT); + DBG("buffer : %d %d", cam->buffer[0], cam->buffer[1]); + DBG("bulk : n=%d size=%d", n, actual_length); + if (n < 0) { + info("error reading bulk msg"); + return 0; + } + if (actual_length < 0 || actual_length > BUFFER_SIZE) { + info("wrong number of bytes"); + return 0; + } + + /* swap bytes if camera needs it */ + if (cam->method == METHOD0) + for (i = 0; i < BUFFER_SIZE; i += 2) { + swap = cam->buffer[i]; + cam->buffer[i] = cam->buffer[i + 1]; + cam->buffer[i + 1] = swap; + } + + /* write the JPEG header */ + if (!head) { + DBG("jpeg header"); + ptr = jpeg; + memcpy(ptr, header1, sizeof(header1)); + ptr += sizeof(header1); + header3 = 0; + memcpy(ptr, &header3, 1); + ptr++; + memcpy(ptr, cam->buffer, 64); + ptr += 64; + header3 = 1; + memcpy(ptr, &header3, 1); + ptr++; + memcpy(ptr, cam->buffer + 64, 64); + ptr += 64; + memcpy(ptr, header2, sizeof(header2)); + ptr += sizeof(header2); + memcpy(ptr, cam->buffer + 128, + actual_length - 128); + ptr += actual_length - 128; + head = 1; + DBG("header : %d %d %d %d %d %d %d %d %d", + cam->buffer[0], cam->buffer[1], cam->buffer[2], + cam->buffer[3], cam->buffer[4], cam->buffer[5], + cam->buffer[6], cam->buffer[7], cam->buffer[8]); + } else { + memcpy(ptr, cam->buffer, actual_length); + ptr += actual_length; + } + } + /* ... until there is no more */ + while (actual_length == BUFFER_SIZE); + + /* we skip the 2 first frames which are usually buggy */ + if (cam->skip) { + cam->skip--; + goto redo; + } + + /* go back to find the JPEG EOI marker */ + size = ptr - jpeg; + ptr -= 2; + while (ptr > jpeg) { + if (*ptr == 0xFF && *(ptr + 1) == 0xD9 + && *(ptr + 2) == 0xFF) + break; + ptr--; + } + if (ptr == jpeg) + DBG("No EOI marker"); + + /* Sometimes there is junk data in the middle of the picture, + * we want to skip this bogus frames */ + while (ptr > jpeg) { + if (*ptr == 0xFF && *(ptr + 1) == 0xFF + && *(ptr + 2) == 0xFF) + break; + ptr--; + } + if (ptr != jpeg) { + DBG("Bogus frame ? %d", cam->nb); + goto redo; + } + + DBG("jpeg : %d %d %d %d %d %d %d %d", + jpeg[0], jpeg[1], jpeg[2], jpeg[3], + jpeg[4], jpeg[5], jpeg[6], jpeg[7]); + + return size; +} + + +static ssize_t zr364xx_read(struct file *file, char *buf, size_t cnt, + loff_t * ppos) +{ + unsigned long count = cnt; + struct video_device *vdev = video_devdata(file); + struct zr364xx_camera *cam; + + DBG("zr364xx_read: read %d bytes.", (int) count); + + if (vdev == NULL) + return -ENODEV; + cam = video_get_drvdata(vdev); + + if (!buf) + return -EINVAL; + + if (!count) + return -EINVAL; + + /* NoMan Sux ! */ + count = read_frame(cam, 0); + + if (copy_to_user(buf, cam->framebuf, count)) + return -EFAULT; + + return count; +} + + +static int zr364xx_vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + memset(cap, 0, sizeof(*cap)); + strcpy(cap->driver, DRIVER_DESC); + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE; + return 0; +} + +static int zr364xx_vidioc_enum_input(struct file *file, void *priv, + struct v4l2_input *i) +{ + if (i->index != 0) + return -EINVAL; + memset(i, 0, sizeof(*i)); + i->index = 0; + strcpy(i->name, DRIVER_DESC " Camera"); + i->type = V4L2_INPUT_TYPE_CAMERA; + return 0; +} + +static int zr364xx_vidioc_g_input(struct file *file, void *priv, + unsigned int *i) +{ + *i = 0; + return 0; +} + +static int zr364xx_vidioc_s_input(struct file *file, void *priv, + unsigned int i) +{ + if (i != 0) + return -EINVAL; + return 0; +} + +static int zr364xx_vidioc_queryctrl(struct file *file, void *priv, + struct v4l2_queryctrl *c) +{ + struct video_device *vdev = video_devdata(file); + struct zr364xx_camera *cam; + + if (vdev == NULL) + return -ENODEV; + cam = video_get_drvdata(vdev); + + switch (c->id) { + case V4L2_CID_BRIGHTNESS: + c->type = V4L2_CTRL_TYPE_INTEGER; + strcpy(c->name, "Brightness"); + c->minimum = 0; + c->maximum = 127; + c->step = 1; + c->default_value = cam->brightness; + c->flags = 0; + break; + default: + return -EINVAL; + } + return 0; +} + +static int zr364xx_vidioc_s_ctrl(struct file *file, void *priv, + struct v4l2_control *c) +{ + struct video_device *vdev = video_devdata(file); + struct zr364xx_camera *cam; + + if (vdev == NULL) + return -ENODEV; + cam = video_get_drvdata(vdev); + + switch (c->id) { + case V4L2_CID_BRIGHTNESS: + cam->brightness = c->value; + break; + default: + return -EINVAL; + } + return 0; +} + +static int zr364xx_vidioc_g_ctrl(struct file *file, void *priv, + struct v4l2_control *c) +{ + struct video_device *vdev = video_devdata(file); + struct zr364xx_camera *cam; + + if (vdev == NULL) + return -ENODEV; + cam = video_get_drvdata(vdev); + + switch (c->id) { + case V4L2_CID_BRIGHTNESS: + c->value = cam->brightness; + break; + default: + return -EINVAL; + } + return 0; +} + +static int zr364xx_vidioc_enum_fmt_cap(struct file *file, + void *priv, struct v4l2_fmtdesc *f) +{ + if (f->index > 0) + return -EINVAL; + if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + memset(f, 0, sizeof(*f)); + f->index = 0; + f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + f->flags = V4L2_FMT_FLAG_COMPRESSED; + strcpy(f->description, "JPEG"); + f->pixelformat = V4L2_PIX_FMT_JPEG; + return 0; +} + +static int zr364xx_vidioc_try_fmt_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct video_device *vdev = video_devdata(file); + struct zr364xx_camera *cam; + + if (vdev == NULL) + return -ENODEV; + cam = video_get_drvdata(vdev); + + if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + if (f->fmt.pix.pixelformat != V4L2_PIX_FMT_JPEG) + return -EINVAL; + if (f->fmt.pix.field != V4L2_FIELD_ANY && + f->fmt.pix.field != V4L2_FIELD_NONE) + return -EINVAL; + f->fmt.pix.field = V4L2_FIELD_NONE; + f->fmt.pix.width = cam->width; + f->fmt.pix.height = cam->height; + f->fmt.pix.bytesperline = f->fmt.pix.width * 2; + f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; + f->fmt.pix.colorspace = 0; + f->fmt.pix.priv = 0; + return 0; +} + +static int zr364xx_vidioc_g_fmt_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct video_device *vdev = video_devdata(file); + struct zr364xx_camera *cam; + + if (vdev == NULL) + return -ENODEV; + cam = video_get_drvdata(vdev); + + if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + memset(&f->fmt.pix, 0, sizeof(struct v4l2_pix_format)); + f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + f->fmt.pix.pixelformat = V4L2_PIX_FMT_JPEG; + f->fmt.pix.field = V4L2_FIELD_NONE; + f->fmt.pix.width = cam->width; + f->fmt.pix.height = cam->height; + f->fmt.pix.bytesperline = f->fmt.pix.width * 2; + f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; + f->fmt.pix.colorspace = 0; + f->fmt.pix.priv = 0; + return 0; +} + +static int zr364xx_vidioc_s_fmt_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct video_device *vdev = video_devdata(file); + struct zr364xx_camera *cam; + + if (vdev == NULL) + return -ENODEV; + cam = video_get_drvdata(vdev); + + if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + if (f->fmt.pix.pixelformat != V4L2_PIX_FMT_JPEG) + return -EINVAL; + if (f->fmt.pix.field != V4L2_FIELD_ANY && + f->fmt.pix.field != V4L2_FIELD_NONE) + return -EINVAL; + f->fmt.pix.field = V4L2_FIELD_NONE; + f->fmt.pix.width = cam->width; + f->fmt.pix.height = cam->height; + f->fmt.pix.bytesperline = f->fmt.pix.width * 2; + f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; + f->fmt.pix.colorspace = 0; + f->fmt.pix.priv = 0; + DBG("ok!"); + return 0; +} + +static int zr364xx_vidioc_streamon(struct file *file, void *priv, + enum v4l2_buf_type type) +{ + return 0; +} + +static int zr364xx_vidioc_streamoff(struct file *file, void *priv, + enum v4l2_buf_type type) +{ + return 0; +} + + +/* open the camera */ +static int zr364xx_open(struct inode *inode, struct file *file) +{ + struct video_device *vdev = video_devdata(file); + struct zr364xx_camera *cam = video_get_drvdata(vdev); + struct usb_device *udev = cam->udev; + int i, err; + + DBG("zr364xx_open"); + + cam->skip = 2; + + err = video_exclusive_open(inode, file); + if (err < 0) + return err; + + if (!cam->framebuf) { + cam->framebuf = vmalloc_32(MAX_FRAME_SIZE * FRAMES); + if (!cam->framebuf) { + info("vmalloc_32 failed!"); + return -ENOMEM; + } + } + + mutex_lock(&cam->lock); + for (i = 0; init[cam->method][i].size != -1; i++) { + err = + send_control_msg(udev, 1, init[cam->method][i].value, + 0, init[cam->method][i].bytes, + init[cam->method][i].size); + if (err < 0) { + info("error during open sequence: %d", i); + mutex_unlock(&cam->lock); + return err; + } + } + + file->private_data = vdev; + + /* Added some delay here, since opening/closing the camera quickly, + * like Ekiga does during its startup, can crash the webcam + */ + mdelay(100); + + mutex_unlock(&cam->lock); + return 0; +} + + +/* release the camera */ +static int zr364xx_release(struct inode *inode, struct file *file) +{ + struct video_device *vdev = video_devdata(file); + struct zr364xx_camera *cam; + struct usb_device *udev; + int i, err; + + DBG("zr364xx_release"); + + if (vdev == NULL) + return -ENODEV; + cam = video_get_drvdata(vdev); + + udev = cam->udev; + + mutex_lock(&cam->lock); + for (i = 0; i < 2; i++) { + err = + send_control_msg(udev, 1, init[cam->method][i].value, + 0, init[i][cam->method].bytes, + init[cam->method][i].size); + if (err < 0) { + info("error during release sequence"); + mutex_unlock(&cam->lock); + return err; + } + } + + file->private_data = NULL; + video_exclusive_release(inode, file); + + /* Added some delay here, since opening/closing the camera quickly, + * like Ekiga does during its startup, can crash the webcam + */ + mdelay(100); + + mutex_unlock(&cam->lock); + return 0; +} + + +static int zr364xx_mmap(struct file *file, struct vm_area_struct *vma) +{ + void *pos; + unsigned long start = vma->vm_start; + unsigned long size = vma->vm_end - vma->vm_start; + struct video_device *vdev = video_devdata(file); + struct zr364xx_camera *cam; + + DBG("zr364xx_mmap: %ld\n", size); + + if (vdev == NULL) + return -ENODEV; + cam = video_get_drvdata(vdev); + + pos = cam->framebuf; + while (size > 0) { + if (vm_insert_page(vma, start, vmalloc_to_page(pos))) + return -EAGAIN; + start += PAGE_SIZE; + pos += PAGE_SIZE; + if (size > PAGE_SIZE) + size -= PAGE_SIZE; + else + size = 0; + } + + return 0; +} + + +static struct file_operations zr364xx_fops = { + .owner = THIS_MODULE, + .open = zr364xx_open, + .release = zr364xx_release, + .read = zr364xx_read, + .mmap = zr364xx_mmap, + .ioctl = video_ioctl2, + .llseek = no_llseek, +}; + +static struct video_device zr364xx_template = { + .owner = THIS_MODULE, + .name = DRIVER_DESC, + .type = VID_TYPE_CAPTURE, + .fops = &zr364xx_fops, + .release = video_device_release, + .minor = -1, + + .vidioc_querycap = zr364xx_vidioc_querycap, + .vidioc_enum_fmt_cap = zr364xx_vidioc_enum_fmt_cap, + .vidioc_try_fmt_cap = zr364xx_vidioc_try_fmt_cap, + .vidioc_s_fmt_cap = zr364xx_vidioc_s_fmt_cap, + .vidioc_g_fmt_cap = zr364xx_vidioc_g_fmt_cap, + .vidioc_enum_input = zr364xx_vidioc_enum_input, + .vidioc_g_input = zr364xx_vidioc_g_input, + .vidioc_s_input = zr364xx_vidioc_s_input, + .vidioc_streamon = zr364xx_vidioc_streamon, + .vidioc_streamoff = zr364xx_vidioc_streamoff, + .vidioc_queryctrl = zr364xx_vidioc_queryctrl, + .vidioc_g_ctrl = zr364xx_vidioc_g_ctrl, + .vidioc_s_ctrl = zr364xx_vidioc_s_ctrl, +}; + + + +/*******************/ +/* USB integration */ +/*******************/ + +static int zr364xx_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct usb_device *udev = interface_to_usbdev(intf); + struct zr364xx_camera *cam = NULL; + + DBG("probing..."); + + info(DRIVER_DESC " compatible webcam plugged"); + info("model %04x:%04x detected", udev->descriptor.idVendor, + udev->descriptor.idProduct); + + if ((cam = + kmalloc(sizeof(struct zr364xx_camera), GFP_KERNEL)) == NULL) { + info("cam: out of memory !"); + return -ENODEV; + } + memset(cam, 0x00, sizeof(struct zr364xx_camera)); + /* save the init method used by this camera */ + cam->method = id->driver_info; + + cam->vdev = video_device_alloc(); + if (cam->vdev == NULL) { + info("cam->vdev: out of memory !"); + kfree(cam); + return -ENODEV; + } + memcpy(cam->vdev, &zr364xx_template, sizeof(zr364xx_template)); + video_set_drvdata(cam->vdev, cam); + if (debug) + cam->vdev->debug = V4L2_DEBUG_IOCTL | V4L2_DEBUG_IOCTL_ARG; + + cam->udev = udev; + + if ((cam->buffer = kmalloc(BUFFER_SIZE, GFP_KERNEL)) == NULL) { + info("cam->buffer: out of memory !"); + video_device_release(cam->vdev); + kfree(cam); + return -ENODEV; + } + + switch (mode) { + case 1: + info("160x120 mode selected"); + cam->width = 160; + cam->height = 120; + break; + case 2: + info("640x480 mode selected"); + cam->width = 640; + cam->height = 480; + break; + default: + info("320x240 mode selected"); + cam->width = 320; + cam->height = 240; + break; + } + + m0d1[0] = mode; + m1[2].value = 0xf000 + mode; + m2[1].value = 0xf000 + mode; + header2[437] = cam->height / 256; + header2[438] = cam->height % 256; + header2[439] = cam->width / 256; + header2[440] = cam->width % 256; + + cam->nb = 0; + cam->brightness = 64; + mutex_init(&cam->lock); + + if (video_register_device(cam->vdev, VFL_TYPE_GRABBER, -1) == -1) { + info("video_register_device failed"); + video_device_release(cam->vdev); + kfree(cam->buffer); + kfree(cam); + return -ENODEV; + } + + usb_set_intfdata(intf, cam); + + info(DRIVER_DESC " controlling video device %d", cam->vdev->minor); + return 0; +} + + +static void zr364xx_disconnect(struct usb_interface *intf) +{ + struct zr364xx_camera *cam = usb_get_intfdata(intf); + usb_set_intfdata(intf, NULL); + dev_set_drvdata(&intf->dev, NULL); + info(DRIVER_DESC " webcam unplugged"); + if (cam->vdev) + video_unregister_device(cam->vdev); + cam->vdev = NULL; + kfree(cam->buffer); + if (cam->framebuf) + vfree(cam->framebuf); + kfree(cam); +} + + + +/**********************/ +/* Module integration */ +/**********************/ + +static struct usb_driver zr364xx_driver = { + .name = "zr364xx", + .probe = zr364xx_probe, + .disconnect = zr364xx_disconnect, + .id_table = device_table +}; + + +static int __init zr364xx_init(void) +{ + int retval; + retval = usb_register(&zr364xx_driver) < 0; + if (retval) + info("usb_register failed!"); + else + info(DRIVER_DESC " module loaded"); + return retval; +} + + +static void __exit zr364xx_exit(void) +{ + info(DRIVER_DESC " module unloaded"); + usb_deregister(&zr364xx_driver); +} + + +module_init(zr364xx_init); +module_exit(zr364xx_exit); + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 58ef4f924cf2824ae198b1fec3eea1e4059a021c Mon Sep 17 00:00:00 2001 From: Hartmut Hackmann Date: Fri, 27 Apr 2007 12:31:12 -0300 Subject: V4L/DVB (5314): Added support for tda827x tuners with preamlifiers This patch contains - new tuning code for the tda827xa silicon tuner. - controls the preamplifier of some boards with this tuner. - support for the Philips Tiger S hybrid DVB-T reference design. - reworked the saa7134-dvb modulue to get rid of most of the small board specific functions. Signed-off-by: Hartmut Hackmann Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/CARDLIST.saa7134 | 1 + drivers/media/video/saa7134/saa7134-cards.c | 78 ++- drivers/media/video/saa7134/saa7134-core.c | 1 + drivers/media/video/saa7134/saa7134-dvb.c | 856 ++++++++++++++-------------- drivers/media/video/saa7134/saa7134-video.c | 9 +- drivers/media/video/saa7134/saa7134.h | 1 + drivers/media/video/tda8290.c | 136 +++-- 7 files changed, 617 insertions(+), 465 deletions(-) (limited to 'Documentation') diff --git a/Documentation/video4linux/CARDLIST.saa7134 b/Documentation/video4linux/CARDLIST.saa7134 index a12246a9bf23..f18533f28e04 100644 --- a/Documentation/video4linux/CARDLIST.saa7134 +++ b/Documentation/video4linux/CARDLIST.saa7134 @@ -107,3 +107,4 @@ 106 -> Encore ENLTV [1131:2342,1131:2341,3016:2344] 107 -> Encore ENLTV-FM [1131:230f] 108 -> Terratec Cinergy HT PCI [153b:1175] +109 -> Philips Tiger - S Reference design diff --git a/drivers/media/video/saa7134/saa7134-cards.c b/drivers/media/video/saa7134/saa7134-cards.c index 4399d1371cc1..782832e7ed6d 100644 --- a/drivers/media/video/saa7134/saa7134-cards.c +++ b/drivers/media/video/saa7134/saa7134-cards.c @@ -2543,10 +2543,10 @@ struct saa7134_board saa7134_boards[] = { .name = "Philips Tiger reference design", .audio_clock = 0x00187de7, .tuner_type = TUNER_PHILIPS_TDA8290, - .tuner_config = 0, .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, + .tuner_config = 0, .mpeg = SAA7134_MPEG_DVB, .gpiomask = 0x0200000, .inputs = {{ @@ -2625,7 +2625,7 @@ struct saa7134_board saa7134_boards[] = { }}, .radio = { .name = name_radio, - .amux = LINE1, + .amux = TV, .gpio = 0x0200000, }, }, @@ -3044,6 +3044,7 @@ struct saa7134_board saa7134_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, + .tuner_config = 1, .mpeg = SAA7134_MPEG_DVB, .gpiomask = 0x000200000, .inputs = {{ @@ -3290,6 +3291,36 @@ struct saa7134_board saa7134_boards[] = { .amux = LINE1, }}, }, + [SAA7134_BOARD_PHILIPS_TIGER_S] = { + .name = "Philips Tiger - S Reference design", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_TDA8290, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tuner_config = 2, + .mpeg = SAA7134_MPEG_DVB, + .gpiomask = 0x0200000, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + },{ + .name = name_comp1, + .vmux = 3, + .amux = LINE1, + },{ + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + }}, + .radio = { + .name = name_radio, + .amux = TV, + .gpio = 0x0200000, + }, + }, }; const unsigned int saa7134_bcount = ARRAY_SIZE(saa7134_boards); @@ -4104,8 +4135,8 @@ int saa7134_board_init1(struct saa7134_dev *dev) break; case SAA7134_BOARD_ADS_DUO_CARDBUS_PTV331: case SAA7134_BOARD_FLYDVBT_HYBRID_CARDBUS: - saa_writeb(SAA7134_GPIO_GPMODE3, 0x08); - saa_writeb(SAA7134_GPIO_GPSTATUS3, 0x00); + saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0x08000000, 0x08000000); + saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x08000000, 0x00000000); break; case SAA7134_BOARD_AVERMEDIA_CARDBUS: /* power-up tuner chip */ @@ -4168,6 +4199,8 @@ int saa7134_board_init2(struct saa7134_dev *dev) tun_setup.mode_mask = T_RADIO | T_ANALOG_TV | T_DIGITAL_TV; tun_setup.type = dev->tuner_type; tun_setup.addr = ADDR_UNSET; + tun_setup.config = 0; + tun_setup.gpio_func = NULL; saa7134_i2c_call_clients (dev, TUNER_SET_TYPE_ADDR, &tun_setup); } @@ -4235,6 +4268,8 @@ int saa7134_board_init2(struct saa7134_dev *dev) tun_setup.mode_mask = T_RADIO | T_ANALOG_TV | T_DIGITAL_TV; tun_setup.type = dev->tuner_type; tun_setup.addr = ADDR_UNSET; + tun_setup.config = 0; + tun_setup.gpio_func = NULL; saa7134_i2c_call_clients (dev, TUNER_SET_TYPE_ADDR,&tun_setup); } @@ -4254,11 +4289,36 @@ int saa7134_board_init2(struct saa7134_dev *dev) tun_setup.mode_mask = T_ANALOG_TV | T_DIGITAL_TV; tun_setup.type = dev->tuner_type; tun_setup.addr = dev->tuner_addr; + tun_setup.config = 0; + tun_setup.gpio_func = NULL; saa7134_i2c_call_clients (dev, TUNER_SET_TYPE_ADDR,&tun_setup); } break; case SAA7134_BOARD_PHILIPS_TIGER: + case SAA7134_BOARD_PHILIPS_TIGER_S: + { + u8 data[] = { 0x3c, 0x33, 0x60}; + struct tuner_setup tun_setup; + struct i2c_msg msg = {.addr=0x08, .flags=0, .buf=data, .len = sizeof(data)}; + if(dev->autodetected && (dev->eedata[0x49] == 0x50)) { + dev->board = SAA7134_BOARD_PHILIPS_TIGER_S; + printk(KERN_INFO "%s: Reconfigured board as %s\n", + dev->name, saa7134_boards[dev->board].name); + } + if(dev->board == SAA7134_BOARD_PHILIPS_TIGER_S) { + tun_setup.mode_mask = T_ANALOG_TV | T_DIGITAL_TV; + tun_setup.type = TUNER_PHILIPS_TDA8290; + tun_setup.addr = 0x4b; + tun_setup.config = 2; + tun_setup.gpio_func = (tuner_gpio_func_t) saa7134_set_gpio; + + saa7134_i2c_call_clients (dev, TUNER_SET_TYPE_ADDR,&tun_setup); + data[2] = 0x68; + } + i2c_transfer(&dev->i2c_adap, &msg, 1); + } + break; case SAA7134_BOARD_PINNACLE_PCTV_310i: case SAA7134_BOARD_TEVION_DVBT_220RF: case SAA7134_BOARD_ASUSTeK_P7131_DUAL: @@ -4268,7 +4328,7 @@ int saa7134_board_init2(struct saa7134_dev *dev) * and configure firmware eeprom address */ { - u8 data[] = { 0x3c, 0x33, 0x68}; + u8 data[] = { 0x3c, 0x33, 0x60}; struct i2c_msg msg = {.addr=0x08, .flags=0, .buf=data, .len = sizeof(data)}; i2c_transfer(&dev->i2c_adap, &msg, 1); } @@ -4282,18 +4342,18 @@ int saa7134_board_init2(struct saa7134_dev *dev) break; case SAA7134_BOARD_ADS_DUO_CARDBUS_PTV331: case SAA7134_BOARD_FLYDVBT_HYBRID_CARDBUS: - /* make the tda10046 find its eeprom */ + /* initialize analog mode */ { - u8 data[] = { 0x3c, 0x33, 0x62}; + u8 data[] = { 0x3c, 0x33, 0x6a}; struct i2c_msg msg = {.addr=0x08, .flags=0, .buf=data, .len = sizeof(data)}; i2c_transfer(&dev->i2c_adap, &msg, 1); } break; case SAA7134_BOARD_CINERGY_HT_PCMCIA: case SAA7134_BOARD_CINERGY_HT_PCI: - /* make the tda10046 find its eeprom */ + /* initialize analog mode */ { - u8 data[] = { 0x3c, 0x33, 0x60}; + u8 data[] = { 0x3c, 0x33, 0x68}; struct i2c_msg msg = {.addr=0x08, .flags=0, .buf=data, .len = sizeof(data)}; i2c_transfer(&dev->i2c_adap, &msg, 1); } diff --git a/drivers/media/video/saa7134/saa7134-core.c b/drivers/media/video/saa7134/saa7134-core.c index 2e0518bb9efd..8ed03d65a34e 100644 --- a/drivers/media/video/saa7134/saa7134-core.c +++ b/drivers/media/video/saa7134/saa7134-core.c @@ -1205,6 +1205,7 @@ module_exit(saa7134_fini); /* ----------------------------------------------------------- */ +EXPORT_SYMBOL(saa7134_set_gpio); EXPORT_SYMBOL(saa7134_i2c_call_clients); EXPORT_SYMBOL(saa7134_devlist); EXPORT_SYMBOL(saa7134_boards); diff --git a/drivers/media/video/saa7134/saa7134-dvb.c b/drivers/media/video/saa7134/saa7134-dvb.c index 6d148a7601ac..61a68c67c4ad 100644 --- a/drivers/media/video/saa7134/saa7134-dvb.c +++ b/drivers/media/video/saa7134/saa7134-dvb.c @@ -54,7 +54,17 @@ static int use_frontend = 0; module_param(use_frontend, int, 0644); MODULE_PARM_DESC(use_frontend,"for cards with multiple frontends (0: terrestrial, 1: satellite)"); -/* ------------------------------------------------------------------ */ +static int debug = 0; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Turn on/off module debugging (default:off)."); + +#define dprintk(fmt, arg...) if (debug) \ + printk(KERN_DEBUG "%s/dvb: " fmt, dev->name , ## arg) + +/* ------------------------------------------------------------------ + * mt352 based DVB-T cards + */ + static int pinnacle_antenna_pwr(struct saa7134_dev *dev, int on) { u32 ok; @@ -185,10 +195,35 @@ static struct mt352_config avermedia_777 = { .demod_init = mt352_aver777_init, }; -/* ------------------------------------------------------------------ */ -static int philips_tda6651_pll_set(u8 addr, struct dvb_frontend *fe, struct dvb_frontend_parameters *params) +/* ================================================================== + * tda1004x based DVB-T cards, helper functions + */ + +static int philips_tda1004x_request_firmware(struct dvb_frontend *fe, + const struct firmware **fw, char *name) { struct saa7134_dev *dev = fe->dvb->priv; + return request_firmware(fw, name, &dev->pci->dev); +} + +static void philips_tda1004x_set_board_name(struct dvb_frontend *fe, char *name) +{ + size_t len; + + len = sizeof(fe->ops.info.name); + strncpy(fe->ops.info.name, name, len); + fe->ops.info.name[len - 1] = 0; +} + +/* ------------------------------------------------------------------ + * these tuners are tu1216, td1316(a) + */ + +static int philips_tda6651_pll_set(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) +{ + struct saa7134_dev *dev = fe->dvb->priv; + struct tda1004x_state *state = fe->demodulator_priv; + u8 addr = state->config->tuner_address; u8 tuner_buf[4]; struct i2c_msg tuner_msg = {.addr = addr,.flags = 0,.buf = tuner_buf,.len = sizeof(tuner_buf) }; @@ -263,15 +298,19 @@ static int philips_tda6651_pll_set(u8 addr, struct dvb_frontend *fe, struct dvb_ if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1); - if (i2c_transfer(&dev->i2c_adap, &tuner_msg, 1) != 1) + if (i2c_transfer(&dev->i2c_adap, &tuner_msg, 1) != 1) { + printk("%s/dvb: could not write to tuner at addr: 0x%02x\n",dev->name, addr << 1); return -EIO; + } msleep(1); return 0; } -static int philips_tda6651_pll_init(u8 addr, struct dvb_frontend *fe) +static int philips_tu1216_init(struct dvb_frontend *fe) { struct saa7134_dev *dev = fe->dvb->priv; + struct tda1004x_state *state = fe->demodulator_priv; + u8 addr = state->config->tuner_address; static u8 tu1216_init[] = { 0x0b, 0xf5, 0x85, 0xab }; struct i2c_msg tuner_msg = {.addr = addr,.flags = 0,.buf = tu1216_init,.len = sizeof(tu1216_init) }; @@ -287,46 +326,17 @@ static int philips_tda6651_pll_init(u8 addr, struct dvb_frontend *fe) /* ------------------------------------------------------------------ */ -static int philips_tu1216_tuner_60_init(struct dvb_frontend *fe) -{ - return philips_tda6651_pll_init(0x60, fe); -} - -static int philips_tu1216_tuner_60_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) -{ - return philips_tda6651_pll_set(0x60, fe, params); -} - -static int philips_tda1004x_request_firmware(struct dvb_frontend *fe, - const struct firmware **fw, char *name) -{ - struct saa7134_dev *dev = fe->dvb->priv; - return request_firmware(fw, name, &dev->pci->dev); -} - static struct tda1004x_config philips_tu1216_60_config = { - .demod_address = 0x8, .invert = 1, .invert_oclk = 0, .xtal_freq = TDA10046_XTAL_4M, .agc_config = TDA10046_AGC_DEFAULT, .if_freq = TDA10046_FREQ_3617, - .request_firmware = philips_tda1004x_request_firmware, + .tuner_address = 0x60, + .request_firmware = philips_tda1004x_request_firmware }; -/* ------------------------------------------------------------------ */ - -static int philips_tu1216_tuner_61_init(struct dvb_frontend *fe) -{ - return philips_tda6651_pll_init(0x61, fe); -} - -static int philips_tu1216_tuner_61_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) -{ - return philips_tda6651_pll_set(0x61, fe, params); -} - static struct tda1004x_config philips_tu1216_61_config = { .demod_address = 0x8, @@ -335,7 +345,8 @@ static struct tda1004x_config philips_tu1216_61_config = { .xtal_freq = TDA10046_XTAL_4M, .agc_config = TDA10046_AGC_DEFAULT, .if_freq = TDA10046_FREQ_3617, - .request_firmware = philips_tda1004x_request_firmware, + .tuner_address = 0x61, + .request_firmware = philips_tda1004x_request_firmware }; /* ------------------------------------------------------------------ */ @@ -343,24 +354,42 @@ static struct tda1004x_config philips_tu1216_61_config = { static int philips_td1316_tuner_init(struct dvb_frontend *fe) { struct saa7134_dev *dev = fe->dvb->priv; + struct tda1004x_state *state = fe->demodulator_priv; + u8 addr = state->config->tuner_address; static u8 msg[] = { 0x0b, 0xf5, 0x86, 0xab }; - struct i2c_msg init_msg = {.addr = 0x61,.flags = 0,.buf = msg,.len = sizeof(msg) }; + struct i2c_msg init_msg = {.addr = addr,.flags = 0,.buf = msg,.len = sizeof(msg) }; /* setup PLL configuration */ if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1); if (i2c_transfer(&dev->i2c_adap, &init_msg, 1) != 1) return -EIO; - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 0); return 0; } static int philips_td1316_tuner_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) { - return philips_tda6651_pll_set(0x61, fe, params); + return philips_tda6651_pll_set(fe, params); +} + +static int philips_td1316_tuner_sleep(struct dvb_frontend *fe) +{ + struct saa7134_dev *dev = fe->dvb->priv; + struct tda1004x_state *state = fe->demodulator_priv; + u8 addr = state->config->tuner_address; + static u8 msg[] = { 0x0b, 0xdc, 0x86, 0xa4 }; + struct i2c_msg analog_msg = {.addr = addr,.flags = 0,.buf = msg,.len = sizeof(msg) }; + + /* switch the tuner to analog mode */ + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if (i2c_transfer(&dev->i2c_adap, &analog_msg, 1) != 1) + return -EIO; + return 0; } +/* ------------------------------------------------------------------ */ + static int philips_europa_tuner_init(struct dvb_frontend *fe) { struct saa7134_dev *dev = fe->dvb->priv; @@ -380,18 +409,14 @@ static int philips_europa_tuner_init(struct dvb_frontend *fe) static int philips_europa_tuner_sleep(struct dvb_frontend *fe) { struct saa7134_dev *dev = fe->dvb->priv; - /* this message actually turns the tuner back to analog mode */ - static u8 msg[] = { 0x0b, 0xdc, 0x86, 0xa4 }; - struct i2c_msg analog_msg = {.addr = 0x61,.flags = 0,.buf = msg,.len = sizeof(msg) }; - i2c_transfer(&dev->i2c_adap, &analog_msg, 1); - msleep(1); + static u8 msg[] = { 0x00, 0x14 }; + struct i2c_msg analog_msg = {.addr = 0x43,.flags = 0,.buf = msg,.len = sizeof(msg) }; + + if (philips_td1316_tuner_sleep(fe)) + return -EIO; /* switch the board to analog mode */ - analog_msg.addr = 0x43; - analog_msg.len = 0x02; - msg[0] = 0x00; - msg[1] = 0x14; if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1); i2c_transfer(&dev->i2c_adap, &analog_msg, 1); @@ -416,7 +441,8 @@ static struct tda1004x_config philips_europa_config = { .xtal_freq = TDA10046_XTAL_4M, .agc_config = TDA10046_AGC_IFO_AUTO_POS, .if_freq = TDA10046_FREQ_052, - .request_firmware = philips_tda1004x_request_firmware, + .tuner_address = 0x61, + .request_firmware = philips_tda1004x_request_firmware }; /* ------------------------------------------------------------------ */ @@ -424,9 +450,11 @@ static struct tda1004x_config philips_europa_config = { static int philips_fmd1216_tuner_init(struct dvb_frontend *fe) { struct saa7134_dev *dev = fe->dvb->priv; + struct tda1004x_state *state = fe->demodulator_priv; + u8 addr = state->config->tuner_address; /* this message is to set up ATC and ALC */ static u8 fmd1216_init[] = { 0x0b, 0xdc, 0x9c, 0xa0 }; - struct i2c_msg tuner_msg = {.addr = 0x61,.flags = 0,.buf = fmd1216_init,.len = sizeof(fmd1216_init) }; + struct i2c_msg tuner_msg = {.addr = addr,.flags = 0,.buf = fmd1216_init,.len = sizeof(fmd1216_init) }; if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1); @@ -440,9 +468,11 @@ static int philips_fmd1216_tuner_init(struct dvb_frontend *fe) static int philips_fmd1216_tuner_sleep(struct dvb_frontend *fe) { struct saa7134_dev *dev = fe->dvb->priv; + struct tda1004x_state *state = fe->demodulator_priv; + u8 addr = state->config->tuner_address; /* this message actually turns the tuner back to analog mode */ static u8 fmd1216_init[] = { 0x0b, 0xdc, 0x9c, 0x60 }; - struct i2c_msg tuner_msg = {.addr = 0x61,.flags = 0,.buf = fmd1216_init,.len = sizeof(fmd1216_init) }; + struct i2c_msg tuner_msg = {.addr = addr,.flags = 0,.buf = fmd1216_init,.len = sizeof(fmd1216_init) }; if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1); @@ -460,8 +490,10 @@ static int philips_fmd1216_tuner_sleep(struct dvb_frontend *fe) static int philips_fmd1216_tuner_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) { struct saa7134_dev *dev = fe->dvb->priv; + struct tda1004x_state *state = fe->demodulator_priv; + u8 addr = state->config->tuner_address; u8 tuner_buf[4]; - struct i2c_msg tuner_msg = {.addr = 0x61,.flags = 0,.buf = tuner_buf,.len = + struct i2c_msg tuner_msg = {.addr = addr,.flags = 0,.buf = tuner_buf,.len = sizeof(tuner_buf) }; int tuner_frequency = 0; int divider = 0; @@ -536,8 +568,10 @@ static int philips_fmd1216_tuner_set_params(struct dvb_frontend *fe, struct dvb_ if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1); - if (i2c_transfer(&dev->i2c_adap, &tuner_msg, 1) != 1) + if (i2c_transfer(&dev->i2c_adap, &tuner_msg, 1) != 1) { + printk("%s/dvb: could not write to tuner at addr: 0x%02x\n",dev->name, addr << 1); return -EIO; + } return 0; } @@ -548,9 +582,76 @@ static struct tda1004x_config medion_cardbus = { .xtal_freq = TDA10046_XTAL_16M, .agc_config = TDA10046_AGC_IFO_AUTO_NEG, .if_freq = TDA10046_FREQ_3613, - .request_firmware = philips_tda1004x_request_firmware, + .tuner_address = 0x61, + .request_firmware = philips_tda1004x_request_firmware }; +/* ------------------------------------------------------------------ + * tda 1004x based cards with philips silicon tuner + */ + +static void philips_tda827x_lna_gain(struct dvb_frontend *fe, int high) +{ + struct saa7134_dev *dev = fe->dvb->priv; + struct tda1004x_state *state = fe->demodulator_priv; + u8 addr = state->config->i2c_gate; + u8 config = state->config->tuner_config; + u8 GP00_CF[] = {0x20, 0x01}; + u8 GP00_LEV[] = {0x22, 0x00}; + + struct i2c_msg msg = {.addr = addr,.flags = 0,.buf = GP00_CF, .len = 2}; + if (config) { + if (high) { + dprintk("setting LNA to high gain\n"); + } else { + dprintk("setting LNA to low gain\n"); + } + } + switch (config) { + case 0: /* no LNA */ + break; + case 1: /* switch is GPIO 0 of tda8290 */ + case 2: + /* turn Vsync off */ + saa7134_set_gpio(dev, 22, 0); + GP00_LEV[1] = high ? 0 : 1; + if (i2c_transfer(&dev->i2c_adap, &msg, 1) != 1) { + printk("%s/dvb: could not access tda8290 at addr: 0x%02x\n",dev->name, addr << 1); + return; + } + msg.buf = GP00_LEV; + if (config == 2) + GP00_LEV[1] = high ? 1 : 0; + i2c_transfer(&dev->i2c_adap, &msg, 1); + break; + case 3: /* switch with GPIO of saa713x */ + saa7134_set_gpio(dev, 22, high); + break; + } +} + +static int tda8290_i2c_gate_ctrl( struct dvb_frontend* fe, int enable) +{ + struct saa7134_dev *dev = fe->dvb->priv; + struct tda1004x_state *state = fe->demodulator_priv; + + u8 addr = state->config->i2c_gate; + static u8 tda8290_close[] = { 0x21, 0xc0}; + static u8 tda8290_open[] = { 0x21, 0x80}; + struct i2c_msg tda8290_msg = {.addr = addr,.flags = 0, .len = 2}; + if (enable) { + tda8290_msg.buf = tda8290_close; + } else { + tda8290_msg.buf = tda8290_open; + } + if (i2c_transfer(&dev->i2c_adap, &tda8290_msg, 1) != 1) { + printk("%s/dvb: could not access tda8290 I2C gate\n",dev->name); + return -EIO; + } + msleep(20); + return 0; +} + /* ------------------------------------------------------------------ */ struct tda827x_data { @@ -595,17 +696,14 @@ static struct tda827x_data tda827x_dvbt[] = { { .lomax = 0, .spd = 0, .bs = 0, .bp = 0, .cp = 0, .gc3 = 0, .div1p5 = 0} }; -static int philips_tda827x_tuner_init(struct dvb_frontend *fe) -{ - return 0; -} - -static int philips_tda827x_tuner_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) +static int philips_tda827xo_pll_set(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) { struct saa7134_dev *dev = fe->dvb->priv; + struct tda1004x_state *state = fe->demodulator_priv; + u8 addr = state->config->tuner_address; u8 tuner_buf[14]; - struct i2c_msg tuner_msg = {.addr = 0x60,.flags = 0,.buf = tuner_buf, + struct i2c_msg tuner_msg = {.addr = addr,.flags = 0,.buf = tuner_buf, .len = sizeof(tuner_buf) }; int i, tuner_freq, if_freq; u32 N; @@ -649,9 +747,10 @@ static int philips_tda827x_tuner_set_params(struct dvb_frontend *fe, struct dvb_ tuner_msg.len = 14; if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1); - if (i2c_transfer(&dev->i2c_adap, &tuner_msg, 1) != 1) + if (i2c_transfer(&dev->i2c_adap, &tuner_msg, 1) != 1) { + printk("%s/dvb: could not write to tuner at addr: 0x%02x\n",dev->name, addr << 1); return -EIO; - + } msleep(500); /* correct CP value */ tuner_buf[0] = 0x30; @@ -664,11 +763,13 @@ static int philips_tda827x_tuner_set_params(struct dvb_frontend *fe, struct dvb_ return 0; } -static int philips_tda827x_tuner_sleep(struct dvb_frontend *fe) +static int philips_tda827xo_tuner_sleep(struct dvb_frontend *fe) { struct saa7134_dev *dev = fe->dvb->priv; + struct tda1004x_state *state = fe->demodulator_priv; + u8 addr = state->config->tuner_address; static u8 tda827x_sleep[] = { 0x30, 0xd0}; - struct i2c_msg tuner_msg = {.addr = 0x60,.flags = 0,.buf = tda827x_sleep, + struct i2c_msg tuner_msg = {.addr = addr,.flags = 0,.buf = tda827x_sleep, .len = sizeof(tda827x_sleep) }; if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1); @@ -676,17 +777,6 @@ static int philips_tda827x_tuner_sleep(struct dvb_frontend *fe) return 0; } -static struct tda1004x_config tda827x_lifeview_config = { - .demod_address = 0x08, - .invert = 1, - .invert_oclk = 0, - .xtal_freq = TDA10046_XTAL_16M, - .agc_config = TDA10046_AGC_TDA827X, - .gpio_config = TDA10046_GP11_I, - .if_freq = TDA10046_FREQ_045, - .request_firmware = philips_tda1004x_request_firmware, -}; - /* ------------------------------------------------------------------ */ struct tda827xa_data { @@ -727,17 +817,20 @@ static struct tda827xa_data tda827xa_dvbt[] = { { .lomax = 911000000, .svco = 3, .spd = 0, .scr = 2, .sbs = 4, .gc3 = 0}, { .lomax = 0, .svco = 0, .spd = 0, .scr = 0, .sbs = 0, .gc3 = 0}}; - -static int philips_tda827xa_pll_set(u8 addr, struct dvb_frontend *fe, struct dvb_frontend_parameters *params) +static int philips_tda827xa_pll_set(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) { struct saa7134_dev *dev = fe->dvb->priv; - u8 tuner_buf[14]; - unsigned char reg2[2]; + struct tda1004x_state *state = fe->demodulator_priv; + u8 addr = state->config->tuner_address; + u8 tuner_buf[10]; struct i2c_msg msg = {.addr = addr,.flags = 0,.buf = tuner_buf}; int i, tuner_freq, if_freq; u32 N; + philips_tda827x_lna_gain( fe, 1); + msleep(20); + switch (params->u.ofdm.bandwidth) { case BANDWIDTH_6_MHZ: if_freq = 4000000; @@ -767,45 +860,82 @@ static int philips_tda827xa_pll_set(u8 addr, struct dvb_frontend *fe, struct dvb tuner_buf[5] = (tda827xa_dvbt[i].spd << 5) + (tda827xa_dvbt[i].svco << 3) + tda827xa_dvbt[i].sbs; tuner_buf[6] = 0x4b + (tda827xa_dvbt[i].gc3 << 4); - tuner_buf[7] = 0x0c; + tuner_buf[7] = 0x1c; tuner_buf[8] = 0x06; tuner_buf[9] = 0x24; - tuner_buf[10] = 0xff; - tuner_buf[11] = 0x60; - tuner_buf[12] = 0x00; - tuner_buf[13] = 0x39; // lpsel - msg.len = 14; + tuner_buf[10] = 0x00; + msg.len = 11; if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1); - if (i2c_transfer(&dev->i2c_adap, &msg, 1) != 1) + if (i2c_transfer(&dev->i2c_adap, &msg, 1) != 1) { + printk("%s/dvb: could not write to tuner at addr: 0x%02x\n",dev->name, addr << 1); return -EIO; + } + tuner_buf[0] = 0x90; + tuner_buf[1] = 0xff; + tuner_buf[2] = 0x60; + tuner_buf[3] = 0x00; + tuner_buf[4] = 0x59; // lpsel, for 6MHz + 2 + msg.len = 5; + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + i2c_transfer(&dev->i2c_adap, &msg, 1); - msg.buf= reg2; + tuner_buf[0] = 0xa0; + tuner_buf[1] = 0x40; msg.len = 2; - reg2[0] = 0x60; - reg2[1] = 0x3c; if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1); i2c_transfer(&dev->i2c_adap, &msg, 1); - reg2[0] = 0xa0; - reg2[1] = 0x40; + msleep(11); + msg.flags = I2C_M_RD; + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + i2c_transfer(&dev->i2c_adap, &msg, 1); + msg.flags = 0; + + tuner_buf[1] >>= 4; + dprintk("tda8275a AGC2 gain is: %d\n", tuner_buf[1]); + if ((tuner_buf[1]) < 2) { + philips_tda827x_lna_gain(fe, 0); + tuner_buf[0] = 0x60; + tuner_buf[1] = 0x0c; + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + i2c_transfer(&dev->i2c_adap, &msg, 1); + } + + tuner_buf[0] = 0xc0; + tuner_buf[1] = 0x99; // lpsel, for 6MHz + 2 + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + i2c_transfer(&dev->i2c_adap, &msg, 1); + + tuner_buf[0] = 0x60; + tuner_buf[1] = 0x3c; if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1); i2c_transfer(&dev->i2c_adap, &msg, 1); - msleep(2); /* correct CP value */ - reg2[0] = 0x30; - reg2[1] = 0x10 + tda827xa_dvbt[i].scr; - msg.len = 2; + tuner_buf[0] = 0x30; + tuner_buf[1] = 0x10 + tda827xa_dvbt[i].scr; + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + i2c_transfer(&dev->i2c_adap, &msg, 1); + + msleep(163); + tuner_buf[0] = 0xc0; + tuner_buf[1] = 0x39; // lpsel, for 6MHz + 2 if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1); i2c_transfer(&dev->i2c_adap, &msg, 1); - msleep(550); - reg2[0] = 0x50; - reg2[1] = 0x4f + (tda827xa_dvbt[i].gc3 << 4); + msleep(3); + /* freeze AGC1 */ + tuner_buf[0] = 0x50; + tuner_buf[1] = 0x4f + (tda827xa_dvbt[i].gc3 << 4); if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1); i2c_transfer(&dev->i2c_adap, &msg, 1); @@ -814,9 +944,11 @@ static int philips_tda827xa_pll_set(u8 addr, struct dvb_frontend *fe, struct dvb } -static int philips_tda827xa_tuner_sleep(u8 addr, struct dvb_frontend *fe) +static int philips_tda827xa_tuner_sleep(struct dvb_frontend *fe) { struct saa7134_dev *dev = fe->dvb->priv; + struct tda1004x_state *state = fe->demodulator_priv; + u8 addr = state->config->tuner_address; static u8 tda827xa_sleep[] = { 0x30, 0x90}; struct i2c_msg tuner_msg = {.addr = addr,.flags = 0,.buf = tda827xa_sleep, .len = sizeof(tda827xa_sleep) }; @@ -828,60 +960,98 @@ static int philips_tda827xa_tuner_sleep(u8 addr, struct dvb_frontend *fe) return 0; } -/* ------------------------------------------------------------------ */ +/* ------------------------------------------------------------------ + * upper layer: distinguish the silicon tuner versions + */ -static int tda8290_i2c_gate_ctrl(struct dvb_frontend* fe, int enable) +static int philips_tda827x_tuner_init(struct dvb_frontend *fe) { struct saa7134_dev *dev = fe->dvb->priv; - static u8 tda8290_close[] = { 0x21, 0xc0}; - static u8 tda8290_open[] = { 0x21, 0x80}; - struct i2c_msg tda8290_msg = {.addr = 0x4b,.flags = 0, .len = 2}; - if (enable) { - tda8290_msg.buf = tda8290_close; + struct tda1004x_state *state = fe->demodulator_priv; + u8 addr = state->config->tuner_address; + u8 data; + struct i2c_msg tuner_msg = {.addr = addr,.flags = I2C_M_RD,.buf = &data, .len = 1}; + state->conf_probed = 0; + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if (i2c_transfer(&dev->i2c_adap, &tuner_msg, 1) != 1) { + printk("%s/dvb: could not read from tuner at addr: 0x%02x\n",dev->name, addr << 1); + return -EIO; + } + if ((data & 0x3c) == 0) { + dprintk("tda827x tuner found\n"); + state->conf_probed = 1; } else { - tda8290_msg.buf = tda8290_open; + dprintk("tda827xa tuner found\n"); + state->conf_probed = 2; + } + switch (state->config->antenna_switch) { + case 0: break; + case 1: dprintk("setting GPIO21 to 0 (TV antenna?)\n"); + saa7134_set_gpio(dev, 21, 0); + break; + case 2: dprintk("setting GPIO21 to 1 (Radio antenna?)\n"); + saa7134_set_gpio(dev, 21, 1); + break; } - if (i2c_transfer(&dev->i2c_adap, &tda8290_msg, 1) != 1) - return -EIO; - msleep(20); return 0; } -/* ------------------------------------------------------------------ */ - -static int philips_tiger_tuner_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) +static int philips_tda827x_tuner_sleep(struct dvb_frontend *fe) { - int ret; - - ret = philips_tda827xa_pll_set(0x61, fe, params); - if (ret != 0) - return ret; + struct saa7134_dev *dev = fe->dvb->priv; + struct tda1004x_state *state = fe->demodulator_priv; + switch (state->conf_probed) { + case 1: philips_tda827xo_tuner_sleep(fe); + break; + case 2: philips_tda827xa_tuner_sleep(fe); + break; + default: dprintk("Huh? unknown tda827x version!\n"); + return -EIO; + } + switch (state->config->antenna_switch) { + case 0: break; + case 1: dprintk("setting GPIO21 to 1 (Radio antenna?)\n"); + saa7134_set_gpio(dev, 21, 1); + break; + case 2: dprintk("setting GPIO21 to 0 (TV antenna?)\n"); + saa7134_set_gpio(dev, 21, 0); + break; + } return 0; } -static int philips_tiger_tuner_init(struct dvb_frontend *fe) +static int philips_tda827x_pll_set(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) { struct saa7134_dev *dev = fe->dvb->priv; - static u8 data[] = { 0x3c, 0x33, 0x6a}; - struct i2c_msg msg = {.addr=0x08, .flags=0, .buf=data, .len = sizeof(data)}; - - if (i2c_transfer(&dev->i2c_adap, &msg, 1) != 1) + struct tda1004x_state *state = fe->demodulator_priv; + switch (state->conf_probed) { + case 1: philips_tda827xo_pll_set(fe, params); + break; + case 2: philips_tda827xa_pll_set(fe, params); + break; + default: dprintk("Huh? unknown tda827x version!\n"); return -EIO; + } return 0; } -static int philips_tiger_tuner_sleep(struct dvb_frontend *fe) +static void configure_tda827x_fe(struct saa7134_dev *dev, struct tda1004x_config *tda_conf, + char *board_name) { - struct saa7134_dev *dev = fe->dvb->priv; - static u8 data[] = { 0x3c, 0x33, 0x68}; - struct i2c_msg msg = {.addr=0x08, .flags=0, .buf=data, .len = sizeof(data)}; - - i2c_transfer(&dev->i2c_adap, &msg, 1); - philips_tda827xa_tuner_sleep( 0x61, fe); - return 0; + dev->dvb.frontend = dvb_attach(tda10046_attach, tda_conf, &dev->i2c_adap); + if (dev->dvb.frontend) { + if (tda_conf->i2c_gate) + dev->dvb.frontend->ops.i2c_gate_ctrl = tda8290_i2c_gate_ctrl; + dev->dvb.frontend->ops.tuner_ops.init = philips_tda827x_tuner_init; + dev->dvb.frontend->ops.tuner_ops.sleep = philips_tda827x_tuner_sleep; + dev->dvb.frontend->ops.tuner_ops.set_params = philips_tda827x_pll_set; + } + philips_tda1004x_set_board_name(dev->dvb.frontend, board_name); } -static struct tda1004x_config philips_tiger_config = { +/* ------------------------------------------------------------------ */ +static struct tda1004x_config tda827x_lifeview_config = { .demod_address = 0x08, .invert = 1, .invert_oclk = 0, @@ -889,31 +1059,24 @@ static struct tda1004x_config philips_tiger_config = { .agc_config = TDA10046_AGC_TDA827X, .gpio_config = TDA10046_GP11_I, .if_freq = TDA10046_FREQ_045, - .request_firmware = philips_tda1004x_request_firmware, + .tuner_address = 0x60, + .request_firmware = philips_tda1004x_request_firmware }; -/* ------------------------------------------------------------------ */ - -static int cinergy_ht_tuner_init(struct dvb_frontend *fe) -{ - struct saa7134_dev *dev = fe->dvb->priv; - static u8 data[] = { 0x3c, 0x33, 0x62}; - struct i2c_msg msg = {.addr=0x08, .flags=0, .buf=data, .len = sizeof(data)}; - - if (i2c_transfer(&dev->i2c_adap, &msg, 1) != 1) - return -EIO; - return 0; -} - -static int cinergy_ht_tuner_sleep(struct dvb_frontend *fe) -{ - struct saa7134_dev *dev = fe->dvb->priv; - static u8 data[] = { 0x3c, 0x33, 0x60}; - struct i2c_msg msg = {.addr=0x08, .flags=0, .buf=data, .len = sizeof(data)}; - i2c_transfer(&dev->i2c_adap, &msg, 1); - philips_tda827xa_tuner_sleep( 0x61, fe); - return 0; -} +static struct tda1004x_config philips_tiger_config = { + .demod_address = 0x08, + .invert = 1, + .invert_oclk = 0, + .xtal_freq = TDA10046_XTAL_16M, + .agc_config = TDA10046_AGC_TDA827X, + .gpio_config = TDA10046_GP11_I, + .if_freq = TDA10046_FREQ_045, + .i2c_gate = 0x4b, + .tuner_address = 0x61, + .tuner_config = 0, + .antenna_switch= 1, + .request_firmware = philips_tda1004x_request_firmware +}; static struct tda1004x_config cinergy_ht_config = { .demod_address = 0x08, @@ -923,10 +1086,40 @@ static struct tda1004x_config cinergy_ht_config = { .agc_config = TDA10046_AGC_TDA827X, .gpio_config = TDA10046_GP01_I, .if_freq = TDA10046_FREQ_045, - .request_firmware = philips_tda1004x_request_firmware, + .i2c_gate = 0x4b, + .tuner_address = 0x61, + .tuner_config = 0, + .request_firmware = philips_tda1004x_request_firmware }; -/* ------------------------------------------------------------------ */ +static struct tda1004x_config cinergy_ht_pci_config = { + .demod_address = 0x08, + .invert = 1, + .invert_oclk = 0, + .xtal_freq = TDA10046_XTAL_16M, + .agc_config = TDA10046_AGC_TDA827X, + .gpio_config = TDA10046_GP01_I, + .if_freq = TDA10046_FREQ_045, + .i2c_gate = 0x4b, + .tuner_address = 0x60, + .tuner_config = 0, + .request_firmware = philips_tda1004x_request_firmware +}; + +static struct tda1004x_config philips_tiger_s_config = { + .demod_address = 0x08, + .invert = 1, + .invert_oclk = 0, + .xtal_freq = TDA10046_XTAL_16M, + .agc_config = TDA10046_AGC_TDA827X, + .gpio_config = TDA10046_GP01_I, + .if_freq = TDA10046_FREQ_045, + .i2c_gate = 0x4b, + .tuner_address = 0x61, + .tuner_config = 2, + .antenna_switch= 1, + .request_firmware = philips_tda1004x_request_firmware +}; static struct tda1004x_config pinnacle_pctv_310i_config = { .demod_address = 0x08, @@ -936,11 +1129,12 @@ static struct tda1004x_config pinnacle_pctv_310i_config = { .agc_config = TDA10046_AGC_TDA827X, .gpio_config = TDA10046_GP11_I, .if_freq = TDA10046_FREQ_045, - .request_firmware = philips_tda1004x_request_firmware, + .i2c_gate = 0x4b, + .tuner_address = 0x61, + .tuner_config = 1, + .request_firmware = philips_tda1004x_request_firmware }; -/* ------------------------------------------------------------------ */ - static struct tda1004x_config hauppauge_hvr_1110_config = { .demod_address = 0x08, .invert = 1, @@ -949,11 +1143,11 @@ static struct tda1004x_config hauppauge_hvr_1110_config = { .agc_config = TDA10046_AGC_TDA827X, .gpio_config = TDA10046_GP11_I, .if_freq = TDA10046_FREQ_045, - .request_firmware = philips_tda1004x_request_firmware, + .i2c_gate = 0x4b, + .tuner_address = 0x61, + .request_firmware = philips_tda1004x_request_firmware }; -/* ------------------------------------------------------------------ */ - static struct tda1004x_config asus_p7131_dual_config = { .demod_address = 0x08, .invert = 1, @@ -962,51 +1156,13 @@ static struct tda1004x_config asus_p7131_dual_config = { .agc_config = TDA10046_AGC_TDA827X, .gpio_config = TDA10046_GP11_I, .if_freq = TDA10046_FREQ_045, - .request_firmware = philips_tda1004x_request_firmware, + .i2c_gate = 0x4b, + .tuner_address = 0x61, + .tuner_config = 0, + .antenna_switch= 2, + .request_firmware = philips_tda1004x_request_firmware }; -static int asus_p7131_dual_tuner_init(struct dvb_frontend *fe) -{ - struct saa7134_dev *dev = fe->dvb->priv; - static u8 data[] = { 0x3c, 0x33, 0x6a}; - struct i2c_msg msg = {.addr=0x08, .flags=0, .buf=data, .len = sizeof(data)}; - - if (i2c_transfer(&dev->i2c_adap, &msg, 1) != 1) - return -EIO; - /* make sure the DVB-T antenna input is set */ - saa_setl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x0200000); - return 0; -} - -static int asus_p7131_dual_tuner_sleep(struct dvb_frontend *fe) -{ - struct saa7134_dev *dev = fe->dvb->priv; - static u8 data[] = { 0x3c, 0x33, 0x68}; - struct i2c_msg msg = {.addr=0x08, .flags=0, .buf=data, .len = sizeof(data)}; - - i2c_transfer(&dev->i2c_adap, &msg, 1); - philips_tda827xa_tuner_sleep( 0x61, fe); - /* reset antenna inputs for analog usage */ - saa_clearl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x0200000); - return 0; -} - -/* ------------------------------------------------------------------ */ - -static int lifeview_trio_tuner_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) -{ - int ret; - - ret = philips_tda827xa_pll_set(0x60, fe, params); - return ret; -} - -static int lifeview_trio_tuner_sleep(struct dvb_frontend *fe) -{ - philips_tda827xa_tuner_sleep(0x60, fe); - return 0; -} - static struct tda1004x_config lifeview_trio_config = { .demod_address = 0x09, .invert = 1, @@ -1015,125 +1171,82 @@ static struct tda1004x_config lifeview_trio_config = { .agc_config = TDA10046_AGC_TDA827X, .gpio_config = TDA10046_GP00_I, .if_freq = TDA10046_FREQ_045, - .request_firmware = philips_tda1004x_request_firmware, + .tuner_address = 0x60, + .request_firmware = philips_tda1004x_request_firmware }; -/* ------------------------------------------------------------------ */ - -static int ads_duo_tuner_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) -{ - int ret; - - ret = philips_tda827xa_pll_set(0x61, fe, params); - return ret; -} - -static int ads_duo_tuner_init(struct dvb_frontend *fe) -{ - struct saa7134_dev *dev = fe->dvb->priv; - /* route TDA8275a AGC input to the channel decoder */ - saa_writeb(SAA7134_GPIO_GPSTATUS2, 0x60); - return 0; -} - -static int ads_duo_tuner_sleep(struct dvb_frontend *fe) -{ - struct saa7134_dev *dev = fe->dvb->priv; - /* route TDA8275a AGC input to the analog IF chip*/ - saa_writeb(SAA7134_GPIO_GPSTATUS2, 0x20); - philips_tda827xa_tuner_sleep( 0x61, fe); - return 0; -} - -static struct tda1004x_config ads_tech_duo_config = { +static struct tda1004x_config tevion_dvbt220rf_config = { .demod_address = 0x08, .invert = 1, .invert_oclk = 0, .xtal_freq = TDA10046_XTAL_16M, .agc_config = TDA10046_AGC_TDA827X, - .gpio_config = TDA10046_GP00_I, + .gpio_config = TDA10046_GP11_I, .if_freq = TDA10046_FREQ_045, - .request_firmware = philips_tda1004x_request_firmware, + .tuner_address = 0x60, + .request_firmware = philips_tda1004x_request_firmware }; -/* ------------------------------------------------------------------ */ - -static int tevion_dvb220rf_tuner_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) -{ - int ret; - ret = philips_tda827xa_pll_set(0x60, fe, params); - return ret; -} - -static int tevion_dvb220rf_tuner_sleep(struct dvb_frontend *fe) -{ - philips_tda827xa_tuner_sleep( 0x61, fe); - return 0; -} - -static struct tda1004x_config tevion_dvbt220rf_config = { +static struct tda1004x_config md8800_dvbt_config = { .demod_address = 0x08, .invert = 1, .invert_oclk = 0, .xtal_freq = TDA10046_XTAL_16M, .agc_config = TDA10046_AGC_TDA827X, - .gpio_config = TDA10046_GP11_I, + .gpio_config = TDA10046_GP01_I, .if_freq = TDA10046_FREQ_045, - .request_firmware = philips_tda1004x_request_firmware, + .i2c_gate = 0x4b, + .tuner_address = 0x60, + .tuner_config = 0, + .request_firmware = philips_tda1004x_request_firmware }; -/* ------------------------------------------------------------------ */ +/* ------------------------------------------------------------------ + * special case: this card uses saa713x GPIO22 for the mode switch + */ -static int md8800_dvbt_analog_mode(struct dvb_frontend *fe) +static int ads_duo_tuner_init(struct dvb_frontend *fe) { struct saa7134_dev *dev = fe->dvb->priv; - static u8 data[] = { 0x3c, 0x33, 0x68}; - struct i2c_msg msg = {.addr=0x08, .flags=0, .buf=data, .len = sizeof(data)}; - - i2c_transfer(&dev->i2c_adap, &msg, 1); - philips_tda827xa_tuner_sleep( 0x61, fe); + philips_tda827x_tuner_init(fe); + /* route TDA8275a AGC input to the channel decoder */ + saa_setl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x0400000); return 0; } -static int md8800_dvbt_pll_set(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) +static int ads_duo_tuner_sleep(struct dvb_frontend *fe) { - int ret; struct saa7134_dev *dev = fe->dvb->priv; - static u8 tda8290_close[] = { 0x21, 0xc0}; - static u8 tda8290_open[] = { 0x21, 0x80}; - struct i2c_msg tda8290_msg = {.addr = 0x4b,.flags = 0, .len = 2}; - /* close tda8290 i2c bridge */ - tda8290_msg.buf = tda8290_close; - ret = i2c_transfer(&dev->i2c_adap, &tda8290_msg, 1); - if (ret != 1) - return -EIO; - msleep(20); - ret = philips_tda827xa_pll_set(0x60, fe, params); - if (ret != 0) - return ret; - /* open tda8290 i2c bridge */ - tda8290_msg.buf = tda8290_open; - i2c_transfer(&dev->i2c_adap, &tda8290_msg, 1); - return ret; + /* route TDA8275a AGC input to the analog IF chip*/ + saa_clearl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x0400000); + philips_tda827x_tuner_sleep(fe); + return 0; } -static struct tda1004x_config md8800_dvbt_config = { +static struct tda1004x_config ads_tech_duo_config = { .demod_address = 0x08, .invert = 1, .invert_oclk = 0, .xtal_freq = TDA10046_XTAL_16M, .agc_config = TDA10046_AGC_TDA827X, - .gpio_config = TDA10046_GP11_I, + .gpio_config = TDA10046_GP00_I, .if_freq = TDA10046_FREQ_045, - .request_firmware = philips_tda1004x_request_firmware, + .tuner_address = 0x61, + .request_firmware = philips_tda1004x_request_firmware }; +/* ================================================================== + * tda10086 based DVB-S cards, helper functions + */ + static struct tda10086_config flydvbs = { .demod_address = 0x0e, .invert = 0, }; -/* ------------------------------------------------------------------ */ +/* ================================================================== + * nxt200x based ATSC cards, helper functions + */ static struct nxt200x_config avertvhda180 = { .demod_address = 0x0a, @@ -1153,10 +1266,13 @@ static struct nxt200x_config kworldatsc110 = { .set_pll_input = nxt200x_set_pll_input, }; -/* ------------------------------------------------------------------ */ +/* ================================================================== + * Core code + */ static int dvb_init(struct saa7134_dev *dev) { + char *board_name; /* init struct videobuf_dvb */ dev->ts.nr_bufs = 32; dev->ts.nr_packets = 32*4; @@ -1194,6 +1310,7 @@ static int dvb_init(struct saa7134_dev *dev) dev->dvb.frontend->ops.tuner_ops.init = philips_fmd1216_tuner_init; dev->dvb.frontend->ops.tuner_ops.sleep = philips_fmd1216_tuner_sleep; dev->dvb.frontend->ops.tuner_ops.set_params = philips_fmd1216_tuner_set_params; + philips_tda1004x_set_board_name(dev->dvb.frontend, "DVB-T Medion MD7134"); } break; case SAA7134_BOARD_PHILIPS_TOUGH: @@ -1201,42 +1318,16 @@ static int dvb_init(struct saa7134_dev *dev) &philips_tu1216_60_config, &dev->i2c_adap); if (dev->dvb.frontend) { - dev->dvb.frontend->ops.tuner_ops.init = philips_tu1216_tuner_60_init; - dev->dvb.frontend->ops.tuner_ops.set_params = philips_tu1216_tuner_60_set_params; + dev->dvb.frontend->ops.tuner_ops.init = philips_tu1216_init; + dev->dvb.frontend->ops.tuner_ops.set_params = philips_tda6651_pll_set; + philips_tda1004x_set_board_name(dev->dvb.frontend, "DVB-T Philips TOUGH"); } break; case SAA7134_BOARD_FLYDVBTDUO: - dev->dvb.frontend = dvb_attach(tda10046_attach, - &tda827x_lifeview_config, - &dev->i2c_adap); - if (dev->dvb.frontend) { - dev->dvb.frontend->ops.tuner_ops.init = philips_tda827x_tuner_init; - dev->dvb.frontend->ops.tuner_ops.sleep = philips_tda827x_tuner_sleep; - dev->dvb.frontend->ops.tuner_ops.set_params = philips_tda827x_tuner_set_params; - } - break; case SAA7134_BOARD_FLYDVBT_DUO_CARDBUS: - dev->dvb.frontend = dvb_attach(tda10046_attach, - &tda827x_lifeview_config, - &dev->i2c_adap); - if (dev->dvb.frontend) { - dev->dvb.frontend->ops.tuner_ops.init = philips_tda827x_tuner_init; - dev->dvb.frontend->ops.tuner_ops.sleep = philips_tda827x_tuner_sleep; - dev->dvb.frontend->ops.tuner_ops.set_params = philips_tda827x_tuner_set_params; - } + configure_tda827x_fe(dev, &tda827x_lifeview_config, "DVB-T Lifeview FlyDVB Duo"); break; case SAA7134_BOARD_PHILIPS_EUROPA: - dev->dvb.frontend = dvb_attach(tda10046_attach, - &philips_europa_config, - &dev->i2c_adap); - if (dev->dvb.frontend) { - dev->original_demod_sleep = dev->dvb.frontend->ops.sleep; - dev->dvb.frontend->ops.sleep = philips_europa_demod_sleep; - dev->dvb.frontend->ops.tuner_ops.init = philips_europa_tuner_init; - dev->dvb.frontend->ops.tuner_ops.sleep = philips_europa_tuner_sleep; - dev->dvb.frontend->ops.tuner_ops.set_params = philips_td1316_tuner_set_params; - } - break; case SAA7134_BOARD_VIDEOMATE_DVBT_300: dev->dvb.frontend = dvb_attach(tda10046_attach, &philips_europa_config, @@ -1247,6 +1338,11 @@ static int dvb_init(struct saa7134_dev *dev) dev->dvb.frontend->ops.tuner_ops.init = philips_europa_tuner_init; dev->dvb.frontend->ops.tuner_ops.sleep = philips_europa_tuner_sleep; dev->dvb.frontend->ops.tuner_ops.set_params = philips_td1316_tuner_set_params; + if (dev->board == SAA7134_BOARD_VIDEOMATE_DVBT_300) + board_name = "DVB-T Compro VideoMate 300"; + else + board_name = "DVB-T Philips Europa"; + philips_tda1004x_set_board_name(dev->dvb.frontend, board_name); } break; case SAA7134_BOARD_VIDEOMATE_DVBT_200: @@ -1254,74 +1350,29 @@ static int dvb_init(struct saa7134_dev *dev) &philips_tu1216_61_config, &dev->i2c_adap); if (dev->dvb.frontend) { - dev->dvb.frontend->ops.tuner_ops.init = philips_tu1216_tuner_61_init; - dev->dvb.frontend->ops.tuner_ops.set_params = philips_tu1216_tuner_61_set_params; + dev->dvb.frontend->ops.tuner_ops.init = philips_tu1216_init; + dev->dvb.frontend->ops.tuner_ops.set_params = philips_tda6651_pll_set; + philips_tda1004x_set_board_name(dev->dvb.frontend, "DVB-T Compro VideoMate 200"); } break; case SAA7134_BOARD_PHILIPS_TIGER: - dev->dvb.frontend = dvb_attach(tda10046_attach, - &philips_tiger_config, - &dev->i2c_adap); - if (dev->dvb.frontend) { - dev->dvb.frontend->ops.i2c_gate_ctrl = tda8290_i2c_gate_ctrl; - dev->dvb.frontend->ops.tuner_ops.init = philips_tiger_tuner_init; - dev->dvb.frontend->ops.tuner_ops.sleep = philips_tiger_tuner_sleep; - dev->dvb.frontend->ops.tuner_ops.set_params = philips_tiger_tuner_set_params; - } + configure_tda827x_fe(dev, &philips_tiger_config, "DVB-T Philips Tiger"); break; case SAA7134_BOARD_PINNACLE_PCTV_310i: - dev->dvb.frontend = dvb_attach(tda10046_attach, - &pinnacle_pctv_310i_config, - &dev->i2c_adap); - if (dev->dvb.frontend) { - dev->dvb.frontend->ops.i2c_gate_ctrl = tda8290_i2c_gate_ctrl; - dev->dvb.frontend->ops.tuner_ops.init = philips_tiger_tuner_init; - dev->dvb.frontend->ops.tuner_ops.sleep = philips_tiger_tuner_sleep; - dev->dvb.frontend->ops.tuner_ops.set_params = philips_tiger_tuner_set_params; - } + configure_tda827x_fe(dev, &pinnacle_pctv_310i_config, "DVB-T Pinnacle PCTV 310i"); break; case SAA7134_BOARD_HAUPPAUGE_HVR1110: - dev->dvb.frontend = dvb_attach(tda10046_attach, - &hauppauge_hvr_1110_config, - &dev->i2c_adap); - if (dev->dvb.frontend) { - dev->dvb.frontend->ops.i2c_gate_ctrl = tda8290_i2c_gate_ctrl; - dev->dvb.frontend->ops.tuner_ops.init = philips_tiger_tuner_init; - dev->dvb.frontend->ops.tuner_ops.sleep = philips_tiger_tuner_sleep; - dev->dvb.frontend->ops.tuner_ops.set_params = philips_tiger_tuner_set_params; - } + configure_tda827x_fe(dev, &hauppauge_hvr_1110_config, "DVB-T Hauppauge HVR 1110"); break; case SAA7134_BOARD_ASUSTeK_P7131_DUAL: - dev->dvb.frontend = dvb_attach(tda10046_attach, - &asus_p7131_dual_config, - &dev->i2c_adap); - if (dev->dvb.frontend) { - dev->dvb.frontend->ops.i2c_gate_ctrl = tda8290_i2c_gate_ctrl; - dev->dvb.frontend->ops.tuner_ops.init = asus_p7131_dual_tuner_init; - dev->dvb.frontend->ops.tuner_ops.sleep = asus_p7131_dual_tuner_sleep; - dev->dvb.frontend->ops.tuner_ops.set_params = philips_tiger_tuner_set_params; - } + configure_tda827x_fe(dev, &asus_p7131_dual_config, "DVB-T Asus P7137 Dual"); break; case SAA7134_BOARD_FLYDVBT_LR301: - dev->dvb.frontend = dvb_attach(tda10046_attach, - &tda827x_lifeview_config, - &dev->i2c_adap); - if (dev->dvb.frontend) { - dev->dvb.frontend->ops.tuner_ops.init = philips_tda827x_tuner_init; - dev->dvb.frontend->ops.tuner_ops.sleep = philips_tda827x_tuner_sleep; - dev->dvb.frontend->ops.tuner_ops.set_params = philips_tda827x_tuner_set_params; - } + configure_tda827x_fe(dev, &tda827x_lifeview_config, "DVB-T Lifeview FlyDVBT LR301"); break; case SAA7134_BOARD_FLYDVB_TRIO: if(! use_frontend) { //terrestrial - dev->dvb.frontend = dvb_attach(tda10046_attach, - &lifeview_trio_config, - &dev->i2c_adap); - if (dev->dvb.frontend) { - dev->dvb.frontend->ops.tuner_ops.sleep = lifeview_trio_tuner_sleep; - dev->dvb.frontend->ops.tuner_ops.set_params = - lifeview_trio_tuner_set_params; - } + configure_tda827x_fe(dev, &lifeview_trio_config, NULL); } else { //satellite dev->dvb.frontend = dvb_attach(tda10086_attach, &flydvbs, &dev->i2c_adap); if (dev->dvb.frontend) { @@ -1337,42 +1388,26 @@ static int dvb_init(struct saa7134_dev *dev) } break; case SAA7134_BOARD_ADS_DUO_CARDBUS_PTV331: + case SAA7134_BOARD_FLYDVBT_HYBRID_CARDBUS: dev->dvb.frontend = dvb_attach(tda10046_attach, &ads_tech_duo_config, &dev->i2c_adap); if (dev->dvb.frontend) { dev->dvb.frontend->ops.tuner_ops.init = ads_duo_tuner_init; dev->dvb.frontend->ops.tuner_ops.sleep = ads_duo_tuner_sleep; - dev->dvb.frontend->ops.tuner_ops.set_params = ads_duo_tuner_set_params; + dev->dvb.frontend->ops.tuner_ops.set_params = philips_tda827x_pll_set; + if (dev->board == SAA7134_BOARD_ADS_DUO_CARDBUS_PTV331) + board_name = "DVB-T ADS DUO Cardbus PTV331"; + else + board_name = "DVB-T Lifeview FlyDVT Cardbus"; + philips_tda1004x_set_board_name(dev->dvb.frontend, board_name); } break; case SAA7134_BOARD_TEVION_DVBT_220RF: - dev->dvb.frontend = dvb_attach(tda10046_attach, - &tevion_dvbt220rf_config, - &dev->i2c_adap); - if (dev->dvb.frontend) { - dev->dvb.frontend->ops.tuner_ops.sleep = tevion_dvb220rf_tuner_sleep; - dev->dvb.frontend->ops.tuner_ops.set_params = tevion_dvb220rf_tuner_set_params; - } - break; - case SAA7134_BOARD_FLYDVBT_HYBRID_CARDBUS: - dev->dvb.frontend = dvb_attach(tda10046_attach, - &ads_tech_duo_config, - &dev->i2c_adap); - if (dev->dvb.frontend) { - dev->dvb.frontend->ops.tuner_ops.init = ads_duo_tuner_init; - dev->dvb.frontend->ops.tuner_ops.sleep = ads_duo_tuner_sleep; - dev->dvb.frontend->ops.tuner_ops.set_params = ads_duo_tuner_set_params; - } + configure_tda827x_fe(dev, &tevion_dvbt220rf_config, "DVB-T Tevion 220RF"); break; case SAA7134_BOARD_MEDION_MD8800_QUADRO: - dev->dvb.frontend = tda10046_attach(&md8800_dvbt_config, - &dev->i2c_adap); - if (dev->dvb.frontend) { - dev->dvb.frontend->ops.tuner_ops.init = philips_tiger_tuner_init; - dev->dvb.frontend->ops.tuner_ops.sleep = md8800_dvbt_analog_mode; - dev->dvb.frontend->ops.tuner_ops.set_params = md8800_dvbt_pll_set; - } + configure_tda827x_fe(dev, &md8800_dvbt_config, "DVB-T Medion MD8800"); break; case SAA7134_BOARD_AVERMEDIA_AVERTVHD_A180: dev->dvb.frontend = dvb_attach(nxt200x_attach, &avertvhda180, @@ -1413,6 +1448,7 @@ static int dvb_init(struct saa7134_dev *dev) dev->dvb.frontend->ops.tuner_ops.init = philips_fmd1216_tuner_init; dev->dvb.frontend->ops.tuner_ops.sleep = philips_fmd1216_tuner_sleep; dev->dvb.frontend->ops.tuner_ops.set_params = philips_fmd1216_tuner_set_params; + philips_tda1004x_set_board_name(dev->dvb.frontend, "DVBT Asus Europa 2 Hybrid"); } break; case SAA7134_BOARD_VIDEOMATE_DVBT_200A: @@ -1422,31 +1458,17 @@ static int dvb_init(struct saa7134_dev *dev) if (dev->dvb.frontend) { dev->dvb.frontend->ops.tuner_ops.init = philips_td1316_tuner_init; dev->dvb.frontend->ops.tuner_ops.set_params = philips_td1316_tuner_set_params; + philips_tda1004x_set_board_name(dev->dvb.frontend, "DVBT Compro Videomate 200a"); } break; case SAA7134_BOARD_CINERGY_HT_PCMCIA: - dev->dvb.frontend = dvb_attach(tda10046_attach, - &cinergy_ht_config, - &dev->i2c_adap); - if (dev->dvb.frontend) { - dev->dvb.frontend->ops.i2c_gate_ctrl = tda8290_i2c_gate_ctrl; - dev->dvb.frontend->ops.tuner_ops.init = cinergy_ht_tuner_init; - dev->dvb.frontend->ops.tuner_ops.sleep = cinergy_ht_tuner_sleep; - dev->dvb.frontend->ops.tuner_ops.set_params = philips_tiger_tuner_set_params; - - } + configure_tda827x_fe(dev, &cinergy_ht_config, "DVB-T Terratec Cinergy HT Cardbus"); break; case SAA7134_BOARD_CINERGY_HT_PCI: - dev->dvb.frontend = dvb_attach(tda10046_attach, - &cinergy_ht_config, - &dev->i2c_adap); - if (dev->dvb.frontend) { - dev->dvb.frontend->ops.i2c_gate_ctrl = tda8290_i2c_gate_ctrl; - dev->dvb.frontend->ops.tuner_ops.init = cinergy_ht_tuner_init; - dev->dvb.frontend->ops.tuner_ops.sleep = cinergy_ht_tuner_sleep; - dev->dvb.frontend->ops.tuner_ops.set_params = md8800_dvbt_pll_set; - - } + configure_tda827x_fe(dev, &cinergy_ht_pci_config, "DVB-T Terratec Cinergy HT PCI"); + break; + case SAA7134_BOARD_PHILIPS_TIGER_S: + configure_tda827x_fe(dev, &philips_tiger_s_config, "DVB-T Philips Tiger S"); break; default: printk("%s: Huh? unknown DVB card?\n",dev->name); diff --git a/drivers/media/video/saa7134/saa7134-video.c b/drivers/media/video/saa7134/saa7134-video.c index f2cb63053041..e1cb273e16bc 100644 --- a/drivers/media/video/saa7134/saa7134-video.c +++ b/drivers/media/video/saa7134/saa7134-video.c @@ -603,7 +603,14 @@ static void set_tvnorm(struct saa7134_dev *dev, struct saa7134_tvnorm *norm) saa_writeb(SAA7134_RAW_DATA_GAIN, 0x40); saa_writeb(SAA7134_RAW_DATA_OFFSET, 0x80); - saa7134_i2c_call_clients(dev,VIDIOC_S_STD,&norm->id); + /* only tell the tuner if this is a tv input */ + if (card_in(dev,dev->ctl_input).tv) { + if ((card(dev).tuner_type == TUNER_PHILIPS_TDA8290) + && ((card(dev).tuner_config == 1) + || (card(dev).tuner_config == 2))) + saa7134_set_gpio(dev, 22, 5); + saa7134_i2c_call_clients(dev,VIDIOC_S_STD,&norm->id); + } } static void video_mux(struct saa7134_dev *dev, int input) diff --git a/drivers/media/video/saa7134/saa7134.h b/drivers/media/video/saa7134/saa7134.h index 303c0806df91..b46265a97565 100644 --- a/drivers/media/video/saa7134/saa7134.h +++ b/drivers/media/video/saa7134/saa7134.h @@ -231,6 +231,7 @@ struct saa7134_format { #define SAA7134_BOARD_ENCORE_ENLTV 106 #define SAA7134_BOARD_ENCORE_ENLTV_FM 107 #define SAA7134_BOARD_CINERGY_HT_PCI 108 +#define SAA7134_BOARD_PHILIPS_TIGER_S 109 #define SAA7134_MAXBOARDS 8 #define SAA7134_INPUT_MAX 8 diff --git a/drivers/media/video/tda8290.c b/drivers/media/video/tda8290.c index 027c8a074dfe..e6c3e6167191 100644 --- a/drivers/media/video/tda8290.c +++ b/drivers/media/video/tda8290.c @@ -192,14 +192,52 @@ static struct tda827xa_data tda827xa_analog[] = { { .lomax = 0, .svco = 0, .spd = 0, .scr = 0, .sbs = 0, .gc3 = 0} /* End */ }; +static void tda827xa_lna_gain(struct i2c_client *c, int high) +{ + struct tuner *t = i2c_get_clientdata(c); + unsigned char buf[] = {0x22, 0x01}; + int arg; + struct i2c_msg msg = {.addr = c->addr, .flags = 0, .buf = buf, .len = sizeof(buf)}; + if (t->config) { + if (high) + tuner_dbg("setting LNA to high gain\n"); + else + tuner_dbg("setting LNA to low gain\n"); + } + switch (t->config) { + case 0: /* no LNA */ + break; + case 1: /* switch is GPIO 0 of tda8290 */ + case 2: + /* turn Vsync on */ + if (t->std & V4L2_STD_MN) + arg = 5; + else + arg = 4; + if (t->gpio_func) + t->gpio_func(c->adapter->algo_data, 22, 5); + buf[1] = high ? 0 : 1; + if (t->config == 2) + buf[1] = high ? 1 : 0; + i2c_transfer(c->adapter, &msg, 1); + break; + case 3: /* switch with GPIO of saa713x */ + if (t->gpio_func) + t->gpio_func(c->adapter->algo_data, 22, high); + break; + } +} + static void tda827xa_tune(struct i2c_client *c, u16 ifc, unsigned int freq) { - unsigned char tuner_reg[14]; - unsigned char reg2[2]; + unsigned char tuner_reg[11]; u32 N; int i; struct tuner *t = i2c_get_clientdata(c); - struct i2c_msg msg = {.addr = t->tda827x_addr, .flags = 0}; + struct i2c_msg msg = {.addr = t->tda827x_addr, .flags = 0, .buf = tuner_reg}; + + tda827xa_lna_gain( c, 1); + msleep(10); if (t->mode == V4L2_TUNER_RADIO) freq = freq / 1000; @@ -222,48 +260,58 @@ static void tda827xa_tune(struct i2c_client *c, u16 ifc, unsigned int freq) tuner_reg[5] = (tda827xa_analog[i].spd << 5) + (tda827xa_analog[i].svco << 3) + tda827xa_analog[i].sbs; tuner_reg[6] = 0x8b + (tda827xa_analog[i].gc3 << 4); - tuner_reg[7] = 0x0c; + tuner_reg[7] = 0x1c; tuner_reg[8] = 4; tuner_reg[9] = 0x20; - tuner_reg[10] = 0xff; - tuner_reg[11] = 0xe0; - tuner_reg[12] = 0; - tuner_reg[13] = 0x39 + (t->tda827x_lpsel << 1); + tuner_reg[10] = 0x00; + msg.len = 11; + i2c_transfer(c->adapter, &msg, 1); - msg.buf = tuner_reg; - msg.len = 14; + tuner_reg[0] = 0x90; + tuner_reg[1] = 0xff; + tuner_reg[2] = 0xe0; + tuner_reg[3] = 0; + tuner_reg[4] = 0x99 + (t->tda827x_lpsel << 1); + msg.len = 5; i2c_transfer(c->adapter, &msg, 1); - msg.buf= reg2; + tuner_reg[0] = 0xa0; + tuner_reg[1] = 0xc0; msg.len = 2; - reg2[0] = 0x60; - reg2[1] = 0x3c; i2c_transfer(c->adapter, &msg, 1); - reg2[0] = 0xa0; - reg2[1] = 0xc0; + tuner_reg[0] = 0x30; + tuner_reg[1] = 0x10 + tda827xa_analog[i].scr; i2c_transfer(c->adapter, &msg, 1); - msleep(2); - reg2[0] = 0x30; - reg2[1] = 0x10 + tda827xa_analog[i].scr; + msg.flags = I2C_M_RD; + i2c_transfer(c->adapter, &msg, 1); + msg.flags = 0; + tuner_reg[1] >>= 4; + tuner_dbg("AGC2 gain is: %d\n", tuner_reg[1]); + if (tuner_reg[1] < 1) + tda827xa_lna_gain( c, 0); + + msleep(100); + tuner_reg[0] = 0x60; + tuner_reg[1] = 0x3c; i2c_transfer(c->adapter, &msg, 1); - msleep(550); - reg2[0] = 0x50; - reg2[1] = 0x8f + (tda827xa_analog[i].gc3 << 4); + msleep(163); + tuner_reg[0] = 0x50; + tuner_reg[1] = 0x8f + (tda827xa_analog[i].gc3 << 4); i2c_transfer(c->adapter, &msg, 1); - reg2[0] = 0x80; - reg2[1] = 0x28; + tuner_reg[0] = 0x80; + tuner_reg[1] = 0x28; i2c_transfer(c->adapter, &msg, 1); - reg2[0] = 0xb0; - reg2[1] = 0x01; + tuner_reg[0] = 0xb0; + tuner_reg[1] = 0x01; i2c_transfer(c->adapter, &msg, 1); - reg2[0] = 0xc0; - reg2[1] = 0x19 + (t->tda827x_lpsel << 1); + tuner_reg[0] = 0xc0; + tuner_reg[1] = 0x19 + (t->tda827x_lpsel << 1); i2c_transfer(c->adapter, &msg, 1); } @@ -319,7 +367,9 @@ static int tda8290_tune(struct i2c_client *c, u16 ifc, unsigned int freq) unsigned char addr_pll_stat = 0x1b; unsigned char adc_sat, agc_stat, pll_stat; + int i; + tuner_dbg("tda827xa config is 0x%02x\n", t->config); i2c_master_send(c, easy_mode, 2); i2c_master_send(c, agc_out_on, 2); i2c_master_send(c, soft_reset, 2); @@ -340,17 +390,22 @@ static int tda8290_tune(struct i2c_client *c, u16 ifc, unsigned int freq) tda827xa_tune(c, ifc, freq); else tda827x_tune(c, ifc, freq); + for (i = 0; i < 3; i++) { + i2c_master_send(c, &addr_pll_stat, 1); + i2c_master_recv(c, &pll_stat, 1); + if (pll_stat & 0x80) { + i2c_master_send(c, &addr_adc_sat, 1); + i2c_master_recv(c, &adc_sat, 1); + i2c_master_send(c, &addr_agc_stat, 1); + i2c_master_recv(c, &agc_stat, 1); + tuner_dbg("tda8290 is locked, AGC: %d\n", agc_stat); + break; + } else { + tuner_dbg("tda8290 not locked, no signal?\n"); + msleep(100); + } + } /* adjust headroom resp. gain */ - i2c_master_send(c, &addr_adc_sat, 1); - i2c_master_recv(c, &adc_sat, 1); - i2c_master_send(c, &addr_agc_stat, 1); - i2c_master_recv(c, &agc_stat, 1); - i2c_master_send(c, &addr_pll_stat, 1); - i2c_master_recv(c, &pll_stat, 1); - if (pll_stat & 0x80) - tuner_dbg("tda8290 is locked, AGC: %d\n", agc_stat); - else - tuner_dbg("tda8290 not locked, no signal?\n"); if ((agc_stat > 115) || (!(pll_stat & 0x80) && (adc_sat < 20))) { tuner_dbg("adjust gain, step 1. Agc: %d, ADC stat: %d, lock: %d\n", agc_stat, adc_sat, pll_stat & 0x80); @@ -487,11 +542,16 @@ static void standby(struct i2c_client *c) static void tda8290_init_if(struct i2c_client *c) { + struct tuner *t = i2c_get_clientdata(c); unsigned char set_VS[] = { 0x30, 0x6F }; + unsigned char set_GP00_CF[] = { 0x20, 0x01 }; unsigned char set_GP01_CF[] = { 0x20, 0x0B }; + if ((t->config == 1) || (t->config == 2)) + i2c_master_send(c, set_GP00_CF, 2); + else + i2c_master_send(c, set_GP01_CF, 2); i2c_master_send(c, set_VS, 2); - i2c_master_send(c, set_GP01_CF, 2); } static void tda8290_init_tuner(struct i2c_client *c) -- cgit v1.2.3 From e95d317da2de7116ef66fa16bd9664cd39f1c11c Mon Sep 17 00:00:00 2001 From: Markus Rechberger Date: Fri, 27 Apr 2007 12:31:19 -0300 Subject: V4L/DVB (5330): Added card definition for AverMedia M102 miniPCI Signed-off-by: Markus Rechberger Signed-off-by: Hartmut Hackmann Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/CARDLIST.saa7134 | 1 + drivers/media/video/saa7134/saa7134-cards.c | 35 ++++++++++++++++++++++++++++- drivers/media/video/saa7134/saa7134.h | 1 + 3 files changed, 36 insertions(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/video4linux/CARDLIST.saa7134 b/Documentation/video4linux/CARDLIST.saa7134 index f18533f28e04..51f8a3fd66dc 100644 --- a/Documentation/video4linux/CARDLIST.saa7134 +++ b/Documentation/video4linux/CARDLIST.saa7134 @@ -108,3 +108,4 @@ 107 -> Encore ENLTV-FM [1131:230f] 108 -> Terratec Cinergy HT PCI [153b:1175] 109 -> Philips Tiger - S Reference design +110 -> Avermedia M102 [1461:f31e] diff --git a/drivers/media/video/saa7134/saa7134-cards.c b/drivers/media/video/saa7134/saa7134-cards.c index 87a8a68efc5c..23cd5f6bed17 100644 --- a/drivers/media/video/saa7134/saa7134-cards.c +++ b/drivers/media/video/saa7134/saa7134-cards.c @@ -3328,6 +3328,29 @@ struct saa7134_board saa7134_boards[] = { .gpio = 0x0200000, }, }, + [SAA7134_BOARD_AVERMEDIA_M102] = { + .name = "Avermedia M102", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_TDA8290, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .gpiomask = 1<<21, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + },{ + .name = name_comp1, + .vmux = 0, + .amux = LINE2, + },{ + .name = name_svideo, + .vmux = 6, + .amux = LINE2, + }}, + }, }; const unsigned int saa7134_bcount = ARRAY_SIZE(saa7134_boards); @@ -3996,6 +4019,12 @@ struct pci_device_id saa7134_pci_tbl[] = { .subvendor = 0x153b, .subdevice = 0x1175, .driver_data = SAA7134_BOARD_CINERGY_HT_PCI, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x1461, /* Avermedia Technologies Inc */ + .subdevice = 0xf31e, + .driver_data = SAA7134_BOARD_AVERMEDIA_M102, },{ /* --- boards without eeprom + subsystem ID --- */ .vendor = PCI_VENDOR_ID_PHILIPS, @@ -4010,7 +4039,6 @@ struct pci_device_id saa7134_pci_tbl[] = { .subdevice = 0, .driver_data = SAA7134_BOARD_NOAUTO, },{ - /* --- default catch --- */ .vendor = PCI_VENDOR_ID_PHILIPS, .device = PCI_DEVICE_ID_PHILIPS_SAA7130, @@ -4176,6 +4204,11 @@ int saa7134_board_init1(struct saa7134_dev *dev) "%s: Dual decoder functionality is disabled for now, use the other chip.\n", dev->name,card(dev).name,dev->name,dev->name); break; + case SAA7134_BOARD_AVERMEDIA_M102: + /* enable tuner */ + saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0x8c040007, 0x8c040007); + saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x0c0007cd, 0x0c0007cd); + break; } return 0; } diff --git a/drivers/media/video/saa7134/saa7134.h b/drivers/media/video/saa7134/saa7134.h index 07376da7ebd5..290e7e55c519 100644 --- a/drivers/media/video/saa7134/saa7134.h +++ b/drivers/media/video/saa7134/saa7134.h @@ -232,6 +232,7 @@ struct saa7134_format { #define SAA7134_BOARD_ENCORE_ENLTV_FM 107 #define SAA7134_BOARD_CINERGY_HT_PCI 108 #define SAA7134_BOARD_PHILIPS_TIGER_S 109 +#define SAA7134_BOARD_AVERMEDIA_M102 110 #define SAA7134_MAXBOARDS 8 #define SAA7134_INPUT_MAX 8 -- cgit v1.2.3 From db4836791dc578f0f6e9573468cffeee00fa7ebc Mon Sep 17 00:00:00 2001 From: Peter Missel Date: Fri, 27 Apr 2007 12:31:20 -0300 Subject: V4L/DVB (5331): Identify MSI TV@nywhere Duo It is a Lifeview Duo with a different ID Signed-off-by: Peter Missel Signed-off-by: Hartmut Hackmann Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/CARDLIST.saa7134 | 2 +- drivers/media/video/saa7134/saa7134-cards.c | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) (limited to 'Documentation') diff --git a/Documentation/video4linux/CARDLIST.saa7134 b/Documentation/video4linux/CARDLIST.saa7134 index 51f8a3fd66dc..9cbb048f4ebc 100644 --- a/Documentation/video4linux/CARDLIST.saa7134 +++ b/Documentation/video4linux/CARDLIST.saa7134 @@ -53,7 +53,7 @@ 52 -> AverMedia AverTV/305 [1461:2108] 53 -> ASUS TV-FM 7135 [1043:4845] 54 -> LifeView FlyTV Platinum FM / Gold [5168:0214,1489:0214,5168:0304] - 55 -> LifeView FlyDVB-T DUO [5168:0306] + 55 -> LifeView FlyDVB-T DUO / MSI TV@nywhere Duo [5168:0306,4E42:0306] 56 -> Avermedia AVerTV 307 [1461:a70a] 57 -> Avermedia AVerTV GO 007 FM [1461:f31f] 58 -> ADS Tech Instant TV (saa7135) [1421:0350,1421:0351,1421:0370,1421:1370] diff --git a/drivers/media/video/saa7134/saa7134-cards.c b/drivers/media/video/saa7134/saa7134-cards.c index 23cd5f6bed17..7572e937c387 100644 --- a/drivers/media/video/saa7134/saa7134-cards.c +++ b/drivers/media/video/saa7134/saa7134-cards.c @@ -1778,7 +1778,7 @@ struct saa7134_board saa7134_boards[] = { [SAA7134_BOARD_FLYDVBTDUO] = { /* LifeView FlyDVB-T DUO */ /* "Nico Sabbi Hartmut Hackmann hartmut.hackmann@t-online.de*/ - .name = "LifeView FlyDVB-T DUO", + .name = "LifeView FlyDVB-T DUO / MSI TV@nywhere Duo", .audio_clock = 0x00200000, .tuner_type = TUNER_PHILIPS_TDA8290, .radio_type = UNSET, @@ -4025,6 +4025,12 @@ struct pci_device_id saa7134_pci_tbl[] = { .subvendor = 0x1461, /* Avermedia Technologies Inc */ .subdevice = 0xf31e, .driver_data = SAA7134_BOARD_AVERMEDIA_M102, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x4E42, /* MSI */ + .subdevice = 0x0306, /* TV@nywhere DUO */ + .driver_data = SAA7134_BOARD_FLYDVBTDUO, },{ /* --- boards without eeprom + subsystem ID --- */ .vendor = PCI_VENDOR_ID_PHILIPS, -- cgit v1.2.3 From 0b20060f6c2cc69c5394cf9782513e7b526e87b9 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 27 Apr 2007 12:31:22 -0300 Subject: V4L/DVB (5336): Cx23416 doc updates + rename CX2341X_ENC_UNKNOWN The documentation of Several miscellaneous commands was updated. As a result of which the CX2341X_ENC_UNKNOWN command was renamed to CX2341X_ENC_SET_VERT_CROP_LINE. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/cx2341x/fw-encoder-api.txt | 19 ++++++++++--------- include/media/cx2341x.h | 2 +- 2 files changed, 11 insertions(+), 10 deletions(-) (limited to 'Documentation') diff --git a/Documentation/video4linux/cx2341x/fw-encoder-api.txt b/Documentation/video4linux/cx2341x/fw-encoder-api.txt index 242104ce5b61..5dd3109a8b3f 100644 --- a/Documentation/video4linux/cx2341x/fw-encoder-api.txt +++ b/Documentation/video4linux/cx2341x/fw-encoder-api.txt @@ -663,12 +663,13 @@ Param[0] ------------------------------------------------------------------------------- -Name CX2341X_ENC_UNKNOWN +Name CX2341X_ENC_SET_VERT_CROP_LINE Enum 219/0xDB Description - Unknown API, it's used by Hauppauge though. + Something to do with 'Vertical Crop Line' Param[0] - 0 This is the value Hauppauge uses, Unknown what it means. + If saa7114 and raw VBI capture and 60 Hz, then set to 10001. + Else 0. ------------------------------------------------------------------------------- @@ -682,11 +683,9 @@ Param[0] Command number: 1=set initial SCR value when starting encoding (works). 2=set quality mode (apparently some test setting). - 3=setup advanced VIM protection handling (supposedly only for the cx23416 - for raw YUV). - Actually it looks like this should be 0 for saa7114/5 based card and 1 - for cx25840 based cards. - 4=generate artificial PTS timestamps + 3=setup advanced VIM protection handling. + Always 1 for the cx23416 and 0 for cx23415. + 4=generate DVD compatible PTS timestamps 5=USB flush mode 6=something to do with the quantization matrix 7=set navigation pack insertion for DVD: adds 0xbf (private stream 2) @@ -698,7 +697,9 @@ Param[0] 9=set history parameters of the video input module 10=set input field order of VIM 11=set quantization matrix - 12=reset audio interface + 12=reset audio interface after channel change or input switch (has no argument). + Needed for the cx2584x, not needed for the mspx4xx, but it doesn't seem to + do any harm calling it regardless. 13=set audio volume delay 14=set audio delay diff --git a/include/media/cx2341x.h b/include/media/cx2341x.h index 583b0621ff43..38c12fed7535 100644 --- a/include/media/cx2341x.h +++ b/include/media/cx2341x.h @@ -166,7 +166,7 @@ void cx2341x_log_status(struct cx2341x_mpeg_params *p, const char *prefix); #define CX2341X_ENC_SET_PLACEHOLDER 0xd7 #define CX2341X_ENC_MUTE_VIDEO 0xd9 #define CX2341X_ENC_MUTE_AUDIO 0xda -#define CX2341X_ENC_UNKNOWN 0xdb +#define CX2341X_ENC_SET_VERT_CROP_LINE 0xdb #define CX2341X_ENC_MISC 0xdc /* OSD API, specific to the cx23415 */ -- cgit v1.2.3 From 3f3f6a5958ffc651227df671704467654b71c09d Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 10 Mar 2007 08:10:11 -0300 Subject: V4L/DVB (5407a): Update feature-removal-schedule.txt: remove VIDIOC_S/G_MPEGCOMP Those two experimental APIs never worked fine nor, afaik, were implemented at the apps. Their functionalities were implemented by other means. So, let's remove those obsolete stuff. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/feature-removal-schedule.txt | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'Documentation') diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt index 6da663607f7b..674856303658 100644 --- a/Documentation/feature-removal-schedule.txt +++ b/Documentation/feature-removal-schedule.txt @@ -6,6 +6,18 @@ be removed from this file. --------------------------- +What: V4L2 VIDIOC_G_MPEGCOMP and VIDIOC_S_MPEGCOMP +When: October 2007 +Why: Broken attempt to set MPEG compression parameters. These ioctls are + not able to implement the wide variety of parameters that can be set + by hardware MPEG encoders. A new MPEG control mechanism was created + in kernel 2.6.18 that replaces these ioctls. See the V4L2 specification + (section 1.9: Extended controls) for more information on this topic. +Who: Hans Verkuil and + Mauro Carvalho Chehab + +--------------------------- + What: /sys/devices/.../power/state dev->power.power_state dpm_runtime_{suspend,resume)() -- cgit v1.2.3 From a542fe47fe3f758e47d71640e7ca328c032a13e1 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 10 Mar 2007 13:56:56 -0300 Subject: V4L/DVB (5409): Add CARDLIST.ivtv and README.ivtv Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/CARDLIST.ivtv | 18 +++ Documentation/video4linux/README.ivtv | 187 ++++++++++++++++++++++++++++++++ 2 files changed, 205 insertions(+) create mode 100644 Documentation/video4linux/CARDLIST.ivtv create mode 100644 Documentation/video4linux/README.ivtv (limited to 'Documentation') diff --git a/Documentation/video4linux/CARDLIST.ivtv b/Documentation/video4linux/CARDLIST.ivtv new file mode 100644 index 000000000000..ddd76a0eb100 --- /dev/null +++ b/Documentation/video4linux/CARDLIST.ivtv @@ -0,0 +1,18 @@ + 1 -> Hauppauge WinTV PVR-250 + 2 -> Hauppauge WinTV PVR-350 + 3 -> Hauppauge WinTV PVR-150 or PVR-500 + 4 -> AVerMedia M179 [1461:a3ce,1461:a3cf] + 5 -> Yuan MPG600/Kuroutoshikou iTVC16-STVLP [12ab:fff3,12ab:ffff] + 6 -> Yuan MPG160/Kuroutoshikou iTVC15-STVLP [12ab:0000,10fc:40a0] + 7 -> Yuan PG600/DiamondMM PVR-550 [ff92:0070,ffab:0600] + 8 -> Adaptec AVC-2410 [9005:0093] + 9 -> Adaptec AVC-2010 [9005:0092] +10 -> NAGASE TRANSGEAR 5000TV [1461:bfff] +11 -> AOpen VA2000MAX-STN6 [0000:ff5f] +12 -> YUAN MPG600GR/Kuroutoshikou CX23416GYC-STVLP [12ab:0600,fbab:0600,1154:0523] +13 -> I/O Data GV-MVP/RX [10fc:d01e,10fc:d038,10fc:d039] +14 -> I/O Data GV-MVP/RX2E [10fc:d025] +15 -> GOTVIEW PCI DVD (partial support only) [12ab:0600] +16 -> GOTVIEW PCI DVD2 Deluxe [ffac:0600] +17 -> Yuan MPC622 [ff01:d998] +18 -> Digital Cowboy DCT-MTVP1 [1461:bfff] diff --git a/Documentation/video4linux/README.ivtv b/Documentation/video4linux/README.ivtv new file mode 100644 index 000000000000..73df22c40bfe --- /dev/null +++ b/Documentation/video4linux/README.ivtv @@ -0,0 +1,187 @@ + +ivtv release notes +================== + +This is a v4l2 device driver for the Conexant cx23415/6 MPEG encoder/decoder. +The cx23415 can do both encoding and decoding, the cx23416 can only do MPEG +encoding. Currently the only card featuring full decoding support is the +Hauppauge PVR-350. + +NOTE: this driver requires the latest encoder firmware (version 2.06.039, size +376836 bytes). Get the firmware from here: + +http://dl.ivtvdriver.org/ivtv/firmware/firmware.tar.gz + +NOTE: 'normal' TV applications do not work with this driver, you need +an application that can handle MPEG input such as mplayer, xine, MythTV, +etc. + +The primary goal of the IVTV project is to provide a "clean room" Linux +Open Source driver implementation for video capture cards based on the +iCompression iTVC15 or Conexant CX23415/CX23416 MPEG Codec. + +Features: + * Hardware mpeg2 capture of broadcast video (and sound) via the tuner or + S-Video/Composite and audio line-in. + * Hardware mpeg2 capture of FM radio where hardware support exists + * Supports NTSC, PAL, SECAM with stereo sound + * Supports SAP and bilingual transmissions. + * Supports raw VBI (closed captions and teletext). + * Supports sliced VBI (closed captions and teletext) and is able to insert + this into the captured MPEG stream. + * Supports raw YUV and PCM input. + +Additional features for the PVR-350 (CX23415 based): + * Provides hardware mpeg2 playback + * Provides comprehensive OSD (On Screen Display: ie. graphics overlaying the + video signal) + * Provides a framebuffer (allowing X applications to appear on the video + device) (this framebuffer is not yet part of the kernel. In the meantime it + is available from www.ivtvdriver.org). + * Supports raw YUV output. + +IMPORTANT: In case of problems first read this page: + http://www.ivtvdriver.org/index.php/Troubleshooting + +See also: + +Homepage + Wiki +http://www.ivtvdriver.org + +IRC +irc://irc.freenode.net/ivtv-dev + +---------------------------------------------------------- + +Devices +======= + +A maximum of 12 ivtv boards are allowed at the moment. + +Cards that don't have a video output capability (i.e. non PVR350 cards) +lack the vbi8, vbi16, video16 and video48 devices. They also do not +support the framebuffer device /dev/fbx for OSD. + +The radio0 device may or may not be present, depending on whether the +card has a radio tuner or not. + +Here is a list of the base v4l devices: +crw-rw---- 1 root video 81, 0 Jun 19 22:22 /dev/video0 +crw-rw---- 1 root video 81, 16 Jun 19 22:22 /dev/video16 +crw-rw---- 1 root video 81, 24 Jun 19 22:22 /dev/video24 +crw-rw---- 1 root video 81, 32 Jun 19 22:22 /dev/video32 +crw-rw---- 1 root video 81, 48 Jun 19 22:22 /dev/video48 +crw-rw---- 1 root video 81, 64 Jun 19 22:22 /dev/radio0 +crw-rw---- 1 root video 81, 224 Jun 19 22:22 /dev/vbi0 +crw-rw---- 1 root video 81, 228 Jun 19 22:22 /dev/vbi8 +crw-rw---- 1 root video 81, 232 Jun 19 22:22 /dev/vbi16 + +Base devices +============ + +For every extra card you have the numbers increased by one. For example, +/dev/video0 is listed as the 'base' encoding capture device so we have: + + /dev/video0 is the encoding capture device for the first card (card 0) + /dev/video1 is the encoding capture device for the second card (card 1) + /dev/video2 is the encoding capture device for the third card (card 2) + +Note that if the first card doesn't have a feature (eg no decoder, so no +video16, the second card will still use video17. The simple rule is 'add +the card number to the base device number'. If you have other capture +cards (e.g. WinTV PCI) that are detected first, then you have to tell +the ivtv module about it so that it will start counting at 1 (or 2, or +whatever). Otherwise the device numbers can get confusing. The ivtv +'ivtv_first_minor' module option can be used for that. + + +/dev/video0 +The encoding capture device(s). +Read-only. + +Reading from this device gets you the MPEG1/2 program stream. +Example: + +cat /dev/video0 > my.mpg (you need to hit ctrl-c to exit) + + +/dev/video16 +The decoder output device(s) +Write-only. Only present if the MPEG decoder (i.e. CX23415) exists. + +An mpeg2 stream sent to this device will appear on the selected video +display, audio will appear on the line-out/audio out. It is only +available for cards that support video out. Example: + +cat my.mpg >/dev/video16 + + +/dev/video24 +The raw audio capture device(s). +Read-only + +The raw audio PCM stereo stream from the currently selected +tuner or audio line-in. Reading from this device results in a raw +(signed 16 bit Little Endian, 48000 Hz, stereo pcm) capture. +This device only captures audio. This should be replaced by an ALSA +device in the future. +Note that there is no corresponding raw audio output device, this is +not supported in the decoder firmware. + + +/dev/video32 +The raw video capture device(s) +Read-only + +The raw YUV video output from the current video input. The YUV format +is non-standard (V4L2_PIX_FMT_HM12). + +Note that the YUV and PCM streams are not synchronized, so they are of +limited use. + + +/dev/video48 +The raw video display device(s) +Write-only. Only present if the MPEG decoder (i.e. CX23415) exists. + +Writes a YUV stream to the decoder of the card. + + +/dev/radio0 +The radio tuner device(s) +Cannot be read or written. + +Used to enable the radio tuner and tune to a frequency. You cannot +read or write audio streams with this device. Once you use this +device to tune the radio, use /dev/video24 to read the raw pcm stream +or /dev/video0 to get an mpeg2 stream with black video. + + +/dev/vbi0 +The 'vertical blank interval' (Teletext, CC, WSS etc) capture device(s) +Read-only + +Captures the raw (or sliced) video data sent during the Vertical Blank +Interval. This data is used to encode teletext, closed captions, VPS, +widescreen signalling, electronic program guide information, and other +services. + + +/dev/vbi8 +Processed vbi feedback device(s) +Read-only. Only present if the MPEG decoder (i.e. CX23415) exists. + +The sliced VBI data embedded in an MPEG stream is reproduced on this +device. So while playing back a recording on /dev/video16, you can +read the embedded VBI data from /dev/vbi8. + + +/dev/vbi16 +The vbi 'display' device(s) +Write-only. Only present if the MPEG decoder (i.e. CX23415) exists. + +Can be used to send sliced VBI data to the video-out connector. + +--------------------------------- + +Hans Verkuil -- cgit v1.2.3 From 733aeaf4c006353b1ed8c54b707c03a6385c30ed Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Sun, 11 Mar 2007 12:48:04 -0300 Subject: V4L/DVB (5431): Cx88: autodetect ADS Tech InstantTV DVB-S The ADS Tech InstantTV DVB-S is a clone of the KWorld DVB-S 100. This patch adds autodetection support for this card based on pci subsystem id. Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/CARDLIST.cx88 | 2 +- drivers/media/video/cx88/cx88-cards.c | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/video4linux/CARDLIST.cx88 b/Documentation/video4linux/CARDLIST.cx88 index 62e32b49cec9..60f838beb9c8 100644 --- a/Documentation/video4linux/CARDLIST.cx88 +++ b/Documentation/video4linux/CARDLIST.cx88 @@ -37,7 +37,7 @@ 36 -> AVerTV 303 (M126) [1461:000a] 37 -> Hauppauge Nova-S-Plus DVB-S [0070:9201,0070:9202] 38 -> Hauppauge Nova-SE2 DVB-S [0070:9200] - 39 -> KWorld DVB-S 100 [17de:08b2] + 39 -> KWorld DVB-S 100 [17de:08b2,1421:0341] 40 -> Hauppauge WinTV-HVR1100 DVB-T/Hybrid [0070:9400,0070:9402] 41 -> Hauppauge WinTV-HVR1100 DVB-T/Hybrid (Low Profile) [0070:9800,0070:9802] 42 -> digitalnow DNTV Live! DVB-T Pro [1822:0025,1822:0019] diff --git a/drivers/media/video/cx88/cx88-cards.c b/drivers/media/video/cx88/cx88-cards.c index 65e9d8096b74..5de82fe1936c 100644 --- a/drivers/media/video/cx88/cx88-cards.c +++ b/drivers/media/video/cx88/cx88-cards.c @@ -1631,6 +1631,10 @@ struct cx88_subid cx88_subids[] = { .subvendor = 0x0070, .subdevice = 0x1402, .card = CX88_BOARD_HAUPPAUGE_HVR3000, + },{ + .subvendor = 0x1421, + .subdevice = 0x0341, /* ADS Tech InstantTV DVB-S */ + .card = CX88_BOARD_KWORLD_DVBS_100, }, }; const unsigned int cx88_idcount = ARRAY_SIZE(cx88_subids); -- cgit v1.2.3 From 59fc7f52898fb35eb053bc7b54430d81629b5966 Mon Sep 17 00:00:00 2001 From: Ian Armstrong Date: Fri, 16 Mar 2007 07:40:48 -0300 Subject: V4L/DVB (5437): Update cx23415 documentation Adds more osd mode switching information. Corrects some information regarding mode selection & local alpha operation for 16 bit modes. Signed-off-by: Ian Armstrong Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/cx2341x/fw-decoder-regs.txt | 12 +++++++----- Documentation/video4linux/cx2341x/fw-osd-api.txt | 12 ++++++++++-- 2 files changed, 17 insertions(+), 7 deletions(-) (limited to 'Documentation') diff --git a/Documentation/video4linux/cx2341x/fw-decoder-regs.txt b/Documentation/video4linux/cx2341x/fw-decoder-regs.txt index db2366c634e8..cf52c8f20b9e 100644 --- a/Documentation/video4linux/cx2341x/fw-decoder-regs.txt +++ b/Documentation/video4linux/cx2341x/fw-decoder-regs.txt @@ -624,11 +624,11 @@ out what values are bad when it hangs. 2A00 bits 0:2 osd colour mode + 000 = 8 bit indexed 001 = 16 bit (565) 010 = 15 bit (555) 011 = 12 bit (444) 100 = 32 bit (8888) - 101 = 8 bit indexed bits 4:5 osd display bpp @@ -676,9 +676,11 @@ out what values are bad when it hangs. completely transparent. When using 565, 555 or 444 colour modes, the colour key is always 16 bits wide. The colour to key on is set in Reg 2A18. - Local alpha is a per-pixel 256 step transparency, with 0 being transparent - and 255 being solid. This is only available in 32 bit & 8 bit indexed - colour modes. + Local alpha works differently depending on the colour mode. For 32bpp & 8 + bit indexed, local alpha is a per-pixel 256 step transparency, with 0 being + transparent and 255 being solid. For the 16bpp modes 555 & 444, the unused + bit(s) act as a simple transparency switch, with 0 being solid & 1 being + fully transparent. There is no local alpha support for 16bit 565. Global alpha is a 256 step transparency that applies to the entire osd, with 0 being transparent & 255 being solid. @@ -811,5 +813,5 @@ out what values are bad when it hangs. -------------------------------------------------------------------------------- -v0.3 - 2 February 2007 - Ian Armstrong (ian@iarmst.demon.co.uk) +v0.4 - 12 March 2007 - Ian Armstrong (ian@iarmst.demon.co.uk) diff --git a/Documentation/video4linux/cx2341x/fw-osd-api.txt b/Documentation/video4linux/cx2341x/fw-osd-api.txt index 0a602f3e601b..89c4601042c1 100644 --- a/Documentation/video4linux/cx2341x/fw-osd-api.txt +++ b/Documentation/video4linux/cx2341x/fw-osd-api.txt @@ -21,7 +21,11 @@ Enum 66/0x42 Description Query OSD format Result[0] - 0=8bit index, 4=AlphaRGB 8:8:8:8 + 0=8bit index + 1=16bit RGB 5:6:5 + 2=16bit ARGB 1:5:5:5 + 3=16bit ARGB 1:4:4:4 + 4=32bit ARGB 8:8:8:8 ------------------------------------------------------------------------------- @@ -30,7 +34,11 @@ Enum 67/0x43 Description Assign pixel format Param[0] - 0=8bit index, 4=AlphaRGB 8:8:8:8 + 0=8bit index + 1=16bit RGB 5:6:5 + 2=16bit ARGB 1:5:5:5 + 3=16bit ARGB 1:4:4:4 + 4=32bit ARGB 8:8:8:8 ------------------------------------------------------------------------------- -- cgit v1.2.3 From e06cea4cb4a16076dfca4863b12e1c4aeb781c8d Mon Sep 17 00:00:00 2001 From: Hartmut Hackmann Date: Tue, 13 Mar 2007 20:58:29 -0300 Subject: V4L/DVB (5445): Added / corrected support for some ASUS hybrid boards There are 2 new entries for p7131 boards and one correction for a board with LNA. Signed-off-by: Hartmut Hackmann Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/CARDLIST.saa7134 | 4 +- drivers/media/video/saa7134/saa7134-cards.c | 72 ++++++++++++++++++++++++++++- drivers/media/video/saa7134/saa7134-dvb.c | 35 ++++++++++++++ drivers/media/video/saa7134/saa7134.h | 2 + 4 files changed, 111 insertions(+), 2 deletions(-) (limited to 'Documentation') diff --git a/Documentation/video4linux/CARDLIST.saa7134 b/Documentation/video4linux/CARDLIST.saa7134 index 9cbb048f4ebc..1210b8bf6ad6 100644 --- a/Documentation/video4linux/CARDLIST.saa7134 +++ b/Documentation/video4linux/CARDLIST.saa7134 @@ -76,7 +76,7 @@ 75 -> AVerMedia AVerTVHD MCE A180 [1461:1044] 76 -> SKNet MonsterTV Mobile [1131:4ee9] 77 -> Pinnacle PCTV 40i/50i/110i (saa7133) [11bd:002e] - 78 -> ASUSTeK P7131 Dual [1043:4862,1043:4876] + 78 -> ASUSTeK P7131 Dual [1043:4862,1043:4857] 79 -> Sedna/MuchTV PC TV Cardbus TV/Radio (ITO25 Rev:2B) 80 -> ASUS Digimatrix TV [1043:0210] 81 -> Philips Tiger reference design [1131:2018] @@ -109,3 +109,5 @@ 108 -> Terratec Cinergy HT PCI [153b:1175] 109 -> Philips Tiger - S Reference design 110 -> Avermedia M102 [1461:f31e] +111 -> ASUS P7131 4871 [1043:4871] +112 -> ASUSTeK P7131 Dual LNA [1043:4876] diff --git a/drivers/media/video/saa7134/saa7134-cards.c b/drivers/media/video/saa7134/saa7134-cards.c index 7572e937c387..a700e897e679 100644 --- a/drivers/media/video/saa7134/saa7134-cards.c +++ b/drivers/media/video/saa7134/saa7134-cards.c @@ -3351,6 +3351,62 @@ struct saa7134_board saa7134_boards[] = { .amux = LINE2, }}, }, + [SAA7134_BOARD_ASUS_P7131_4871] = { + .name = "ASUS P7131 4871", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_TDA8290, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tuner_config = 2, + .mpeg = SAA7134_MPEG_DVB, + .gpiomask = 0x0200000, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + .gpio = 0x0200000, + }}, + }, + [SAA7134_BOARD_ASUSTeK_P7131_DUAL_LNA] = { + .name = "ASUSTeK P7131 Dual LNA", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_TDA8290, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tuner_config = 2, + .gpiomask = 1 << 21, + .mpeg = SAA7134_MPEG_DVB, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + .gpio = 0x0000000, + },{ + .name = name_comp1, + .vmux = 3, + .amux = LINE2, + .gpio = 0x0200000, + },{ + .name = name_comp2, + .vmux = 0, + .amux = LINE2, + .gpio = 0x0200000, + },{ + .name = name_svideo, + .vmux = 8, + .amux = LINE2, + .gpio = 0x0200000, + }}, + .radio = { + .name = name_radio, + .amux = TV, + .gpio = 0x0200000, + }, + }, }; const unsigned int saa7134_bcount = ARRAY_SIZE(saa7134_boards); @@ -3976,7 +4032,7 @@ struct pci_device_id saa7134_pci_tbl[] = { .device = PCI_DEVICE_ID_PHILIPS_SAA7133, .subvendor = 0x1043, .subdevice = 0x4876, - .driver_data = SAA7134_BOARD_ASUSTeK_P7131_DUAL, + .driver_data = SAA7134_BOARD_ASUSTeK_P7131_DUAL_LNA, },{ .vendor = PCI_VENDOR_ID_PHILIPS, .device = PCI_DEVICE_ID_PHILIPS_SAA7133, @@ -4031,6 +4087,18 @@ struct pci_device_id saa7134_pci_tbl[] = { .subvendor = 0x4E42, /* MSI */ .subdevice = 0x0306, /* TV@nywhere DUO */ .driver_data = SAA7134_BOARD_FLYDVBTDUO, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x1043, + .subdevice = 0x4871, + .driver_data = SAA7134_BOARD_ASUS_P7131_4871, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x1043, + .subdevice = 0x4857, + .driver_data = SAA7134_BOARD_ASUSTeK_P7131_DUAL, },{ /* --- boards without eeprom + subsystem ID --- */ .vendor = PCI_VENDOR_ID_PHILIPS, @@ -4136,6 +4204,7 @@ int saa7134_board_init1(struct saa7134_dev *dev) case SAA7134_BOARD_SEDNA_PC_TV_CARDBUS: case SAA7134_BOARD_FLYDVBT_LR301: case SAA7134_BOARD_ASUSTeK_P7131_DUAL: + case SAA7134_BOARD_ASUSTeK_P7131_DUAL_LNA: case SAA7134_BOARD_FLYDVBTDUO: case SAA7134_BOARD_PROTEUS_2309: case SAA7134_BOARD_AVERMEDIA_A16AR: @@ -4359,6 +4428,7 @@ int saa7134_board_init2(struct saa7134_dev *dev) case SAA7134_BOARD_PINNACLE_PCTV_310i: case SAA7134_BOARD_TEVION_DVBT_220RF: case SAA7134_BOARD_ASUSTeK_P7131_DUAL: + case SAA7134_BOARD_ASUSTeK_P7131_DUAL_LNA: case SAA7134_BOARD_MEDION_MD8800_QUADRO: case SAA7134_BOARD_HAUPPAUGE_HVR1110: /* this is a hybrid board, initialize to analog mode diff --git a/drivers/media/video/saa7134/saa7134-dvb.c b/drivers/media/video/saa7134/saa7134-dvb.c index 531eb57e331f..f94754f06259 100644 --- a/drivers/media/video/saa7134/saa7134-dvb.c +++ b/drivers/media/video/saa7134/saa7134-dvb.c @@ -858,6 +858,35 @@ static struct tda1004x_config md8800_dvbt_config = { .request_firmware = philips_tda1004x_request_firmware }; +static struct tda1004x_config asus_p7131_4871_config = { + .demod_address = 0x08, + .invert = 1, + .invert_oclk = 0, + .xtal_freq = TDA10046_XTAL_16M, + .agc_config = TDA10046_AGC_TDA827X, + .gpio_config = TDA10046_GP01_I, + .if_freq = TDA10046_FREQ_045, + .i2c_gate = 0x4b, + .tuner_address = 0x61, + .tuner_config = 2, + .antenna_switch= 2, + .request_firmware = philips_tda1004x_request_firmware +}; + +static struct tda1004x_config asus_p7131_dual_lna_config = { + .demod_address = 0x08, + .invert = 1, + .invert_oclk = 0, + .xtal_freq = TDA10046_XTAL_16M, + .agc_config = TDA10046_AGC_TDA827X, + .gpio_config = TDA10046_GP11_I, + .if_freq = TDA10046_FREQ_045, + .i2c_gate = 0x4b, + .tuner_address = 0x61, + .tuner_config = 2, + .antenna_switch= 2, + .request_firmware = philips_tda1004x_request_firmware +}; /* ------------------------------------------------------------------ * special case: this card uses saa713x GPIO22 for the mode switch */ @@ -1121,6 +1150,12 @@ static int dvb_init(struct saa7134_dev *dev) case SAA7134_BOARD_PHILIPS_TIGER_S: configure_tda827x_fe(dev, &philips_tiger_s_config); break; + case SAA7134_BOARD_ASUS_P7131_4871: + configure_tda827x_fe(dev, &asus_p7131_4871_config); + break; + case SAA7134_BOARD_ASUSTeK_P7131_DUAL_LNA: + configure_tda827x_fe(dev, &asus_p7131_dual_lna_config); + break; default: wprintk("Huh? unknown DVB card?\n"); break; diff --git a/drivers/media/video/saa7134/saa7134.h b/drivers/media/video/saa7134/saa7134.h index 290e7e55c519..0c8818113857 100644 --- a/drivers/media/video/saa7134/saa7134.h +++ b/drivers/media/video/saa7134/saa7134.h @@ -233,6 +233,8 @@ struct saa7134_format { #define SAA7134_BOARD_CINERGY_HT_PCI 108 #define SAA7134_BOARD_PHILIPS_TIGER_S 109 #define SAA7134_BOARD_AVERMEDIA_M102 110 +#define SAA7134_BOARD_ASUS_P7131_4871 111 +#define SAA7134_BOARD_ASUSTeK_P7131_DUAL_LNA 112 #define SAA7134_MAXBOARDS 8 #define SAA7134_INPUT_MAX 8 -- cgit v1.2.3 From f3eec0c001a6781224d39912d41b58eaf2126586 Mon Sep 17 00:00:00 2001 From: Hartmut Hackmann Date: Wed, 14 Mar 2007 20:33:55 -0300 Subject: V4L/DVB (5446): Renamed ASUStek P7131 card [1043:4876] The new name fits to what it is and what is on the box. Signed-off-by: Hartmut Hackmann Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/CARDLIST.saa7134 | 2 +- drivers/media/video/saa7134/saa7134-cards.c | 10 +++++----- drivers/media/video/saa7134/saa7134-dvb.c | 6 +++--- drivers/media/video/saa7134/saa7134.h | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) (limited to 'Documentation') diff --git a/Documentation/video4linux/CARDLIST.saa7134 b/Documentation/video4linux/CARDLIST.saa7134 index 1210b8bf6ad6..d7bb2e2e4d9b 100644 --- a/Documentation/video4linux/CARDLIST.saa7134 +++ b/Documentation/video4linux/CARDLIST.saa7134 @@ -110,4 +110,4 @@ 109 -> Philips Tiger - S Reference design 110 -> Avermedia M102 [1461:f31e] 111 -> ASUS P7131 4871 [1043:4871] -112 -> ASUSTeK P7131 Dual LNA [1043:4876] +112 -> ASUSTeK P7131 Hybrid [1043:4876] diff --git a/drivers/media/video/saa7134/saa7134-cards.c b/drivers/media/video/saa7134/saa7134-cards.c index a700e897e679..e88fd7e2ffc8 100644 --- a/drivers/media/video/saa7134/saa7134-cards.c +++ b/drivers/media/video/saa7134/saa7134-cards.c @@ -3369,8 +3369,8 @@ struct saa7134_board saa7134_boards[] = { .gpio = 0x0200000, }}, }, - [SAA7134_BOARD_ASUSTeK_P7131_DUAL_LNA] = { - .name = "ASUSTeK P7131 Dual LNA", + [SAA7134_BOARD_ASUSTeK_P7131_HYBRID_LNA] = { + .name = "ASUSTeK P7131 Hybrid", .audio_clock = 0x00187de7, .tuner_type = TUNER_PHILIPS_TDA8290, .radio_type = UNSET, @@ -4032,7 +4032,7 @@ struct pci_device_id saa7134_pci_tbl[] = { .device = PCI_DEVICE_ID_PHILIPS_SAA7133, .subvendor = 0x1043, .subdevice = 0x4876, - .driver_data = SAA7134_BOARD_ASUSTeK_P7131_DUAL_LNA, + .driver_data = SAA7134_BOARD_ASUSTeK_P7131_HYBRID_LNA, },{ .vendor = PCI_VENDOR_ID_PHILIPS, .device = PCI_DEVICE_ID_PHILIPS_SAA7133, @@ -4204,7 +4204,7 @@ int saa7134_board_init1(struct saa7134_dev *dev) case SAA7134_BOARD_SEDNA_PC_TV_CARDBUS: case SAA7134_BOARD_FLYDVBT_LR301: case SAA7134_BOARD_ASUSTeK_P7131_DUAL: - case SAA7134_BOARD_ASUSTeK_P7131_DUAL_LNA: + case SAA7134_BOARD_ASUSTeK_P7131_HYBRID_LNA: case SAA7134_BOARD_FLYDVBTDUO: case SAA7134_BOARD_PROTEUS_2309: case SAA7134_BOARD_AVERMEDIA_A16AR: @@ -4428,7 +4428,7 @@ int saa7134_board_init2(struct saa7134_dev *dev) case SAA7134_BOARD_PINNACLE_PCTV_310i: case SAA7134_BOARD_TEVION_DVBT_220RF: case SAA7134_BOARD_ASUSTeK_P7131_DUAL: - case SAA7134_BOARD_ASUSTeK_P7131_DUAL_LNA: + case SAA7134_BOARD_ASUSTeK_P7131_HYBRID_LNA: case SAA7134_BOARD_MEDION_MD8800_QUADRO: case SAA7134_BOARD_HAUPPAUGE_HVR1110: /* this is a hybrid board, initialize to analog mode diff --git a/drivers/media/video/saa7134/saa7134-dvb.c b/drivers/media/video/saa7134/saa7134-dvb.c index f94754f06259..71c32b311654 100644 --- a/drivers/media/video/saa7134/saa7134-dvb.c +++ b/drivers/media/video/saa7134/saa7134-dvb.c @@ -873,7 +873,7 @@ static struct tda1004x_config asus_p7131_4871_config = { .request_firmware = philips_tda1004x_request_firmware }; -static struct tda1004x_config asus_p7131_dual_lna_config = { +static struct tda1004x_config asus_p7131_hybrid_lna_config = { .demod_address = 0x08, .invert = 1, .invert_oclk = 0, @@ -1153,8 +1153,8 @@ static int dvb_init(struct saa7134_dev *dev) case SAA7134_BOARD_ASUS_P7131_4871: configure_tda827x_fe(dev, &asus_p7131_4871_config); break; - case SAA7134_BOARD_ASUSTeK_P7131_DUAL_LNA: - configure_tda827x_fe(dev, &asus_p7131_dual_lna_config); + case SAA7134_BOARD_ASUSTeK_P7131_HYBRID_LNA: + configure_tda827x_fe(dev, &asus_p7131_hybrid_lna_config); break; default: wprintk("Huh? unknown DVB card?\n"); diff --git a/drivers/media/video/saa7134/saa7134.h b/drivers/media/video/saa7134/saa7134.h index 0c8818113857..62224cc958f1 100644 --- a/drivers/media/video/saa7134/saa7134.h +++ b/drivers/media/video/saa7134/saa7134.h @@ -234,7 +234,7 @@ struct saa7134_format { #define SAA7134_BOARD_PHILIPS_TIGER_S 109 #define SAA7134_BOARD_AVERMEDIA_M102 110 #define SAA7134_BOARD_ASUS_P7131_4871 111 -#define SAA7134_BOARD_ASUSTeK_P7131_DUAL_LNA 112 +#define SAA7134_BOARD_ASUSTeK_P7131_HYBRID_LNA 112 #define SAA7134_MAXBOARDS 8 #define SAA7134_INPUT_MAX 8 -- cgit v1.2.3 From f423b9a86a6dd3d2bc08d78f4d21525a14c40a6b Mon Sep 17 00:00:00 2001 From: Luca Risolia Date: Mon, 26 Mar 2007 16:12:04 -0300 Subject: V4L/DVB (5474): SN9C1xx driver updates @ Don't assume that SOF headers can't cross packets boundaries @ Fix compression quality selection + Add support for MI-0360 image sensor * Documentation updates @ Fix sysfs @ MI0343 rewritten * HV7131R color fixes and add new ABLC control * Rename the archive from "sn9c102" to "sn9c1xx" * fix typos * better support for TAS5110D @ fix OV7630 wrong colors @ Don't return an error if no input buffers are enqueued yet on VIDIOC_STREAMON * Add informations about colorspaces * More appropriate error codes in case of failure of some system calls * More precise hardware detection * Add more informations about supported hardware in the documentation + More supported devices + Add support for HV7131R image sensor Signed-off-by: Luca Risolia Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/sn9c102.txt | 64 ++-- MAINTAINERS | 2 +- drivers/media/video/sn9c102/Makefile | 17 +- drivers/media/video/sn9c102/sn9c102.h | 17 +- drivers/media/video/sn9c102/sn9c102_core.c | 199 ++++++---- drivers/media/video/sn9c102/sn9c102_devtable.h | 14 +- drivers/media/video/sn9c102/sn9c102_hv7131d.c | 7 +- drivers/media/video/sn9c102/sn9c102_hv7131r.c | 458 +++++++++++++++++++++++ drivers/media/video/sn9c102/sn9c102_mi0343.c | 111 +++--- drivers/media/video/sn9c102/sn9c102_mi0360.c | 353 +++++++++++++++++ drivers/media/video/sn9c102/sn9c102_ov7630.c | 63 ++-- drivers/media/video/sn9c102/sn9c102_ov7660.c | 3 - drivers/media/video/sn9c102/sn9c102_pas106b.c | 5 +- drivers/media/video/sn9c102/sn9c102_pas202bcb.c | 15 +- drivers/media/video/sn9c102/sn9c102_tas5110c1b.c | 6 +- drivers/media/video/sn9c102/sn9c102_tas5110d.c | 121 ++++++ drivers/media/video/sn9c102/sn9c102_tas5130d1b.c | 5 +- 17 files changed, 1223 insertions(+), 237 deletions(-) create mode 100644 drivers/media/video/sn9c102/sn9c102_hv7131r.c create mode 100644 drivers/media/video/sn9c102/sn9c102_mi0360.c create mode 100644 drivers/media/video/sn9c102/sn9c102_tas5110d.c (limited to 'Documentation') diff --git a/Documentation/video4linux/sn9c102.txt b/Documentation/video4linux/sn9c102.txt index 2913da3d0878..5fe0ad7dfc20 100644 --- a/Documentation/video4linux/sn9c102.txt +++ b/Documentation/video4linux/sn9c102.txt @@ -25,7 +25,7 @@ Index 1. Copyright ============ -Copyright (C) 2004-2006 by Luca Risolia +Copyright (C) 2004-2007 by Luca Risolia 2. Disclaimer @@ -216,10 +216,10 @@ Description: Debugging information level, from 0 to 3: 1 = critical errors 2 = significant informations 3 = more verbose messages - Level 3 is useful for testing only, when only one device - is used. It also shows some more informations about the - hardware being detected. This parameter can be changed at - runtime thanks to the /sys filesystem interface. + Level 3 is useful for testing only. It also shows some more + informations about the hardware being detected. + This parameter can be changed at runtime thanks to the /sys + filesystem interface. Default: 2 ------------------------------------------------------------------------------- @@ -235,7 +235,7 @@ created in the /sys/class/video4linux/videoX directory. You can set the green channel's gain by writing the desired value to it. The value may range from 0 to 15 for the SN9C101 or SN9C102 bridges, from 0 to 127 for the SN9C103, SN9C105 and SN9C120 bridges. -Similarly, only for the SN9C103, SN9C105 and SN9120 controllers, blue and red +Similarly, only for the SN9C103, SN9C105 and SN9C120 controllers, blue and red gain control files are available in the same directory, for which accepted values may range from 0 to 127. @@ -402,38 +402,49 @@ Vendor ID Product ID 0x0c45 0x60bc 0x0c45 0x60be 0x0c45 0x60c0 +0x0c45 0x60c2 0x0c45 0x60c8 0x0c45 0x60cc 0x0c45 0x60ea 0x0c45 0x60ec +0x0c45 0x60ef 0x0c45 0x60fa 0x0c45 0x60fb 0x0c45 0x60fc 0x0c45 0x60fe +0x0c45 0x6102 +0x0c45 0x6108 +0x0c45 0x610f 0x0c45 0x6130 +0x0c45 0x6138 0x0c45 0x613a 0x0c45 0x613b 0x0c45 0x613c 0x0c45 0x613e The list above does not imply that all those devices work with this driver: up -until now only the ones that assemble the following image sensors are -supported; kernel messages will always tell you whether this is the case (see -"Module loading" paragraph): - -Model Manufacturer ------ ------------ -HV7131D Hynix Semiconductor, Inc. -MI-0343 Micron Technology, Inc. -OV7630 OmniVision Technologies, Inc. -OV7660 OmniVision Technologies, Inc. -PAS106B PixArt Imaging, Inc. -PAS202BCA PixArt Imaging, Inc. -PAS202BCB PixArt Imaging, Inc. -TAS5110C1B Taiwan Advanced Sensor Corporation -TAS5130D1B Taiwan Advanced Sensor Corporation - -Some of the available control settings of each image sensor are supported +until now only the ones that assemble the following pairs of SN9C1xx bridges +and image sensors are supported; kernel messages will always tell you whether +this is the case (see "Module loading" paragraph): + +Image sensor / SN9C1xx bridge | SN9C10[12] SN9C103 SN9C105 SN9C120 +------------------------------------------------------------------------------- +HV7131D Hynix Semiconductor | Yes No No No +HV7131R Hynix Semiconductor | No Yes Yes Yes +MI-0343 Micron Technology | Yes No No No +MI-0360 Micron Technology | No Yes No No +OV7630 OmniVision Technologies | Yes Yes No No +OV7660 OmniVision Technologies | No No Yes Yes +PAS106B PixArt Imaging | Yes No No No +PAS202B PixArt Imaging | Yes Yes No No +TAS5110C1B Taiwan Advanced Sensor | Yes No No No +TAS5110D Taiwan Advanced Sensor | Yes No No No +TAS5130D1B Taiwan Advanced Sensor | Yes No No No + +"Yes" means that the pair is supported by the driver, while "No" means that the +pair does not exist or is not supported by the driver. + +Only some of the available control settings of each image sensor are supported through the V4L2 interface. Donations of new models for further testing and support would be much @@ -482,8 +493,8 @@ The SN9C1xx PC Camera Controllers can send images in two possible video formats over the USB: either native "Sequential RGB Bayer" or compressed. The compression is used to achieve high frame rates. With regard to the SN9C101, SN9C102 and SN9C103, the compression is based on the Huffman encoding -algorithm described below, while the SN9C105 and SN9C120 the compression is -based on the JPEG standard. +algorithm described below, while with regard to the SN9C105 and SN9C120 the +compression is based on the JPEG standard. The current video format may be selected or queried from the user application by calling the VIDIOC_S_FMT or VIDIOC_G_FMT ioctl's, as described in the V4L2 API specifications. @@ -573,4 +584,5 @@ order): - Mizuno Takafumi for the donation of a webcam; - an "anonymous" donator (who didn't want his name to be revealed) for the donation of a webcam. -- an anonymous donator for the donation of four webcams. +- an anonymous donator for the donation of four webcams and two boards with ten + image sensors. diff --git a/MAINTAINERS b/MAINTAINERS index 8a2f2fd5403e..415011c9d84e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3606,7 +3606,7 @@ L: linux-usb-devel@lists.sourceforge.net W: http://www.connecttech.com S: Supported -USB SN9C10x DRIVER +USB SN9C1xx DRIVER P: Luca Risolia M: luca.risolia@studio.unibo.it L: linux-usb-devel@lists.sourceforge.net diff --git a/drivers/media/video/sn9c102/Makefile b/drivers/media/video/sn9c102/Makefile index 30e3dfe537fe..a56d16f69c71 100644 --- a/drivers/media/video/sn9c102/Makefile +++ b/drivers/media/video/sn9c102/Makefile @@ -1,7 +1,14 @@ -sn9c102-objs := sn9c102_core.o sn9c102_hv7131d.o sn9c102_mi0343.o \ - sn9c102_ov7630.o sn9c102_ov7660.o sn9c102_pas106b.o \ - sn9c102_pas202bcb.o sn9c102_tas5110c1b.o \ - sn9c102_tas5130d1b.o +sn9c102-objs := sn9c102_core.o \ + sn9c102_hv7131d.o \ + sn9c102_hv7131r.o \ + sn9c102_mi0343.o \ + sn9c102_mi0360.o \ + sn9c102_ov7630.o \ + sn9c102_ov7660.o \ + sn9c102_pas106b.o \ + sn9c102_pas202bcb.o \ + sn9c102_tas5110c1b.o \ + sn9c102_tas5110d.o \ + sn9c102_tas5130d1b.o obj-$(CONFIG_USB_SN9C102) += sn9c102.o - diff --git a/drivers/media/video/sn9c102/sn9c102.h b/drivers/media/video/sn9c102/sn9c102.h index 5428f34e7c5b..680e74634527 100644 --- a/drivers/media/video/sn9c102/sn9c102.h +++ b/drivers/media/video/sn9c102/sn9c102.h @@ -78,8 +78,13 @@ enum sn9c102_stream_state { typedef char sn9c102_sof_header_t[62]; +struct sn9c102_sof_t { + sn9c102_sof_header_t header; + u16 bytesread; +}; + struct sn9c102_sysfs_attr { - u8 reg, i2c_reg; + u16 reg, i2c_reg; sn9c102_sof_header_t frame_header; }; @@ -112,7 +117,7 @@ struct sn9c102_device { struct v4l2_jpegcompression compression; struct sn9c102_sysfs_attr sysfs; - sn9c102_sof_header_t sof_header; + struct sn9c102_sof_t sof; u16 reg[384]; struct sn9c102_module_param module_param; @@ -182,8 +187,8 @@ do { \ if ((level) == 1 || (level) == 2) \ pr_info("sn9c102: " fmt "\n", ## args); \ else if ((level) == 3) \ - pr_debug("sn9c102: [%s:%d] " fmt "\n", __FUNCTION__, \ - __LINE__ , ## args); \ + pr_debug("sn9c102: [%s:%d] " fmt "\n", \ + __FUNCTION__, __LINE__ , ## args); \ } \ } while (0) #else @@ -194,8 +199,8 @@ do { \ #undef PDBG #define PDBG(fmt, args...) \ -dev_info(&cam->usbdev->dev, "[%s:%d] " fmt "\n", \ - __FUNCTION__, __LINE__ , ## args) +dev_info(&cam->usbdev->dev, "[%s:%s:%d] " fmt "\n", __FILE__, __FUNCTION__, \ + __LINE__ , ## args) #undef PDBGG #define PDBGG(fmt, args...) do {;} while(0) /* placeholder */ diff --git a/drivers/media/video/sn9c102/sn9c102_core.c b/drivers/media/video/sn9c102/sn9c102_core.c index d0e2b40a7725..f09caf2b6e75 100644 --- a/drivers/media/video/sn9c102/sn9c102_core.c +++ b/drivers/media/video/sn9c102/sn9c102_core.c @@ -44,11 +44,12 @@ /*****************************************************************************/ #define SN9C102_MODULE_NAME "V4L2 driver for SN9C1xx PC Camera Controllers" -#define SN9C102_MODULE_AUTHOR "(C) 2004-2006 Luca Risolia" +#define SN9C102_MODULE_ALIAS "sn9c1xx" +#define SN9C102_MODULE_AUTHOR "(C) 2004-2007 Luca Risolia" #define SN9C102_AUTHOR_EMAIL "" #define SN9C102_MODULE_LICENSE "GPL" -#define SN9C102_MODULE_VERSION "1:1.34" -#define SN9C102_MODULE_VERSION_CODE KERNEL_VERSION(1, 1, 34) +#define SN9C102_MODULE_VERSION "1:1.39" +#define SN9C102_MODULE_VERSION_CODE KERNEL_VERSION(1, 1, 39) /*****************************************************************************/ @@ -56,6 +57,7 @@ MODULE_DEVICE_TABLE(usb, sn9c102_id_table); MODULE_AUTHOR(SN9C102_MODULE_AUTHOR " " SN9C102_AUTHOR_EMAIL); MODULE_DESCRIPTION(SN9C102_MODULE_NAME); +MODULE_ALIAS(SN9C102_MODULE_ALIAS); MODULE_VERSION(SN9C102_MODULE_VERSION); MODULE_LICENSE(SN9C102_MODULE_LICENSE); @@ -106,8 +108,7 @@ MODULE_PARM_DESC(debug, "\n1 = critical errors" "\n2 = significant informations" "\n3 = more verbose messages" - "\nLevel 3 is useful for testing only, when only " - "one device is used." + "\nLevel 3 is useful for testing only." "\nDefault value is "__MODULE_STRING(SN9C102_DEBUG_LEVEL)"." "\n"); #endif @@ -121,8 +122,8 @@ sn9c102_request_buffers(struct sn9c102_device* cam, u32 count, struct v4l2_pix_format* p = &(cam->sensor.pix_format); struct v4l2_rect* r = &(cam->sensor.cropcap.bounds); size_t imagesize = cam->module_param.force_munmap || io == IO_READ ? - (p->width * p->height * p->priv) / 8 : - (r->width * r->height * p->priv) / 8; + (p->width * p->height * p->priv) / 8 : + (r->width * r->height * p->priv) / 8; void* buff = NULL; u32 i; @@ -485,18 +486,43 @@ static size_t sn9c102_sof_length(struct sn9c102_device* cam) static void* sn9c102_find_sof_header(struct sn9c102_device* cam, void* mem, size_t len) { - char sof_header[6] = {0xff, 0xff, 0x00, 0xc4, 0xc4, 0x96}; - size_t soflen = 0, i; + const char marker[6] = {0xff, 0xff, 0x00, 0xc4, 0xc4, 0x96}; + const char *m = mem; + size_t soflen = 0, i, j; soflen = sn9c102_sof_length(cam); - for (i = 0; (len >= soflen) && (i <= len - soflen); i++) - if (!memcmp(mem + i, sof_header, sizeof(sof_header))) { - memcpy(cam->sof_header, mem + i, - sizeof(sn9c102_sof_header_t)); - /* Skip the header */ - return mem + i + soflen; + for (i = 0; i < len; i++) { + size_t b; + + /* Read the variable part of the header */ + if (unlikely(cam->sof.bytesread >= sizeof(marker))) { + cam->sof.header[cam->sof.bytesread] = *(m+i); + if (++cam->sof.bytesread == soflen) { + cam->sof.bytesread = 0; + return mem + i; + } + continue; + } + + /* Search for the SOF marker (fixed part) in the header */ + for (j = 0, b=cam->sof.bytesread; j+b < sizeof(marker); j++) { + if (unlikely(i+j) == len) + return NULL; + if (*(m+i+j) == marker[cam->sof.bytesread]) { + cam->sof.header[cam->sof.bytesread] = *(m+i+j); + if (++cam->sof.bytesread == sizeof(marker)) { + PDBGG("Bytes to analyze: %zd. SOF " + "starts at byte #%zd", len, i); + i += j+1; + break; + } + } else { + cam->sof.bytesread = 0; + break; } + } + } return NULL; } @@ -513,10 +539,16 @@ sn9c102_find_eof_header(struct sn9c102_device* cam, void* mem, size_t len) }; size_t i, j; + /* The EOF header does not exist in compressed data */ if (cam->sensor.pix_format.pixelformat == V4L2_PIX_FMT_SN9C10X || cam->sensor.pix_format.pixelformat == V4L2_PIX_FMT_JPEG) - return NULL; /* EOF header does not exist in compressed data */ + return NULL; + /* + The EOF header might cross the packet boundary, but this is not a + problem, since the end of a frame is determined by checking its size + in the first place. + */ for (i = 0; (len >= 4) && (i <= len - 4); i++) for (j = 0; j < ARRAY_SIZE(eof_header); j++) if (!memcmp(mem + i, eof_header[j], 4)) @@ -639,6 +671,7 @@ static void sn9c102_urb_complete(struct urb *urb) cam->stream = STREAM_OFF; if ((*f)) (*f)->state = F_QUEUED; + cam->sof.bytesread = 0; DBG(3, "Stream interrupted by application"); wake_up(&cam->wait_stream); } @@ -676,6 +709,7 @@ static void sn9c102_urb_complete(struct urb *urb) if (status) { DBG(3, "Error in isochronous frame"); (*f)->state = F_ERROR; + cam->sof.bytesread = 0; continue; } @@ -692,13 +726,13 @@ end_of_frame: if (eof) img = (eof > pos) ? eof - pos - 1 : 0; - if ((*f)->buf.bytesused+img > imagesize) { + if ((*f)->buf.bytesused + img > imagesize) { u32 b; b = (*f)->buf.bytesused + img - imagesize; img = imagesize - (*f)->buf.bytesused; - DBG(3, "Expected EOF not found: " - "video frame cut"); + PDBGG("Expected EOF not found: video " + "frame cut"); if (eof) DBG(3, "Exceeded limit: +%u " "bytes", (unsigned)(b)); @@ -719,11 +753,6 @@ end_of_frame: V4L2_PIX_FMT_JPEG) && eof)) { u32 b; - if (cam->sensor.pix_format.pixelformat - == V4L2_PIX_FMT_JPEG) - sn9c102_write_eoimarker(cam, - (*f)); - b = (*f)->buf.bytesused; (*f)->state = F_DONE; (*f)->buf.sequence= ++cam->frame_count; @@ -741,7 +770,7 @@ end_of_frame: spin_unlock(&cam->queue_lock); memcpy(cam->sysfs.frame_header, - cam->sof_header, soflen); + cam->sof.header, soflen); DBG(3, "Video frame captured: %lu " "bytes", (unsigned long)(b)); @@ -791,7 +820,13 @@ start_of_frame: V4L2_PIX_FMT_SN9C10X || cam->sensor.pix_format.pixelformat == V4L2_PIX_FMT_JPEG) { - eof = sof - soflen; + if (sof - pos >= soflen) { + eof = sof - soflen; + } else { /* remove header */ + eof = pos; + (*f)->buf.bytesused -= + (soflen - (sof - pos)); + } goto end_of_frame; } else { DBG(3, "SOF before expected EOF after " @@ -878,6 +913,7 @@ static int sn9c102_start_transfer(struct sn9c102_device* cam) } cam->frame_current = NULL; + cam->sof.bytesread = 0; for (i = 0; i < SN9C102_URBS; i++) { err = usb_submit_urb(cam->urb[i], GFP_KERNEL); @@ -959,9 +995,9 @@ static u16 sn9c102_strtou16(const char* buff, size_t len, ssize_t* count) if (len < 6) { strncpy(str, buff, len); - str[len+1] = '\0'; + str[len] = '\0'; } else { - strncpy(str, buff, 4); + strncpy(str, buff, 6); str[6] = '\0'; } @@ -1062,7 +1098,7 @@ static ssize_t sn9c102_show_val(struct class_device* cd, char* buf) count = sprintf(buf, "%d\n", val); - DBG(3, "Read bytes: %zd", count); + DBG(3, "Read bytes: %zd, value: %d", count, val); mutex_unlock(&sn9c102_sysfs_lock); @@ -1197,7 +1233,7 @@ static ssize_t sn9c102_show_i2c_val(struct class_device* cd, char* buf) count = sprintf(buf, "%d\n", val); - DBG(3, "Read bytes: %zd", count); + DBG(3, "Read bytes: %zd, value: %d", count, val); mutex_unlock(&sn9c102_sysfs_lock); @@ -1477,10 +1513,10 @@ sn9c102_set_compression(struct sn9c102_device* cam, case BRIDGE_SN9C101: case BRIDGE_SN9C102: case BRIDGE_SN9C103: - if (compression->quality == 0) + if (compression->quality == 0) err += sn9c102_write_reg(cam, cam->reg[0x17] | 0x01, 0x17); - else if (compression->quality == 1) + else if (compression->quality == 1) err += sn9c102_write_reg(cam, cam->reg[0x17] & 0xfe, 0x17); break; @@ -1489,10 +1525,10 @@ sn9c102_set_compression(struct sn9c102_device* cam, if (compression->quality == 0) { for (i = 0; i <= 63; i++) { err += sn9c102_write_reg(cam, - SN9C102_Y_QTABLE0[i], + SN9C102_Y_QTABLE1[i], 0x100 + i); err += sn9c102_write_reg(cam, - SN9C102_UV_QTABLE0[i], + SN9C102_UV_QTABLE1[i], 0x140 + i); } err += sn9c102_write_reg(cam, cam->reg[0x18] & 0xbf, @@ -1597,9 +1633,13 @@ static int sn9c102_init(struct sn9c102_device* cam) if (cam->bridge == BRIDGE_SN9C101 || cam->bridge == BRIDGE_SN9C102 || cam->bridge == BRIDGE_SN9C103) { + if (s->pix_format.pixelformat == V4L2_PIX_FMT_JPEG) + s->pix_format.pixelformat= V4L2_PIX_FMT_SBGGR8; cam->compression.quality = cam->reg[0x17] & 0x01 ? 0 : 1; } else { + if (s->pix_format.pixelformat == V4L2_PIX_FMT_SN9C10X) + s->pix_format.pixelformat = V4L2_PIX_FMT_JPEG; cam->compression.quality = cam->reg[0x18] & 0x40 ? 0 : 1; err += sn9c102_set_compression(cam, &cam->compression); @@ -1805,7 +1845,7 @@ sn9c102_read(struct file* filp, char __user * buf, size_t count, loff_t* f_pos) DBG(3, "Close and open the device again to choose " "the read method"); mutex_unlock(&cam->fileop_mutex); - return -EINVAL; + return -EBUSY; } if (cam->io == IO_NONE) { @@ -1845,16 +1885,16 @@ sn9c102_read(struct file* filp, char __user * buf, size_t count, loff_t* f_pos) return err; } } else { - timeout = wait_event_interruptible_timeout - ( cam->wait_frame, - (!list_empty(&cam->outqueue)) || - (cam->state & DEV_DISCONNECTED) || - (cam->state & DEV_MISCONFIGURED), - cam->module_param.frame_timeout * - 1000 * msecs_to_jiffies(1) ); - if (timeout < 0) { - mutex_unlock(&cam->fileop_mutex); - return timeout; + timeout = wait_event_interruptible_timeout + ( cam->wait_frame, + (!list_empty(&cam->outqueue)) || + (cam->state & DEV_DISCONNECTED) || + (cam->state & DEV_MISCONFIGURED), + cam->module_param.frame_timeout * + 1000 * msecs_to_jiffies(1) ); + if (timeout < 0) { + mutex_unlock(&cam->fileop_mutex); + return timeout; } else if (timeout == 0 && !(cam->state & DEV_DISCONNECTED)) { DBG(1, "Video frame timeout elapsed"); @@ -2001,7 +2041,12 @@ static int sn9c102_mmap(struct file* filp, struct vm_area_struct *vma) return -EIO; } - if (cam->io != IO_MMAP || !(vma->vm_flags & VM_WRITE) || + if (!(vma->vm_flags & (VM_WRITE | VM_READ))) { + mutex_unlock(&cam->fileop_mutex); + return -EACCES; + } + + if (cam->io != IO_MMAP || size != PAGE_ALIGN(cam->frame[0].buf.length)) { mutex_unlock(&cam->fileop_mutex); return -EINVAL; @@ -2267,7 +2312,7 @@ sn9c102_vidioc_s_crop(struct sn9c102_device* cam, void __user * arg) if (cam->frame[i].vma_use_count) { DBG(3, "VIDIOC_S_CROP failed. " "Unmap the buffers first."); - return -EINVAL; + return -EBUSY; } /* Preserve R,G or B origin */ @@ -2410,8 +2455,8 @@ sn9c102_vidioc_enum_fmt(struct sn9c102_device* cam, void __user * arg) case BRIDGE_SN9C101: case BRIDGE_SN9C102: case BRIDGE_SN9C103: - strcpy(fmtd.description, "compressed"); - fmtd.pixelformat = V4L2_PIX_FMT_SN9C10X; + strcpy(fmtd.description, "compressed"); + fmtd.pixelformat = V4L2_PIX_FMT_SN9C10X; break; case BRIDGE_SN9C105: case BRIDGE_SN9C120: @@ -2445,8 +2490,10 @@ sn9c102_vidioc_g_fmt(struct sn9c102_device* cam, void __user * arg) if (format.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; - pfmt->bytesperline = (pfmt->pixelformat==V4L2_PIX_FMT_SN9C10X || - pfmt->pixelformat==V4L2_PIX_FMT_JPEG) + pfmt->colorspace = (pfmt->pixelformat == V4L2_PIX_FMT_JPEG) ? + V4L2_COLORSPACE_JPEG : V4L2_COLORSPACE_SRGB; + pfmt->bytesperline = (pfmt->pixelformat == V4L2_PIX_FMT_SN9C10X || + pfmt->pixelformat == V4L2_PIX_FMT_JPEG) ? 0 : (pfmt->width * pfmt->priv) / 8; pfmt->sizeimage = pfmt->height * ((pfmt->width*pfmt->priv)/8); pfmt->field = V4L2_FIELD_NONE; @@ -2521,9 +2568,9 @@ sn9c102_vidioc_try_s_fmt(struct sn9c102_device* cam, unsigned int cmd, case BRIDGE_SN9C101: case BRIDGE_SN9C102: case BRIDGE_SN9C103: - if (pix->pixelformat != V4L2_PIX_FMT_SN9C10X && - pix->pixelformat != V4L2_PIX_FMT_SBGGR8) - pix->pixelformat = pfmt->pixelformat; + if (pix->pixelformat != V4L2_PIX_FMT_SN9C10X && + pix->pixelformat != V4L2_PIX_FMT_SBGGR8) + pix->pixelformat = pfmt->pixelformat; break; case BRIDGE_SN9C105: case BRIDGE_SN9C120: @@ -2533,7 +2580,8 @@ sn9c102_vidioc_try_s_fmt(struct sn9c102_device* cam, unsigned int cmd, break; } pix->priv = pfmt->priv; /* bpp */ - pix->colorspace = pfmt->colorspace; + pix->colorspace = (pix->pixelformat == V4L2_PIX_FMT_JPEG) ? + V4L2_COLORSPACE_JPEG : V4L2_COLORSPACE_SRGB; pix->bytesperline = (pix->pixelformat == V4L2_PIX_FMT_SN9C10X || pix->pixelformat == V4L2_PIX_FMT_JPEG) ? 0 : (pix->width * pix->priv) / 8; @@ -2551,7 +2599,7 @@ sn9c102_vidioc_try_s_fmt(struct sn9c102_device* cam, unsigned int cmd, if (cam->frame[i].vma_use_count) { DBG(3, "VIDIOC_S_FMT failed. Unmap the " "buffers first."); - return -EINVAL; + return -EBUSY; } if (cam->stream == STREAM_ON) @@ -2666,14 +2714,14 @@ sn9c102_vidioc_reqbufs(struct sn9c102_device* cam, void __user * arg) if (cam->io == IO_READ) { DBG(3, "Close and open the device again to choose the mmap " "I/O method"); - return -EINVAL; + return -EBUSY; } for (i = 0; i < cam->nbuffers; i++) if (cam->frame[i].vma_use_count) { DBG(3, "VIDIOC_REQBUFS failed. Previous buffers are " "still mapped."); - return -EINVAL; + return -EBUSY; } if (cam->stream == STREAM_ON) @@ -2785,15 +2833,15 @@ sn9c102_vidioc_dqbuf(struct sn9c102_device* cam, struct file* filp, if (err) return err; } else { - timeout = wait_event_interruptible_timeout - ( cam->wait_frame, - (!list_empty(&cam->outqueue)) || - (cam->state & DEV_DISCONNECTED) || - (cam->state & DEV_MISCONFIGURED), - cam->module_param.frame_timeout * - 1000 * msecs_to_jiffies(1) ); - if (timeout < 0) - return timeout; + timeout = wait_event_interruptible_timeout + ( cam->wait_frame, + (!list_empty(&cam->outqueue)) || + (cam->state & DEV_DISCONNECTED) || + (cam->state & DEV_MISCONFIGURED), + cam->module_param.frame_timeout * + 1000 * msecs_to_jiffies(1) ); + if (timeout < 0) + return timeout; else if (timeout == 0 && !(cam->state & DEV_DISCONNECTED)) { DBG(1, "Video frame timeout elapsed"); @@ -2837,9 +2885,6 @@ sn9c102_vidioc_streamon(struct sn9c102_device* cam, void __user * arg) if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io != IO_MMAP) return -EINVAL; - if (list_empty(&cam->inqueue)) - return -EINVAL; - cam->stream = STREAM_ON; DBG(3, "Stream on"); @@ -3166,8 +3211,8 @@ sn9c102_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) r = sn9c102_read_reg(cam, 0x00); if (r < 0 || (r != 0x10 && r != 0x11 && r != 0x12)) { - DBG(1, "Sorry, this is not a SN9C1xx based camera " - "(vid/pid 0x%04X:0x%04X)", id->idVendor, id->idProduct); + DBG(1, "Sorry, this is not a SN9C1xx-based camera " + "(vid:pid 0x%04X:0x%04X)", id->idVendor, id->idProduct); err = -ENODEV; goto fail; } @@ -3177,19 +3222,19 @@ sn9c102_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) case BRIDGE_SN9C101: case BRIDGE_SN9C102: DBG(2, "SN9C10[12] PC Camera Controller detected " - "(vid/pid 0x%04X:0x%04X)", id->idVendor, id->idProduct); + "(vid:pid 0x%04X:0x%04X)", id->idVendor, id->idProduct); break; case BRIDGE_SN9C103: DBG(2, "SN9C103 PC Camera Controller detected " - "(vid/pid 0x%04X:0x%04X)", id->idVendor, id->idProduct); + "(vid:pid 0x%04X:0x%04X)", id->idVendor, id->idProduct); break; case BRIDGE_SN9C105: DBG(2, "SN9C105 PC Camera Controller detected " - "(vid/pid 0x%04X:0x%04X)", id->idVendor, id->idProduct); + "(vid:pid 0x%04X:0x%04X)", id->idVendor, id->idProduct); break; case BRIDGE_SN9C120: DBG(2, "SN9C120 PC Camera Controller detected " - "(vid/pid 0x%04X:0x%04X)", id->idVendor, id->idProduct); + "(vid:pid 0x%04X:0x%04X)", id->idVendor, id->idProduct); break; } @@ -3260,6 +3305,8 @@ sn9c102_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) "device controlling. Error #%d", err); #else DBG(2, "Optional device control through 'sysfs' interface disabled"); + DBG(3, "Compile the kernel with the 'CONFIG_VIDEO_ADV_DEBUG' " + "configuration option to enable it."); #endif usb_set_intfdata(intf, cam); diff --git a/drivers/media/video/sn9c102/sn9c102_devtable.h b/drivers/media/video/sn9c102/sn9c102_devtable.h index 3a682eca6c65..f49bd8c5b86e 100644 --- a/drivers/media/video/sn9c102/sn9c102_devtable.h +++ b/drivers/media/video/sn9c102/sn9c102_devtable.h @@ -89,16 +89,22 @@ static const struct usb_device_id sn9c102_id_table[] = { { SN9C102_USB_DEVICE(0x0471, 0x0327, BRIDGE_SN9C105), }, { SN9C102_USB_DEVICE(0x0471, 0x0328, BRIDGE_SN9C105), }, { SN9C102_USB_DEVICE(0x0c45, 0x60c0, BRIDGE_SN9C105), }, + { SN9C102_USB_DEVICE(0x0c45, 0x60c2, BRIDGE_SN9C105), }, { SN9C102_USB_DEVICE(0x0c45, 0x60c8, BRIDGE_SN9C105), }, { SN9C102_USB_DEVICE(0x0c45, 0x60cc, BRIDGE_SN9C105), }, { SN9C102_USB_DEVICE(0x0c45, 0x60ea, BRIDGE_SN9C105), }, { SN9C102_USB_DEVICE(0x0c45, 0x60ec, BRIDGE_SN9C105), }, + { SN9C102_USB_DEVICE(0x0c45, 0x60ef, BRIDGE_SN9C105), }, { SN9C102_USB_DEVICE(0x0c45, 0x60fa, BRIDGE_SN9C105), }, { SN9C102_USB_DEVICE(0x0c45, 0x60fb, BRIDGE_SN9C105), }, { SN9C102_USB_DEVICE(0x0c45, 0x60fc, BRIDGE_SN9C105), }, { SN9C102_USB_DEVICE(0x0c45, 0x60fe, BRIDGE_SN9C105), }, /* SN9C120 */ + { SN9C102_USB_DEVICE(0x0c45, 0x6102, BRIDGE_SN9C120), }, + { SN9C102_USB_DEVICE(0x0c45, 0x6108, BRIDGE_SN9C120), }, + { SN9C102_USB_DEVICE(0x0c45, 0x610f, BRIDGE_SN9C120), }, { SN9C102_USB_DEVICE(0x0c45, 0x6130, BRIDGE_SN9C120), }, + { SN9C102_USB_DEVICE(0x0c45, 0x6138, BRIDGE_SN9C120), }, { SN9C102_USB_DEVICE(0x0c45, 0x613a, BRIDGE_SN9C120), }, { SN9C102_USB_DEVICE(0x0c45, 0x613b, BRIDGE_SN9C120), }, { SN9C102_USB_DEVICE(0x0c45, 0x613c, BRIDGE_SN9C120), }, @@ -114,12 +120,15 @@ static const struct usb_device_id sn9c102_id_table[] = { Functions must return 0 on success, the appropriate error otherwise. */ extern int sn9c102_probe_hv7131d(struct sn9c102_device* cam); +extern int sn9c102_probe_hv7131r(struct sn9c102_device* cam); extern int sn9c102_probe_mi0343(struct sn9c102_device* cam); +extern int sn9c102_probe_mi0360(struct sn9c102_device* cam); extern int sn9c102_probe_ov7630(struct sn9c102_device* cam); extern int sn9c102_probe_ov7660(struct sn9c102_device* cam); extern int sn9c102_probe_pas106b(struct sn9c102_device* cam); extern int sn9c102_probe_pas202bcb(struct sn9c102_device* cam); extern int sn9c102_probe_tas5110c1b(struct sn9c102_device* cam); +extern int sn9c102_probe_tas5110d(struct sn9c102_device* cam); extern int sn9c102_probe_tas5130d1b(struct sn9c102_device* cam); /* @@ -128,13 +137,16 @@ extern int sn9c102_probe_tas5130d1b(struct sn9c102_device* cam); the order of the list below, from top to bottom. */ static int (*sn9c102_sensor_table[])(struct sn9c102_device*) = { + &sn9c102_probe_hv7131d, /* strong detection based on SENSOR ids */ + &sn9c102_probe_hv7131r, /* strong detection based on SENSOR ids */ &sn9c102_probe_mi0343, /* strong detection based on SENSOR ids */ + &sn9c102_probe_mi0360, /* strong detection based on SENSOR ids */ &sn9c102_probe_pas106b, /* strong detection based on SENSOR ids */ &sn9c102_probe_pas202bcb, /* strong detection based on SENSOR ids */ - &sn9c102_probe_hv7131d, /* strong detection based on SENSOR ids */ &sn9c102_probe_ov7630, /* strong detection based on SENSOR ids */ &sn9c102_probe_ov7660, /* strong detection based on SENSOR ids */ &sn9c102_probe_tas5110c1b, /* detection based on USB pid/vid */ + &sn9c102_probe_tas5110d, /* detection based on USB pid/vid */ &sn9c102_probe_tas5130d1b, /* detection based on USB pid/vid */ NULL, }; diff --git a/drivers/media/video/sn9c102/sn9c102_hv7131d.c b/drivers/media/video/sn9c102/sn9c102_hv7131d.c index 7ae368f60d89..9b2e2d68c739 100644 --- a/drivers/media/video/sn9c102/sn9c102_hv7131d.c +++ b/drivers/media/video/sn9c102/sn9c102_hv7131d.c @@ -22,9 +22,6 @@ #include "sn9c102_sensor.h" -static struct sn9c102_sensor hv7131d; - - static int hv7131d_init(struct sn9c102_device* cam) { int err = 0; @@ -153,7 +150,7 @@ static int hv7131d_set_pix_format(struct sn9c102_device* cam, static struct sn9c102_sensor hv7131d = { .name = "HV7131D", .maintainer = "Luca Risolia ", - .supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102 | BRIDGE_SN9C103, + .supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102, .sysfs_ops = SN9C102_I2C_READ | SN9C102_I2C_WRITE, .frequency = SN9C102_I2C_100KHZ, .interface = SN9C102_I2C_2WIRES, @@ -263,7 +260,7 @@ int sn9c102_probe_hv7131d(struct sn9c102_device* cam) if (r0 < 0 || r1 < 0) return -EIO; - if (r0 != 0x00 && r1 != 0x04) + if (r0 != 0x00 || r1 != 0x04) return -ENODEV; sn9c102_attach_sensor(cam, &hv7131d); diff --git a/drivers/media/video/sn9c102/sn9c102_hv7131r.c b/drivers/media/video/sn9c102/sn9c102_hv7131r.c new file mode 100644 index 000000000000..c4a3e3991e88 --- /dev/null +++ b/drivers/media/video/sn9c102/sn9c102_hv7131r.c @@ -0,0 +1,458 @@ +/*************************************************************************** + * Plug-in for HV7131R image sensor connected to the SN9C1xx PC Camera * + * Controllers * + * * + * Copyright (C) 2007 by Luca Risolia * + * * + * 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 "sn9c102_sensor.h" + + +static int hv7131r_init(struct sn9c102_device* cam) +{ + int err = 0; + + switch (sn9c102_get_bridge(cam)) { + case BRIDGE_SN9C103: + err += sn9c102_write_reg(cam, 0x00, 0x03); + err += sn9c102_write_reg(cam, 0x1a, 0x04); + err += sn9c102_write_reg(cam, 0x20, 0x05); + err += sn9c102_write_reg(cam, 0x20, 0x06); + err += sn9c102_write_reg(cam, 0x03, 0x10); + err += sn9c102_write_reg(cam, 0x00, 0x14); + err += sn9c102_write_reg(cam, 0x60, 0x17); + err += sn9c102_write_reg(cam, 0x0a, 0x18); + err += sn9c102_write_reg(cam, 0xf0, 0x19); + err += sn9c102_write_reg(cam, 0x1d, 0x1a); + err += sn9c102_write_reg(cam, 0x10, 0x1b); + err += sn9c102_write_reg(cam, 0x02, 0x1c); + err += sn9c102_write_reg(cam, 0x03, 0x1d); + err += sn9c102_write_reg(cam, 0x0f, 0x1e); + err += sn9c102_write_reg(cam, 0x0c, 0x1f); + err += sn9c102_write_reg(cam, 0x00, 0x20); + err += sn9c102_write_reg(cam, 0x10, 0x21); + err += sn9c102_write_reg(cam, 0x20, 0x22); + err += sn9c102_write_reg(cam, 0x30, 0x23); + err += sn9c102_write_reg(cam, 0x40, 0x24); + err += sn9c102_write_reg(cam, 0x50, 0x25); + err += sn9c102_write_reg(cam, 0x60, 0x26); + err += sn9c102_write_reg(cam, 0x70, 0x27); + err += sn9c102_write_reg(cam, 0x80, 0x28); + err += sn9c102_write_reg(cam, 0x90, 0x29); + err += sn9c102_write_reg(cam, 0xa0, 0x2a); + err += sn9c102_write_reg(cam, 0xb0, 0x2b); + err += sn9c102_write_reg(cam, 0xc0, 0x2c); + err += sn9c102_write_reg(cam, 0xd0, 0x2d); + err += sn9c102_write_reg(cam, 0xe0, 0x2e); + err += sn9c102_write_reg(cam, 0xf0, 0x2f); + err += sn9c102_write_reg(cam, 0xff, 0x30); + break; + case BRIDGE_SN9C105: + case BRIDGE_SN9C120: + err += sn9c102_write_reg(cam, 0x44, 0x01); + err += sn9c102_write_reg(cam, 0x40, 0x02); + err += sn9c102_write_reg(cam, 0x00, 0x03); + err += sn9c102_write_reg(cam, 0x1a, 0x04); + err += sn9c102_write_reg(cam, 0x44, 0x05); + err += sn9c102_write_reg(cam, 0x3e, 0x06); + err += sn9c102_write_reg(cam, 0x1a, 0x07); + err += sn9c102_write_reg(cam, 0x03, 0x10); + err += sn9c102_write_reg(cam, 0x08, 0x14); + err += sn9c102_write_reg(cam, 0xa3, 0x17); + err += sn9c102_write_reg(cam, 0x4b, 0x18); + err += sn9c102_write_reg(cam, 0x00, 0x19); + err += sn9c102_write_reg(cam, 0x1d, 0x1a); + err += sn9c102_write_reg(cam, 0x10, 0x1b); + err += sn9c102_write_reg(cam, 0x02, 0x1c); + err += sn9c102_write_reg(cam, 0x03, 0x1d); + err += sn9c102_write_reg(cam, 0x0f, 0x1e); + err += sn9c102_write_reg(cam, 0x0c, 0x1f); + err += sn9c102_write_reg(cam, 0x00, 0x20); + err += sn9c102_write_reg(cam, 0x29, 0x21); + err += sn9c102_write_reg(cam, 0x40, 0x22); + err += sn9c102_write_reg(cam, 0x54, 0x23); + err += sn9c102_write_reg(cam, 0x66, 0x24); + err += sn9c102_write_reg(cam, 0x76, 0x25); + err += sn9c102_write_reg(cam, 0x85, 0x26); + err += sn9c102_write_reg(cam, 0x94, 0x27); + err += sn9c102_write_reg(cam, 0xa1, 0x28); + err += sn9c102_write_reg(cam, 0xae, 0x29); + err += sn9c102_write_reg(cam, 0xbb, 0x2a); + err += sn9c102_write_reg(cam, 0xc7, 0x2b); + err += sn9c102_write_reg(cam, 0xd3, 0x2c); + err += sn9c102_write_reg(cam, 0xde, 0x2d); + err += sn9c102_write_reg(cam, 0xea, 0x2e); + err += sn9c102_write_reg(cam, 0xf4, 0x2f); + err += sn9c102_write_reg(cam, 0xff, 0x30); + err += sn9c102_write_reg(cam, 0x00, 0x3F); + err += sn9c102_write_reg(cam, 0xC7, 0x40); + err += sn9c102_write_reg(cam, 0x01, 0x41); + err += sn9c102_write_reg(cam, 0x44, 0x42); + err += sn9c102_write_reg(cam, 0x00, 0x43); + err += sn9c102_write_reg(cam, 0x44, 0x44); + err += sn9c102_write_reg(cam, 0x00, 0x45); + err += sn9c102_write_reg(cam, 0x44, 0x46); + err += sn9c102_write_reg(cam, 0x00, 0x47); + err += sn9c102_write_reg(cam, 0xC7, 0x48); + err += sn9c102_write_reg(cam, 0x01, 0x49); + err += sn9c102_write_reg(cam, 0xC7, 0x4A); + err += sn9c102_write_reg(cam, 0x01, 0x4B); + err += sn9c102_write_reg(cam, 0xC7, 0x4C); + err += sn9c102_write_reg(cam, 0x01, 0x4D); + err += sn9c102_write_reg(cam, 0x44, 0x4E); + err += sn9c102_write_reg(cam, 0x00, 0x4F); + err += sn9c102_write_reg(cam, 0x44, 0x50); + err += sn9c102_write_reg(cam, 0x00, 0x51); + err += sn9c102_write_reg(cam, 0x44, 0x52); + err += sn9c102_write_reg(cam, 0x00, 0x53); + err += sn9c102_write_reg(cam, 0xC7, 0x54); + err += sn9c102_write_reg(cam, 0x01, 0x55); + err += sn9c102_write_reg(cam, 0xC7, 0x56); + err += sn9c102_write_reg(cam, 0x01, 0x57); + err += sn9c102_write_reg(cam, 0xC7, 0x58); + err += sn9c102_write_reg(cam, 0x01, 0x59); + err += sn9c102_write_reg(cam, 0x44, 0x5A); + err += sn9c102_write_reg(cam, 0x00, 0x5B); + err += sn9c102_write_reg(cam, 0x44, 0x5C); + err += sn9c102_write_reg(cam, 0x00, 0x5D); + err += sn9c102_write_reg(cam, 0x44, 0x5E); + err += sn9c102_write_reg(cam, 0x00, 0x5F); + err += sn9c102_write_reg(cam, 0xC7, 0x60); + err += sn9c102_write_reg(cam, 0x01, 0x61); + err += sn9c102_write_reg(cam, 0xC7, 0x62); + err += sn9c102_write_reg(cam, 0x01, 0x63); + err += sn9c102_write_reg(cam, 0xC7, 0x64); + err += sn9c102_write_reg(cam, 0x01, 0x65); + err += sn9c102_write_reg(cam, 0x44, 0x66); + err += sn9c102_write_reg(cam, 0x00, 0x67); + err += sn9c102_write_reg(cam, 0x44, 0x68); + err += sn9c102_write_reg(cam, 0x00, 0x69); + err += sn9c102_write_reg(cam, 0x44, 0x6A); + err += sn9c102_write_reg(cam, 0x00, 0x6B); + err += sn9c102_write_reg(cam, 0xC7, 0x6C); + err += sn9c102_write_reg(cam, 0x01, 0x6D); + err += sn9c102_write_reg(cam, 0xC7, 0x6E); + err += sn9c102_write_reg(cam, 0x01, 0x6F); + err += sn9c102_write_reg(cam, 0xC7, 0x70); + err += sn9c102_write_reg(cam, 0x01, 0x71); + err += sn9c102_write_reg(cam, 0x44, 0x72); + err += sn9c102_write_reg(cam, 0x00, 0x73); + err += sn9c102_write_reg(cam, 0x44, 0x74); + err += sn9c102_write_reg(cam, 0x00, 0x75); + err += sn9c102_write_reg(cam, 0x44, 0x76); + err += sn9c102_write_reg(cam, 0x00, 0x77); + err += sn9c102_write_reg(cam, 0xC7, 0x78); + err += sn9c102_write_reg(cam, 0x01, 0x79); + err += sn9c102_write_reg(cam, 0xC7, 0x7A); + err += sn9c102_write_reg(cam, 0x01, 0x7B); + err += sn9c102_write_reg(cam, 0xC7, 0x7C); + err += sn9c102_write_reg(cam, 0x01, 0x7D); + err += sn9c102_write_reg(cam, 0x44, 0x7E); + err += sn9c102_write_reg(cam, 0x00, 0x7F); + err += sn9c102_write_reg(cam, 0x14, 0x84); + err += sn9c102_write_reg(cam, 0x00, 0x85); + err += sn9c102_write_reg(cam, 0x27, 0x86); + err += sn9c102_write_reg(cam, 0x00, 0x87); + err += sn9c102_write_reg(cam, 0x07, 0x88); + err += sn9c102_write_reg(cam, 0x00, 0x89); + err += sn9c102_write_reg(cam, 0xEC, 0x8A); + err += sn9c102_write_reg(cam, 0x0f, 0x8B); + err += sn9c102_write_reg(cam, 0xD8, 0x8C); + err += sn9c102_write_reg(cam, 0x0f, 0x8D); + err += sn9c102_write_reg(cam, 0x3D, 0x8E); + err += sn9c102_write_reg(cam, 0x00, 0x8F); + err += sn9c102_write_reg(cam, 0x3D, 0x90); + err += sn9c102_write_reg(cam, 0x00, 0x91); + err += sn9c102_write_reg(cam, 0xCD, 0x92); + err += sn9c102_write_reg(cam, 0x0f, 0x93); + err += sn9c102_write_reg(cam, 0xf7, 0x94); + err += sn9c102_write_reg(cam, 0x0f, 0x95); + err += sn9c102_write_reg(cam, 0x0C, 0x96); + err += sn9c102_write_reg(cam, 0x00, 0x97); + err += sn9c102_write_reg(cam, 0x00, 0x98); + err += sn9c102_write_reg(cam, 0x66, 0x99); + err += sn9c102_write_reg(cam, 0x05, 0x9A); + err += sn9c102_write_reg(cam, 0x00, 0x9B); + err += sn9c102_write_reg(cam, 0x04, 0x9C); + err += sn9c102_write_reg(cam, 0x00, 0x9D); + err += sn9c102_write_reg(cam, 0x08, 0x9E); + err += sn9c102_write_reg(cam, 0x00, 0x9F); + err += sn9c102_write_reg(cam, 0x2D, 0xC0); + err += sn9c102_write_reg(cam, 0x2D, 0xC1); + err += sn9c102_write_reg(cam, 0x3A, 0xC2); + err += sn9c102_write_reg(cam, 0x05, 0xC3); + err += sn9c102_write_reg(cam, 0x04, 0xC4); + err += sn9c102_write_reg(cam, 0x3F, 0xC5); + err += sn9c102_write_reg(cam, 0x00, 0xC6); + err += sn9c102_write_reg(cam, 0x00, 0xC7); + err += sn9c102_write_reg(cam, 0x50, 0xC8); + err += sn9c102_write_reg(cam, 0x3C, 0xC9); + err += sn9c102_write_reg(cam, 0x28, 0xCA); + err += sn9c102_write_reg(cam, 0xD8, 0xCB); + err += sn9c102_write_reg(cam, 0x14, 0xCC); + err += sn9c102_write_reg(cam, 0xEC, 0xCD); + err += sn9c102_write_reg(cam, 0x32, 0xCE); + err += sn9c102_write_reg(cam, 0xDD, 0xCF); + err += sn9c102_write_reg(cam, 0x32, 0xD0); + err += sn9c102_write_reg(cam, 0xDD, 0xD1); + err += sn9c102_write_reg(cam, 0x6A, 0xD2); + err += sn9c102_write_reg(cam, 0x50, 0xD3); + err += sn9c102_write_reg(cam, 0x00, 0xD4); + err += sn9c102_write_reg(cam, 0x00, 0xD5); + err += sn9c102_write_reg(cam, 0x00, 0xD6); + break; + default: + break; + } + + err += sn9c102_i2c_write(cam, 0x20, 0x00); + err += sn9c102_i2c_write(cam, 0x21, 0xd6); + err += sn9c102_i2c_write(cam, 0x25, 0x06); + + return err; +} + + +static int hv7131r_get_ctrl(struct sn9c102_device* cam, + struct v4l2_control* ctrl) +{ + switch (ctrl->id) { + case V4L2_CID_GAIN: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x30)) < 0) + return -EIO; + return 0; + case V4L2_CID_RED_BALANCE: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x31)) < 0) + return -EIO; + ctrl->value = ctrl->value & 0x3f; + return 0; + case V4L2_CID_BLUE_BALANCE: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x33)) < 0) + return -EIO; + ctrl->value = ctrl->value & 0x3f; + return 0; + case SN9C102_V4L2_CID_GREEN_BALANCE: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x32)) < 0) + return -EIO; + ctrl->value = ctrl->value & 0x3f; + return 0; + case V4L2_CID_BLACK_LEVEL: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x01)) < 0) + return -EIO; + ctrl->value = (ctrl->value & 0x08) ? 1 : 0; + return 0; + default: + return -EINVAL; + } +} + + +static int hv7131r_set_ctrl(struct sn9c102_device* cam, + const struct v4l2_control* ctrl) +{ + int err = 0; + + switch (ctrl->id) { + case V4L2_CID_GAIN: + err += sn9c102_i2c_write(cam, 0x30, ctrl->value); + break; + case V4L2_CID_RED_BALANCE: + err += sn9c102_i2c_write(cam, 0x31, ctrl->value); + break; + case V4L2_CID_BLUE_BALANCE: + err += sn9c102_i2c_write(cam, 0x33, ctrl->value); + break; + case SN9C102_V4L2_CID_GREEN_BALANCE: + err += sn9c102_i2c_write(cam, 0x32, ctrl->value); + break; + case V4L2_CID_BLACK_LEVEL: + { + int r = sn9c102_i2c_read(cam, 0x01); + if (r < 0) + return -EIO; + err += sn9c102_i2c_write(cam, 0x01, + (ctrl->value<<3) | (r&0xf7)); + } + break; + default: + return -EINVAL; + } + + return err ? -EIO : 0; +} + + +static int hv7131r_set_crop(struct sn9c102_device* cam, + const struct v4l2_rect* rect) +{ + struct sn9c102_sensor* s = sn9c102_get_sensor(cam); + int err = 0; + u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 1, + v_start = (u8)(rect->top - s->cropcap.bounds.top) + 1; + + err += sn9c102_write_reg(cam, h_start, 0x12); + err += sn9c102_write_reg(cam, v_start, 0x13); + + return err; +} + + +static int hv7131r_set_pix_format(struct sn9c102_device* cam, + const struct v4l2_pix_format* pix) +{ + int err = 0; + + switch (sn9c102_get_bridge(cam)) { + case BRIDGE_SN9C103: + if (pix->pixelformat == V4L2_PIX_FMT_SBGGR8) { + err += sn9c102_write_reg(cam, 0xa0, 0x19); + err += sn9c102_i2c_write(cam, 0x01, 0x04); + } else { + err += sn9c102_write_reg(cam, 0x30, 0x19); + err += sn9c102_i2c_write(cam, 0x01, 0x04); + } + break; + case BRIDGE_SN9C105: + case BRIDGE_SN9C120: + if (pix->pixelformat == V4L2_PIX_FMT_SBGGR8) { + err += sn9c102_write_reg(cam, 0xa5, 0x17); + err += sn9c102_i2c_write(cam, 0x01, 0x24); + } else { + err += sn9c102_write_reg(cam, 0xa3, 0x17); + err += sn9c102_i2c_write(cam, 0x01, 0x04); + } + break; + default: + break; + } + + return err; +} + + +static struct sn9c102_sensor hv7131r = { + .name = "HV7131R", + .maintainer = "Luca Risolia ", + .supported_bridge = BRIDGE_SN9C103 | BRIDGE_SN9C105 | BRIDGE_SN9C120, + .sysfs_ops = SN9C102_I2C_READ | SN9C102_I2C_WRITE, + .frequency = SN9C102_I2C_100KHZ, + .interface = SN9C102_I2C_2WIRES, + .i2c_slave_id = 0x11, + .init = &hv7131r_init, + .qctrl = { + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "global gain", + .minimum = 0x00, + .maximum = 0xff, + .step = 0x01, + .default_value = 0x40, + .flags = 0, + }, + { + .id = V4L2_CID_RED_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "red balance", + .minimum = 0x00, + .maximum = 0x3f, + .step = 0x01, + .default_value = 0x08, + .flags = 0, + }, + { + .id = V4L2_CID_BLUE_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "blue balance", + .minimum = 0x00, + .maximum = 0x3f, + .step = 0x01, + .default_value = 0x1a, + .flags = 0, + }, + { + .id = SN9C102_V4L2_CID_GREEN_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "green balance", + .minimum = 0x00, + .maximum = 0x3f, + .step = 0x01, + .default_value = 0x2f, + .flags = 0, + }, + { + .id = V4L2_CID_BLACK_LEVEL, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "auto black level compensation", + .minimum = 0x00, + .maximum = 0x01, + .step = 0x01, + .default_value = 0x00, + .flags = 0, + }, + }, + .get_ctrl = &hv7131r_get_ctrl, + .set_ctrl = &hv7131r_set_ctrl, + .cropcap = { + .bounds = { + .left = 0, + .top = 0, + .width = 640, + .height = 480, + }, + .defrect = { + .left = 0, + .top = 0, + .width = 640, + .height = 480, + }, + }, + .set_crop = &hv7131r_set_crop, + .pix_format = { + .width = 640, + .height = 480, + .pixelformat = V4L2_PIX_FMT_SBGGR8, + .priv = 8, + }, + .set_pix_format = &hv7131r_set_pix_format +}; + + +int sn9c102_probe_hv7131r(struct sn9c102_device* cam) +{ + int devid, err = 0; + + err += sn9c102_write_reg(cam, 0x09, 0x01); + err += sn9c102_write_reg(cam, 0x44, 0x02); + err += sn9c102_write_reg(cam, 0x34, 0x01); + err += sn9c102_write_reg(cam, 0x20, 0x17); + err += sn9c102_write_reg(cam, 0x34, 0x01); + err += sn9c102_write_reg(cam, 0x46, 0x01); + if (err) + return -EIO; + + devid = sn9c102_i2c_try_read(cam, &hv7131r, 0x00); + if (devid < 0) + return -EIO; + + if (devid != 0x02) + return -ENODEV; + + sn9c102_attach_sensor(cam, &hv7131r); + + return 0; +} diff --git a/drivers/media/video/sn9c102/sn9c102_mi0343.c b/drivers/media/video/sn9c102/sn9c102_mi0343.c index a33d1bc10f90..441156d61106 100644 --- a/drivers/media/video/sn9c102/sn9c102_mi0343.c +++ b/drivers/media/video/sn9c102/sn9c102_mi0343.c @@ -22,12 +22,9 @@ #include "sn9c102_sensor.h" -static struct sn9c102_sensor mi0343; -static u8 mi0343_i2c_data[5+1]; - - static int mi0343_init(struct sn9c102_device* cam) { + struct sn9c102_sensor* s = sn9c102_get_sensor(cam); int err = 0; err += sn9c102_write_reg(cam, 0x00, 0x10); @@ -38,20 +35,20 @@ static int mi0343_init(struct sn9c102_device* cam) err += sn9c102_write_reg(cam, 0x07, 0x18); err += sn9c102_write_reg(cam, 0xa0, 0x19); - err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4, mi0343.i2c_slave_id, - 0x0d, 0x00, 0x01, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4, mi0343.i2c_slave_id, - 0x0d, 0x00, 0x00, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4, mi0343.i2c_slave_id, - 0x03, 0x01, 0xe1, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4, mi0343.i2c_slave_id, - 0x04, 0x02, 0x81, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4, mi0343.i2c_slave_id, - 0x05, 0x00, 0x17, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4, mi0343.i2c_slave_id, - 0x06, 0x00, 0x11, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4, mi0343.i2c_slave_id, - 0x62, 0x04, 0x9a, 0, 0); + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x0d, + 0x00, 0x01, 0, 0); + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x0d, + 0x00, 0x00, 0, 0); + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x03, + 0x01, 0xe1, 0, 0); + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x04, + 0x02, 0x81, 0, 0); + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x05, + 0x00, 0x17, 0, 0); + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x06, + 0x00, 0x11, 0, 0); + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x62, + 0x04, 0x9a, 0, 0); return err; } @@ -60,43 +57,46 @@ static int mi0343_init(struct sn9c102_device* cam) static int mi0343_get_ctrl(struct sn9c102_device* cam, struct v4l2_control* ctrl) { + struct sn9c102_sensor* s = sn9c102_get_sensor(cam); + u8 data[5+1]; + switch (ctrl->id) { case V4L2_CID_EXPOSURE: - if (sn9c102_i2c_try_raw_read(cam, &mi0343, mi0343.i2c_slave_id, - 0x09, 2+1, mi0343_i2c_data) < 0) + if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x09, + 2+1, data) < 0) return -EIO; - ctrl->value = mi0343_i2c_data[2]; + ctrl->value = data[2]; return 0; case V4L2_CID_GAIN: - if (sn9c102_i2c_try_raw_read(cam, &mi0343, mi0343.i2c_slave_id, - 0x35, 2+1, mi0343_i2c_data) < 0) + if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x35, + 2+1, data) < 0) return -EIO; break; case V4L2_CID_HFLIP: - if (sn9c102_i2c_try_raw_read(cam, &mi0343, mi0343.i2c_slave_id, - 0x20, 2+1, mi0343_i2c_data) < 0) + if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x20, + 2+1, data) < 0) return -EIO; - ctrl->value = mi0343_i2c_data[3] & 0x20 ? 1 : 0; + ctrl->value = data[3] & 0x20 ? 1 : 0; return 0; case V4L2_CID_VFLIP: - if (sn9c102_i2c_try_raw_read(cam, &mi0343, mi0343.i2c_slave_id, - 0x20, 2+1, mi0343_i2c_data) < 0) + if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x20, + 2+1, data) < 0) return -EIO; - ctrl->value = mi0343_i2c_data[3] & 0x80 ? 1 : 0; + ctrl->value = data[3] & 0x80 ? 1 : 0; return 0; case V4L2_CID_RED_BALANCE: - if (sn9c102_i2c_try_raw_read(cam, &mi0343, mi0343.i2c_slave_id, - 0x2d, 2+1, mi0343_i2c_data) < 0) + if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x2d, + 2+1, data) < 0) return -EIO; break; case V4L2_CID_BLUE_BALANCE: - if (sn9c102_i2c_try_raw_read(cam, &mi0343, mi0343.i2c_slave_id, - 0x2c, 2+1, mi0343_i2c_data) < 0) + if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x2c, + 2+1, data) < 0) return -EIO; break; case SN9C102_V4L2_CID_GREEN_BALANCE: - if (sn9c102_i2c_try_raw_read(cam, &mi0343, mi0343.i2c_slave_id, - 0x2e, 2+1, mi0343_i2c_data) < 0) + if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x2e, + 2+1, data) < 0) return -EIO; break; default: @@ -108,7 +108,7 @@ static int mi0343_get_ctrl(struct sn9c102_device* cam, case V4L2_CID_RED_BALANCE: case V4L2_CID_BLUE_BALANCE: case SN9C102_V4L2_CID_GREEN_BALANCE: - ctrl->value = mi0343_i2c_data[3] | (mi0343_i2c_data[2] << 8); + ctrl->value = data[3] | (data[2] << 8); if (ctrl->value >= 0x10 && ctrl->value <= 0x3f) ctrl->value -= 0x10; else if (ctrl->value >= 0x60 && ctrl->value <= 0x7f) @@ -124,6 +124,7 @@ static int mi0343_get_ctrl(struct sn9c102_device* cam, static int mi0343_set_ctrl(struct sn9c102_device* cam, const struct v4l2_control* ctrl) { + struct sn9c102_sensor* s = sn9c102_get_sensor(cam); u16 reg = 0; int err = 0; @@ -143,50 +144,42 @@ static int mi0343_set_ctrl(struct sn9c102_device* cam, switch (ctrl->id) { case V4L2_CID_EXPOSURE: - err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4, - mi0343.i2c_slave_id, + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x09, ctrl->value, 0x00, 0, 0); break; case V4L2_CID_GAIN: - err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4, - mi0343.i2c_slave_id, + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x35, reg >> 8, reg & 0xff, 0, 0); break; case V4L2_CID_HFLIP: - err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4, - mi0343.i2c_slave_id, + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x20, ctrl->value ? 0x40:0x00, ctrl->value ? 0x20:0x00, 0, 0); break; case V4L2_CID_VFLIP: - err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4, - mi0343.i2c_slave_id, + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x20, ctrl->value ? 0x80:0x00, ctrl->value ? 0x80:0x00, 0, 0); break; case V4L2_CID_RED_BALANCE: - err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4, - mi0343.i2c_slave_id, + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x2d, reg >> 8, reg & 0xff, 0, 0); break; case V4L2_CID_BLUE_BALANCE: - err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4, - mi0343.i2c_slave_id, + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x2c, reg >> 8, reg & 0xff, 0, 0); break; case SN9C102_V4L2_CID_GREEN_BALANCE: - err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4, - mi0343.i2c_slave_id, + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x2b, reg >> 8, reg & 0xff, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4, - mi0343.i2c_slave_id, + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x2e, reg >> 8, reg & 0xff, 0, 0); break; @@ -216,16 +209,15 @@ static int mi0343_set_crop(struct sn9c102_device* cam, static int mi0343_set_pix_format(struct sn9c102_device* cam, const struct v4l2_pix_format* pix) { + struct sn9c102_sensor* s = sn9c102_get_sensor(cam); int err = 0; if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X) { - err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4, - mi0343.i2c_slave_id, + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x0a, 0x00, 0x03, 0, 0); err += sn9c102_write_reg(cam, 0x20, 0x19); } else { - err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4, - mi0343.i2c_slave_id, + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x0a, 0x00, 0x05, 0, 0); err += sn9c102_write_reg(cam, 0xa0, 0x19); } @@ -237,7 +229,7 @@ static int mi0343_set_pix_format(struct sn9c102_device* cam, static struct sn9c102_sensor mi0343 = { .name = "MI-0343", .maintainer = "Luca Risolia ", - .supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102 | BRIDGE_SN9C103, + .supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102, .frequency = SN9C102_I2C_100KHZ, .interface = SN9C102_I2C_2WIRES, .i2c_slave_id = 0x5d, @@ -343,6 +335,7 @@ static struct sn9c102_sensor mi0343 = { int sn9c102_probe_mi0343(struct sn9c102_device* cam) { + u8 data[5+1]; int err = 0; err += sn9c102_write_reg(cam, 0x01, 0x01); @@ -352,10 +345,10 @@ int sn9c102_probe_mi0343(struct sn9c102_device* cam) return -EIO; if (sn9c102_i2c_try_raw_read(cam, &mi0343, mi0343.i2c_slave_id, 0x00, - 2, mi0343_i2c_data) < 0) + 2, data) < 0) return -EIO; - if (mi0343_i2c_data[4] != 0x32 && mi0343_i2c_data[3] != 0xe3) + if (data[4] != 0x32 || data[3] != 0xe3) return -ENODEV; sn9c102_attach_sensor(cam, &mi0343); diff --git a/drivers/media/video/sn9c102/sn9c102_mi0360.c b/drivers/media/video/sn9c102/sn9c102_mi0360.c new file mode 100644 index 000000000000..7154dd0534ff --- /dev/null +++ b/drivers/media/video/sn9c102/sn9c102_mi0360.c @@ -0,0 +1,353 @@ +/*************************************************************************** + * Plug-in for MI-0360 image sensor connected to the SN9C1xx PC Camera * + * Controllers * + * * + * Copyright (C) 2007 by Luca Risolia * + * * + * 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 "sn9c102_sensor.h" + + +static int mi0360_init(struct sn9c102_device* cam) +{ + struct sn9c102_sensor* s = sn9c102_get_sensor(cam); + int err = 0; + + err += sn9c102_write_reg(cam, 0x00, 0x10); + err += sn9c102_write_reg(cam, 0x00, 0x11); + err += sn9c102_write_reg(cam, 0x0a, 0x14); + err += sn9c102_write_reg(cam, 0x40, 0x01); + err += sn9c102_write_reg(cam, 0x20, 0x17); + err += sn9c102_write_reg(cam, 0x07, 0x18); + err += sn9c102_write_reg(cam, 0xa0, 0x19); + err += sn9c102_write_reg(cam, 0x02, 0x1c); + err += sn9c102_write_reg(cam, 0x03, 0x1d); + err += sn9c102_write_reg(cam, 0x0f, 0x1e); + err += sn9c102_write_reg(cam, 0x0c, 0x1f); + err += sn9c102_write_reg(cam, 0x00, 0x20); + err += sn9c102_write_reg(cam, 0x10, 0x21); + err += sn9c102_write_reg(cam, 0x20, 0x22); + err += sn9c102_write_reg(cam, 0x30, 0x23); + err += sn9c102_write_reg(cam, 0x40, 0x24); + err += sn9c102_write_reg(cam, 0x50, 0x25); + err += sn9c102_write_reg(cam, 0x60, 0x26); + err += sn9c102_write_reg(cam, 0x70, 0x27); + err += sn9c102_write_reg(cam, 0x80, 0x28); + err += sn9c102_write_reg(cam, 0x90, 0x29); + err += sn9c102_write_reg(cam, 0xa0, 0x2a); + err += sn9c102_write_reg(cam, 0xb0, 0x2b); + err += sn9c102_write_reg(cam, 0xc0, 0x2c); + err += sn9c102_write_reg(cam, 0xd0, 0x2d); + err += sn9c102_write_reg(cam, 0xe0, 0x2e); + err += sn9c102_write_reg(cam, 0xf0, 0x2f); + err += sn9c102_write_reg(cam, 0xff, 0x30); + + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x0d, + 0x00, 0x01, 0, 0); + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x0d, + 0x00, 0x00, 0, 0); + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x03, + 0x01, 0xe1, 0, 0); + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x04, + 0x02, 0x81, 0, 0); + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x05, + 0x00, 0x17, 0, 0); + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x06, + 0x00, 0x11, 0, 0); + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x62, + 0x04, 0x9a, 0, 0); + + return err; +} + + +static int mi0360_get_ctrl(struct sn9c102_device* cam, + struct v4l2_control* ctrl) +{ + struct sn9c102_sensor* s = sn9c102_get_sensor(cam); + u8 data[5+1]; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x09, + 2+1, data) < 0) + return -EIO; + ctrl->value = data[2]; + return 0; + case V4L2_CID_GAIN: + if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x35, + 2+1, data) < 0) + return -EIO; + ctrl->value = data[3]; + return 0; + case V4L2_CID_RED_BALANCE: + if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x2c, + 2+1, data) < 0) + return -EIO; + ctrl->value = data[3]; + return 0; + case V4L2_CID_BLUE_BALANCE: + if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x2d, + 2+1, data) < 0) + return -EIO; + ctrl->value = data[3]; + return 0; + case SN9C102_V4L2_CID_GREEN_BALANCE: + if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x2e, + 2+1, data) < 0) + return -EIO; + ctrl->value = data[3]; + return 0; + case V4L2_CID_HFLIP: + if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x20, + 2+1, data) < 0) + return -EIO; + ctrl->value = data[3] & 0x20 ? 1 : 0; + return 0; + case V4L2_CID_VFLIP: + if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x20, + 2+1, data) < 0) + return -EIO; + ctrl->value = data[3] & 0x80 ? 1 : 0; + return 0; + default: + return -EINVAL; + } + + return 0; +} + + +static int mi0360_set_ctrl(struct sn9c102_device* cam, + const struct v4l2_control* ctrl) +{ + struct sn9c102_sensor* s = sn9c102_get_sensor(cam); + int err = 0; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, + 0x09, ctrl->value, 0x00, + 0, 0); + break; + case V4L2_CID_GAIN: + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, + 0x35, 0x03, ctrl->value, + 0, 0); + break; + case V4L2_CID_RED_BALANCE: + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, + 0x2c, 0x03, ctrl->value, + 0, 0); + break; + case V4L2_CID_BLUE_BALANCE: + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, + 0x2d, 0x03, ctrl->value, + 0, 0); + break; + case SN9C102_V4L2_CID_GREEN_BALANCE: + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, + 0x2b, 0x03, ctrl->value, + 0, 0); + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, + 0x2e, 0x03, ctrl->value, + 0, 0); + break; + case V4L2_CID_HFLIP: + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, + 0x20, ctrl->value ? 0x40:0x00, + ctrl->value ? 0x20:0x00, + 0, 0); + break; + case V4L2_CID_VFLIP: + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, + 0x20, ctrl->value ? 0x80:0x00, + ctrl->value ? 0x80:0x00, + 0, 0); + break; + default: + return -EINVAL; + } + + return err ? -EIO : 0; +} + + +static int mi0360_set_crop(struct sn9c102_device* cam, + const struct v4l2_rect* rect) +{ + struct sn9c102_sensor* s = sn9c102_get_sensor(cam); + int err = 0; + u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 0, + v_start = (u8)(rect->top - s->cropcap.bounds.top) + 1; + + err += sn9c102_write_reg(cam, h_start, 0x12); + err += sn9c102_write_reg(cam, v_start, 0x13); + + return err; +} + + +static int mi0360_set_pix_format(struct sn9c102_device* cam, + const struct v4l2_pix_format* pix) +{ + struct sn9c102_sensor* s = sn9c102_get_sensor(cam); + int err = 0; + + if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X) { + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, + 0x0a, 0x00, 0x02, 0, 0); + err += sn9c102_write_reg(cam, 0x20, 0x19); + } else { + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, + 0x0a, 0x00, 0x05, 0, 0); + err += sn9c102_write_reg(cam, 0x60, 0x19); + } + + return err; +} + + +static struct sn9c102_sensor mi0360 = { + .name = "MI-0360", + .maintainer = "Luca Risolia ", + .supported_bridge = BRIDGE_SN9C103, + .frequency = SN9C102_I2C_100KHZ, + .interface = SN9C102_I2C_2WIRES, + .i2c_slave_id = 0x5d, + .init = &mi0360_init, + .qctrl = { + { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "exposure", + .minimum = 0x00, + .maximum = 0x0f, + .step = 0x01, + .default_value = 0x05, + .flags = 0, + }, + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "global gain", + .minimum = 0x00, + .maximum = 0x7f, + .step = 0x01, + .default_value = 0x25, + .flags = 0, + }, + { + .id = V4L2_CID_HFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "horizontal mirror", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + .flags = 0, + }, + { + .id = V4L2_CID_VFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "vertical mirror", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + .flags = 0, + }, + { + .id = V4L2_CID_BLUE_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "blue balance", + .minimum = 0x00, + .maximum = 0x7f, + .step = 0x01, + .default_value = 0x0f, + .flags = 0, + }, + { + .id = V4L2_CID_RED_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "red balance", + .minimum = 0x00, + .maximum = 0x7f, + .step = 0x01, + .default_value = 0x32, + .flags = 0, + }, + { + .id = SN9C102_V4L2_CID_GREEN_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "green balance", + .minimum = 0x00, + .maximum = 0x7f, + .step = 0x01, + .default_value = 0x25, + .flags = 0, + }, + }, + .get_ctrl = &mi0360_get_ctrl, + .set_ctrl = &mi0360_set_ctrl, + .cropcap = { + .bounds = { + .left = 0, + .top = 0, + .width = 640, + .height = 480, + }, + .defrect = { + .left = 0, + .top = 0, + .width = 640, + .height = 480, + }, + }, + .set_crop = &mi0360_set_crop, + .pix_format = { + .width = 640, + .height = 480, + .pixelformat = V4L2_PIX_FMT_SBGGR8, + .priv = 8, + }, + .set_pix_format = &mi0360_set_pix_format +}; + + +int sn9c102_probe_mi0360(struct sn9c102_device* cam) +{ + u8 data[5+1]; + int err = 0; + + err += sn9c102_write_reg(cam, 0x01, 0x01); + err += sn9c102_write_reg(cam, 0x00, 0x01); + err += sn9c102_write_reg(cam, 0x28, 0x17); + if (err) + return -EIO; + + if (sn9c102_i2c_try_raw_read(cam, &mi0360, mi0360.i2c_slave_id, 0x00, + 2+1, data) < 0) + return -EIO; + + if (data[2] != 0x82 || data[3] != 0x43) + return -ENODEV; + + sn9c102_attach_sensor(cam, &mi0360); + + return 0; +} diff --git a/drivers/media/video/sn9c102/sn9c102_ov7630.c b/drivers/media/video/sn9c102/sn9c102_ov7630.c index 7df09ff38e63..ad9fb2ca2735 100644 --- a/drivers/media/video/sn9c102/sn9c102_ov7630.c +++ b/drivers/media/video/sn9c102/sn9c102_ov7630.c @@ -22,9 +22,6 @@ #include "sn9c102_sensor.h" -static struct sn9c102_sensor ov7630; - - static int ov7630_init(struct sn9c102_device* cam) { int err = 0; @@ -32,21 +29,21 @@ static int ov7630_init(struct sn9c102_device* cam) switch (sn9c102_get_bridge(cam)) { case BRIDGE_SN9C101: case BRIDGE_SN9C102: - err += sn9c102_write_reg(cam, 0x00, 0x14); - err += sn9c102_write_reg(cam, 0x60, 0x17); - err += sn9c102_write_reg(cam, 0x0f, 0x18); - err += sn9c102_write_reg(cam, 0x50, 0x19); + err += sn9c102_write_reg(cam, 0x00, 0x14); + err += sn9c102_write_reg(cam, 0x60, 0x17); + err += sn9c102_write_reg(cam, 0x0f, 0x18); + err += sn9c102_write_reg(cam, 0x50, 0x19); err += sn9c102_i2c_write(cam, 0x12, 0x8d); err += sn9c102_i2c_write(cam, 0x12, 0x0d); err += sn9c102_i2c_write(cam, 0x11, 0x00); - err += sn9c102_i2c_write(cam, 0x15, 0x34); - err += sn9c102_i2c_write(cam, 0x16, 0x03); - err += sn9c102_i2c_write(cam, 0x17, 0x1c); - err += sn9c102_i2c_write(cam, 0x18, 0xbd); - err += sn9c102_i2c_write(cam, 0x19, 0x06); - err += sn9c102_i2c_write(cam, 0x1a, 0xf6); - err += sn9c102_i2c_write(cam, 0x1b, 0x04); + err += sn9c102_i2c_write(cam, 0x15, 0x35); + err += sn9c102_i2c_write(cam, 0x16, 0x03); + err += sn9c102_i2c_write(cam, 0x17, 0x1c); + err += sn9c102_i2c_write(cam, 0x18, 0xbd); + err += sn9c102_i2c_write(cam, 0x19, 0x06); + err += sn9c102_i2c_write(cam, 0x1a, 0xf6); + err += sn9c102_i2c_write(cam, 0x1b, 0x04); err += sn9c102_i2c_write(cam, 0x20, 0x44); err += sn9c102_i2c_write(cam, 0x23, 0xee); err += sn9c102_i2c_write(cam, 0x26, 0xa0); @@ -108,23 +105,23 @@ static int ov7630_init(struct sn9c102_device* cam) err += sn9c102_i2c_write(cam, 0x11, 0x01); err += sn9c102_i2c_write(cam, 0x1b, 0x04); err += sn9c102_i2c_write(cam, 0x20, 0x44); - err += sn9c102_i2c_write(cam, 0x23, 0xee); - err += sn9c102_i2c_write(cam, 0x26, 0xa0); - err += sn9c102_i2c_write(cam, 0x27, 0x9a); + err += sn9c102_i2c_write(cam, 0x23, 0xee); + err += sn9c102_i2c_write(cam, 0x26, 0xa0); + err += sn9c102_i2c_write(cam, 0x27, 0x9a); err += sn9c102_i2c_write(cam, 0x28, 0x20); - err += sn9c102_i2c_write(cam, 0x29, 0x30); - err += sn9c102_i2c_write(cam, 0x2f, 0x3d); - err += sn9c102_i2c_write(cam, 0x30, 0x24); - err += sn9c102_i2c_write(cam, 0x32, 0x86); - err += sn9c102_i2c_write(cam, 0x60, 0xa9); - err += sn9c102_i2c_write(cam, 0x61, 0x42); - err += sn9c102_i2c_write(cam, 0x65, 0x00); - err += sn9c102_i2c_write(cam, 0x69, 0x38); - err += sn9c102_i2c_write(cam, 0x6f, 0x88); - err += sn9c102_i2c_write(cam, 0x70, 0x0b); - err += sn9c102_i2c_write(cam, 0x71, 0x00); - err += sn9c102_i2c_write(cam, 0x74, 0x21); - err += sn9c102_i2c_write(cam, 0x7d, 0xf7); + err += sn9c102_i2c_write(cam, 0x29, 0x30); + err += sn9c102_i2c_write(cam, 0x2f, 0x3d); + err += sn9c102_i2c_write(cam, 0x30, 0x24); + err += sn9c102_i2c_write(cam, 0x32, 0x86); + err += sn9c102_i2c_write(cam, 0x60, 0xa9); + err += sn9c102_i2c_write(cam, 0x61, 0x42); + err += sn9c102_i2c_write(cam, 0x65, 0x00); + err += sn9c102_i2c_write(cam, 0x69, 0x38); + err += sn9c102_i2c_write(cam, 0x6f, 0x88); + err += sn9c102_i2c_write(cam, 0x70, 0x0b); + err += sn9c102_i2c_write(cam, 0x71, 0x00); + err += sn9c102_i2c_write(cam, 0x74, 0x21); + err += sn9c102_i2c_write(cam, 0x7d, 0xf7); break; default: break; @@ -428,9 +425,9 @@ int sn9c102_probe_ov7630(struct sn9c102_device* cam) switch (sn9c102_get_bridge(cam)) { case BRIDGE_SN9C101: case BRIDGE_SN9C102: - err += sn9c102_write_reg(cam, 0x01, 0x01); - err += sn9c102_write_reg(cam, 0x00, 0x01); - err += sn9c102_write_reg(cam, 0x28, 0x17); + err += sn9c102_write_reg(cam, 0x01, 0x01); + err += sn9c102_write_reg(cam, 0x00, 0x01); + err += sn9c102_write_reg(cam, 0x28, 0x17); break; case BRIDGE_SN9C103: /* do _not_ change anything! */ err += sn9c102_write_reg(cam, 0x09, 0x01); diff --git a/drivers/media/video/sn9c102/sn9c102_ov7660.c b/drivers/media/video/sn9c102/sn9c102_ov7660.c index d670c24d4435..eef90ff3d474 100644 --- a/drivers/media/video/sn9c102/sn9c102_ov7660.c +++ b/drivers/media/video/sn9c102/sn9c102_ov7660.c @@ -22,9 +22,6 @@ #include "sn9c102_sensor.h" -static struct sn9c102_sensor ov7660; - - static int ov7660_init(struct sn9c102_device* cam) { int err = 0; diff --git a/drivers/media/video/sn9c102/sn9c102_pas106b.c b/drivers/media/video/sn9c102/sn9c102_pas106b.c index 8d79a5fae5de..a67057210cab 100644 --- a/drivers/media/video/sn9c102/sn9c102_pas106b.c +++ b/drivers/media/video/sn9c102/sn9c102_pas106b.c @@ -23,9 +23,6 @@ #include "sn9c102_sensor.h" -static struct sn9c102_sensor pas106b; - - static int pas106b_init(struct sn9c102_device* cam) { int err = 0; @@ -172,7 +169,7 @@ static int pas106b_set_pix_format(struct sn9c102_device* cam, static struct sn9c102_sensor pas106b = { .name = "PAS106B", .maintainer = "Luca Risolia ", - .supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102 | BRIDGE_SN9C103, + .supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102, .sysfs_ops = SN9C102_I2C_READ | SN9C102_I2C_WRITE, .frequency = SN9C102_I2C_400KHZ | SN9C102_I2C_100KHZ, .interface = SN9C102_I2C_2WIRES, diff --git a/drivers/media/video/sn9c102/sn9c102_pas202bcb.c b/drivers/media/video/sn9c102/sn9c102_pas202bcb.c index 7894f01b56e8..4447d7cb1e92 100644 --- a/drivers/media/video/sn9c102/sn9c102_pas202bcb.c +++ b/drivers/media/video/sn9c102/sn9c102_pas202bcb.c @@ -28,9 +28,6 @@ #include "sn9c102_sensor.h" -static struct sn9c102_sensor pas202bcb; - - static int pas202bcb_init(struct sn9c102_device* cam) { int err = 0; @@ -38,12 +35,12 @@ static int pas202bcb_init(struct sn9c102_device* cam) switch (sn9c102_get_bridge(cam)) { case BRIDGE_SN9C101: case BRIDGE_SN9C102: - err += sn9c102_write_reg(cam, 0x00, 0x10); - err += sn9c102_write_reg(cam, 0x00, 0x11); - err += sn9c102_write_reg(cam, 0x00, 0x14); - err += sn9c102_write_reg(cam, 0x20, 0x17); - err += sn9c102_write_reg(cam, 0x30, 0x19); - err += sn9c102_write_reg(cam, 0x09, 0x18); + err += sn9c102_write_reg(cam, 0x00, 0x10); + err += sn9c102_write_reg(cam, 0x00, 0x11); + err += sn9c102_write_reg(cam, 0x00, 0x14); + err += sn9c102_write_reg(cam, 0x20, 0x17); + err += sn9c102_write_reg(cam, 0x30, 0x19); + err += sn9c102_write_reg(cam, 0x09, 0x18); break; case BRIDGE_SN9C103: err += sn9c102_write_reg(cam, 0x00, 0x02); diff --git a/drivers/media/video/sn9c102/sn9c102_tas5110c1b.c b/drivers/media/video/sn9c102/sn9c102_tas5110c1b.c index 90023ad63adc..a265767e5f31 100644 --- a/drivers/media/video/sn9c102/sn9c102_tas5110c1b.c +++ b/drivers/media/video/sn9c102/sn9c102_tas5110c1b.c @@ -22,9 +22,6 @@ #include "sn9c102_sensor.h" -static struct sn9c102_sensor tas5110c1b; - - static int tas5110c1b_init(struct sn9c102_device* cam) { int err = 0; @@ -98,7 +95,7 @@ static int tas5110c1b_set_pix_format(struct sn9c102_device* cam, static struct sn9c102_sensor tas5110c1b = { .name = "TAS5110C1B", .maintainer = "Luca Risolia ", - .supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102 | BRIDGE_SN9C103, + .supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102, .sysfs_ops = SN9C102_I2C_WRITE, .frequency = SN9C102_I2C_100KHZ, .interface = SN9C102_I2C_3WIRES, @@ -146,7 +143,6 @@ int sn9c102_probe_tas5110c1b(struct sn9c102_device* cam) const struct usb_device_id tas5110c1b_id_table[] = { { USB_DEVICE(0x0c45, 0x6001), }, { USB_DEVICE(0x0c45, 0x6005), }, - { USB_DEVICE(0x0c45, 0x6007), }, { USB_DEVICE(0x0c45, 0x60ab), }, { } }; diff --git a/drivers/media/video/sn9c102/sn9c102_tas5110d.c b/drivers/media/video/sn9c102/sn9c102_tas5110d.c new file mode 100644 index 000000000000..4681cfa1bf57 --- /dev/null +++ b/drivers/media/video/sn9c102/sn9c102_tas5110d.c @@ -0,0 +1,121 @@ +/*************************************************************************** + * Plug-in for TAS5110D image sensor connected to the SN9C1xx PC Camera * + * Controllers * + * * + * Copyright (C) 2007 by Luca Risolia * + * * + * 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 "sn9c102_sensor.h" + + +static int tas5110d_init(struct sn9c102_device* cam) +{ + int err = 0; + + err += sn9c102_write_reg(cam, 0x01, 0x01); + err += sn9c102_write_reg(cam, 0x04, 0x01); + err += sn9c102_write_reg(cam, 0x0a, 0x14); + err += sn9c102_write_reg(cam, 0x60, 0x17); + err += sn9c102_write_reg(cam, 0x06, 0x18); + err += sn9c102_write_reg(cam, 0xfb, 0x19); + + err += sn9c102_i2c_write(cam, 0x9a, 0xca); + + return err; +} + + +static int tas5110d_set_crop(struct sn9c102_device* cam, + const struct v4l2_rect* rect) +{ + struct sn9c102_sensor* s = sn9c102_get_sensor(cam); + int err = 0; + u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 69, + v_start = (u8)(rect->top - s->cropcap.bounds.top) + 9; + + err += sn9c102_write_reg(cam, h_start, 0x12); + err += sn9c102_write_reg(cam, v_start, 0x13); + + err += sn9c102_write_reg(cam, 0x14, 0x1a); + err += sn9c102_write_reg(cam, 0x0a, 0x1b); + + return err; +} + + +static int tas5110d_set_pix_format(struct sn9c102_device* cam, + const struct v4l2_pix_format* pix) +{ + int err = 0; + + if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X) + err += sn9c102_write_reg(cam, 0x3b, 0x19); + else + err += sn9c102_write_reg(cam, 0xfb, 0x19); + + return err; +} + + +static struct sn9c102_sensor tas5110d = { + .name = "TAS5110D", + .maintainer = "Luca Risolia ", + .supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102, + .sysfs_ops = SN9C102_I2C_WRITE, + .frequency = SN9C102_I2C_100KHZ, + .interface = SN9C102_I2C_2WIRES, + .i2c_slave_id = 0x61, + .init = &tas5110d_init, + .cropcap = { + .bounds = { + .left = 0, + .top = 0, + .width = 352, + .height = 288, + }, + .defrect = { + .left = 0, + .top = 0, + .width = 352, + .height = 288, + }, + }, + .set_crop = &tas5110d_set_crop, + .pix_format = { + .width = 352, + .height = 288, + .pixelformat = V4L2_PIX_FMT_SBGGR8, + .priv = 8, + }, + .set_pix_format = &tas5110d_set_pix_format +}; + + +int sn9c102_probe_tas5110d(struct sn9c102_device* cam) +{ + const struct usb_device_id tas5110d_id_table[] = { + { USB_DEVICE(0x0c45, 0x6007), }, + { } + }; + + if (!sn9c102_match_id(cam, tas5110d_id_table)) + return -ENODEV; + + sn9c102_attach_sensor(cam, &tas5110d); + + return 0; +} diff --git a/drivers/media/video/sn9c102/sn9c102_tas5130d1b.c b/drivers/media/video/sn9c102/sn9c102_tas5130d1b.c index cb1b318bc1ff..a7f711396152 100644 --- a/drivers/media/video/sn9c102/sn9c102_tas5130d1b.c +++ b/drivers/media/video/sn9c102/sn9c102_tas5130d1b.c @@ -22,9 +22,6 @@ #include "sn9c102_sensor.h" -static struct sn9c102_sensor tas5130d1b; - - static int tas5130d1b_init(struct sn9c102_device* cam) { int err = 0; @@ -99,7 +96,7 @@ static int tas5130d1b_set_pix_format(struct sn9c102_device* cam, static struct sn9c102_sensor tas5130d1b = { .name = "TAS5130D1B", .maintainer = "Luca Risolia ", - .supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102 | BRIDGE_SN9C103, + .supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102, .sysfs_ops = SN9C102_I2C_WRITE, .frequency = SN9C102_I2C_100KHZ, .interface = SN9C102_I2C_3WIRES, -- cgit v1.2.3 From 1ebba670edac28d4ea37579453417ced71fd9128 Mon Sep 17 00:00:00 2001 From: Scott Alfter Date: Mon, 2 Apr 2007 14:22:39 -0300 Subject: V4L/DVB (5497): Additional card support for bttv driver SSAI (www.ssai.us) makes several Bt878-based capture cards that get used in our surveillance, conferencing, and medical imaging systems. The attached relatively small patch adds support for these cards, which fall into two broad * boards with one or more Bt878s, one or more composite inputs, and no S-video or tuner inputs * boards with one Bt878, one composite input, one S-video input, and no tuner input Signed-off-by: Scott Alfter Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/CARDLIST.bttv | 2 ++ drivers/media/video/bt8xx/bttv-cards.c | 25 +++++++++++++++++++++++++ drivers/media/video/bt8xx/bttv.h | 2 ++ 3 files changed, 29 insertions(+) (limited to 'Documentation') diff --git a/Documentation/video4linux/CARDLIST.bttv b/Documentation/video4linux/CARDLIST.bttv index fc2fe9bc6713..b60639130a51 100644 --- a/Documentation/video4linux/CARDLIST.bttv +++ b/Documentation/video4linux/CARDLIST.bttv @@ -143,3 +143,5 @@ 142 -> Sabrent TV-FM (bttv version) 143 -> Hauppauge ImpactVCB (bt878) [0070:13eb] 144 -> MagicTV +145 -> SSAI Security Video Interface [4149:5353] +146 -> SSAI Ultrasound Video Interface [414a:5353] diff --git a/drivers/media/video/bt8xx/bttv-cards.c b/drivers/media/video/bt8xx/bttv-cards.c index 1ee0fe39237b..6b31e50fb951 100644 --- a/drivers/media/video/bt8xx/bttv-cards.c +++ b/drivers/media/video/bt8xx/bttv-cards.c @@ -291,6 +291,9 @@ static struct CARD { { 0x15409511, BTTV_BOARD_ACORP_Y878F, "Acorp Y878F" }, + { 0x53534149, BTTV_BOARD_SSAI_SECURITY, "SSAI Security Video Interface" }, + { 0x5353414a, BTTV_BOARD_SSAI_ULTRASOUND, "SSAI Ultrasound Video Interface" }, + /* likely broken, vendor id doesn't match the other magic views ... * { 0xa0fca04f, BTTV_BOARD_MAGICTVIEW063, "Guillemot Maxi TV Video 3" }, */ @@ -2907,6 +2910,28 @@ struct tvcard bttv_tvcards[] = { .has_radio = 1, .has_remote = 1, }, + [BTTV_BOARD_SSAI_SECURITY] = { + .name = "SSAI Security Video Interface", + .video_inputs = 4, + .audio_inputs = 0, + .tuner = -1, + .svhs = -1, + .muxsel = { 0, 1, 2, 3 }, + .tuner_type = -1, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + }, + [BTTV_BOARD_SSAI_ULTRASOUND] = { + .name = "SSAI Ultrasound Video Interface", + .video_inputs = 2, + .audio_inputs = 0, + .tuner = -1, + .svhs = 1, + .muxsel = { 2, 0, 1, 3 }, + .tuner_type = -1, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + }, }; static const unsigned int bttv_num_tvcards = ARRAY_SIZE(bttv_tvcards); diff --git a/drivers/media/video/bt8xx/bttv.h b/drivers/media/video/bt8xx/bttv.h index 78f0eb039183..f821ba69db99 100644 --- a/drivers/media/video/bt8xx/bttv.h +++ b/drivers/media/video/bt8xx/bttv.h @@ -168,6 +168,8 @@ #define BTTV_BOARD_SABRENT_TVFM 0x8e #define BTTV_BOARD_HAUPPAUGE_IMPACTVCB 0x8f #define BTTV_BOARD_MACHTV_MAGICTV 0x90 +#define BTTV_BOARD_SSAI_SECURITY 0x91 +#define BTTV_BOARD_SSAI_ULTRASOUND 0x92 /* more card-specific defines */ #define PT2254_L_CHANNEL 0x10 -- cgit v1.2.3 From 0ee32871c18a3662d8958a8e9998eb4d2ae94159 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 3 Apr 2007 18:08:19 -0300 Subject: V4L/DVB (5500): Add a CARDLIST for the supported devices by usbvision driver Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/CARDLIST.usbvision | 65 ++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 Documentation/video4linux/CARDLIST.usbvision (limited to 'Documentation') diff --git a/Documentation/video4linux/CARDLIST.usbvision b/Documentation/video4linux/CARDLIST.usbvision new file mode 100644 index 000000000000..9f03e86e9ce6 --- /dev/null +++ b/Documentation/video4linux/CARDLIST.usbvision @@ -0,0 +1,65 @@ + 0 -> Custom Dummy USBVision Device [fff0:fff0] + 1 -> Xanboo [0a6f:0400] + 2 -> Belkin USB VideoBus II Adapter [050d:0106] + 3 -> Belkin Components USB VideoBus [050d:0207] + 4 -> Belkin USB VideoBus II [050d:0208] + 5 -> echoFX InterView Lite [0571:0002] + 6 -> USBGear USBG-V1 resp. HAMA USB [0573:0003] + 7 -> D-Link V100 [0573:0400] + 8 -> X10 USB Camera [0573:2000] + 9 -> Hauppauge WinTV USB Live (PAL B/G) [0573:2d00] + 10 -> Hauppauge WinTV USB Live Pro (NTSC M/N) [0573:2d01] + 11 -> Zoran Co. PMD (Nogatech) AV-grabber Manhattan [0573:2101] + 12 -> Nogatech USB-TV (NTSC) FM [0573:4100] + 13 -> PNY USB-TV (NTSC) FM [0573:4110] + 14 -> PixelView PlayTv-USB PRO (PAL) FM [0573:4450] + 15 -> ZTV ZT-721 2.4GHz USB A/V Receiver [0573:4550] + 16 -> Hauppauge WinTV USB (NTSC M/N) [0573:4d00] + 17 -> Hauppauge WinTV USB (PAL B/G) [0573:4d01] + 18 -> Hauppauge WinTV USB (PAL I) [0573:4d02] + 19 -> Hauppauge WinTV USB (PAL/SECAM L) [0573:4d03] + 20 -> Hauppauge WinTV USB (PAL D/K) [0573:4d04] + 21 -> Hauppauge WinTV USB (NTSC FM) [0573:4d10] + 22 -> Hauppauge WinTV USB (PAL B/G FM) [0573:4d11] + 23 -> Hauppauge WinTV USB (PAL I FM) [0573:4d12] + 24 -> Hauppauge WinTV USB (PAL D/K FM) [0573:4d14] + 25 -> Hauppauge WinTV USB Pro (NTSC M/N) [0573:4d2a] + 26 -> Hauppauge WinTV USB Pro (NTSC M/N) [0573:4d2b] + 27 -> Hauppauge WinTV USB Pro (PAL/SECAM B/G/I/D/K/L) [0573:4d2c] + 28 -> Hauppauge WinTV USB Pro (NTSC M/N) [0573:4d20] + 29 -> Hauppauge WinTV USB Pro (PAL B/G) [0573:4d21] + 30 -> Hauppauge WinTV USB Pro (PAL I) [0573:4d22] + 31 -> Hauppauge WinTV USB Pro (PAL/SECAM L) [0573:4d23] + 32 -> Hauppauge WinTV USB Pro (PAL D/K) [0573:4d24] + 33 -> Hauppauge WinTV USB Pro (PAL/SECAM BGDK/I/L) [0573:4d25] + 34 -> Hauppauge WinTV USB Pro (PAL/SECAM BGDK/I/L) [0573:4d26] + 35 -> Hauppauge WinTV USB Pro (PAL B/G) [0573:4d27] + 36 -> Hauppauge WinTV USB Pro (PAL B/G,D/K) [0573:4d28] + 37 -> Hauppauge WinTV USB Pro (PAL I,D/K) [0573:4d29] + 38 -> Hauppauge WinTV USB Pro (NTSC M/N FM) [0573:4d30] + 39 -> Hauppauge WinTV USB Pro (PAL B/G FM) [0573:4d31] + 40 -> Hauppauge WinTV USB Pro (PAL I FM) [0573:4d32] + 41 -> Hauppauge WinTV USB Pro (PAL D/K FM) [0573:4d34] + 42 -> Hauppauge WinTV USB Pro (Temic PAL/SECAM B/G/I/D/K/L FM) [0573:4d35] + 43 -> Hauppauge WinTV USB Pro (Temic PAL B/G FM) [0573:4d36] + 44 -> Hauppauge WinTV USB Pro (PAL/SECAM B/G/I/D/K/L FM) [0573:4d37] + 45 -> Hauppauge WinTV USB Pro (NTSC M/N FM) [0573:4d38] + 46 -> Camtel Technology USB TV Genie Pro FM Model TVB330 [0768:0006] + 47 -> Digital Video Creator I [07d0:0001] + 48 -> Global Village GV-007 (NTSC) [07d0:0002] + 49 -> Dazzle Fusion Model DVC-50 Rev 1 (NTSC) [07d0:0003] + 50 -> Dazzle Fusion Model DVC-80 Rev 1 (PAL) [07d0:0004] + 51 -> Dazzle Fusion Model DVC-90 Rev 1 (SECAM) [07d0:0005] + 52 -> Eskape Labs MyTV2Go [07f8:9104] + 53 -> Pinnacle Studio PCTV USB (PAL) [2304:010d] + 54 -> Pinnacle Studio PCTV USB (SECAM) [2304:0109] + 55 -> Pinnacle Studio PCTV USB (PAL) FM [2304:0110] + 56 -> Miro PCTV USB [2304:0111] + 57 -> Pinnacle Studio PCTV USB (NTSC) FM [2304:0112] + 58 -> Pinnacle Studio PCTV USB (PAL) FM [2304:0210] + 59 -> Pinnacle Studio PCTV USB (NTSC) FM [2304:0212] + 60 -> Pinnacle Studio PCTV USB (PAL) FM [2304:0214] + 61 -> Pinnacle Studio Linx Video input cable (NTSC) [2304:0300] + 62 -> Pinnacle Studio Linx Video input cable (PAL) [2304:0301] + 63 -> Pinnacle PCTV Bungee USB (PAL) FM [2304:0419] + 64 -> Hauppauge WinTv-USB [2400:4200] -- cgit v1.2.3 From 659ae56dcd5a50e4560cb526a0e0dc881418dad4 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 14 Apr 2007 15:09:59 -0300 Subject: V4L/DVB (5515): Use a better format to represent usbvision supported boards Changed usbvision cards table to allow: 1) Not repeat USB ID on two structs; 2) Not need to specify both usb and card description tables at the same order, removing some magic; Some cards had duplicated names. Fixed. A test for an specific board were doing by using a string comparation. The comparation were wrong. Also, it is not a good practice to recognize a board based on his string name. Acked-by: Thierry MERLE Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/CARDLIST.usbvision | 16 +- drivers/media/video/usbvision/usbvision-cards.c | 1305 ++++++++++++++++++++--- drivers/media/video/usbvision/usbvision-cards.h | 65 ++ drivers/media/video/usbvision/usbvision-video.c | 47 +- drivers/media/video/usbvision/usbvision.h | 2 - 5 files changed, 1266 insertions(+), 169 deletions(-) create mode 100644 drivers/media/video/usbvision/usbvision-cards.h (limited to 'Documentation') diff --git a/Documentation/video4linux/CARDLIST.usbvision b/Documentation/video4linux/CARDLIST.usbvision index 9f03e86e9ce6..ab110b7b5b9e 100644 --- a/Documentation/video4linux/CARDLIST.usbvision +++ b/Documentation/video4linux/CARDLIST.usbvision @@ -24,16 +24,16 @@ 23 -> Hauppauge WinTV USB (PAL I FM) [0573:4d12] 24 -> Hauppauge WinTV USB (PAL D/K FM) [0573:4d14] 25 -> Hauppauge WinTV USB Pro (NTSC M/N) [0573:4d2a] - 26 -> Hauppauge WinTV USB Pro (NTSC M/N) [0573:4d2b] + 26 -> Hauppauge WinTV USB Pro (NTSC M/N) V2 [0573:4d2b] 27 -> Hauppauge WinTV USB Pro (PAL/SECAM B/G/I/D/K/L) [0573:4d2c] - 28 -> Hauppauge WinTV USB Pro (NTSC M/N) [0573:4d20] + 28 -> Hauppauge WinTV USB Pro (NTSC M/N) V3 [0573:4d20] 29 -> Hauppauge WinTV USB Pro (PAL B/G) [0573:4d21] 30 -> Hauppauge WinTV USB Pro (PAL I) [0573:4d22] 31 -> Hauppauge WinTV USB Pro (PAL/SECAM L) [0573:4d23] 32 -> Hauppauge WinTV USB Pro (PAL D/K) [0573:4d24] 33 -> Hauppauge WinTV USB Pro (PAL/SECAM BGDK/I/L) [0573:4d25] - 34 -> Hauppauge WinTV USB Pro (PAL/SECAM BGDK/I/L) [0573:4d26] - 35 -> Hauppauge WinTV USB Pro (PAL B/G) [0573:4d27] + 34 -> Hauppauge WinTV USB Pro (PAL/SECAM BGDK/I/L) V2 [0573:4d26] + 35 -> Hauppauge WinTV USB Pro (PAL B/G) V2 [0573:4d27] 36 -> Hauppauge WinTV USB Pro (PAL B/G,D/K) [0573:4d28] 37 -> Hauppauge WinTV USB Pro (PAL I,D/K) [0573:4d29] 38 -> Hauppauge WinTV USB Pro (NTSC M/N FM) [0573:4d30] @@ -43,7 +43,7 @@ 42 -> Hauppauge WinTV USB Pro (Temic PAL/SECAM B/G/I/D/K/L FM) [0573:4d35] 43 -> Hauppauge WinTV USB Pro (Temic PAL B/G FM) [0573:4d36] 44 -> Hauppauge WinTV USB Pro (PAL/SECAM B/G/I/D/K/L FM) [0573:4d37] - 45 -> Hauppauge WinTV USB Pro (NTSC M/N FM) [0573:4d38] + 45 -> Hauppauge WinTV USB Pro (NTSC M/N FM) V2 [0573:4d38] 46 -> Camtel Technology USB TV Genie Pro FM Model TVB330 [0768:0006] 47 -> Digital Video Creator I [07d0:0001] 48 -> Global Village GV-007 (NTSC) [07d0:0002] @@ -56,9 +56,9 @@ 55 -> Pinnacle Studio PCTV USB (PAL) FM [2304:0110] 56 -> Miro PCTV USB [2304:0111] 57 -> Pinnacle Studio PCTV USB (NTSC) FM [2304:0112] - 58 -> Pinnacle Studio PCTV USB (PAL) FM [2304:0210] - 59 -> Pinnacle Studio PCTV USB (NTSC) FM [2304:0212] - 60 -> Pinnacle Studio PCTV USB (PAL) FM [2304:0214] + 58 -> Pinnacle Studio PCTV USB (PAL) FM V2 [2304:0210] + 59 -> Pinnacle Studio PCTV USB (NTSC) FM V2 [2304:0212] + 60 -> Pinnacle Studio PCTV USB (PAL) FM V3 [2304:0214] 61 -> Pinnacle Studio Linx Video input cable (NTSC) [2304:0300] 62 -> Pinnacle Studio Linx Video input cable (PAL) [2304:0301] 63 -> Pinnacle PCTV Bungee USB (PAL) FM [2304:0419] diff --git a/drivers/media/video/usbvision/usbvision-cards.c b/drivers/media/video/usbvision/usbvision-cards.c index 78d2c845b363..c63048da9713 100644 --- a/drivers/media/video/usbvision/usbvision-cards.c +++ b/drivers/media/video/usbvision/usbvision-cards.c @@ -1,6 +1,6 @@ /* - * USBVISION.H - * usbvision header file + * usbvision-cards.c + * usbvision cards definition file * * Copyright (c) 1999-2005 Joerg Heckenbach * @@ -28,145 +28,1186 @@ #include #include #include "usbvision.h" +#include "usbvision-cards.h" /* Supported Devices: A table for usbvision.c*/ struct usbvision_device_data_st usbvision_device_data[] = { - {0xfff0, 0xfff0, -1, CODEC_SAA7111, 3, V4L2_STD_NTSC, 1, 1, 1, 1, TUNER_PHILIPS_NTSC_M, -1, -1, -1, -1, -1, "Custom Dummy USBVision Device"}, - {0x0a6f, 0x0400, -1, CODEC_SAA7113, 4, V4L2_STD_NTSC, 1, 0, 1, 0, 0, -1, -1, -1, -1, -1, "Xanboo"}, - {0x050d, 0x0106, -1, CODEC_SAA7113, 2, V4L2_STD_PAL, 1, 0, 1, 0, 0, -1, -1, 0, 3, 7, "Belkin USB VideoBus II Adapter"}, - {0x050d, 0x0207, -1, CODEC_SAA7111, 2, V4L2_STD_NTSC, 1, 0, 1, 0, 0, -1, -1, -1, -1, -1, "Belkin Components USB VideoBus"}, - {0x050d, 0x0208, -1, CODEC_SAA7113, 2, V4L2_STD_PAL, 1, 0, 1, 0, 0, -1, -1, 0, 3, 7, "Belkin USB VideoBus II"}, - {0x0571, 0x0002, 0, CODEC_SAA7111, 2, V4L2_STD_PAL, 0, 0, 1, 0, 0, -1, -1, -1, -1, 7, "echoFX InterView Lite"}, - {0x0573, 0x0003, -1, CODEC_SAA7111, 2, V4L2_STD_NTSC, 1, 0, 1, 0, 0, -1, -1, -1, -1, -1, "USBGear USBG-V1 resp. HAMA USB"}, - {0x0573, 0x0400, -1, CODEC_SAA7113, 4, V4L2_STD_NTSC, 0, 0, 1, 0, 0, -1, -1, 0, 3, 7, "D-Link V100"}, - {0x0573, 0x2000, -1, CODEC_SAA7111, 2, V4L2_STD_NTSC, 1, 0, 1, 0, 0, -1, -1, -1, -1, -1, "X10 USB Camera"}, - {0x0573, 0x2d00, -1, CODEC_SAA7111, 2, V4L2_STD_PAL, 1, 0, 1, 0, 0, -1, -1, -1, 3, 7, "Hauppauge WinTV USB Live (PAL B/G)"}, - {0x0573, 0x2d01, -1, CODEC_SAA7113, 2, V4L2_STD_NTSC, 0, 0, 1, 0, 0, -1, -1, 0, 3, 7, "Hauppauge WinTV USB Live Pro (NTSC M/N)"}, - {0x0573, 0x2101, -1, CODEC_SAA7113, 2, V4L2_STD_PAL, 2, 0, 1, 0, 0, -1, -1, 0, 3, 7, "Zoran Co. PMD (Nogatech) AV-grabber Manhattan"}, - {0x0573, 0x4100, -1, CODEC_SAA7111, 3, V4L2_STD_NTSC, 1, 1, 1, 1, TUNER_PHILIPS_NTSC_M, -1, -1, -1, 20, -1, "Nogatech USB-TV (NTSC) FM"}, - {0x0573, 0x4110, -1, CODEC_SAA7111, 3, V4L2_STD_NTSC, 1, 1, 1, 1, TUNER_PHILIPS_NTSC_M, -1, -1, -1, 20, -1, "PNY USB-TV (NTSC) FM"}, - {0x0573, 0x4450, 0, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_PHILIPS_PAL, -1, -1, 0, 3, 7, "PixelView PlayTv-USB PRO (PAL) FM"}, - {0x0573, 0x4550, 0, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_PHILIPS_PAL, -1, -1, 0, 3, 7, "ZTV ZT-721 2.4GHz USB A/V Receiver"}, - {0x0573, 0x4d00, -1, CODEC_SAA7111, 3, V4L2_STD_NTSC, 1, 0, 1, 1, TUNER_PHILIPS_NTSC_M, -1, -1, -1, 20, -1, "Hauppauge WinTV USB (NTSC M/N)"}, - {0x0573, 0x4d01, -1, CODEC_SAA7111, 3, V4L2_STD_PAL, 1, 0, 1, 1, TUNER_PHILIPS_PAL, -1, -1, -1, -1, -1, "Hauppauge WinTV USB (PAL B/G)"}, - {0x0573, 0x4d02, -1, CODEC_SAA7111, 3, V4L2_STD_PAL, 1, 0, 1, 1, TUNER_PHILIPS_PAL, -1, -1, -1, -1, -1, "Hauppauge WinTV USB (PAL I)"}, - {0x0573, 0x4d03, -1, CODEC_SAA7111, 3, V4L2_STD_SECAM, 1, 0, 1, 1, TUNER_PHILIPS_SECAM, -1, -1, -1, -1, -1, "Hauppauge WinTV USB (PAL/SECAM L)"}, - {0x0573, 0x4d04, -1, CODEC_SAA7111, 3, V4L2_STD_PAL, 1, 0, 1, 1, TUNER_PHILIPS_PAL, -1, -1, -1, -1, -1, "Hauppauge WinTV USB (PAL D/K)"}, - {0x0573, 0x4d10, -1, CODEC_SAA7111, 3, V4L2_STD_NTSC, 1, 1, 1, 1, TUNER_PHILIPS_NTSC_M, -1, -1, -1, -1, -1, "Hauppauge WinTV USB (NTSC FM)"}, - {0x0573, 0x4d11, -1, CODEC_SAA7111, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_PHILIPS_PAL, -1, -1, -1, -1, -1, "Hauppauge WinTV USB (PAL B/G FM)"}, - {0x0573, 0x4d12, -1, CODEC_SAA7111, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_PHILIPS_PAL, -1, -1, -1, -1, -1, "Hauppauge WinTV USB (PAL I FM)"}, - {0x0573, 0x4d14, -1, CODEC_SAA7111, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_PHILIPS_PAL, -1, -1, -1, -1, -1, "Hauppauge WinTV USB (PAL D/K FM)"}, - {0x0573, 0x4d2a, 0, CODEC_SAA7113, 3, V4L2_STD_NTSC, 1, 1, 1, 1, TUNER_MICROTUNE_4049FM5, -1, -1, 0, 3, 7, "Hauppauge WinTV USB Pro (NTSC M/N)"}, - {0x0573, 0x4d2b, 0, CODEC_SAA7113, 3, V4L2_STD_NTSC, 1, 1, 1, 1, TUNER_MICROTUNE_4049FM5, -1, -1, 0, 3, 7, "Hauppauge WinTV USB Pro (NTSC M/N)"}, - {0x0573, 0x4d2c, 0, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 0, 1, 1, TUNER_PHILIPS_FM1216ME_MK3, -1, -1, 0, 3, 7, "Hauppauge WinTV USB Pro (PAL/SECAM B/G/I/D/K/L)"}, - {0x0573, 0x4d20, 0, CODEC_SAA7113, 3, V4L2_STD_NTSC, 1, 1, 1, 1, TUNER_PHILIPS_NTSC_M, -1, -1, 0, 3, 7, "Hauppauge WinTV USB Pro (NTSC M/N)"}, - {0x0573, 0x4d21, 0, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 0, 1, 1, TUNER_PHILIPS_PAL, -1, -1, 0, 3, 7, "Hauppauge WinTV USB Pro (PAL B/G)"}, - {0x0573, 0x4d22, 0, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 0, 1, 1, TUNER_PHILIPS_PAL, -1, -1, 0, 3, 7, "Hauppauge WinTV USB Pro (PAL I)"}, - {0x0573, 0x4d23, -1, CODEC_SAA7113, 3, V4L2_STD_SECAM, 1, 0, 1, 1, TUNER_PHILIPS_SECAM, -1, -1, 0, 3, 7, "Hauppauge WinTV USB Pro (PAL/SECAM L)"}, - {0x0573, 0x4d24, -1, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 0, 1, 1, TUNER_PHILIPS_PAL, -1, -1, 0, 3, 7, "Hauppauge WinTV USB Pro (PAL D/K)"}, - {0x0573, 0x4d25, -1, CODEC_SAA7113, 3, V4L2_STD_SECAM, 1, 0, 1, 1, TUNER_PHILIPS_SECAM, -1, -1, 0, 3, 7, "Hauppauge WinTV USB Pro (PAL/SECAM BGDK/I/L)"}, - {0x0573, 0x4d26, -1, CODEC_SAA7113, 3, V4L2_STD_SECAM, 1, 0, 1, 1, TUNER_PHILIPS_SECAM, -1, -1, 0, 3, 7, "Hauppauge WinTV USB Pro (PAL/SECAM BGDK/I/L)"}, - {0x0573, 0x4d27, -1, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 0, 1, 1, TUNER_ALPS_TSBE1_PAL, -1, -1, 0, 3, 7, "Hauppauge WinTV USB Pro (PAL B/G)"}, - {0x0573, 0x4d28, -1, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 0, 1, 1, TUNER_ALPS_TSBE1_PAL, -1, -1, 0, 3, 7, "Hauppauge WinTV USB Pro (PAL B/G,D/K)"}, - {0x0573, 0x4d29, -1, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 0, 1, 1, TUNER_PHILIPS_PAL, -1, -1, 0, 3, 7, "Hauppauge WinTV USB Pro (PAL I,D/K)"}, - {0x0573, 0x4d30, -1, CODEC_SAA7113, 3, V4L2_STD_NTSC, 1, 1, 1, 1, TUNER_PHILIPS_NTSC_M, -1, -1, 0, 3, 7, "Hauppauge WinTV USB Pro (NTSC M/N FM)"}, - {0x0573, 0x4d31, 0, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_PHILIPS_PAL, -1, -1, 0, 3, 7, "Hauppauge WinTV USB Pro (PAL B/G FM)"}, - {0x0573, 0x4d32, 0, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_PHILIPS_PAL, -1, -1, 0, 3, 7, "Hauppauge WinTV USB Pro (PAL I FM)"}, - {0x0573, 0x4d34, 0, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_PHILIPS_PAL, -1, -1, 0, 3, 7, "Hauppauge WinTV USB Pro (PAL D/K FM)"}, - {0x0573, 0x4d35, 0, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_MICROTUNE_4049FM5, -1, -1, 0, 3, 7, "Hauppauge WinTV USB Pro (Temic PAL/SECAM B/G/I/D/K/L FM)"}, - {0x0573, 0x4d36, 0, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_MICROTUNE_4049FM5, -1, -1, 0, 3, 7, "Hauppauge WinTV USB Pro (Temic PAL B/G FM)"}, - {0x0573, 0x4d37, 0, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_PHILIPS_FM1216ME_MK3, -1, -1, 0, 3, 7, "Hauppauge WinTV USB Pro (PAL/SECAM B/G/I/D/K/L FM)"}, - {0x0573, 0x4d38, 0, CODEC_SAA7113, 3, V4L2_STD_NTSC, 1, 1, 1, 1, TUNER_PHILIPS_NTSC_M, -1, -1, 0, 3, 7, "Hauppauge WinTV USB Pro (NTSC M/N FM)"}, - {0x0768, 0x0006, -1, CODEC_SAA7113, 3, V4L2_STD_NTSC, 1, 1, 1, 1, TUNER_PHILIPS_NTSC_M, -1, -1, 5, 5, -1, "Camtel Technology USB TV Genie Pro FM Model TVB330"}, - {0x07d0, 0x0001, -1, CODEC_SAA7113, 2, V4L2_STD_PAL, 0, 0, 1, 0, 0, -1, -1, 0, 3, 7, "Digital Video Creator I"}, - {0x07d0, 0x0002, -1, CODEC_SAA7111, 2, V4L2_STD_NTSC, 0, 0, 1, 0, 0, -1, -1, 82, 20, 7, "Global Village GV-007 (NTSC)"}, - {0x07d0, 0x0003, 0, CODEC_SAA7113, 2, V4L2_STD_NTSC, 0, 0, 1, 0, 0, -1, -1, 0, 3, 7, "Dazzle Fusion Model DVC-50 Rev 1 (NTSC)"}, - {0x07d0, 0x0004, 0, CODEC_SAA7113, 2, V4L2_STD_PAL, 0, 0, 1, 0, 0, -1, -1, 0, 3, 7, "Dazzle Fusion Model DVC-80 Rev 1 (PAL)"}, - {0x07d0, 0x0005, 0, CODEC_SAA7113, 2, V4L2_STD_SECAM, 0, 0, 1, 0, 0, -1, -1, 0, 3, 7, "Dazzle Fusion Model DVC-90 Rev 1 (SECAM)"}, - {0x07f8, 0x9104, 0, CODEC_SAA7113, 2, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_PHILIPS_FM1216ME_MK3, -1, -1, 0, 3, 7, "Eskape Labs MyTV2Go"}, - {0x2304, 0x010d, -1, CODEC_SAA7111, 3, V4L2_STD_PAL, 1, 0, 0, 1, TUNER_TEMIC_4066FY5_PAL_I, -1, -1, -1, -1, -1, "Pinnacle Studio PCTV USB (PAL)"}, - {0x2304, 0x0109, -1, CODEC_SAA7111, 3, V4L2_STD_SECAM, 1, 0, 1, 1, TUNER_PHILIPS_SECAM, -1, -1, -1, -1, -1, "Pinnacle Studio PCTV USB (SECAM)"}, - {0x2304, 0x0110, -1, CODEC_SAA7111, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_PHILIPS_PAL, -1, -1,128, 23, -1, "Pinnacle Studio PCTV USB (PAL) FM"}, - {0x2304, 0x0111, -1, CODEC_SAA7111, 3, V4L2_STD_PAL, 1, 0, 1, 1, TUNER_PHILIPS_PAL, -1, -1, -1, -1, -1, "Miro PCTV USB"}, - {0x2304, 0x0112, -1, CODEC_SAA7111, 3, V4L2_STD_NTSC, 1, 1, 1, 1, TUNER_PHILIPS_NTSC_M, -1, -1, -1, -1, -1, "Pinnacle Studio PCTV USB (NTSC) FM"}, - {0x2304, 0x0210, -1, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_TEMIC_4009FR5_PAL, -1, -1, 0, 3, 7, "Pinnacle Studio PCTV USB (PAL) FM"}, - {0x2304, 0x0212, -1, CODEC_SAA7111, 3, V4L2_STD_NTSC, 1, 1, 1, 1, TUNER_TEMIC_4039FR5_NTSC, -1, -1, 0, 3, 7, "Pinnacle Studio PCTV USB (NTSC) FM"}, - {0x2304, 0x0214, -1, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_TEMIC_4009FR5_PAL, -1, -1, 0, 3, 7, "Pinnacle Studio PCTV USB (PAL) FM"}, - {0x2304, 0x0300, -1, CODEC_SAA7113, 2, V4L2_STD_NTSC, 1, 0, 1, 0, 0, -1, -1, 0, 3, 7, "Pinnacle Studio Linx Video input cable (NTSC)"}, - {0x2304, 0x0301, -1, CODEC_SAA7113, 2, V4L2_STD_PAL, 1, 0, 1, 0, 0, -1, -1, 0, 3, 7, "Pinnacle Studio Linx Video input cable (PAL)"}, - {0x2304, 0x0419, -1, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_TEMIC_4009FR5_PAL, -1, -1, 0, 3, 7, "Pinnacle PCTV Bungee USB (PAL) FM"}, - {0x2400, 0x4200, -1, CODEC_SAA7111, 3, V4L2_STD_NTSC, 1, 0, 1, 1, TUNER_PHILIPS_NTSC_M, -1, -1, -1, -1, -1, "Hauppauge WinTv-USB"}, + [DUMMY_DEVICE] = { + .Interface = -1, + .Codec = CODEC_SAA7111, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_NTSC, + .AudioChannels = 1, + .Radio = 1, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_PHILIPS_NTSC_M, + .Vin_Reg1 = -1, + .Vin_Reg2 = -1, + .X_Offset = -1, + .Y_Offset = -1, + .Dvi_yuv = -1, + .ModelString = "Custom Dummy USBVision Device", + }, + [XANBOO] = { + .Interface = -1, + .Codec = CODEC_SAA7113, + .VideoChannels = 4, + .VideoNorm = V4L2_STD_NTSC, + .AudioChannels = 1, + .Radio = 0, + .vbi = 1, + .Tuner = 0, + .TunerType = 0, + .Vin_Reg1 = -1, + .Vin_Reg2 = -1, + .X_Offset = -1, + .Y_Offset = -1, + .Dvi_yuv = -1, + .ModelString = "Xanboo", + }, + [BELKIN_VIDEOBUS_II] = { + .Interface = -1, + .Codec = CODEC_SAA7113, + .VideoChannels = 2, + .VideoNorm = V4L2_STD_PAL, + .AudioChannels = 1, + .Radio = 0, + .vbi = 1, + .Tuner = 0, + .TunerType = 0, + .Vin_Reg1 = -1, + .Vin_Reg2 = -1, + .X_Offset = 0, + .Y_Offset = 3, + .Dvi_yuv = 7, + .ModelString = "Belkin USB VideoBus II Adapter", + }, + [BELKIN_VIDEOBUS] = { + .Interface = -1, + .Codec = CODEC_SAA7111, + .VideoChannels = 2, + .VideoNorm = V4L2_STD_NTSC, + .AudioChannels = 1, + .Radio = 0, + .vbi = 1, + .Tuner = 0, + .TunerType = 0, + .Vin_Reg1 = -1, + .Vin_Reg2 = -1, + .X_Offset = -1, + .Y_Offset = -1, + .Dvi_yuv = -1, + .ModelString = "Belkin Components USB VideoBus", + }, + [BELKIN_USB_VIDEOBUS_II] = { + .Interface = -1, + .Codec = CODEC_SAA7113, + .VideoChannels = 2, + .VideoNorm = V4L2_STD_PAL, + .AudioChannels = 1, + .Radio = 0, + .vbi = 1, + .Tuner = 0, + .TunerType = 0, + .Vin_Reg1 = -1, + .Vin_Reg2 = -1, + .X_Offset = 0, + .Y_Offset = 3, + .Dvi_yuv = 7, + .ModelString = "Belkin USB VideoBus II", + }, + [ECHOFX_INTERVIEW_LITE] = { + .Interface = 0, + .Codec = CODEC_SAA7111, + .VideoChannels = 2, + .VideoNorm = V4L2_STD_PAL, + .AudioChannels = 0, + .Radio = 0, + .vbi = 1, + .Tuner = 0, + .TunerType = 0, + .Vin_Reg1 = -1, + .Vin_Reg2 = -1, + .X_Offset = -1, + .Y_Offset = -1, + .Dvi_yuv = 7, + .ModelString = "echoFX InterView Lite", + }, + [USBGEAR_USBG_V1] = { + .Interface = -1, + .Codec = CODEC_SAA7111, + .VideoChannels = 2, + .VideoNorm = V4L2_STD_NTSC, + .AudioChannels = 1, + .Radio = 0, + .vbi = 1, + .Tuner = 0, + .TunerType = 0, + .Vin_Reg1 = -1, + .Vin_Reg2 = -1, + .X_Offset = -1, + .Y_Offset = -1, + .Dvi_yuv = -1, + .ModelString = "USBGear USBG-V1 resp. HAMA USB", + }, + [D_LINK_V100] = { + .Interface = -1, + .Codec = CODEC_SAA7113, + .VideoChannels = 4, + .VideoNorm = V4L2_STD_NTSC, + .AudioChannels = 0, + .Radio = 0, + .vbi = 1, + .Tuner = 0, + .TunerType = 0, + .Vin_Reg1 = -1, + .Vin_Reg2 = -1, + .X_Offset = 0, + .Y_Offset = 3, + .Dvi_yuv = 7, + .ModelString = "D-Link V100", + }, + [X10_USB_CAMERA] = { + .Interface = -1, + .Codec = CODEC_SAA7111, + .VideoChannels = 2, + .VideoNorm = V4L2_STD_NTSC, + .AudioChannels = 1, + .Radio = 0, + .vbi = 1, + .Tuner = 0, + .TunerType = 0, + .Vin_Reg1 = -1, + .Vin_Reg2 = -1, + .X_Offset = -1, + .Y_Offset = -1, + .Dvi_yuv = -1, + .ModelString = "X10 USB Camera", + }, + [HPG_WINTV_LIVE_PAL_BG] = { + .Interface = -1, + .Codec = CODEC_SAA7111, + .VideoChannels = 2, + .VideoNorm = V4L2_STD_PAL, + .AudioChannels = 1, + .Radio = 0, + .vbi = 1, + .Tuner = 0, + .TunerType = 0, + .Vin_Reg1 = -1, + .Vin_Reg2 = -1, + .X_Offset = -1, + .Y_Offset = 3, + .Dvi_yuv = 7, + .ModelString = "Hauppauge WinTV USB Live (PAL B/G)", + }, + [HPG_WINTV_LIVE_PRO_NTSC_MN] = { + .Interface = -1, + .Codec = CODEC_SAA7113, + .VideoChannels = 2, + .VideoNorm = V4L2_STD_NTSC, + .AudioChannels = 0, + .Radio = 0, + .vbi = 1, + .Tuner = 0, + .TunerType = 0, + .Vin_Reg1 = -1, + .Vin_Reg2 = -1, + .X_Offset = 0, + .Y_Offset = 3, + .Dvi_yuv = 7, + .ModelString = "Hauppauge WinTV USB Live Pro (NTSC M/N)", + }, + [ZORAN_PMD_NOGATECH] = { + .Interface = -1, + .Codec = CODEC_SAA7113, + .VideoChannels = 2, + .VideoNorm = V4L2_STD_PAL, + .AudioChannels = 2, + .Radio = 0, + .vbi = 1, + .Tuner = 0, + .TunerType = 0, + .Vin_Reg1 = -1, + .Vin_Reg2 = -1, + .X_Offset = 0, + .Y_Offset = 3, + .Dvi_yuv = 7, + .ModelString = "Zoran Co. PMD (Nogatech) AV-grabber Manhattan", + }, + [NOGATECH_USB_TV_NTSC_FM] = { + .Interface = -1, + .Codec = CODEC_SAA7111, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_NTSC, + .AudioChannels = 1, + .Radio = 1, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_PHILIPS_NTSC_M, + .Vin_Reg1 = -1, + .Vin_Reg2 = -1, + .X_Offset = -1, + .Y_Offset = 20, + .Dvi_yuv = -1, + .ModelString = "Nogatech USB-TV (NTSC) FM", + }, + [PNY_USB_TV_NTSC_FM] = { + .Interface = -1, + .Codec = CODEC_SAA7111, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_NTSC, + .AudioChannels = 1, + .Radio = 1, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_PHILIPS_NTSC_M, + .Vin_Reg1 = -1, + .Vin_Reg2 = -1, + .X_Offset = -1, + .Y_Offset = 20, + .Dvi_yuv = -1, + .ModelString = "PNY USB-TV (NTSC) FM", + }, + [PV_PLAYTV_USB_PRO_PAL_FM] = { + .Interface = 0, + .Codec = CODEC_SAA7113, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_PAL, + .AudioChannels = 1, + .Radio = 1, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_PHILIPS_PAL, + .Vin_Reg1 = -1, + .Vin_Reg2 = -1, + .X_Offset = 0, + .Y_Offset = 3, + .Dvi_yuv = 7, + .ModelString = "PixelView PlayTv-USB PRO (PAL) FM", + }, + [ZT_721] = { + .Interface = 0, + .Codec = CODEC_SAA7113, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_PAL, + .AudioChannels = 1, + .Radio = 1, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_PHILIPS_PAL, + .Vin_Reg1 = -1, + .Vin_Reg2 = -1, + .X_Offset = 0, + .Y_Offset = 3, + .Dvi_yuv = 7, + .ModelString = "ZTV ZT-721 2.4GHz USB A/V Receiver", + }, + [HPG_WINTV_NTSC_MN] = { + .Interface = -1, + .Codec = CODEC_SAA7111, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_NTSC, + .AudioChannels = 1, + .Radio = 0, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_PHILIPS_NTSC_M, + .Vin_Reg1 = -1, + .Vin_Reg2 = -1, + .X_Offset = -1, + .Y_Offset = 20, + .Dvi_yuv = -1, + .ModelString = "Hauppauge WinTV USB (NTSC M/N)", + }, + [HPG_WINTV_PAL_BG] = { + .Interface = -1, + .Codec = CODEC_SAA7111, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_PAL, + .AudioChannels = 1, + .Radio = 0, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_PHILIPS_PAL, + .Vin_Reg1 = -1, + .Vin_Reg2 = -1, + .X_Offset = -1, + .Y_Offset = -1, + .Dvi_yuv = -1, + .ModelString = "Hauppauge WinTV USB (PAL B/G)", + }, + [HPG_WINTV_PAL_I] = { + .Interface = -1, + .Codec = CODEC_SAA7111, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_PAL, + .AudioChannels = 1, + .Radio = 0, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_PHILIPS_PAL, + .Vin_Reg1 = -1, + .Vin_Reg2 = -1, + .X_Offset = -1, + .Y_Offset = -1, + .Dvi_yuv = -1, + .ModelString = "Hauppauge WinTV USB (PAL I)", + }, + [HPG_WINTV_PAL_SECAM_L] = { + .Interface = -1, + .Codec = CODEC_SAA7111, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_SECAM, + .AudioChannels = 1, + .Radio = 0, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_PHILIPS_SECAM, + .Vin_Reg1 = -1, + .Vin_Reg2 = -1, + .X_Offset = -1, + .Y_Offset = -1, + .Dvi_yuv = -1, + .ModelString = "Hauppauge WinTV USB (PAL/SECAM L)", + }, + [HPG_WINTV_PAL_D_K] = { + .Interface = -1, + .Codec = CODEC_SAA7111, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_PAL, + .AudioChannels = 1, + .Radio = 0, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_PHILIPS_PAL, + .Vin_Reg1 = -1, + .Vin_Reg2 = -1, + .X_Offset = -1, + .Y_Offset = -1, + .Dvi_yuv = -1, + .ModelString = "Hauppauge WinTV USB (PAL D/K)", + }, + [HPG_WINTV_NTSC_FM] = { + .Interface = -1, + .Codec = CODEC_SAA7111, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_NTSC, + .AudioChannels = 1, + .Radio = 1, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_PHILIPS_NTSC_M, + .Vin_Reg1 = -1, + .Vin_Reg2 = -1, + .X_Offset = -1, + .Y_Offset = -1, + .Dvi_yuv = -1, + .ModelString = "Hauppauge WinTV USB (NTSC FM)", + }, + [HPG_WINTV_PAL_BG_FM] = { + .Interface = -1, + .Codec = CODEC_SAA7111, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_PAL, + .AudioChannels = 1, + .Radio = 1, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_PHILIPS_PAL, + .Vin_Reg1 = -1, + .Vin_Reg2 = -1, + .X_Offset = -1, + .Y_Offset = -1, + .Dvi_yuv = -1, + .ModelString = "Hauppauge WinTV USB (PAL B/G FM)", + }, + [HPG_WINTV_PAL_I_FM] = { + .Interface = -1, + .Codec = CODEC_SAA7111, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_PAL, + .AudioChannels = 1, + .Radio = 1, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_PHILIPS_PAL, + .Vin_Reg1 = -1, + .Vin_Reg2 = -1, + .X_Offset = -1, + .Y_Offset = -1, + .Dvi_yuv = -1, + .ModelString = "Hauppauge WinTV USB (PAL I FM)", + }, + [HPG_WINTV_PAL_D_K_FM] = { + .Interface = -1, + .Codec = CODEC_SAA7111, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_PAL, + .AudioChannels = 1, + .Radio = 1, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_PHILIPS_PAL, + .Vin_Reg1 = -1, + .Vin_Reg2 = -1, + .X_Offset = -1, + .Y_Offset = -1, + .Dvi_yuv = -1, + .ModelString = "Hauppauge WinTV USB (PAL D/K FM)", + }, + [HPG_WINTV_PRO_NTSC_MN] = { + .Interface = 0, + .Codec = CODEC_SAA7113, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_NTSC, + .AudioChannels = 1, + .Radio = 1, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_MICROTUNE_4049FM5, + .Vin_Reg1 = -1, + .Vin_Reg2 = -1, + .X_Offset = 0, + .Y_Offset = 3, + .Dvi_yuv = 7, + .ModelString = "Hauppauge WinTV USB Pro (NTSC M/N)", + }, + [HPG_WINTV_PRO_NTSC_MN_V2] = { + .Interface = 0, + .Codec = CODEC_SAA7113, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_NTSC, + .AudioChannels = 1, + .Radio = 1, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_MICROTUNE_4049FM5, + .Vin_Reg1 = -1, + .Vin_Reg2 = -1, + .X_Offset = 0, + .Y_Offset = 3, + .Dvi_yuv = 7, + .ModelString = "Hauppauge WinTV USB Pro (NTSC M/N) V2", + }, + [HPG_WINTV_PRO_PAL] = { + .Interface = 0, + .Codec = CODEC_SAA7113, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_PAL, + .AudioChannels = 1, + .Radio = 0, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_PHILIPS_FM1216ME_MK3, + .Vin_Reg1 = -1, + .Vin_Reg2 = -1, + .X_Offset = 0, + .Y_Offset = 3, + .Dvi_yuv = 7, + .ModelString = "Hauppauge WinTV USB Pro (PAL/SECAM B/G/I/D/K/L)", + }, + [HPG_WINTV_PRO_NTSC_MN_V3] = { + .Interface = 0, + .Codec = CODEC_SAA7113, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_NTSC, + .AudioChannels = 1, + .Radio = 1, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_PHILIPS_NTSC_M, + .Vin_Reg1 = -1, + .Vin_Reg2 = -1, + .X_Offset = 0, + .Y_Offset = 3, + .Dvi_yuv = 7, + .ModelString = "Hauppauge WinTV USB Pro (NTSC M/N) V3", + }, + [HPG_WINTV_PRO_PAL_BG] = { + .Interface = 0, + .Codec = CODEC_SAA7113, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_PAL, + .AudioChannels = 1, + .Radio = 0, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_PHILIPS_PAL, + .Vin_Reg1 = -1, + .Vin_Reg2 = -1, + .X_Offset = 0, + .Y_Offset = 3, + .Dvi_yuv = 7, + .ModelString = "Hauppauge WinTV USB Pro (PAL B/G)", + }, + [HPG_WINTV_PRO_PAL_I] = { + .Interface = 0, + .Codec = CODEC_SAA7113, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_PAL, + .AudioChannels = 1, + .Radio = 0, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_PHILIPS_PAL, + .Vin_Reg1 = -1, + .Vin_Reg2 = -1, + .X_Offset = 0, + .Y_Offset = 3, + .Dvi_yuv = 7, + .ModelString = "Hauppauge WinTV USB Pro (PAL I)", + }, + [HPG_WINTV_PRO_PAL_SECAM_L] = { + .Interface = -1, + .Codec = CODEC_SAA7113, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_SECAM, + .AudioChannels = 1, + .Radio = 0, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_PHILIPS_SECAM, + .Vin_Reg1 = -1, + .Vin_Reg2 = -1, + .X_Offset = 0, + .Y_Offset = 3, + .Dvi_yuv = 7, + .ModelString = "Hauppauge WinTV USB Pro (PAL/SECAM L)", + }, + [HPG_WINTV_PRO_PAL_D_K] = { + .Interface = -1, + .Codec = CODEC_SAA7113, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_PAL, + .AudioChannels = 1, + .Radio = 0, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_PHILIPS_PAL, + .Vin_Reg1 = -1, + .Vin_Reg2 = -1, + .X_Offset = 0, + .Y_Offset = 3, + .Dvi_yuv = 7, + .ModelString = "Hauppauge WinTV USB Pro (PAL D/K)", + }, + [HPG_WINTV_PRO_PAL_SECAM] = { + .Interface = -1, + .Codec = CODEC_SAA7113, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_SECAM, + .AudioChannels = 1, + .Radio = 0, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_PHILIPS_SECAM, + .Vin_Reg1 = -1, + .Vin_Reg2 = -1, + .X_Offset = 0, + .Y_Offset = 3, + .Dvi_yuv = 7, + .ModelString = "Hauppauge WinTV USB Pro (PAL/SECAM BGDK/I/L)", + }, + [HPG_WINTV_PRO_PAL_SECAM_V2] = { + .Interface = -1, + .Codec = CODEC_SAA7113, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_SECAM, + .AudioChannels = 1, + .Radio = 0, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_PHILIPS_SECAM, + .Vin_Reg1 = -1, + .Vin_Reg2 = -1, + .X_Offset = 0, + .Y_Offset = 3, + .Dvi_yuv = 7, + .ModelString = "Hauppauge WinTV USB Pro (PAL/SECAM BGDK/I/L) V2", + }, + [HPG_WINTV_PRO_PAL_BG_V2] = { + .Interface = -1, + .Codec = CODEC_SAA7113, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_PAL, + .AudioChannels = 1, + .Radio = 0, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_ALPS_TSBE1_PAL, + .Vin_Reg1 = -1, + .Vin_Reg2 = -1, + .X_Offset = 0, + .Y_Offset = 3, + .Dvi_yuv = 7, + .ModelString = "Hauppauge WinTV USB Pro (PAL B/G) V2", + }, + [HPG_WINTV_PRO_PAL_BG_D_K] = { + .Interface = -1, + .Codec = CODEC_SAA7113, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_PAL, + .AudioChannels = 1, + .Radio = 0, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_ALPS_TSBE1_PAL, + .Vin_Reg1 = -1, + .Vin_Reg2 = -1, + .X_Offset = 0, + .Y_Offset = 3, + .Dvi_yuv = 7, + .ModelString = "Hauppauge WinTV USB Pro (PAL B/G,D/K)", + }, + [HPG_WINTV_PRO_PAL_I_D_K] = { + .Interface = -1, + .Codec = CODEC_SAA7113, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_PAL, + .AudioChannels = 1, + .Radio = 0, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_PHILIPS_PAL, + .Vin_Reg1 = -1, + .Vin_Reg2 = -1, + .X_Offset = 0, + .Y_Offset = 3, + .Dvi_yuv = 7, + .ModelString = "Hauppauge WinTV USB Pro (PAL I,D/K)", + }, + [HPG_WINTV_PRO_NTSC_MN_FM] = { + .Interface = -1, + .Codec = CODEC_SAA7113, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_NTSC, + .AudioChannels = 1, + .Radio = 1, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_PHILIPS_NTSC_M, + .Vin_Reg1 = -1, + .Vin_Reg2 = -1, + .X_Offset = 0, + .Y_Offset = 3, + .Dvi_yuv = 7, + .ModelString = "Hauppauge WinTV USB Pro (NTSC M/N FM)", + }, + [HPG_WINTV_PRO_PAL_BG_FM] = { + .Interface = 0, + .Codec = CODEC_SAA7113, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_PAL, + .AudioChannels = 1, + .Radio = 1, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_PHILIPS_PAL, + .Vin_Reg1 = -1, + .Vin_Reg2 = -1, + .X_Offset = 0, + .Y_Offset = 3, + .Dvi_yuv = 7, + .ModelString = "Hauppauge WinTV USB Pro (PAL B/G FM)", + }, + [HPG_WINTV_PRO_PAL_I_FM] = { + .Interface = 0, + .Codec = CODEC_SAA7113, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_PAL, + .AudioChannels = 1, + .Radio = 1, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_PHILIPS_PAL, + .Vin_Reg1 = -1, + .Vin_Reg2 = -1, + .X_Offset = 0, + .Y_Offset = 3, + .Dvi_yuv = 7, + .ModelString = "Hauppauge WinTV USB Pro (PAL I FM)", + }, + [HPG_WINTV_PRO_PAL_D_K_FM] = { + .Interface = 0, + .Codec = CODEC_SAA7113, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_PAL, + .AudioChannels = 1, + .Radio = 1, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_PHILIPS_PAL, + .Vin_Reg1 = -1, + .Vin_Reg2 = -1, + .X_Offset = 0, + .Y_Offset = 3, + .Dvi_yuv = 7, + .ModelString = "Hauppauge WinTV USB Pro (PAL D/K FM)", + }, + [HPG_WINTV_PRO_TEMIC_PAL_FM] = { + .Interface = 0, + .Codec = CODEC_SAA7113, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_PAL, + .AudioChannels = 1, + .Radio = 1, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_MICROTUNE_4049FM5, + .Vin_Reg1 = -1, + .Vin_Reg2 = -1, + .X_Offset = 0, + .Y_Offset = 3, + .Dvi_yuv = 7, + .ModelString = "Hauppauge WinTV USB Pro (Temic PAL/SECAM B/G/I/D/K/L FM)", + }, + [HPG_WINTV_PRO_TEMIC_PAL_BG_FM] = { + .Interface = 0, + .Codec = CODEC_SAA7113, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_PAL, + .AudioChannels = 1, + .Radio = 1, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_MICROTUNE_4049FM5, + .Vin_Reg1 = -1, + .Vin_Reg2 = -1, + .X_Offset = 0, + .Y_Offset = 3, + .Dvi_yuv = 7, + .ModelString = "Hauppauge WinTV USB Pro (Temic PAL B/G FM)", + }, + [HPG_WINTV_PRO_PAL_FM] = { + .Interface = 0, + .Codec = CODEC_SAA7113, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_PAL, + .AudioChannels = 1, + .Radio = 1, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_PHILIPS_FM1216ME_MK3, + .Vin_Reg1 = -1, + .Vin_Reg2 = -1, + .X_Offset = 0, + .Y_Offset = 3, + .Dvi_yuv = 7, + .ModelString = "Hauppauge WinTV USB Pro (PAL/SECAM B/G/I/D/K/L FM)", + }, + [HPG_WINTV_PRO_NTSC_MN_FM_V2] = { + .Interface = 0, + .Codec = CODEC_SAA7113, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_NTSC, + .AudioChannels = 1, + .Radio = 1, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_PHILIPS_NTSC_M, + .Vin_Reg1 = -1, + .Vin_Reg2 = -1, + .X_Offset = 0, + .Y_Offset = 3, + .Dvi_yuv = 7, + .ModelString = "Hauppauge WinTV USB Pro (NTSC M/N FM) V2", + }, + [CAMTEL_TVB330] = { + .Interface = -1, + .Codec = CODEC_SAA7113, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_NTSC, + .AudioChannels = 1, + .Radio = 1, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_PHILIPS_NTSC_M, + .Vin_Reg1 = -1, + .Vin_Reg2 = -1, + .X_Offset = 5, + .Y_Offset = 5, + .Dvi_yuv = -1, + .ModelString = "Camtel Technology USB TV Genie Pro FM Model TVB330", + }, + [DIGITAL_VIDEO_CREATOR_I] = { + .Interface = -1, + .Codec = CODEC_SAA7113, + .VideoChannels = 2, + .VideoNorm = V4L2_STD_PAL, + .AudioChannels = 0, + .Radio = 0, + .vbi = 1, + .Tuner = 0, + .TunerType = 0, + .Vin_Reg1 = -1, + .Vin_Reg2 = -1, + .X_Offset = 0, + .Y_Offset = 3, + .Dvi_yuv = 7, + .ModelString = "Digital Video Creator I", + }, + [GLOBAL_VILLAGE_GV_007_NTSC] = { + .Interface = -1, + .Codec = CODEC_SAA7111, + .VideoChannels = 2, + .VideoNorm = V4L2_STD_NTSC, + .AudioChannels = 0, + .Radio = 0, + .vbi = 1, + .Tuner = 0, + .TunerType = 0, + .Vin_Reg1 = -1, + .Vin_Reg2 = -1, + .X_Offset = 82, + .Y_Offset = 20, + .Dvi_yuv = 7, + .ModelString = "Global Village GV-007 (NTSC)", + }, + [DAZZLE_DVC_50_REV_1_NTSC] = { + .Interface = 0, + .Codec = CODEC_SAA7113, + .VideoChannels = 2, + .VideoNorm = V4L2_STD_NTSC, + .AudioChannels = 0, + .Radio = 0, + .vbi = 1, + .Tuner = 0, + .TunerType = 0, + .Vin_Reg1 = -1, + .Vin_Reg2 = -1, + .X_Offset = 0, + .Y_Offset = 3, + .Dvi_yuv = 7, + .ModelString = "Dazzle Fusion Model DVC-50 Rev 1 (NTSC)", + }, + [DAZZLE_DVC_80_REV_1_PAL] = { + .Interface = 0, + .Codec = CODEC_SAA7113, + .VideoChannels = 2, + .VideoNorm = V4L2_STD_PAL, + .AudioChannels = 0, + .Radio = 0, + .vbi = 1, + .Tuner = 0, + .TunerType = 0, + .Vin_Reg1 = -1, + .Vin_Reg2 = -1, + .X_Offset = 0, + .Y_Offset = 3, + .Dvi_yuv = 7, + .ModelString = "Dazzle Fusion Model DVC-80 Rev 1 (PAL)", + }, + [DAZZLE_DVC_90_REV_1_SECAM] = { + .Interface = 0, + .Codec = CODEC_SAA7113, + .VideoChannels = 2, + .VideoNorm = V4L2_STD_SECAM, + .AudioChannels = 0, + .Radio = 0, + .vbi = 1, + .Tuner = 0, + .TunerType = 0, + .Vin_Reg1 = -1, + .Vin_Reg2 = -1, + .X_Offset = 0, + .Y_Offset = 3, + .Dvi_yuv = 7, + .ModelString = "Dazzle Fusion Model DVC-90 Rev 1 (SECAM)", + }, + [ESKAPE_LABS_MYTV2GO] = { + .Interface = 0, + .Codec = CODEC_SAA7113, + .VideoChannels = 2, + .VideoNorm = V4L2_STD_PAL, + .AudioChannels = 1, + .Radio = 1, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_PHILIPS_FM1216ME_MK3, + .Vin_Reg1 = -1, + .Vin_Reg2 = -1, + .X_Offset = 0, + .Y_Offset = 3, + .Dvi_yuv = 7, + .ModelString = "Eskape Labs MyTV2Go", + }, + [PINNA_PCTV_USB_PAL] = { + .Interface = -1, + .Codec = CODEC_SAA7111, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_PAL, + .AudioChannels = 1, + .Radio = 0, + .vbi = 0, + .Tuner = 1, + .TunerType = TUNER_TEMIC_4066FY5_PAL_I, + .Vin_Reg1 = -1, + .Vin_Reg2 = -1, + .X_Offset = -1, + .Y_Offset = -1, + .Dvi_yuv = -1, + .ModelString = "Pinnacle Studio PCTV USB (PAL)", + }, + [PINNA_PCTV_USB_SECAM] = { + .Interface = -1, + .Codec = CODEC_SAA7111, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_SECAM, + .AudioChannels = 1, + .Radio = 0, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_PHILIPS_SECAM, + .Vin_Reg1 = -1, + .Vin_Reg2 = -1, + .X_Offset = -1, + .Y_Offset = -1, + .Dvi_yuv = -1, + .ModelString = "Pinnacle Studio PCTV USB (SECAM)", + }, + [PINNA_PCTV_USB_PAL_FM] = { + .Interface = -1, + .Codec = CODEC_SAA7111, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_PAL, + .AudioChannels = 1, + .Radio = 1, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_PHILIPS_PAL, + .Vin_Reg1 = -1, + .Vin_Reg2 = -1, + .X_Offset = 128, + .Y_Offset = 23, + .Dvi_yuv = -1, + .ModelString = "Pinnacle Studio PCTV USB (PAL) FM", + }, + [MIRO_PCTV_USB] = { + .Interface = -1, + .Codec = CODEC_SAA7111, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_PAL, + .AudioChannels = 1, + .Radio = 0, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_PHILIPS_PAL, + .Vin_Reg1 = -1, + .Vin_Reg2 = -1, + .X_Offset = -1, + .Y_Offset = -1, + .Dvi_yuv = -1, + .ModelString = "Miro PCTV USB", + }, + [PINNA_PCTV_USB_NTSC_FM] = { + .Interface = -1, + .Codec = CODEC_SAA7111, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_NTSC, + .AudioChannels = 1, + .Radio = 1, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_PHILIPS_NTSC_M, + .Vin_Reg1 = -1, + .Vin_Reg2 = -1, + .X_Offset = -1, + .Y_Offset = -1, + .Dvi_yuv = -1, + .ModelString = "Pinnacle Studio PCTV USB (NTSC) FM", + }, + [PINNA_PCTV_USB_PAL_FM_V2] = { + .Interface = -1, + .Codec = CODEC_SAA7113, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_PAL, + .AudioChannels = 1, + .Radio = 1, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_TEMIC_4009FR5_PAL, + .Vin_Reg1 = -1, + .Vin_Reg2 = -1, + .X_Offset = 0, + .Y_Offset = 3, + .Dvi_yuv = 7, + .ModelString = "Pinnacle Studio PCTV USB (PAL) FM V2", + }, + [PINNA_PCTV_USB_NTSC_FM_V2] = { + .Interface = -1, + .Codec = CODEC_SAA7111, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_NTSC, + .AudioChannels = 1, + .Radio = 1, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_TEMIC_4039FR5_NTSC, + .Vin_Reg1 = -1, + .Vin_Reg2 = -1, + .X_Offset = 0, + .Y_Offset = 3, + .Dvi_yuv = 7, + .ModelString = "Pinnacle Studio PCTV USB (NTSC) FM V2", + }, + [PINNA_PCTV_USB_PAL_FM_V3] = { + .Interface = -1, + .Codec = CODEC_SAA7113, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_PAL, + .AudioChannels = 1, + .Radio = 1, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_TEMIC_4009FR5_PAL, + .Vin_Reg1 = -1, + .Vin_Reg2 = -1, + .X_Offset = 0, + .Y_Offset = 3, + .Dvi_yuv = 7, + .ModelString = "Pinnacle Studio PCTV USB (PAL) FM V3", + }, + [PINNA_LINX_VD_IN_CAB_NTSC] = { + .Interface = -1, + .Codec = CODEC_SAA7113, + .VideoChannels = 2, + .VideoNorm = V4L2_STD_NTSC, + .AudioChannels = 1, + .Radio = 0, + .vbi = 1, + .Tuner = 0, + .TunerType = 0, + .Vin_Reg1 = -1, + .Vin_Reg2 = -1, + .X_Offset = 0, + .Y_Offset = 3, + .Dvi_yuv = 7, + .ModelString = "Pinnacle Studio Linx Video input cable (NTSC)", + }, + [PINNA_LINX_VD_IN_CAB_PAL] = { + .Interface = -1, + .Codec = CODEC_SAA7113, + .VideoChannels = 2, + .VideoNorm = V4L2_STD_PAL, + .AudioChannels = 1, + .Radio = 0, + .vbi = 1, + .Tuner = 0, + .TunerType = 0, + .Vin_Reg1 = -1, + .Vin_Reg2 = -1, + .X_Offset = 0, + .Y_Offset = 3, + .Dvi_yuv = 7, + .ModelString = "Pinnacle Studio Linx Video input cable (PAL)", + }, + [PINNA_PCTV_BUNGEE_PAL_FM] = { + .Interface = -1, + .Codec = CODEC_SAA7113, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_PAL, + .AudioChannels = 1, + .Radio = 1, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_TEMIC_4009FR5_PAL, + .Vin_Reg1 = -1, + .Vin_Reg2 = -1, + .X_Offset = 0, + .Y_Offset = 3, + .Dvi_yuv = 7, + .ModelString = "Pinnacle PCTV Bungee USB (PAL) FM", + }, + [HPG_WINTV] = { + .Interface = -1, + .Codec = CODEC_SAA7111, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_NTSC, + .AudioChannels = 1, + .Radio = 0, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_PHILIPS_NTSC_M, + .Vin_Reg1 = -1, + .Vin_Reg2 = -1, + .X_Offset = -1, + .Y_Offset = -1, + .Dvi_yuv = -1, + .ModelString = "Hauppauge WinTv-USB", + }, {} /* Terminating entry */ }; /* Supported Devices */ struct usb_device_id usbvision_table [] = { - { USB_DEVICE(0xfff0, 0xfff0) }, /* Custom Dummy USBVision Device */ - { USB_DEVICE(0x0a6f, 0x0400) }, /* Xanboo */ - { USB_DEVICE(0x050d, 0x0106) }, /* Belkin USB VideoBus II Adapter */ - { USB_DEVICE(0x050d, 0x0207) }, /* Belkin Components USB VideoBus */ - { USB_DEVICE(0x050d, 0x0208) }, /* Belkin USB VideoBus II */ - { USB_DEVICE(0x0571, 0x0002) }, /* echoFX InterView Lite */ - { USB_DEVICE(0x0573, 0x0003) }, /* USBGear USBG-V1 resp. HAMA USB */ - { USB_DEVICE(0x0573, 0x0400) }, /* D-Link V100 */ - { USB_DEVICE(0x0573, 0x2000) }, /* X10 USB Camera */ - { USB_DEVICE(0x0573, 0x2d00) }, /* Hauppauge WinTV USB Live (PAL B/G) */ - { USB_DEVICE(0x0573, 0x2d01) }, /* Hauppauge WinTV USB Live Pro (NTSC M/N) */ - { USB_DEVICE(0x0573, 0x2101) }, /* Zoran Co. PMD (Nogatech) AV-grabber Manhattan */ - { USB_DEVICE(0x0573, 0x4100) }, /* Nogatech USB-TV FM (NTSC) */ - { USB_DEVICE(0x0573, 0x4110) }, /* PNY USB-TV (NTSC) FM */ - { USB_DEVICE(0x0573, 0x4450) }, /* PixelView PlayTv-USB PRO (PAL) FM */ - { USB_DEVICE(0x0573, 0x4550) }, /* ZTV ZT-721 2.4GHz USB A/V Receiver */ - { USB_DEVICE(0x0573, 0x4d00) }, /* Hauppauge WinTV USB (NTSC M/N) */ - { USB_DEVICE(0x0573, 0x4d01) }, /* Hauppauge WinTV USB (PAL B/G) */ - { USB_DEVICE(0x0573, 0x4d02) }, /* Hauppauge WinTV USB (PAL I) */ - { USB_DEVICE(0x0573, 0x4d03) }, /* Hauppauge WinTV USB (PAL/SECAM L) */ - { USB_DEVICE(0x0573, 0x4d04) }, /* Hauppauge WinTV USB (PAL D/K) */ - { USB_DEVICE(0x0573, 0x4d10) }, /* Hauppauge WinTV USB (NTSC FM) */ - { USB_DEVICE(0x0573, 0x4d11) }, /* Hauppauge WinTV USB (PAL B/G FM) */ - { USB_DEVICE(0x0573, 0x4d12) }, /* Hauppauge WinTV USB (PAL I FM) */ - { USB_DEVICE(0x0573, 0x4d14) }, /* Hauppauge WinTV USB (PAL D/K FM) */ - { USB_DEVICE(0x0573, 0x4d2a) }, /* Hauppauge WinTV USB Pro (NTSC M/N) */ - { USB_DEVICE(0x0573, 0x4d2b) }, /* Hauppauge WinTV USB Pro (NTSC M/N) */ - { USB_DEVICE(0x0573, 0x4d2c) }, /* Hauppauge WinTV USB Pro (PAL/SECAM B/G/I/D/K/L) */ - { USB_DEVICE(0x0573, 0x4d20) }, /* Hauppauge WinTV USB Pro (NTSC M/N) */ - { USB_DEVICE(0x0573, 0x4d21) }, /* Hauppauge WinTV USB Pro (PAL B/G) */ - { USB_DEVICE(0x0573, 0x4d22) }, /* Hauppauge WinTV USB Pro (PAL I) */ - { USB_DEVICE(0x0573, 0x4d23) }, /* Hauppauge WinTV USB Pro (PAL/SECAM L) */ - { USB_DEVICE(0x0573, 0x4d24) }, /* Hauppauge WinTV USB Pro (PAL D/K) */ - { USB_DEVICE(0x0573, 0x4d25) }, /* Hauppauge WinTV USB Pro (PAL/SECAM BGDK/I/L) */ - { USB_DEVICE(0x0573, 0x4d26) }, /* Hauppauge WinTV USB Pro (PAL/SECAM BGDK/I/L) */ - { USB_DEVICE(0x0573, 0x4d27) }, /* Hauppauge WinTV USB Pro (PAL B/G) */ - { USB_DEVICE(0x0573, 0x4d28) }, /* Hauppauge WinTV USB Pro (PAL B/G,D/K) */ - { USB_DEVICE(0x0573, 0x4d29) }, /* Hauppauge WinTV USB Pro (PAL I,D/K) */ - { USB_DEVICE(0x0573, 0x4d30) }, /* Hauppauge WinTV USB Pro (NTSC M/N FM) */ - { USB_DEVICE(0x0573, 0x4d31) }, /* Hauppauge WinTV USB Pro (PAL B/G FM) */ - { USB_DEVICE(0x0573, 0x4d32) }, /* Hauppauge WinTV USB Pro (PAL I FM) */ - { USB_DEVICE(0x0573, 0x4d34) }, /* Hauppauge WinTV USB Pro (PAL D/K FM) */ - { USB_DEVICE(0x0573, 0x4d35) }, /* Hauppauge WinTV USB Pro (Temic PAL/SECAM B/G/I/D/K/L FM) */ - { USB_DEVICE(0x0573, 0x4d36) }, /* Hauppauge WinTV USB Pro (Temic PAL B/G FM) */ - { USB_DEVICE(0x0573, 0x4d37) }, /* Hauppauge WinTV USB Pro (PAL/SECAM B/G/I/D/K/L FM) */ - { USB_DEVICE(0x0573, 0x4d38) }, /* Hauppauge WinTV USB Pro (NTSC M/N FM) */ - { USB_DEVICE(0x0768, 0x0006) }, /* Camtel Technology USB TV Genie Pro FM Model TVB330 */ - { USB_DEVICE(0x07d0, 0x0001) }, /* Digital Video Creator I */ - { USB_DEVICE(0x07d0, 0x0002) }, /* Global Village GV-007 (NTSC) */ - { USB_DEVICE(0x07d0, 0x0003) }, /* Dazzle Fusion Model DVC-50 Rev 1 (NTSC) */ - { USB_DEVICE(0x07d0, 0x0004) }, /* Dazzle Fusion Model DVC-80 Rev 1 (PAL) */ - { USB_DEVICE(0x07d0, 0x0005) }, /* Dazzle Fusion Model DVC-90 Rev 1 (SECAM) */ - { USB_DEVICE(0x07f8, 0x9104) }, /* Eskape Labs MyTV2Go */ - { USB_DEVICE(0x2304, 0x010d) }, /* Pinnacle Studio PCTV USB (PAL) */ - { USB_DEVICE(0x2304, 0x0109) }, /* Pinnacle Studio PCTV USB (SECAM) */ - { USB_DEVICE(0x2304, 0x0110) }, /* Pinnacle Studio PCTV USB (PAL) */ - { USB_DEVICE(0x2304, 0x0111) }, /* Miro PCTV USB */ - { USB_DEVICE(0x2304, 0x0112) }, /* Pinnacle Studio PCTV USB (NTSC) with FM radio */ - { USB_DEVICE(0x2304, 0x0210) }, /* Pinnacle Studio PCTV USB (PAL) with FM radio */ - { USB_DEVICE(0x2304, 0x0212) }, /* Pinnacle Studio PCTV USB (NTSC) with FM radio */ - { USB_DEVICE(0x2304, 0x0214) }, /* Pinnacle Studio PCTV USB (PAL) with FM radio */ - { USB_DEVICE(0x2304, 0x0300) }, /* Pinnacle Studio Linx Video input cable (NTSC) */ - { USB_DEVICE(0x2304, 0x0301) }, /* Pinnacle Studio Linx Video input cable (PAL) */ - { USB_DEVICE(0x2304, 0x0419) }, /* Pinnacle PCTV Bungee USB (PAL) FM */ - { USB_DEVICE(0x2400, 0x4200) }, /* Hauppauge WinTv-USB2 Model 42012 */ + { USB_DEVICE(0xfff0, 0xfff0), .driver_info=DUMMY_DEVICE }, + { USB_DEVICE(0x0a6f, 0x0400), .driver_info=XANBOO }, + { USB_DEVICE(0x050d, 0x0106), .driver_info=BELKIN_VIDEOBUS_II }, + { USB_DEVICE(0x050d, 0x0207), .driver_info=BELKIN_VIDEOBUS }, + { USB_DEVICE(0x050d, 0x0208), .driver_info=BELKIN_USB_VIDEOBUS_II }, + { USB_DEVICE(0x0571, 0x0002), .driver_info=ECHOFX_INTERVIEW_LITE }, + { USB_DEVICE(0x0573, 0x0003), .driver_info=USBGEAR_USBG_V1 }, + { USB_DEVICE(0x0573, 0x0400), .driver_info=D_LINK_V100 }, + { USB_DEVICE(0x0573, 0x2000), .driver_info=X10_USB_CAMERA }, + { USB_DEVICE(0x0573, 0x2d00), .driver_info=HPG_WINTV_LIVE_PAL_BG }, + { USB_DEVICE(0x0573, 0x2d01), .driver_info=HPG_WINTV_LIVE_PRO_NTSC_MN }, + { USB_DEVICE(0x0573, 0x2101), .driver_info=ZORAN_PMD_NOGATECH }, + { USB_DEVICE(0x0573, 0x4100), .driver_info=NOGATECH_USB_TV_NTSC_FM }, + { USB_DEVICE(0x0573, 0x4110), .driver_info=PNY_USB_TV_NTSC_FM }, + { USB_DEVICE(0x0573, 0x4450), .driver_info=PV_PLAYTV_USB_PRO_PAL_FM }, + { USB_DEVICE(0x0573, 0x4550), .driver_info=ZT_721 }, + { USB_DEVICE(0x0573, 0x4d00), .driver_info=HPG_WINTV_NTSC_MN }, + { USB_DEVICE(0x0573, 0x4d01), .driver_info=HPG_WINTV_PAL_BG }, + { USB_DEVICE(0x0573, 0x4d02), .driver_info=HPG_WINTV_PAL_I }, + { USB_DEVICE(0x0573, 0x4d03), .driver_info=HPG_WINTV_PAL_SECAM_L }, + { USB_DEVICE(0x0573, 0x4d04), .driver_info=HPG_WINTV_PAL_D_K }, + { USB_DEVICE(0x0573, 0x4d10), .driver_info=HPG_WINTV_NTSC_FM }, + { USB_DEVICE(0x0573, 0x4d11), .driver_info=HPG_WINTV_PAL_BG_FM }, + { USB_DEVICE(0x0573, 0x4d12), .driver_info=HPG_WINTV_PAL_I_FM }, + { USB_DEVICE(0x0573, 0x4d14), .driver_info=HPG_WINTV_PAL_D_K_FM }, + { USB_DEVICE(0x0573, 0x4d2a), .driver_info=HPG_WINTV_PRO_NTSC_MN }, + { USB_DEVICE(0x0573, 0x4d2b), .driver_info=HPG_WINTV_PRO_NTSC_MN_V2 }, + { USB_DEVICE(0x0573, 0x4d2c), .driver_info=HPG_WINTV_PRO_PAL }, + { USB_DEVICE(0x0573, 0x4d20), .driver_info=HPG_WINTV_PRO_NTSC_MN_V3 }, + { USB_DEVICE(0x0573, 0x4d21), .driver_info=HPG_WINTV_PRO_PAL_BG }, + { USB_DEVICE(0x0573, 0x4d22), .driver_info=HPG_WINTV_PRO_PAL_I }, + { USB_DEVICE(0x0573, 0x4d23), .driver_info=HPG_WINTV_PRO_PAL_SECAM_L }, + { USB_DEVICE(0x0573, 0x4d24), .driver_info=HPG_WINTV_PRO_PAL_D_K }, + { USB_DEVICE(0x0573, 0x4d25), .driver_info=HPG_WINTV_PRO_PAL_SECAM }, + { USB_DEVICE(0x0573, 0x4d26), .driver_info=HPG_WINTV_PRO_PAL_SECAM_V2 }, + { USB_DEVICE(0x0573, 0x4d27), .driver_info=HPG_WINTV_PRO_PAL_BG_V2 }, + { USB_DEVICE(0x0573, 0x4d28), .driver_info=HPG_WINTV_PRO_PAL_BG_D_K }, + { USB_DEVICE(0x0573, 0x4d29), .driver_info=HPG_WINTV_PRO_PAL_I_D_K }, + { USB_DEVICE(0x0573, 0x4d30), .driver_info=HPG_WINTV_PRO_NTSC_MN_FM }, + { USB_DEVICE(0x0573, 0x4d31), .driver_info=HPG_WINTV_PRO_PAL_BG_FM }, + { USB_DEVICE(0x0573, 0x4d32), .driver_info=HPG_WINTV_PRO_PAL_I_FM }, + { USB_DEVICE(0x0573, 0x4d34), .driver_info=HPG_WINTV_PRO_PAL_D_K_FM }, + { USB_DEVICE(0x0573, 0x4d35), .driver_info=HPG_WINTV_PRO_TEMIC_PAL_FM }, + { USB_DEVICE(0x0573, 0x4d36), .driver_info=HPG_WINTV_PRO_TEMIC_PAL_BG_FM }, + { USB_DEVICE(0x0573, 0x4d37), .driver_info=HPG_WINTV_PRO_PAL_FM }, + { USB_DEVICE(0x0573, 0x4d38), .driver_info=HPG_WINTV_PRO_NTSC_MN_FM_V2 }, + { USB_DEVICE(0x0768, 0x0006), .driver_info=CAMTEL_TVB330 }, + { USB_DEVICE(0x07d0, 0x0001), .driver_info=DIGITAL_VIDEO_CREATOR_I }, + { USB_DEVICE(0x07d0, 0x0002), .driver_info=GLOBAL_VILLAGE_GV_007_NTSC }, + { USB_DEVICE(0x07d0, 0x0003), .driver_info=DAZZLE_DVC_50_REV_1_NTSC }, + { USB_DEVICE(0x07d0, 0x0004), .driver_info=DAZZLE_DVC_80_REV_1_PAL }, + { USB_DEVICE(0x07d0, 0x0005), .driver_info=DAZZLE_DVC_90_REV_1_SECAM }, + { USB_DEVICE(0x07f8, 0x9104), .driver_info=ESKAPE_LABS_MYTV2GO }, + { USB_DEVICE(0x2304, 0x010d), .driver_info=PINNA_PCTV_USB_PAL }, + { USB_DEVICE(0x2304, 0x0109), .driver_info=PINNA_PCTV_USB_SECAM }, + { USB_DEVICE(0x2304, 0x0110), .driver_info=PINNA_PCTV_USB_PAL_FM }, + { USB_DEVICE(0x2304, 0x0111), .driver_info=MIRO_PCTV_USB }, + { USB_DEVICE(0x2304, 0x0112), .driver_info=PINNA_PCTV_USB_NTSC_FM }, + { USB_DEVICE(0x2304, 0x0210), .driver_info=PINNA_PCTV_USB_PAL_FM_V2 }, + { USB_DEVICE(0x2304, 0x0212), .driver_info=PINNA_PCTV_USB_NTSC_FM_V2 }, + { USB_DEVICE(0x2304, 0x0214), .driver_info=PINNA_PCTV_USB_PAL_FM_V3 }, + { USB_DEVICE(0x2304, 0x0300), .driver_info=PINNA_LINX_VD_IN_CAB_NTSC }, + { USB_DEVICE(0x2304, 0x0301), .driver_info=PINNA_LINX_VD_IN_CAB_PAL }, + { USB_DEVICE(0x2304, 0x0419), .driver_info=PINNA_PCTV_BUNGEE_PAL_FM }, + { USB_DEVICE(0x2400, 0x4200), .driver_info=HPG_WINTV }, { } /* Terminating entry */ }; diff --git a/drivers/media/video/usbvision/usbvision-cards.h b/drivers/media/video/usbvision/usbvision-cards.h new file mode 100644 index 000000000000..37d619b1e038 --- /dev/null +++ b/drivers/media/video/usbvision/usbvision-cards.h @@ -0,0 +1,65 @@ +#define DUMMY_DEVICE 0 +#define XANBOO 1 +#define BELKIN_VIDEOBUS_II 2 +#define BELKIN_VIDEOBUS 3 +#define BELKIN_USB_VIDEOBUS_II 4 +#define ECHOFX_INTERVIEW_LITE 5 +#define USBGEAR_USBG_V1 6 +#define D_LINK_V100 7 +#define X10_USB_CAMERA 8 +#define HPG_WINTV_LIVE_PAL_BG 9 +#define HPG_WINTV_LIVE_PRO_NTSC_MN 10 +#define ZORAN_PMD_NOGATECH 11 +#define NOGATECH_USB_TV_NTSC_FM 12 +#define PNY_USB_TV_NTSC_FM 13 +#define PV_PLAYTV_USB_PRO_PAL_FM 14 +#define ZT_721 15 +#define HPG_WINTV_NTSC_MN 16 +#define HPG_WINTV_PAL_BG 17 +#define HPG_WINTV_PAL_I 18 +#define HPG_WINTV_PAL_SECAM_L 19 +#define HPG_WINTV_PAL_D_K 20 +#define HPG_WINTV_NTSC_FM 21 +#define HPG_WINTV_PAL_BG_FM 22 +#define HPG_WINTV_PAL_I_FM 23 +#define HPG_WINTV_PAL_D_K_FM 24 +#define HPG_WINTV_PRO_NTSC_MN 25 +#define HPG_WINTV_PRO_NTSC_MN_V2 26 +#define HPG_WINTV_PRO_PAL 27 +#define HPG_WINTV_PRO_NTSC_MN_V3 28 +#define HPG_WINTV_PRO_PAL_BG 29 +#define HPG_WINTV_PRO_PAL_I 30 +#define HPG_WINTV_PRO_PAL_SECAM_L 31 +#define HPG_WINTV_PRO_PAL_D_K 32 +#define HPG_WINTV_PRO_PAL_SECAM 33 +#define HPG_WINTV_PRO_PAL_SECAM_V2 34 +#define HPG_WINTV_PRO_PAL_BG_V2 35 +#define HPG_WINTV_PRO_PAL_BG_D_K 36 +#define HPG_WINTV_PRO_PAL_I_D_K 37 +#define HPG_WINTV_PRO_NTSC_MN_FM 38 +#define HPG_WINTV_PRO_PAL_BG_FM 39 +#define HPG_WINTV_PRO_PAL_I_FM 40 +#define HPG_WINTV_PRO_PAL_D_K_FM 41 +#define HPG_WINTV_PRO_TEMIC_PAL_FM 42 +#define HPG_WINTV_PRO_TEMIC_PAL_BG_FM 43 +#define HPG_WINTV_PRO_PAL_FM 44 +#define HPG_WINTV_PRO_NTSC_MN_FM_V2 45 +#define CAMTEL_TVB330 46 +#define DIGITAL_VIDEO_CREATOR_I 47 +#define GLOBAL_VILLAGE_GV_007_NTSC 48 +#define DAZZLE_DVC_50_REV_1_NTSC 49 +#define DAZZLE_DVC_80_REV_1_PAL 50 +#define DAZZLE_DVC_90_REV_1_SECAM 51 +#define ESKAPE_LABS_MYTV2GO 52 +#define PINNA_PCTV_USB_PAL 53 +#define PINNA_PCTV_USB_SECAM 54 +#define PINNA_PCTV_USB_PAL_FM 55 +#define MIRO_PCTV_USB 56 +#define PINNA_PCTV_USB_NTSC_FM 57 +#define PINNA_PCTV_USB_PAL_FM_V2 58 +#define PINNA_PCTV_USB_NTSC_FM_V2 59 +#define PINNA_PCTV_USB_PAL_FM_V3 60 +#define PINNA_LINX_VD_IN_CAB_NTSC 61 +#define PINNA_LINX_VD_IN_CAB_PAL 62 +#define PINNA_PCTV_BUNGEE_PAL_FM 63 +#define HPG_WINTV 64 diff --git a/drivers/media/video/usbvision/usbvision-video.c b/drivers/media/video/usbvision/usbvision-video.c index 6fc14557d623..22c2889d3b5c 100644 --- a/drivers/media/video/usbvision/usbvision-video.c +++ b/drivers/media/video/usbvision/usbvision-video.c @@ -76,6 +76,7 @@ #endif #include "usbvision.h" +#include "usbvision-cards.h" #define DRIVER_AUTHOR "Joerg Heckenbach , Dwaine Garden " #define DRIVER_NAME "usbvision" @@ -1775,7 +1776,8 @@ static void usbvision_configure_video(struct usb_usbvision *usbvision) * if it looks like USBVISION video device * */ -static int __devinit usbvision_probe(struct usb_interface *intf, const struct usb_device_id *devid) +static int __devinit usbvision_probe(struct usb_interface *intf, + const struct usb_device_id *devid) { struct usb_device *dev = usb_get_dev(interface_to_usbdev(intf)); struct usb_interface *uif; @@ -1786,25 +1788,13 @@ static int __devinit usbvision_probe(struct usb_interface *intf, const struct us int model,i; PDEBUG(DBG_PROBE, "VID=%#04x, PID=%#04x, ifnum=%u", - dev->descriptor.idVendor, dev->descriptor.idProduct, ifnum); + dev->descriptor.idVendor, + dev->descriptor.idProduct, ifnum); - /* Is it an USBVISION video dev? */ - model = 0; - for(model = 0; usbvision_device_data[model].idVendor; model++) { - if (le16_to_cpu(dev->descriptor.idVendor) != usbvision_device_data[model].idVendor) { - continue; - } - if (le16_to_cpu(dev->descriptor.idProduct) != usbvision_device_data[model].idProduct) { - continue; - } + model = devid->driver_info; + printk(KERN_INFO "%s: %s found\n", __FUNCTION__, + usbvision_device_data[model].ModelString); - printk(KERN_INFO "%s: %s found\n", __FUNCTION__, usbvision_device_data[model].ModelString); - break; - } - - if (usbvision_device_data[model].idVendor == 0) { - return -ENODEV; //no matching device - } if (usbvision_device_data[model].Interface >= 0) { interface = &dev->actconfig->interface[usbvision_device_data[model].Interface]->altsetting[0]; } @@ -1828,10 +1818,11 @@ static int __devinit usbvision_probe(struct usb_interface *intf, const struct us err("%s: couldn't allocate USBVision struct", __FUNCTION__); return -ENOMEM; } + if (dev->descriptor.bNumConfigurations > 1) { usbvision->bridgeType = BRIDGE_NT1004; } - else if (usbvision_device_data[model].ModelString == "Dazzle Fusion Model DVC-90 Rev 1 (SECAM)") { + else if (model == DAZZLE_DVC_90_REV_1_SECAM) { usbvision->bridgeType = BRIDGE_NT1005; } else { @@ -1958,6 +1949,8 @@ static struct usb_driver usbvision_driver = { */ static void customdevice_process(void) { + unsigned int id_vend,id_prod; + usbvision_device_data[0]=usbvision_device_data[1]; usbvision_table[0]=usbvision_table[1]; @@ -1965,7 +1958,7 @@ static void customdevice_process(void) { char *parse=CustomDevice; - PDEBUG(DBG_PROBE, "CustomDevide=%s", CustomDevice); + PDEBUG(DBG_PROBE, "CustomDevice=%s", CustomDevice); /*format is CustomDevice="0x0573 0x4D31 0 7113 3 PAL 1 1 1 5 -1 -1 -1 -1 -1" usbvision_device_data[0].idVendor; @@ -1990,13 +1983,16 @@ static void customdevice_process(void) usbvision_device_data[0].ModelString="USBVISION Custom Device"; parse+=2; - sscanf(parse,"%x",&usbvision_device_data[0].idVendor); + sscanf(parse,"%u",&id_vend); + usbvision_table[0].idVendor=id_vend; + goto2next(parse); - PDEBUG(DBG_PROBE, "idVendor=0x%.4X", usbvision_device_data[0].idVendor); + PDEBUG(DBG_PROBE, "idVendor=0x%.4X", usbvision_table[0].idVendor); parse+=2; - sscanf(parse,"%x",&usbvision_device_data[0].idProduct); + sscanf(parse,"%u",&id_prod); + usbvision_table[0].idProduct=id_prod; goto2next(parse); - PDEBUG(DBG_PROBE, "idProduct=0x%.4X", usbvision_device_data[0].idProduct); + PDEBUG(DBG_PROBE, "idProduct=0x%.4X", usbvision_table[0].idProduct); sscanf(parse,"%d",&usbvision_device_data[0].Interface); goto2next(parse); PDEBUG(DBG_PROBE, "Interface=%d", usbvision_device_data[0].Interface); @@ -2060,9 +2056,6 @@ static void customdevice_process(void) //add to usbvision_table also usbvision_table[0].match_flags=USB_DEVICE_ID_MATCH_DEVICE; - usbvision_table[0].idVendor=usbvision_device_data[0].idVendor; - usbvision_table[0].idProduct=usbvision_device_data[0].idProduct; - } } diff --git a/drivers/media/video/usbvision/usbvision.h b/drivers/media/video/usbvision/usbvision.h index ad6afd3e42a4..4639f3485e76 100644 --- a/drivers/media/video/usbvision/usbvision.h +++ b/drivers/media/video/usbvision/usbvision.h @@ -342,8 +342,6 @@ struct usbvision_frame { #define BRIDGE_NT1005 1005 struct usbvision_device_data_st { - int idVendor; - int idProduct; int Interface; /* to handle special interface number like BELKIN and Hauppauge WinTV-USB II */ int Codec; int VideoChannels; -- cgit v1.2.3 From ec709bb801a98dcac0a95c060c431eda73e31587 Mon Sep 17 00:00:00 2001 From: Thierry MERLE Date: Tue, 17 Apr 2007 02:28:32 -0300 Subject: V4L/DVB (5530): Usbvision: remove CustomDevice facility usbvision has a module parameter that ables the user to add a new USB entry at driver load. This functionality is useless by experience (adding statically the entry is easy). Furthermore, the USB_DEVICE(0xfff0, 0xfff0) USB entry caused usbvision_probe to be called for all unclaimed devices. Signed-off-by: Thierry MERLE Acked-by: Dwaine Garden Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/CARDLIST.usbvision | 129 +++++++++++---------- drivers/media/video/usbvision/usbvision-cards.c | 15 --- drivers/media/video/usbvision/usbvision-cards.h | 129 +++++++++++---------- drivers/media/video/usbvision/usbvision-video.c | 143 ------------------------ 4 files changed, 128 insertions(+), 288 deletions(-) (limited to 'Documentation') diff --git a/Documentation/video4linux/CARDLIST.usbvision b/Documentation/video4linux/CARDLIST.usbvision index ab110b7b5b9e..3d6850ef0245 100644 --- a/Documentation/video4linux/CARDLIST.usbvision +++ b/Documentation/video4linux/CARDLIST.usbvision @@ -1,65 +1,64 @@ - 0 -> Custom Dummy USBVision Device [fff0:fff0] - 1 -> Xanboo [0a6f:0400] - 2 -> Belkin USB VideoBus II Adapter [050d:0106] - 3 -> Belkin Components USB VideoBus [050d:0207] - 4 -> Belkin USB VideoBus II [050d:0208] - 5 -> echoFX InterView Lite [0571:0002] - 6 -> USBGear USBG-V1 resp. HAMA USB [0573:0003] - 7 -> D-Link V100 [0573:0400] - 8 -> X10 USB Camera [0573:2000] - 9 -> Hauppauge WinTV USB Live (PAL B/G) [0573:2d00] - 10 -> Hauppauge WinTV USB Live Pro (NTSC M/N) [0573:2d01] - 11 -> Zoran Co. PMD (Nogatech) AV-grabber Manhattan [0573:2101] - 12 -> Nogatech USB-TV (NTSC) FM [0573:4100] - 13 -> PNY USB-TV (NTSC) FM [0573:4110] - 14 -> PixelView PlayTv-USB PRO (PAL) FM [0573:4450] - 15 -> ZTV ZT-721 2.4GHz USB A/V Receiver [0573:4550] - 16 -> Hauppauge WinTV USB (NTSC M/N) [0573:4d00] - 17 -> Hauppauge WinTV USB (PAL B/G) [0573:4d01] - 18 -> Hauppauge WinTV USB (PAL I) [0573:4d02] - 19 -> Hauppauge WinTV USB (PAL/SECAM L) [0573:4d03] - 20 -> Hauppauge WinTV USB (PAL D/K) [0573:4d04] - 21 -> Hauppauge WinTV USB (NTSC FM) [0573:4d10] - 22 -> Hauppauge WinTV USB (PAL B/G FM) [0573:4d11] - 23 -> Hauppauge WinTV USB (PAL I FM) [0573:4d12] - 24 -> Hauppauge WinTV USB (PAL D/K FM) [0573:4d14] - 25 -> Hauppauge WinTV USB Pro (NTSC M/N) [0573:4d2a] - 26 -> Hauppauge WinTV USB Pro (NTSC M/N) V2 [0573:4d2b] - 27 -> Hauppauge WinTV USB Pro (PAL/SECAM B/G/I/D/K/L) [0573:4d2c] - 28 -> Hauppauge WinTV USB Pro (NTSC M/N) V3 [0573:4d20] - 29 -> Hauppauge WinTV USB Pro (PAL B/G) [0573:4d21] - 30 -> Hauppauge WinTV USB Pro (PAL I) [0573:4d22] - 31 -> Hauppauge WinTV USB Pro (PAL/SECAM L) [0573:4d23] - 32 -> Hauppauge WinTV USB Pro (PAL D/K) [0573:4d24] - 33 -> Hauppauge WinTV USB Pro (PAL/SECAM BGDK/I/L) [0573:4d25] - 34 -> Hauppauge WinTV USB Pro (PAL/SECAM BGDK/I/L) V2 [0573:4d26] - 35 -> Hauppauge WinTV USB Pro (PAL B/G) V2 [0573:4d27] - 36 -> Hauppauge WinTV USB Pro (PAL B/G,D/K) [0573:4d28] - 37 -> Hauppauge WinTV USB Pro (PAL I,D/K) [0573:4d29] - 38 -> Hauppauge WinTV USB Pro (NTSC M/N FM) [0573:4d30] - 39 -> Hauppauge WinTV USB Pro (PAL B/G FM) [0573:4d31] - 40 -> Hauppauge WinTV USB Pro (PAL I FM) [0573:4d32] - 41 -> Hauppauge WinTV USB Pro (PAL D/K FM) [0573:4d34] - 42 -> Hauppauge WinTV USB Pro (Temic PAL/SECAM B/G/I/D/K/L FM) [0573:4d35] - 43 -> Hauppauge WinTV USB Pro (Temic PAL B/G FM) [0573:4d36] - 44 -> Hauppauge WinTV USB Pro (PAL/SECAM B/G/I/D/K/L FM) [0573:4d37] - 45 -> Hauppauge WinTV USB Pro (NTSC M/N FM) V2 [0573:4d38] - 46 -> Camtel Technology USB TV Genie Pro FM Model TVB330 [0768:0006] - 47 -> Digital Video Creator I [07d0:0001] - 48 -> Global Village GV-007 (NTSC) [07d0:0002] - 49 -> Dazzle Fusion Model DVC-50 Rev 1 (NTSC) [07d0:0003] - 50 -> Dazzle Fusion Model DVC-80 Rev 1 (PAL) [07d0:0004] - 51 -> Dazzle Fusion Model DVC-90 Rev 1 (SECAM) [07d0:0005] - 52 -> Eskape Labs MyTV2Go [07f8:9104] - 53 -> Pinnacle Studio PCTV USB (PAL) [2304:010d] - 54 -> Pinnacle Studio PCTV USB (SECAM) [2304:0109] - 55 -> Pinnacle Studio PCTV USB (PAL) FM [2304:0110] - 56 -> Miro PCTV USB [2304:0111] - 57 -> Pinnacle Studio PCTV USB (NTSC) FM [2304:0112] - 58 -> Pinnacle Studio PCTV USB (PAL) FM V2 [2304:0210] - 59 -> Pinnacle Studio PCTV USB (NTSC) FM V2 [2304:0212] - 60 -> Pinnacle Studio PCTV USB (PAL) FM V3 [2304:0214] - 61 -> Pinnacle Studio Linx Video input cable (NTSC) [2304:0300] - 62 -> Pinnacle Studio Linx Video input cable (PAL) [2304:0301] - 63 -> Pinnacle PCTV Bungee USB (PAL) FM [2304:0419] - 64 -> Hauppauge WinTv-USB [2400:4200] + 0 -> Xanboo [0a6f:0400] + 1 -> Belkin USB VideoBus II Adapter [050d:0106] + 2 -> Belkin Components USB VideoBus [050d:0207] + 3 -> Belkin USB VideoBus II [050d:0208] + 4 -> echoFX InterView Lite [0571:0002] + 5 -> USBGear USBG-V1 resp. HAMA USB [0573:0003] + 6 -> D-Link V100 [0573:0400] + 7 -> X10 USB Camera [0573:2000] + 8 -> Hauppauge WinTV USB Live (PAL B/G) [0573:2d00] + 9 -> Hauppauge WinTV USB Live Pro (NTSC M/N) [0573:2d01] + 10 -> Zoran Co. PMD (Nogatech) AV-grabber Manhattan [0573:2101] + 11 -> Nogatech USB-TV (NTSC) FM [0573:4100] + 12 -> PNY USB-TV (NTSC) FM [0573:4110] + 13 -> PixelView PlayTv-USB PRO (PAL) FM [0573:4450] + 14 -> ZTV ZT-721 2.4GHz USB A/V Receiver [0573:4550] + 15 -> Hauppauge WinTV USB (NTSC M/N) [0573:4d00] + 16 -> Hauppauge WinTV USB (PAL B/G) [0573:4d01] + 17 -> Hauppauge WinTV USB (PAL I) [0573:4d02] + 18 -> Hauppauge WinTV USB (PAL/SECAM L) [0573:4d03] + 19 -> Hauppauge WinTV USB (PAL D/K) [0573:4d04] + 20 -> Hauppauge WinTV USB (NTSC FM) [0573:4d10] + 21 -> Hauppauge WinTV USB (PAL B/G FM) [0573:4d11] + 22 -> Hauppauge WinTV USB (PAL I FM) [0573:4d12] + 23 -> Hauppauge WinTV USB (PAL D/K FM) [0573:4d14] + 24 -> Hauppauge WinTV USB Pro (NTSC M/N) [0573:4d2a] + 25 -> Hauppauge WinTV USB Pro (NTSC M/N) V2 [0573:4d2b] + 26 -> Hauppauge WinTV USB Pro (PAL/SECAM B/G/I/D/K/L) [0573:4d2c] + 27 -> Hauppauge WinTV USB Pro (NTSC M/N) V3 [0573:4d20] + 28 -> Hauppauge WinTV USB Pro (PAL B/G) [0573:4d21] + 29 -> Hauppauge WinTV USB Pro (PAL I) [0573:4d22] + 30 -> Hauppauge WinTV USB Pro (PAL/SECAM L) [0573:4d23] + 31 -> Hauppauge WinTV USB Pro (PAL D/K) [0573:4d24] + 32 -> Hauppauge WinTV USB Pro (PAL/SECAM BGDK/I/L) [0573:4d25] + 33 -> Hauppauge WinTV USB Pro (PAL/SECAM BGDK/I/L) V2 [0573:4d26] + 34 -> Hauppauge WinTV USB Pro (PAL B/G) V2 [0573:4d27] + 35 -> Hauppauge WinTV USB Pro (PAL B/G,D/K) [0573:4d28] + 36 -> Hauppauge WinTV USB Pro (PAL I,D/K) [0573:4d29] + 37 -> Hauppauge WinTV USB Pro (NTSC M/N FM) [0573:4d30] + 38 -> Hauppauge WinTV USB Pro (PAL B/G FM) [0573:4d31] + 39 -> Hauppauge WinTV USB Pro (PAL I FM) [0573:4d32] + 40 -> Hauppauge WinTV USB Pro (PAL D/K FM) [0573:4d34] + 41 -> Hauppauge WinTV USB Pro (Temic PAL/SECAM B/G/I/D/K/L FM) [0573:4d35] + 42 -> Hauppauge WinTV USB Pro (Temic PAL B/G FM) [0573:4d36] + 43 -> Hauppauge WinTV USB Pro (PAL/SECAM B/G/I/D/K/L FM) [0573:4d37] + 44 -> Hauppauge WinTV USB Pro (NTSC M/N FM) V2 [0573:4d38] + 45 -> Camtel Technology USB TV Genie Pro FM Model TVB330 [0768:0006] + 46 -> Digital Video Creator I [07d0:0001] + 47 -> Global Village GV-007 (NTSC) [07d0:0002] + 48 -> Dazzle Fusion Model DVC-50 Rev 1 (NTSC) [07d0:0003] + 49 -> Dazzle Fusion Model DVC-80 Rev 1 (PAL) [07d0:0004] + 50 -> Dazzle Fusion Model DVC-90 Rev 1 (SECAM) [07d0:0005] + 51 -> Eskape Labs MyTV2Go [07f8:9104] + 52 -> Pinnacle Studio PCTV USB (PAL) [2304:010d] + 53 -> Pinnacle Studio PCTV USB (SECAM) [2304:0109] + 54 -> Pinnacle Studio PCTV USB (PAL) FM [2304:0110] + 55 -> Miro PCTV USB [2304:0111] + 56 -> Pinnacle Studio PCTV USB (NTSC) FM [2304:0112] + 57 -> Pinnacle Studio PCTV USB (PAL) FM V2 [2304:0210] + 58 -> Pinnacle Studio PCTV USB (NTSC) FM V2 [2304:0212] + 59 -> Pinnacle Studio PCTV USB (PAL) FM V3 [2304:0214] + 60 -> Pinnacle Studio Linx Video input cable (NTSC) [2304:0300] + 61 -> Pinnacle Studio Linx Video input cable (PAL) [2304:0301] + 62 -> Pinnacle PCTV Bungee USB (PAL) FM [2304:0419] + 63 -> Hauppauge WinTv-USB [2400:4200] diff --git a/drivers/media/video/usbvision/usbvision-cards.c b/drivers/media/video/usbvision/usbvision-cards.c index edee094cf15d..13f69fe6360d 100644 --- a/drivers/media/video/usbvision/usbvision-cards.c +++ b/drivers/media/video/usbvision/usbvision-cards.c @@ -32,20 +32,6 @@ /* Supported Devices: A table for usbvision.c*/ struct usbvision_device_data_st usbvision_device_data[] = { - [DUMMY_DEVICE] = { - .Interface = -1, - .Codec = CODEC_SAA7111, - .VideoChannels = 3, - .VideoNorm = V4L2_STD_NTSC, - .AudioChannels = 1, - .Radio = 1, - .vbi = 1, - .Tuner = 1, - .TunerType = TUNER_PHILIPS_NTSC_M, - .X_Offset = -1, - .Y_Offset = -1, - .ModelString = "Custom Dummy USBVision Device", - }, [XANBOO] = { .Interface = -1, .Codec = CODEC_SAA7113, @@ -1032,7 +1018,6 @@ const int usbvision_device_data_size=ARRAY_SIZE(usbvision_device_data); /* Supported Devices */ struct usb_device_id usbvision_table [] = { - { USB_DEVICE(0xfff0, 0xfff0), .driver_info=DUMMY_DEVICE }, { USB_DEVICE(0x0a6f, 0x0400), .driver_info=XANBOO }, { USB_DEVICE(0x050d, 0x0106), .driver_info=BELKIN_VIDEOBUS_II }, { USB_DEVICE(0x050d, 0x0207), .driver_info=BELKIN_VIDEOBUS }, diff --git a/drivers/media/video/usbvision/usbvision-cards.h b/drivers/media/video/usbvision/usbvision-cards.h index 766accc927ef..512c5cee4145 100644 --- a/drivers/media/video/usbvision/usbvision-cards.h +++ b/drivers/media/video/usbvision/usbvision-cards.h @@ -1,67 +1,66 @@ -#define DUMMY_DEVICE 0 -#define XANBOO 1 -#define BELKIN_VIDEOBUS_II 2 -#define BELKIN_VIDEOBUS 3 -#define BELKIN_USB_VIDEOBUS_II 4 -#define ECHOFX_INTERVIEW_LITE 5 -#define USBGEAR_USBG_V1 6 -#define D_LINK_V100 7 -#define X10_USB_CAMERA 8 -#define HPG_WINTV_LIVE_PAL_BG 9 -#define HPG_WINTV_LIVE_PRO_NTSC_MN 10 -#define ZORAN_PMD_NOGATECH 11 -#define NOGATECH_USB_TV_NTSC_FM 12 -#define PNY_USB_TV_NTSC_FM 13 -#define PV_PLAYTV_USB_PRO_PAL_FM 14 -#define ZT_721 15 -#define HPG_WINTV_NTSC_MN 16 -#define HPG_WINTV_PAL_BG 17 -#define HPG_WINTV_PAL_I 18 -#define HPG_WINTV_PAL_SECAM_L 19 -#define HPG_WINTV_PAL_D_K 20 -#define HPG_WINTV_NTSC_FM 21 -#define HPG_WINTV_PAL_BG_FM 22 -#define HPG_WINTV_PAL_I_FM 23 -#define HPG_WINTV_PAL_D_K_FM 24 -#define HPG_WINTV_PRO_NTSC_MN 25 -#define HPG_WINTV_PRO_NTSC_MN_V2 26 -#define HPG_WINTV_PRO_PAL 27 -#define HPG_WINTV_PRO_NTSC_MN_V3 28 -#define HPG_WINTV_PRO_PAL_BG 29 -#define HPG_WINTV_PRO_PAL_I 30 -#define HPG_WINTV_PRO_PAL_SECAM_L 31 -#define HPG_WINTV_PRO_PAL_D_K 32 -#define HPG_WINTV_PRO_PAL_SECAM 33 -#define HPG_WINTV_PRO_PAL_SECAM_V2 34 -#define HPG_WINTV_PRO_PAL_BG_V2 35 -#define HPG_WINTV_PRO_PAL_BG_D_K 36 -#define HPG_WINTV_PRO_PAL_I_D_K 37 -#define HPG_WINTV_PRO_NTSC_MN_FM 38 -#define HPG_WINTV_PRO_PAL_BG_FM 39 -#define HPG_WINTV_PRO_PAL_I_FM 40 -#define HPG_WINTV_PRO_PAL_D_K_FM 41 -#define HPG_WINTV_PRO_TEMIC_PAL_FM 42 -#define HPG_WINTV_PRO_TEMIC_PAL_BG_FM 43 -#define HPG_WINTV_PRO_PAL_FM 44 -#define HPG_WINTV_PRO_NTSC_MN_FM_V2 45 -#define CAMTEL_TVB330 46 -#define DIGITAL_VIDEO_CREATOR_I 47 -#define GLOBAL_VILLAGE_GV_007_NTSC 48 -#define DAZZLE_DVC_50_REV_1_NTSC 49 -#define DAZZLE_DVC_80_REV_1_PAL 50 -#define DAZZLE_DVC_90_REV_1_SECAM 51 -#define ESKAPE_LABS_MYTV2GO 52 -#define PINNA_PCTV_USB_PAL 53 -#define PINNA_PCTV_USB_SECAM 54 -#define PINNA_PCTV_USB_PAL_FM 55 -#define MIRO_PCTV_USB 56 -#define PINNA_PCTV_USB_NTSC_FM 57 -#define PINNA_PCTV_USB_PAL_FM_V2 58 -#define PINNA_PCTV_USB_NTSC_FM_V2 59 -#define PINNA_PCTV_USB_PAL_FM_V3 60 -#define PINNA_LINX_VD_IN_CAB_NTSC 61 -#define PINNA_LINX_VD_IN_CAB_PAL 62 -#define PINNA_PCTV_BUNGEE_PAL_FM 63 -#define HPG_WINTV 64 +#define XANBOO 0 +#define BELKIN_VIDEOBUS_II 1 +#define BELKIN_VIDEOBUS 2 +#define BELKIN_USB_VIDEOBUS_II 3 +#define ECHOFX_INTERVIEW_LITE 4 +#define USBGEAR_USBG_V1 5 +#define D_LINK_V100 6 +#define X10_USB_CAMERA 7 +#define HPG_WINTV_LIVE_PAL_BG 8 +#define HPG_WINTV_LIVE_PRO_NTSC_MN 9 +#define ZORAN_PMD_NOGATECH 10 +#define NOGATECH_USB_TV_NTSC_FM 11 +#define PNY_USB_TV_NTSC_FM 12 +#define PV_PLAYTV_USB_PRO_PAL_FM 13 +#define ZT_721 14 +#define HPG_WINTV_NTSC_MN 15 +#define HPG_WINTV_PAL_BG 16 +#define HPG_WINTV_PAL_I 17 +#define HPG_WINTV_PAL_SECAM_L 18 +#define HPG_WINTV_PAL_D_K 19 +#define HPG_WINTV_NTSC_FM 20 +#define HPG_WINTV_PAL_BG_FM 21 +#define HPG_WINTV_PAL_I_FM 22 +#define HPG_WINTV_PAL_D_K_FM 23 +#define HPG_WINTV_PRO_NTSC_MN 24 +#define HPG_WINTV_PRO_NTSC_MN_V2 25 +#define HPG_WINTV_PRO_PAL 26 +#define HPG_WINTV_PRO_NTSC_MN_V3 27 +#define HPG_WINTV_PRO_PAL_BG 28 +#define HPG_WINTV_PRO_PAL_I 29 +#define HPG_WINTV_PRO_PAL_SECAM_L 30 +#define HPG_WINTV_PRO_PAL_D_K 31 +#define HPG_WINTV_PRO_PAL_SECAM 32 +#define HPG_WINTV_PRO_PAL_SECAM_V2 33 +#define HPG_WINTV_PRO_PAL_BG_V2 34 +#define HPG_WINTV_PRO_PAL_BG_D_K 35 +#define HPG_WINTV_PRO_PAL_I_D_K 36 +#define HPG_WINTV_PRO_NTSC_MN_FM 37 +#define HPG_WINTV_PRO_PAL_BG_FM 38 +#define HPG_WINTV_PRO_PAL_I_FM 39 +#define HPG_WINTV_PRO_PAL_D_K_FM 40 +#define HPG_WINTV_PRO_TEMIC_PAL_FM 41 +#define HPG_WINTV_PRO_TEMIC_PAL_BG_FM 42 +#define HPG_WINTV_PRO_PAL_FM 43 +#define HPG_WINTV_PRO_NTSC_MN_FM_V2 44 +#define CAMTEL_TVB330 45 +#define DIGITAL_VIDEO_CREATOR_I 46 +#define GLOBAL_VILLAGE_GV_007_NTSC 47 +#define DAZZLE_DVC_50_REV_1_NTSC 48 +#define DAZZLE_DVC_80_REV_1_PAL 49 +#define DAZZLE_DVC_90_REV_1_SECAM 50 +#define ESKAPE_LABS_MYTV2GO 51 +#define PINNA_PCTV_USB_PAL 52 +#define PINNA_PCTV_USB_SECAM 53 +#define PINNA_PCTV_USB_PAL_FM 54 +#define MIRO_PCTV_USB 55 +#define PINNA_PCTV_USB_NTSC_FM 56 +#define PINNA_PCTV_USB_PAL_FM_V2 57 +#define PINNA_PCTV_USB_NTSC_FM_V2 58 +#define PINNA_PCTV_USB_PAL_FM_V3 59 +#define PINNA_LINX_VD_IN_CAB_NTSC 60 +#define PINNA_LINX_VD_IN_CAB_PAL 61 +#define PINNA_PCTV_BUNGEE_PAL_FM 62 +#define HPG_WINTV 63 extern const int usbvision_device_data_size; diff --git a/drivers/media/video/usbvision/usbvision-video.c b/drivers/media/video/usbvision/usbvision-video.c index d9e517d497f7..216704170a4c 100644 --- a/drivers/media/video/usbvision/usbvision-video.c +++ b/drivers/media/video/usbvision/usbvision-video.c @@ -151,7 +151,6 @@ static int PowerOnAtOpen = 1; // Set the default device to power on at startu static int video_nr = -1; // Sequential Number of Video Device static int radio_nr = -1; // Sequential Number of Radio Device static int vbi_nr = -1; // Sequential Number of VBI Device -static char *CustomDevice=NULL; // Set as nothing.... // Grab parameters for the device driver @@ -162,7 +161,6 @@ module_param(PowerOnAtOpen, int, 0444); module_param(video_nr, int, 0444); module_param(radio_nr, int, 0444); module_param(vbi_nr, int, 0444); -module_param(CustomDevice, charp, 0444); #else // Old Style MODULE_PARAM(isocMode, "i"); MODULE_PARM(video_debug, "i"); // Grab the Debug Mode of the device driver @@ -172,7 +170,6 @@ MODULE_PARM(SwitchSVideoInput, "i"); // To help people with Black and White ou MODULE_PARM(video_nr, "i"); // video_nr option allows to specify a certain /dev/videoX device (like /dev/video0 or /dev/video1 ...) MODULE_PARM(radio_nr, "i"); // radio_nr option allows to specify a certain /dev/radioX device (like /dev/radio0 or /dev/radio1 ...) MODULE_PARM(vbi_nr, "i"); // vbi_nr option allows to specify a certain /dev/vbiX device (like /dev/vbi0 or /dev/vbi1 ...) -MODULE_PARM(CustomDevice, "s"); // .... CustomDevice #endif MODULE_PARM_DESC(isocMode, " Set the default format for ISOC endpoint. Default: 0x60 (Compression On)"); @@ -181,7 +178,6 @@ MODULE_PARM_DESC(PowerOnAtOpen, " Set the default device to power on when device MODULE_PARM_DESC(video_nr, "Set video device number (/dev/videoX). Default: -1 (autodetect)"); MODULE_PARM_DESC(radio_nr, "Set radio device number (/dev/radioX). Default: -1 (autodetect)"); MODULE_PARM_DESC(vbi_nr, "Set vbi device number (/dev/vbiX). Default: -1 (autodetect)"); -MODULE_PARM_DESC(CustomDevice, " Define the fine tuning parameters for the device. Default: null"); // Misc stuff @@ -1943,143 +1939,6 @@ static struct usb_driver usbvision_driver = { .disconnect = usbvision_disconnect }; -/* - * customdevice_process() - * - * This procedure preprocesses CustomDevice parameter if any - * - */ -static void customdevice_process(void) -{ - unsigned int id_vend,id_prod,radio,tuner; - - usbvision_device_data[0]=usbvision_device_data[1]; - usbvision_table[0]=usbvision_table[1]; - - if(CustomDevice) - { - char *parse=CustomDevice; - int tmp; - - PDEBUG(DBG_PROBE, "CustomDevice=%s", CustomDevice); - - /*format is CustomDevice="0x0573 0x4D31 0 7113 3 PAL 1 1 1 5 -1 -1 -1 -1 -1" - usbvision_device_data[0].idVendor; - usbvision_device_data[0].idProduct; - usbvision_device_data[0].Interface; - usbvision_device_data[0].Codec; - usbvision_device_data[0].VideoChannels; - usbvision_device_data[0].VideoNorm; - usbvision_device_data[0].AudioChannels; - usbvision_device_data[0].Radio; - usbvision_device_data[0].Tuner; - usbvision_device_data[0].TunerType; - usbvision_device_data[0].Vin_Reg1; - usbvision_device_data[0].Vin_Reg2; - usbvision_device_data[0].X_Offset; - usbvision_device_data[0].Y_Offset; - usbvision_device_data[0].Dvi_yuv; - usbvision_device_data[0].ModelString; - */ - - rmspace(parse); - usbvision_device_data[0].ModelString="USBVISION Custom Device"; - - parse+=2; - sscanf(parse,"%u",&id_vend); - usbvision_table[0].idVendor=id_vend; - - goto2next(parse); - PDEBUG(DBG_PROBE, "idVendor=0x%.4X", usbvision_table[0].idVendor); - parse+=2; - sscanf(parse,"%u",&id_prod); - usbvision_table[0].idProduct=id_prod; - goto2next(parse); - PDEBUG(DBG_PROBE, "idProduct=0x%.4X", usbvision_table[0].idProduct); - sscanf(parse,"%d",&usbvision_device_data[0].Interface); - goto2next(parse); - PDEBUG(DBG_PROBE, "Interface=%d", usbvision_device_data[0].Interface); - sscanf(parse,"%hd",&usbvision_device_data[0].Codec); - goto2next(parse); - PDEBUG(DBG_PROBE, "Codec=%d", usbvision_device_data[0].Codec); - sscanf(parse,"%d",&tmp); - usbvision_device_data[0].VideoChannels = tmp; - goto2next(parse); - PDEBUG(DBG_PROBE, "VideoChannels=%d", usbvision_device_data[0].VideoChannels); - - switch(*parse) - { - case 'P': - PDEBUG(DBG_PROBE, "VideoNorm=PAL"); - usbvision_device_data[0].VideoNorm=V4L2_STD_PAL; - break; - - case 'S': - PDEBUG(DBG_PROBE, "VideoNorm=SECAM"); - usbvision_device_data[0].VideoNorm=V4L2_STD_SECAM; - break; - - case 'N': - PDEBUG(DBG_PROBE, "VideoNorm=NTSC"); - usbvision_device_data[0].VideoNorm=V4L2_STD_NTSC; - break; - - default: - PDEBUG(DBG_PROBE, "VideoNorm=PAL (by default)"); - usbvision_device_data[0].VideoNorm=V4L2_STD_PAL; - break; - } - goto2next(parse); - - sscanf(parse,"%d",&tmp); - usbvision_device_data[0].AudioChannels = tmp; - goto2next(parse); - PDEBUG(DBG_PROBE, "AudioChannels=%d", usbvision_device_data[0].AudioChannels); - sscanf(parse,"%d",&radio); - usbvision_device_data[0].Radio=(radio?1:0); - goto2next(parse); - PDEBUG(DBG_PROBE, "Radio=%d", usbvision_device_data[0].Radio); - sscanf(parse,"%d",&tuner); - usbvision_device_data[0].Tuner=(tuner?1:0); - goto2next(parse); - PDEBUG(DBG_PROBE, "Tuner=%d", usbvision_device_data[0].Tuner); - sscanf(parse,"%hhu",&usbvision_device_data[0].TunerType); - goto2next(parse); - PDEBUG(DBG_PROBE, "TunerType=%d", usbvision_device_data[0].TunerType); - sscanf(parse,"%d",&tmp); - if(tmp>0) { - usbvision_device_data[0].Vin_Reg1_override = 1; - usbvision_device_data[0].Vin_Reg1 = tmp&0xff; - } - goto2next(parse); - PDEBUG(DBG_PROBE, "Vin_Reg1=%d", usbvision_device_data[0].Vin_Reg1); - sscanf(parse,"%d",&tmp); - if(tmp>0) { - usbvision_device_data[0].Vin_Reg2_override = 1; - usbvision_device_data[0].Vin_Reg2 = tmp&0xff; - } - goto2next(parse); - PDEBUG(DBG_PROBE, "Vin_Reg2=%d", usbvision_device_data[0].Vin_Reg2); - sscanf(parse,"%hd",&usbvision_device_data[0].X_Offset); - goto2next(parse); - PDEBUG(DBG_PROBE, "X_Offset=%d", usbvision_device_data[0].X_Offset); - sscanf(parse,"%hd",&usbvision_device_data[0].Y_Offset); - goto2next(parse); - PDEBUG(DBG_PROBE, "Y_Offset=%d", usbvision_device_data[0].Y_Offset); - sscanf(parse,"%d",&tmp); - if(tmp>0) { - usbvision_device_data[0].Dvi_yuv_override = 1; - usbvision_device_data[0].Dvi_yuv = tmp&0xff; - } - PDEBUG(DBG_PROBE, "Dvi_yuv=%d", usbvision_device_data[0].Dvi_yuv); - - //add to usbvision_table also - usbvision_table[0].match_flags=USB_DEVICE_ID_MATCH_DEVICE; - } -} - - - /* * usbvision_init() * @@ -2104,8 +1963,6 @@ static int __init usbvision_init(void) usbvision_v4l2_format[7].supported = 0; // V4L2_PIX_FMT_YUV422P } - customdevice_process(); - errCode = usb_register(&usbvision_driver); if (errCode == 0) { -- cgit v1.2.3 From f1c9e30b5e4cdd8aae5f0ea87004b1b61ec41881 Mon Sep 17 00:00:00 2001 From: Pete Zaitcev Date: Sat, 24 Feb 2007 19:27:33 -0800 Subject: usbmon: Extended text API This patch adds a new text API, codenamed '1u', which captures more URB fields than old '1t' interface did. Also the '1u' text API is compatible with the future "bus zero" extension. Signed-off-by: Pete Zaitcev Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- Documentation/usb/usbmon.txt | 80 ++++++++---- drivers/usb/mon/mon_text.c | 296 +++++++++++++++++++++++++++++++++++++------ drivers/usb/mon/usb_mon.h | 1 + 3 files changed, 313 insertions(+), 64 deletions(-) (limited to 'Documentation') diff --git a/Documentation/usb/usbmon.txt b/Documentation/usb/usbmon.txt index 0f6808abd612..53ae866ae37b 100644 --- a/Documentation/usb/usbmon.txt +++ b/Documentation/usb/usbmon.txt @@ -16,7 +16,7 @@ situation as with tcpdump. Unlike the packet socket, usbmon has an interface which provides traces in a text format. This is used for two purposes. First, it serves as a -common trace exchange format for tools while most sophisticated formats +common trace exchange format for tools while more sophisticated formats are finalized. Second, humans can read it in case tools are not available. To collect a raw text trace, execute following steps. @@ -34,7 +34,7 @@ if usbmon is built into the kernel. Verify that bus sockets are present. # ls /sys/kernel/debug/usbmon -1s 1t 2s 2t 3s 3t 4s 4t +1s 1t 1u 2s 2t 2u 3s 3t 3u 4s 4t 4u # 2. Find which bus connects to the desired device @@ -54,7 +54,7 @@ Bus=03 means it's bus 3. 3. Start 'cat' -# cat /sys/kernel/debug/usbmon/3t > /tmp/1.mon.out +# cat /sys/kernel/debug/usbmon/3u > /tmp/1.mon.out This process will be reading until killed. Naturally, the output can be redirected to a desirable location. This is preferred, because it is going @@ -75,46 +75,80 @@ that the file size is not excessive for your favourite editor. * Raw text data format -The '1t' type data consists of a stream of events, such as URB submission, +Two formats are supported currently: the original, or '1t' format, and +the '1u' format. The '1t' format is deprecated in kernel 2.6.21. The '1u' +format adds a few fields, such as ISO frame descriptors, interval, etc. +It produces slightly longer lines, but otherwise is a perfect superset +of '1t' format. + +If it is desired to recognize one from the other in a program, look at the +"address" word (see below), where '1u' format adds a bus number. If 2 colons +are present, it's the '1t' format, otherwise '1u'. + +Any text format data consists of a stream of events, such as URB submission, URB callback, submission error. Every event is a text line, which consists of whitespace separated words. The number or position of words may depend on the event type, but there is a set of words, common for all types. Here is the list of words, from left to right: + - URB Tag. This is used to identify URBs is normally a kernel mode address of the URB structure in hexadecimal. + - Timestamp in microseconds, a decimal number. The timestamp's resolution depends on available clock, and so it can be much worse than a microsecond (if the implementation uses jiffies, for example). + - Event Type. This type refers to the format of the event, not URB type. Available types are: S - submission, C - callback, E - submission error. -- "Pipe". The pipe concept is deprecated. This is a composite word, used to - be derived from information in pipes. It consists of three fields, separated - by colons: URB type and direction, Device address, Endpoint number. + +- "Address" word (formerly a "pipe"). It consists of four fields, separated by + colons: URB type and direction, Bus number, Device address, Endpoint number. Type and direction are encoded with two bytes in the following manner: Ci Co Control input and output Zi Zo Isochronous input and output Ii Io Interrupt input and output Bi Bo Bulk input and output - Device address and Endpoint number are 3-digit and 2-digit (respectively) - decimal numbers, with leading zeroes. -- URB Status. In most cases, this field contains a number, sometimes negative, - which represents a "status" field of the URB. This field makes no sense for - submissions, but is present anyway to help scripts with parsing. When an - error occurs, the field contains the error code. In case of a submission of - a Control packet, this field contains a Setup Tag instead of an error code. - It is easy to tell whether the Setup Tag is present because it is never a - number. Thus if scripts find a number in this field, they proceed to read - Data Length. If they find something else, like a letter, they read the setup - packet before reading the Data Length. + Bus number, Device address, and Endpoint are decimal numbers, but they may + have leading zeros, for the sake of human readers. + +- URB Status word. This is either a letter, or several numbers separated + by colons: URB status, interval, start frame, and error count. Unlike the + "address" word, all fields save the status are optional. Interval is printed + only for interrupt and isochronous URBs. Start frame is printed only for + isochronous URBs. Error count is printed only for isochronous callback + events. + + The status field is a decimal number, sometimes negative, which represents + a "status" field of the URB. This field makes no sense for submissions, but + is present anyway to help scripts with parsing. When an error occurs, the + field contains the error code. + + In case of a submission of a Control packet, this field contains a Setup Tag + instead of an group of numbers. It is easy to tell whether the Setup Tag is + present because it is never a number. Thus if scripts find a set of numbers + in this word, they proceed to read Data Length (except for isochronous URBs). + If they find something else, like a letter, they read the setup packet before + reading the Data Length or isochronous descriptors. + - Setup packet, if present, consists of 5 words: one of each for bmRequestType, bRequest, wValue, wIndex, wLength, as specified by the USB Specification 2.0. These words are safe to decode if Setup Tag was 's'. Otherwise, the setup packet was present, but not captured, and the fields contain filler. + +- Number of isochronous frame descriptors and descriptors themselves. + If an Isochronous transfer event has a set of descriptors, a total number + of them in an URB is printed first, then a word per descriptor, up to a + total of 5. The word consists of 3 colon-separated decimal numbers for + status, offset, and length respectively. For submissions, initial length + is reported. For callbacks, actual length is reported. + - Data Length. For submissions, this is the requested length. For callbacks, this is the actual length. + - Data tag. The usbmon may not always capture data, even if length is nonzero. The data words are present only if this tag is '='. + - Data words follow, in big endian hexadecimal format. Notice that they are not machine words, but really just a byte stream split into words to make it easier to read. Thus, the last word may contain from one to four bytes. @@ -153,20 +187,18 @@ class ParsedLine { } } -This format may be changed in the future. - Examples: An input control transfer to get a port status. -d5ea89a0 3575914555 S Ci:001:00 s a3 00 0000 0003 0004 4 < -d5ea89a0 3575914560 C Ci:001:00 0 4 = 01050000 +d5ea89a0 3575914555 S Ci:1:001:0 s a3 00 0000 0003 0004 4 < +d5ea89a0 3575914560 C Ci:1:001:0 0 4 = 01050000 An output bulk transfer to send a SCSI command 0x5E in a 31-byte Bulk wrapper to a storage device at address 5: -dd65f0e8 4128379752 S Bo:005:02 -115 31 = 55534243 5e000000 00000000 00000600 00000000 00000000 00000000 000000 -dd65f0e8 4128379808 C Bo:005:02 0 31 > +dd65f0e8 4128379752 S Bo:1:005:2 -115 31 = 55534243 5e000000 00000000 00000600 00000000 00000000 00000000 000000 +dd65f0e8 4128379808 C Bo:1:005:2 0 31 > * Raw binary format and API diff --git a/drivers/usb/mon/mon_text.c b/drivers/usb/mon/mon_text.c index 494ee3b9a226..bbf54396e5f2 100644 --- a/drivers/usb/mon/mon_text.c +++ b/drivers/usb/mon/mon_text.c @@ -31,9 +31,21 @@ * to a local DoS. But we have to keep to root in order to prevent * password sniffing from HID devices. */ -#define EVENT_MAX (2*PAGE_SIZE / sizeof(struct mon_event_text)) +#define EVENT_MAX (4*PAGE_SIZE / sizeof(struct mon_event_text)) -#define PRINTF_DFL 160 +/* + * Potentially unlimited number; we limit it for similar allocations. + * The usbfs limits this to 128, but we're not quite as generous. + */ +#define ISODESC_MAX 5 + +#define PRINTF_DFL 250 /* with 5 ISOs segs */ + +struct mon_iso_desc { + int status; + unsigned int offset; + unsigned int length; /* Unsigned here, signed in URB. Historic. */ +}; struct mon_event_text { struct list_head e_link; @@ -41,10 +53,16 @@ struct mon_event_text { unsigned int pipe; /* Pipe */ unsigned long id; /* From pointer, most of the time */ unsigned int tstamp; + int busnum; int length; /* Depends on type: xfer length or act length */ int status; + int interval; + int start_frame; + int error_count; char setup_flag; char data_flag; + int numdesc; /* Full number */ + struct mon_iso_desc isodesc[ISODESC_MAX]; unsigned char setup[SETUP_MAX]; unsigned char data[DATA_MAX]; }; @@ -68,6 +86,28 @@ static struct dentry *mon_dir; /* Usually /sys/kernel/debug/usbmon */ static void mon_text_ctor(void *, struct kmem_cache *, unsigned long); +struct mon_text_ptr { + int cnt, limit; + char *pbuf; +}; + +static struct mon_event_text * + mon_text_read_wait(struct mon_reader_text *rp, struct file *file); +static void mon_text_read_head_t(struct mon_reader_text *rp, + struct mon_text_ptr *p, const struct mon_event_text *ep); +static void mon_text_read_head_u(struct mon_reader_text *rp, + struct mon_text_ptr *p, const struct mon_event_text *ep); +static void mon_text_read_statset(struct mon_reader_text *rp, + struct mon_text_ptr *p, const struct mon_event_text *ep); +static void mon_text_read_intstat(struct mon_reader_text *rp, + struct mon_text_ptr *p, const struct mon_event_text *ep); +static void mon_text_read_isostat(struct mon_reader_text *rp, + struct mon_text_ptr *p, const struct mon_event_text *ep); +static void mon_text_read_isodesc(struct mon_reader_text *rp, + struct mon_text_ptr *p, const struct mon_event_text *ep); +static void mon_text_read_data(struct mon_reader_text *rp, + struct mon_text_ptr *p, const struct mon_event_text *ep); + /* * mon_text_submit * mon_text_complete @@ -104,10 +144,10 @@ static inline char mon_text_get_data(struct mon_event_text *ep, struct urb *urb, len = DATA_MAX; if (usb_pipein(pipe)) { - if (ev_type == 'S') + if (ev_type != 'C') return '<'; } else { - if (ev_type == 'C') + if (ev_type != 'S') return '>'; } @@ -146,6 +186,9 @@ static void mon_text_event(struct mon_reader_text *rp, struct urb *urb, { struct mon_event_text *ep; unsigned int stamp; + struct usb_iso_packet_descriptor *fp; + struct mon_iso_desc *dp; + int i, ndesc; stamp = mon_get_timestamp(); @@ -158,12 +201,36 @@ static void mon_text_event(struct mon_reader_text *rp, struct urb *urb, ep->type = ev_type; ep->pipe = urb->pipe; ep->id = (unsigned long) urb; + ep->busnum = rp->r.m_bus->u_bus->busnum; ep->tstamp = stamp; ep->length = (ev_type == 'S') ? urb->transfer_buffer_length : urb->actual_length; /* Collecting status makes debugging sense for submits, too */ ep->status = urb->status; + if (usb_pipeint(urb->pipe)) { + ep->interval = urb->interval; + } else if (usb_pipeisoc(urb->pipe)) { + ep->interval = urb->interval; + ep->start_frame = urb->start_frame; + ep->error_count = urb->error_count; + } + ep->numdesc = urb->number_of_packets; + if (usb_pipeisoc(urb->pipe) && urb->number_of_packets > 0) { + if ((ndesc = urb->number_of_packets) > ISODESC_MAX) + ndesc = ISODESC_MAX; + fp = urb->iso_frame_desc; + dp = ep->isodesc; + for (i = 0; i < ndesc; i++) { + dp->status = fp->status; + dp->offset = fp->offset; + dp->length = (ev_type == 'S') ? + fp->length : fp->actual_length; + fp++; + dp++; + } + } + ep->setup_flag = mon_text_get_setup(ep, urb, ev_type, rp->r.m_bus); ep->data_flag = mon_text_get_data(ep, urb, ep->length, ev_type, rp->r.m_bus); @@ -199,6 +266,7 @@ static void mon_text_error(void *data, struct urb *urb, int error) ep->type = 'E'; ep->pipe = urb->pipe; ep->id = (unsigned long) urb; + ep->busnum = 0; ep->tstamp = 0; ep->length = 0; ep->status = error; @@ -300,17 +368,75 @@ err_alloc: * dd if=/dbg/usbmon/0t bs=10 * Also, we do not allow seeks and do not bother advancing the offset. */ -static ssize_t mon_text_read(struct file *file, char __user *buf, +static ssize_t mon_text_read_t(struct file *file, char __user *buf, + size_t nbytes, loff_t *ppos) +{ + struct mon_reader_text *rp = file->private_data; + struct mon_event_text *ep; + struct mon_text_ptr ptr; + + if (IS_ERR(ep = mon_text_read_wait(rp, file))) + return PTR_ERR(ep); + mutex_lock(&rp->printf_lock); + ptr.cnt = 0; + ptr.pbuf = rp->printf_buf; + ptr.limit = rp->printf_size; + + mon_text_read_head_t(rp, &ptr, ep); + mon_text_read_statset(rp, &ptr, ep); + ptr.cnt += snprintf(ptr.pbuf + ptr.cnt, ptr.limit - ptr.cnt, + " %d", ep->length); + mon_text_read_data(rp, &ptr, ep); + + if (copy_to_user(buf, rp->printf_buf, ptr.cnt)) + ptr.cnt = -EFAULT; + mutex_unlock(&rp->printf_lock); + kmem_cache_free(rp->e_slab, ep); + return ptr.cnt; +} + +static ssize_t mon_text_read_u(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos) { struct mon_reader_text *rp = file->private_data; + struct mon_event_text *ep; + struct mon_text_ptr ptr; + + if (IS_ERR(ep = mon_text_read_wait(rp, file))) + return PTR_ERR(ep); + mutex_lock(&rp->printf_lock); + ptr.cnt = 0; + ptr.pbuf = rp->printf_buf; + ptr.limit = rp->printf_size; + + mon_text_read_head_u(rp, &ptr, ep); + if (ep->type == 'E') { + mon_text_read_statset(rp, &ptr, ep); + } else if (usb_pipeisoc(ep->pipe)) { + mon_text_read_isostat(rp, &ptr, ep); + mon_text_read_isodesc(rp, &ptr, ep); + } else if (usb_pipeint(ep->pipe)) { + mon_text_read_intstat(rp, &ptr, ep); + } else { + mon_text_read_statset(rp, &ptr, ep); + } + ptr.cnt += snprintf(ptr.pbuf + ptr.cnt, ptr.limit - ptr.cnt, + " %d", ep->length); + mon_text_read_data(rp, &ptr, ep); + + if (copy_to_user(buf, rp->printf_buf, ptr.cnt)) + ptr.cnt = -EFAULT; + mutex_unlock(&rp->printf_lock); + kmem_cache_free(rp->e_slab, ep); + return ptr.cnt; +} + +static struct mon_event_text *mon_text_read_wait(struct mon_reader_text *rp, + struct file *file) +{ struct mon_bus *mbus = rp->r.m_bus; DECLARE_WAITQUEUE(waita, current); struct mon_event_text *ep; - int cnt, limit; - char *pbuf; - char udir, utype; - int data_len, i; add_wait_queue(&rp->wait, &waita); set_current_state(TASK_INTERRUPTIBLE); @@ -318,7 +444,7 @@ static ssize_t mon_text_read(struct file *file, char __user *buf, if (file->f_flags & O_NONBLOCK) { set_current_state(TASK_RUNNING); remove_wait_queue(&rp->wait, &waita); - return -EWOULDBLOCK; /* Same as EAGAIN in Linux */ + return ERR_PTR(-EWOULDBLOCK); } /* * We do not count nwaiters, because ->release is supposed @@ -327,17 +453,19 @@ static ssize_t mon_text_read(struct file *file, char __user *buf, schedule(); if (signal_pending(current)) { remove_wait_queue(&rp->wait, &waita); - return -EINTR; + return ERR_PTR(-EINTR); } set_current_state(TASK_INTERRUPTIBLE); } set_current_state(TASK_RUNNING); remove_wait_queue(&rp->wait, &waita); + return ep; +} - mutex_lock(&rp->printf_lock); - cnt = 0; - pbuf = rp->printf_buf; - limit = rp->printf_size; +static void mon_text_read_head_t(struct mon_reader_text *rp, + struct mon_text_ptr *p, const struct mon_event_text *ep) +{ + char udir, utype; udir = usb_pipein(ep->pipe) ? 'i' : 'o'; switch (usb_pipetype(ep->pipe)) { @@ -346,13 +474,38 @@ static ssize_t mon_text_read(struct file *file, char __user *buf, case PIPE_CONTROL: utype = 'C'; break; default: /* PIPE_BULK */ utype = 'B'; } - cnt += snprintf(pbuf + cnt, limit - cnt, + p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt, "%lx %u %c %c%c:%03u:%02u", ep->id, ep->tstamp, ep->type, - utype, udir, usb_pipedevice(ep->pipe), usb_pipeendpoint(ep->pipe)); + utype, udir, + usb_pipedevice(ep->pipe), usb_pipeendpoint(ep->pipe)); +} + +static void mon_text_read_head_u(struct mon_reader_text *rp, + struct mon_text_ptr *p, const struct mon_event_text *ep) +{ + char udir, utype; + + udir = usb_pipein(ep->pipe) ? 'i' : 'o'; + switch (usb_pipetype(ep->pipe)) { + case PIPE_ISOCHRONOUS: utype = 'Z'; break; + case PIPE_INTERRUPT: utype = 'I'; break; + case PIPE_CONTROL: utype = 'C'; break; + default: /* PIPE_BULK */ utype = 'B'; + } + p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt, + "%lx %u %c %c%c:%d:%03u:%u", + ep->id, ep->tstamp, ep->type, + utype, udir, + ep->busnum, usb_pipedevice(ep->pipe), usb_pipeendpoint(ep->pipe)); +} + +static void mon_text_read_statset(struct mon_reader_text *rp, + struct mon_text_ptr *p, const struct mon_event_text *ep) +{ if (ep->setup_flag == 0) { /* Setup packet is present and captured */ - cnt += snprintf(pbuf + cnt, limit - cnt, + p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt, " s %02x %02x %04x %04x %04x", ep->setup[0], ep->setup[1], @@ -360,40 +513,86 @@ static ssize_t mon_text_read(struct file *file, char __user *buf, (ep->setup[5] << 8) | ep->setup[4], (ep->setup[7] << 8) | ep->setup[6]); } else if (ep->setup_flag != '-') { /* Unable to capture setup packet */ - cnt += snprintf(pbuf + cnt, limit - cnt, + p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt, " %c __ __ ____ ____ ____", ep->setup_flag); } else { /* No setup for this kind of URB */ - cnt += snprintf(pbuf + cnt, limit - cnt, " %d", ep->status); + p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt, + " %d", ep->status); + } +} + +static void mon_text_read_intstat(struct mon_reader_text *rp, + struct mon_text_ptr *p, const struct mon_event_text *ep) +{ + p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt, + " %d:%d", ep->status, ep->interval); +} + +static void mon_text_read_isostat(struct mon_reader_text *rp, + struct mon_text_ptr *p, const struct mon_event_text *ep) +{ + if (ep->type == 'S') { + p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt, + " %d:%d:%d", ep->status, ep->interval, ep->start_frame); + } else { + p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt, + " %d:%d:%d:%d", + ep->status, ep->interval, ep->start_frame, ep->error_count); } - cnt += snprintf(pbuf + cnt, limit - cnt, " %d", ep->length); +} + +static void mon_text_read_isodesc(struct mon_reader_text *rp, + struct mon_text_ptr *p, const struct mon_event_text *ep) +{ + int ndesc; /* Display this many */ + int i; + const struct mon_iso_desc *dp; + + p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt, + " %d", ep->numdesc); + ndesc = ep->numdesc; + if (ndesc > ISODESC_MAX) + ndesc = ISODESC_MAX; + if (ndesc < 0) + ndesc = 0; + dp = ep->isodesc; + for (i = 0; i < ndesc; i++) { + p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt, + " %d:%u:%u", dp->status, dp->offset, dp->length); + dp++; + } +} + +static void mon_text_read_data(struct mon_reader_text *rp, + struct mon_text_ptr *p, const struct mon_event_text *ep) +{ + int data_len, i; if ((data_len = ep->length) > 0) { if (ep->data_flag == 0) { - cnt += snprintf(pbuf + cnt, limit - cnt, " ="); + p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt, + " ="); if (data_len >= DATA_MAX) data_len = DATA_MAX; for (i = 0; i < data_len; i++) { if (i % 4 == 0) { - cnt += snprintf(pbuf + cnt, limit - cnt, + p->cnt += snprintf(p->pbuf + p->cnt, + p->limit - p->cnt, " "); } - cnt += snprintf(pbuf + cnt, limit - cnt, + p->cnt += snprintf(p->pbuf + p->cnt, + p->limit - p->cnt, "%02x", ep->data[i]); } - cnt += snprintf(pbuf + cnt, limit - cnt, "\n"); + p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt, + "\n"); } else { - cnt += snprintf(pbuf + cnt, limit - cnt, + p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt, " %c\n", ep->data_flag); } } else { - cnt += snprintf(pbuf + cnt, limit - cnt, "\n"); + p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt, "\n"); } - - if (copy_to_user(buf, rp->printf_buf, cnt)) - cnt = -EFAULT; - mutex_unlock(&rp->printf_lock); - kmem_cache_free(rp->e_slab, ep); - return cnt; } static int mon_text_release(struct inode *inode, struct file *file) @@ -439,14 +638,19 @@ static int mon_text_release(struct inode *inode, struct file *file) return 0; } -static const struct file_operations mon_fops_text = { +static const struct file_operations mon_fops_text_t = { .owner = THIS_MODULE, .open = mon_text_open, .llseek = no_llseek, - .read = mon_text_read, - /* .write = mon_text_write, */ - /* .poll = mon_text_poll, */ - /* .ioctl = mon_text_ioctl, */ + .read = mon_text_read_t, + .release = mon_text_release, +}; + +static const struct file_operations mon_fops_text_u = { + .owner = THIS_MODULE, + .open = mon_text_open, + .llseek = no_llseek, + .read = mon_text_read_u, .release = mon_text_release, }; @@ -460,12 +664,19 @@ int mon_text_add(struct mon_bus *mbus, const struct usb_bus *ubus) rc = snprintf(name, NAMESZ, "%dt", ubus->busnum); if (rc <= 0 || rc >= NAMESZ) goto err_print_t; - d = debugfs_create_file(name, 0600, mon_dir, mbus, &mon_fops_text); + d = debugfs_create_file(name, 0600, mon_dir, mbus, &mon_fops_text_t); if (d == NULL) goto err_create_t; mbus->dent_t = d; - /* XXX The stats do not belong to here (text API), but oh well... */ + rc = snprintf(name, NAMESZ, "%du", ubus->busnum); + if (rc <= 0 || rc >= NAMESZ) + goto err_print_u; + d = debugfs_create_file(name, 0600, mon_dir, mbus, &mon_fops_text_u); + if (d == NULL) + goto err_create_u; + mbus->dent_u = d; + rc = snprintf(name, NAMESZ, "%ds", ubus->busnum); if (rc <= 0 || rc >= NAMESZ) goto err_print_s; @@ -478,6 +689,10 @@ int mon_text_add(struct mon_bus *mbus, const struct usb_bus *ubus) err_create_s: err_print_s: + debugfs_remove(mbus->dent_u); + mbus->dent_u = NULL; +err_create_u: +err_print_u: debugfs_remove(mbus->dent_t); mbus->dent_t = NULL; err_create_t: @@ -487,6 +702,7 @@ err_print_t: void mon_text_del(struct mon_bus *mbus) { + debugfs_remove(mbus->dent_u); debugfs_remove(mbus->dent_t); debugfs_remove(mbus->dent_s); } diff --git a/drivers/usb/mon/usb_mon.h b/drivers/usb/mon/usb_mon.h index efdfd8993d9e..a1e2b8e9d777 100644 --- a/drivers/usb/mon/usb_mon.h +++ b/drivers/usb/mon/usb_mon.h @@ -22,6 +22,7 @@ struct mon_bus { int text_inited; struct dentry *dent_s; /* Debugging file */ struct dentry *dent_t; /* Text interface file */ + struct dentry *dent_u; /* Second text interface file */ int uses_dma; /* Ref */ -- cgit v1.2.3 From eaafbc3a8adab16babe2c20e54ad3ba40d1fbbc9 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Tue, 13 Mar 2007 16:39:15 -0400 Subject: USB: Allow autosuspend delay to equal 0 This patch (as867) adds an entry for the new power/autosuspend attribute in Documentation/ABI/testing, and it changes the behavior of the delay value. Now a delay of 0 means to autosuspend as soon as possible, and negative values will prevent autosuspend. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- Documentation/ABI/testing/sysfs-bus-usb | 15 +++++++++++++++ Documentation/kernel-parameters.txt | 2 +- drivers/usb/core/driver.c | 2 +- drivers/usb/core/sysfs.c | 16 ++++++++++------ drivers/usb/core/usb.c | 2 +- include/linux/usb.h | 2 +- 6 files changed, 29 insertions(+), 10 deletions(-) create mode 100644 Documentation/ABI/testing/sysfs-bus-usb (limited to 'Documentation') diff --git a/Documentation/ABI/testing/sysfs-bus-usb b/Documentation/ABI/testing/sysfs-bus-usb new file mode 100644 index 000000000000..00a84326325f --- /dev/null +++ b/Documentation/ABI/testing/sysfs-bus-usb @@ -0,0 +1,15 @@ +What: /sys/bus/usb/devices/.../power/autosuspend +Date: March 2007 +KernelVersion: 2.6.21 +Contact: Alan Stern +Description: + Each USB device directory will contain a file named + power/autosuspend. This file holds the time (in seconds) + the device must be idle before it will be autosuspended. + 0 means the device will be autosuspended as soon as + possible. Negative values will prevent the device from + being autosuspended at all, and writing a negative value + will resume the device if it is already suspended. + + The autosuspend delay for newly-created devices is set to + the value of the usbcore.autosuspend module parameter. diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 12533a958c51..2017942e0966 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -1792,7 +1792,7 @@ and is between 256 and 4096 characters. It is defined in the file for newly-detected USB devices (default 2). This is the time required before an idle device will be autosuspended. Devices for which the delay is set - to 0 won't be autosuspended at all. + to a negative value won't be autosuspended at all. usbhid.mousepoll= [USBHID] The interval which mice are to be polled at. diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index abea48de8766..884179f1e163 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -970,7 +970,7 @@ static int autosuspend_check(struct usb_device *udev) udev->do_remote_wakeup = device_may_wakeup(&udev->dev); if (udev->pm_usage_cnt > 0) return -EBUSY; - if (!udev->autosuspend_delay) + if (udev->autosuspend_delay < 0) return -EPERM; if (udev->actconfig) { diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c index 311d5df80386..731001f7d2c1 100644 --- a/drivers/usb/core/sysfs.c +++ b/drivers/usb/core/sysfs.c @@ -165,7 +165,7 @@ show_autosuspend(struct device *dev, struct device_attribute *attr, char *buf) { struct usb_device *udev = to_usb_device(dev); - return sprintf(buf, "%u\n", udev->autosuspend_delay / HZ); + return sprintf(buf, "%d\n", udev->autosuspend_delay / HZ); } static ssize_t @@ -173,17 +173,21 @@ set_autosuspend(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct usb_device *udev = to_usb_device(dev); - unsigned value, old; + int value; - if (sscanf(buf, "%u", &value) != 1 || value >= INT_MAX/HZ) + if (sscanf(buf, "%d", &value) != 1 || value >= INT_MAX/HZ || + value <= - INT_MAX/HZ) return -EINVAL; value *= HZ; - old = udev->autosuspend_delay; udev->autosuspend_delay = value; - if (value > 0 && old == 0) + if (value >= 0) usb_try_autosuspend_device(udev); - + else { + usb_lock_device(udev); + usb_external_resume_device(udev); + usb_unlock_device(udev); + } return count; } diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index 138252e0a1cf..6f35dce8a95d 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -55,7 +55,7 @@ struct workqueue_struct *ksuspend_usb_wq; #ifdef CONFIG_USB_SUSPEND static int usb_autosuspend_delay = 2; /* Default delay value, * in seconds */ -module_param_named(autosuspend, usb_autosuspend_delay, uint, 0644); +module_param_named(autosuspend, usb_autosuspend_delay, int, 0644); MODULE_PARM_DESC(autosuspend, "default autosuspend delay"); #else diff --git a/include/linux/usb.h b/include/linux/usb.h index 87dc75a6cee1..cc24d089faa0 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -394,7 +394,7 @@ struct usb_device { struct delayed_work autosuspend; /* for delayed autosuspends */ struct mutex pm_mutex; /* protects PM operations */ - unsigned autosuspend_delay; /* in jiffies */ + int autosuspend_delay; /* in jiffies */ unsigned auto_pm:1; /* autosuspend/resume in progress */ unsigned do_remote_wakeup:1; /* remote wakeup should be enabled */ -- cgit v1.2.3 From 2add5229d77a3de08015feef437653e02372162f Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Tue, 20 Mar 2007 14:59:39 -0400 Subject: USB: add power/level sysfs attribute This patch (as874) adds another piece to the user-visible part of the USB autosuspend interface. The new power/level sysfs attribute allows users to force the device on (with autosuspend off), force the device to sleep (with autoresume off), or return to normal automatic operation. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- Documentation/ABI/testing/sysfs-bus-usb | 26 +++++++++++ drivers/usb/core/driver.c | 15 ++++-- drivers/usb/core/quirks.c | 2 +- drivers/usb/core/sysfs.c | 81 +++++++++++++++++++++++++++++++-- include/linux/usb.h | 2 + 5 files changed, 118 insertions(+), 8 deletions(-) (limited to 'Documentation') diff --git a/Documentation/ABI/testing/sysfs-bus-usb b/Documentation/ABI/testing/sysfs-bus-usb index 00a84326325f..f9937add033d 100644 --- a/Documentation/ABI/testing/sysfs-bus-usb +++ b/Documentation/ABI/testing/sysfs-bus-usb @@ -13,3 +13,29 @@ Description: The autosuspend delay for newly-created devices is set to the value of the usbcore.autosuspend module parameter. + +What: /sys/bus/usb/devices/.../power/level +Date: March 2007 +KernelVersion: 2.6.21 +Contact: Alan Stern +Description: + Each USB device directory will contain a file named + power/level. This file holds a power-level setting for + the device, one of "on", "auto", or "suspend". + + "on" means that the device is not allowed to autosuspend, + although normal suspends for system sleep will still + be honored. "auto" means the device will autosuspend + and autoresume in the usual manner, according to the + capabilities of its driver. "suspend" means the device + is forced into a suspended state and it will not autoresume + in response to I/O requests. However remote-wakeup requests + from the device may still be enabled (the remote-wakeup + setting is controlled separately by the power/wakeup + attribute). + + During normal use, devices should be left in the "auto" + level. The other levels are meant for administrative uses. + If you want to suspend a device immediately but leave it + free to wake up in response to I/O requests, you should + write "0" to power/autosuspend. diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 884179f1e163..9b6a60fafddb 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -872,8 +872,10 @@ static int usb_resume_device(struct usb_device *udev) done: // dev_dbg(&udev->dev, "%s: status %d\n", __FUNCTION__, status); - if (status == 0) + if (status == 0) { + udev->autoresume_disabled = 0; udev->dev.power.power_state.event = PM_EVENT_ON; + } return status; } @@ -970,7 +972,7 @@ static int autosuspend_check(struct usb_device *udev) udev->do_remote_wakeup = device_may_wakeup(&udev->dev); if (udev->pm_usage_cnt > 0) return -EBUSY; - if (udev->autosuspend_delay < 0) + if (udev->autosuspend_delay < 0 || udev->autosuspend_disabled) return -EPERM; if (udev->actconfig) { @@ -1116,6 +1118,8 @@ static int usb_resume_both(struct usb_device *udev) struct usb_interface *intf; struct usb_device *parent = udev->parent; + if (udev->auto_pm && udev->autoresume_disabled) + return -EPERM; cancel_delayed_work(&udev->autosuspend); if (udev->state == USB_STATE_NOTATTACHED) return -ENODEV; @@ -1486,9 +1490,14 @@ static int usb_suspend(struct device *dev, pm_message_t message) static int usb_resume(struct device *dev) { + struct usb_device *udev; + if (!is_usb_device(dev)) /* Ignore PM for interfaces */ return 0; - return usb_external_resume_device(to_usb_device(dev)); + udev = to_usb_device(dev); + if (udev->autoresume_disabled) + return -EPERM; + return usb_external_resume_device(udev); } #else diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c index f08ec85a6d64..739f520908aa 100644 --- a/drivers/usb/core/quirks.c +++ b/drivers/usb/core/quirks.c @@ -42,7 +42,7 @@ static void usb_autosuspend_quirk(struct usb_device *udev) { #ifdef CONFIG_USB_SUSPEND /* disable autosuspend, but allow the user to re-enable it via sysfs */ - udev->autosuspend_delay = 0; + udev->autosuspend_disabled = 1; #endif } diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c index 731001f7d2c1..2ea47a38aefa 100644 --- a/drivers/usb/core/sysfs.c +++ b/drivers/usb/core/sysfs.c @@ -11,6 +11,7 @@ #include +#include #include #include "usb.h" @@ -184,9 +185,8 @@ set_autosuspend(struct device *dev, struct device_attribute *attr, if (value >= 0) usb_try_autosuspend_device(udev); else { - usb_lock_device(udev); - usb_external_resume_device(udev); - usb_unlock_device(udev); + if (usb_autoresume_device(udev) == 0) + usb_autosuspend_device(udev); } return count; } @@ -194,21 +194,94 @@ set_autosuspend(struct device *dev, struct device_attribute *attr, static DEVICE_ATTR(autosuspend, S_IRUGO | S_IWUSR, show_autosuspend, set_autosuspend); +static const char on_string[] = "on"; +static const char auto_string[] = "auto"; +static const char suspend_string[] = "suspend"; + +static ssize_t +show_level(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct usb_device *udev = to_usb_device(dev); + const char *p = auto_string; + + if (udev->state == USB_STATE_SUSPENDED) { + if (udev->autoresume_disabled) + p = suspend_string; + } else { + if (udev->autosuspend_disabled) + p = on_string; + } + return sprintf(buf, "%s\n", p); +} + +static ssize_t +set_level(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct usb_device *udev = to_usb_device(dev); + int len = count; + char *cp; + int rc = 0; + + cp = memchr(buf, '\n', count); + if (cp) + len = cp - buf; + + usb_lock_device(udev); + + /* Setting the flags without calling usb_pm_lock is a subject to + * races, but who cares... + */ + if (len == sizeof on_string - 1 && + strncmp(buf, on_string, len) == 0) { + udev->autosuspend_disabled = 1; + udev->autoresume_disabled = 0; + rc = usb_external_resume_device(udev); + + } else if (len == sizeof auto_string - 1 && + strncmp(buf, auto_string, len) == 0) { + udev->autosuspend_disabled = 0; + udev->autoresume_disabled = 0; + rc = usb_external_resume_device(udev); + + } else if (len == sizeof suspend_string - 1 && + strncmp(buf, suspend_string, len) == 0) { + udev->autosuspend_disabled = 0; + udev->autoresume_disabled = 1; + rc = usb_external_suspend_device(udev, PMSG_SUSPEND); + + } else + rc = -EINVAL; + + usb_unlock_device(udev); + return (rc < 0 ? rc : count); +} + +static DEVICE_ATTR(level, S_IRUGO | S_IWUSR, show_level, set_level); + static char power_group[] = "power"; static int add_power_attributes(struct device *dev) { int rc = 0; - if (is_usb_device(dev)) + if (is_usb_device(dev)) { rc = sysfs_add_file_to_group(&dev->kobj, &dev_attr_autosuspend.attr, power_group); + if (rc == 0) + rc = sysfs_add_file_to_group(&dev->kobj, + &dev_attr_level.attr, + power_group); + } return rc; } static void remove_power_attributes(struct device *dev) { + sysfs_remove_file_from_group(&dev->kobj, + &dev_attr_level.attr, + power_group); sysfs_remove_file_from_group(&dev->kobj, &dev_attr_autosuspend.attr, power_group); diff --git a/include/linux/usb.h b/include/linux/usb.h index cc24d089faa0..5e8e144afbae 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -398,6 +398,8 @@ struct usb_device { unsigned auto_pm:1; /* autosuspend/resume in progress */ unsigned do_remote_wakeup:1; /* remote wakeup should be enabled */ + unsigned autosuspend_disabled:1; /* autosuspend and autoresume */ + unsigned autoresume_disabled:1; /* disabled by the user */ #endif }; #define to_usb_device(d) container_of(d, struct usb_device, dev) -- cgit v1.2.3 From 70a734391a7e12ae177f703b8caf37c7f2014682 Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Tue, 20 Feb 2007 10:33:13 -0600 Subject: [PATCH] bcm43xx: Update Documentation/bcm43xx.txt The in-kernel documentation of the bcm43xx driver is out of date. Signed-off-by: Larry Finger Signed-off-by: John W. Linville --- Documentation/networking/bcm43xx.txt | 97 ++++++++++++++++++++++++++++-------- 1 file changed, 75 insertions(+), 22 deletions(-) (limited to 'Documentation') diff --git a/Documentation/networking/bcm43xx.txt b/Documentation/networking/bcm43xx.txt index 28541d2bee1e..a136721499bf 100644 --- a/Documentation/networking/bcm43xx.txt +++ b/Documentation/networking/bcm43xx.txt @@ -2,35 +2,88 @@ BCM43xx Linux Driver Project ============================ -About this software -------------------- +Introduction +------------ -The goal of this project is to develop a linux driver for Broadcom -BCM43xx chips, based on the specification at -http://bcm-specs.sipsolutions.net/ +Many of the wireless devices found in modern notebook computers are +based on the wireless chips produced by Broadcom. These devices have +been a problem for Linux users as there is no open-source driver +available. In addition, Broadcom has not released specifications +for the device, and driver availability has been limited to the +binary-only form used in the GPL versions of AP hardware such as the +Linksys WRT54G, and the Windows and OS X drivers. Before this project +began, the only way to use these devices were to use the Windows or +OS X drivers with either the Linuxant or ndiswrapper modules. There +is a strong penalty if this method is used as loading the binary-only +module "taints" the kernel, and no kernel developer will help diagnose +any kernel problems. -The project page is http://bcm43xx.berlios.de/ +Development +----------- +This driver has been developed using +a clean-room technique that is described at +http://bcm-specs.sipsolutions.net/ReverseEngineeringProcess. For legal +reasons, none of the clean-room crew works on the on the Linux driver, +and none of the Linux developers sees anything but the specifications, +which are the ultimate product of the reverse-engineering group. -Requirements ------------- +Software +-------- + +Since the release of the 2.6.17 kernel, the bcm43xx driver has been +distributed with the kernel source, and is prebuilt in most, if not +all, distributions. There is, however, additional software that is +required. The firmware used by the chip is the intellectual property +of Broadcom and they have not given the bcm43xx team redistribution +rights to this firmware. Since we cannot legally redistribute +the firwmare we cannot include it with the driver. Furthermore, it +cannot be placed in the downloadable archives of any distributing +organization; therefore, the user is responsible for obtaining the +firmware and placing it in the appropriate location so that the driver +can find it when initializing. + +To help with this process, the bcm43xx developers provide a separate +program named bcm43xx-fwcutter to "cut" the firmware out of a +Windows or OS X driver and write the extracted files to the proper +location. This program is usually provided with the distribution; +however, it may be downloaded from + +http://developer.berlios.de/project/showfiles.php?group_id=4547 -1) Linux Kernel 2.6.16 or later - http://www.kernel.org/ +The firmware is available in two versions. V3 firmware is used with +the in-kernel bcm43xx driver that uses a software MAC layer called +SoftMAC, and will have a microcode revision of 0x127 or smaller. The +V4 firmware is used by an out-of-kernel driver employing a variation of +the Devicescape MAC layer known as d80211. Once bcm43xx-d80211 reaches +a satisfactory level of development, it will replace bcm43xx-softmac +in the kernel as it is much more flexible and powerful. - You may want to configure your kernel with: +A source for the latest V3 firmware is - CONFIG_DEBUG_FS (optional): - -> Kernel hacking - -> Debug Filesystem +http://downloads.openwrt.org/sources/wl_apsta-3.130.20.0.o -2) SoftMAC IEEE 802.11 Networking Stack extension and patched ieee80211 - modules: - http://softmac.sipsolutions.net/ +Once this file is downloaded, the command +'bcm43xx-fwcutter -w ' +will extract the microcode and write it to directory +. The correct directory will depend on your distribution; +however, most use '/lib/firmware'. Once this step is completed, +the bcm3xx driver should load when the system is booted. To see +any messages relating to the driver, issue the command 'dmesg | +grep bcm43xx' from a terminal window. If there are any problems, +please send that output to Bcm43xx-dev@lists.berlios.de. -3) Firmware Files +Although the driver has been in-kernel since 2.6.17, the earliest +version is quite limited in its capability. Patches that include +all features of later versions are available for the stable kernel +versions from 2.6.18. These will be needed if you use a BCM4318, +or a PCI Express version (BCM4311 and BCM4312). In addition, if you +have an early BCM4306 and more than 1 GB RAM, your kernel will need +to be patched. These patches, which are being updated regularly, +are available at ftp://lwfinger.dynalias.org/patches. Look for +combined_2.6.YY.patch. Of course you will need kernel source downloaded +from kernel.org, or the source from your distribution. - Please try fwcutter. Fwcutter can extract the firmware from various - binary driver files. It supports driver files from Windows, MacOS and - Linux. You can get fwcutter from http://bcm43xx.berlios.de/. - Also, fwcutter comes with a README file for further instructions. +If you build your own kernel, please enable CONFIG_BCM43XX_DEBUG +and CONFIG_IEEE80211_SOFTMAC_DEBUG. The log information provided is +essential for solving any problems. -- cgit v1.2.3 From b3df0da886ffdb3e70c3197f589e959e5f8c9c04 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Tue, 6 Mar 2007 02:41:48 -0800 Subject: phy layer: add kernel-doc + DocBook Convert function documentation in drivers/net/phy/ to kernel-doc and add it to DocBook. Signed-off-by: Randy Dunlap Signed-off-by: Andrew Morton Signed-off-by: Jeff Garzik --- Documentation/DocBook/kernel-api.tmpl | 6 ++ drivers/net/phy/mdio_bus.c | 19 ++-- drivers/net/phy/phy.c | 188 ++++++++++++++++++++++++---------- drivers/net/phy/phy_device.c | 114 ++++++++++++++------- 4 files changed, 232 insertions(+), 95 deletions(-) (limited to 'Documentation') diff --git a/Documentation/DocBook/kernel-api.tmpl b/Documentation/DocBook/kernel-api.tmpl index 0bb90237e230..b61dfc79e1b8 100644 --- a/Documentation/DocBook/kernel-api.tmpl +++ b/Documentation/DocBook/kernel-api.tmpl @@ -236,6 +236,12 @@ X!Ilib/string.c !Enet/core/dev.c !Enet/ethernet/eth.c !Iinclude/linux/etherdevice.h +!Edrivers/net/phy/phy.c +!Idrivers/net/phy/phy.c +!Edrivers/net/phy/phy_device.c +!Idrivers/net/phy/phy_device.c +!Edrivers/net/phy/mdio_bus.c +!Idrivers/net/phy/mdio_bus.c diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c index b31ce278bf35..fc4aee96cdfd 100644 --- a/drivers/net/phy/mdio_bus.c +++ b/drivers/net/phy/mdio_bus.c @@ -35,10 +35,14 @@ #include #include -/* mdiobus_register +/** + * mdiobus_register - bring up all the PHYs on a given bus and attach them to bus + * @bus: target mii_bus * - * description: Called by a bus driver to bring up all the PHYs - * on a given bus, and attach them to the bus + * Description: Called by a bus driver to bring up all the PHYs + * on a given bus, and attach them to the bus. + * + * Returns 0 on success or < 0 on error. */ int mdiobus_register(struct mii_bus *bus) { @@ -114,10 +118,13 @@ void mdiobus_unregister(struct mii_bus *bus) } EXPORT_SYMBOL(mdiobus_unregister); -/* mdio_bus_match +/** + * mdio_bus_match - determine if given PHY driver supports the given PHY device + * @dev: target PHY device + * @drv: given PHY driver * - * description: Given a PHY device, and a PHY driver, return 1 if - * the driver supports the device. Otherwise, return 0 + * Description: Given a PHY device, and a PHY driver, return 1 if + * the driver supports the device. Otherwise, return 0. */ static int mdio_bus_match(struct device *dev, struct device_driver *drv) { diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index c94a1fb3a4be..a602d06d85ab 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -39,7 +39,9 @@ #include #include -/* Convenience function to print out the current phy status +/** + * phy_print_status - Convenience function to print out the current phy status + * @phydev: the phy_device struct */ void phy_print_status(struct phy_device *phydev) { @@ -55,10 +57,15 @@ void phy_print_status(struct phy_device *phydev) EXPORT_SYMBOL(phy_print_status); -/* Convenience functions for reading/writing a given PHY - * register. They MUST NOT be called from interrupt context, +/** + * phy_read - Convenience function for reading a given PHY register + * @phydev: the phy_device struct + * @regnum: register number to read + * + * NOTE: MUST NOT be called from interrupt context, * because the bus read/write functions may wait for an interrupt - * to conclude the operation. */ + * to conclude the operation. + */ int phy_read(struct phy_device *phydev, u16 regnum) { int retval; @@ -72,6 +79,16 @@ int phy_read(struct phy_device *phydev, u16 regnum) } EXPORT_SYMBOL(phy_read); +/** + * phy_write - Convenience function for writing a given PHY register + * @phydev: the phy_device struct + * @regnum: register number to write + * @val: value to write to @regnum + * + * NOTE: MUST NOT be called from interrupt context, + * because the bus read/write functions may wait for an interrupt + * to conclude the operation. + */ int phy_write(struct phy_device *phydev, u16 regnum, u16 val) { int err; @@ -85,7 +102,15 @@ int phy_write(struct phy_device *phydev, u16 regnum, u16 val) } EXPORT_SYMBOL(phy_write); - +/** + * phy_clear_interrupt - Ack the phy device's interrupt + * @phydev: the phy_device struct + * + * If the @phydev driver has an ack_interrupt function, call it to + * ack and clear the phy device's interrupt. + * + * Returns 0 on success on < 0 on error. + */ int phy_clear_interrupt(struct phy_device *phydev) { int err = 0; @@ -96,7 +121,13 @@ int phy_clear_interrupt(struct phy_device *phydev) return err; } - +/** + * phy_config_interrupt - configure the PHY device for the requested interrupts + * @phydev: the phy_device struct + * @interrupts: interrupt flags to configure for this @phydev + * + * Returns 0 on success on < 0 on error. + */ int phy_config_interrupt(struct phy_device *phydev, u32 interrupts) { int err = 0; @@ -109,9 +140,11 @@ int phy_config_interrupt(struct phy_device *phydev, u32 interrupts) } -/* phy_aneg_done +/** + * phy_aneg_done - return auto-negotiation status + * @phydev: target phy_device struct * - * description: Reads the status register and returns 0 either if + * Description: Reads the status register and returns 0 either if * auto-negotiation is incomplete, or if there was an error. * Returns BMSR_ANEGCOMPLETE if auto-negotiation is done. */ @@ -173,9 +206,12 @@ static const struct phy_setting settings[] = { #define MAX_NUM_SETTINGS (sizeof(settings)/sizeof(struct phy_setting)) -/* phy_find_setting +/** + * phy_find_setting - find a PHY settings array entry that matches speed & duplex + * @speed: speed to match + * @duplex: duplex to match * - * description: Searches the settings array for the setting which + * Description: Searches the settings array for the setting which * matches the desired speed and duplex, and returns the index * of that setting. Returns the index of the last setting if * none of the others match. @@ -192,11 +228,12 @@ static inline int phy_find_setting(int speed, int duplex) return idx < MAX_NUM_SETTINGS ? idx : MAX_NUM_SETTINGS - 1; } -/* phy_find_valid - * idx: The first index in settings[] to search - * features: A mask of the valid settings +/** + * phy_find_valid - find a PHY setting that matches the requested features mask + * @idx: The first index in settings[] to search + * @features: A mask of the valid settings * - * description: Returns the index of the first valid setting less + * Description: Returns the index of the first valid setting less * than or equal to the one pointed to by idx, as determined by * the mask in features. Returns the index of the last setting * if nothing else matches. @@ -209,11 +246,13 @@ static inline int phy_find_valid(int idx, u32 features) return idx < MAX_NUM_SETTINGS ? idx : MAX_NUM_SETTINGS - 1; } -/* phy_sanitize_settings +/** + * phy_sanitize_settings - make sure the PHY is set to supported speed and duplex + * @phydev: the target phy_device struct * - * description: Make sure the PHY is set to supported speeds and + * Description: Make sure the PHY is set to supported speeds and * duplexes. Drop down by one in this order: 1000/FULL, - * 1000/HALF, 100/FULL, 100/HALF, 10/FULL, 10/HALF + * 1000/HALF, 100/FULL, 100/HALF, 10/FULL, 10/HALF. */ void phy_sanitize_settings(struct phy_device *phydev) { @@ -232,16 +271,17 @@ void phy_sanitize_settings(struct phy_device *phydev) } EXPORT_SYMBOL(phy_sanitize_settings); -/* phy_ethtool_sset: - * A generic ethtool sset function. Handles all the details +/** + * phy_ethtool_sset - generic ethtool sset function, handles all the details + * @phydev: target phy_device struct + * @cmd: ethtool_cmd * * A few notes about parameter checking: * - We don't set port or transceiver, so we don't care what they * were set to. * - phy_start_aneg() will make sure forced settings are sane, and * choose the next best ones from the ones selected, so we don't - * care if ethtool tries to give us bad values - * + * care if ethtool tries to give us bad values. */ int phy_ethtool_sset(struct phy_device *phydev, struct ethtool_cmd *cmd) { @@ -304,9 +344,15 @@ int phy_ethtool_gset(struct phy_device *phydev, struct ethtool_cmd *cmd) } EXPORT_SYMBOL(phy_ethtool_gset); -/* Note that this function is currently incompatible with the +/** + * phy_mii_ioctl - generic PHY MII ioctl interface + * @phydev: the phy_device struct + * @mii_data: MII ioctl data + * @cmd: ioctl cmd to execute + * + * Note that this function is currently incompatible with the * PHYCONTROL layer. It changes registers without regard to - * current state. Use at own risk + * current state. Use at own risk. */ int phy_mii_ioctl(struct phy_device *phydev, struct mii_ioctl_data *mii_data, int cmd) @@ -358,13 +404,14 @@ int phy_mii_ioctl(struct phy_device *phydev, return 0; } -/* phy_start_aneg +/** + * phy_start_aneg - start auto-negotiation for this PHY device + * @phydev: the phy_device struct * - * description: Sanitizes the settings (if we're not - * autonegotiating them), and then calls the driver's - * config_aneg function. If the PHYCONTROL Layer is operating, - * we change the state to reflect the beginning of - * Auto-negotiation or forcing. + * Description: Sanitizes the settings (if we're not autonegotiating + * them), and then calls the driver's config_aneg function. + * If the PHYCONTROL Layer is operating, we change the state to + * reflect the beginning of Auto-negotiation or forcing. */ int phy_start_aneg(struct phy_device *phydev) { @@ -400,15 +447,19 @@ EXPORT_SYMBOL(phy_start_aneg); static void phy_change(struct work_struct *work); static void phy_timer(unsigned long data); -/* phy_start_machine: +/** + * phy_start_machine - start PHY state machine tracking + * @phydev: the phy_device struct + * @handler: callback function for state change notifications * - * description: The PHY infrastructure can run a state machine + * Description: The PHY infrastructure can run a state machine * which tracks whether the PHY is starting up, negotiating, * etc. This function starts the timer which tracks the state - * of the PHY. If you want to be notified when the state - * changes, pass in the callback, otherwise, pass NULL. If you + * of the PHY. If you want to be notified when the state changes, + * pass in the callback @handler, otherwise, pass NULL. If you * want to maintain your own state machine, do not call this - * function. */ + * function. + */ void phy_start_machine(struct phy_device *phydev, void (*handler)(struct net_device *)) { @@ -420,9 +471,11 @@ void phy_start_machine(struct phy_device *phydev, mod_timer(&phydev->phy_timer, jiffies + HZ); } -/* phy_stop_machine +/** + * phy_stop_machine - stop the PHY state machine tracking + * @phydev: target phy_device struct * - * description: Stops the state machine timer, sets the state to UP + * Description: Stops the state machine timer, sets the state to UP * (unless it wasn't up yet). This function must be called BEFORE * phy_detach. */ @@ -438,12 +491,14 @@ void phy_stop_machine(struct phy_device *phydev) phydev->adjust_state = NULL; } -/* phy_force_reduction +/** + * phy_force_reduction - reduce PHY speed/duplex settings by one step + * @phydev: target phy_device struct * - * description: Reduces the speed/duplex settings by - * one notch. The order is so: - * 1000/FULL, 1000/HALF, 100/FULL, 100/HALF, - * 10/FULL, 10/HALF. The function bottoms out at 10/HALF. + * Description: Reduces the speed/duplex settings by one notch, + * in this order-- + * 1000/FULL, 1000/HALF, 100/FULL, 100/HALF, 10/FULL, 10/HALF. + * The function bottoms out at 10/HALF. */ static void phy_force_reduction(struct phy_device *phydev) { @@ -464,7 +519,9 @@ static void phy_force_reduction(struct phy_device *phydev) } -/* phy_error: +/** + * phy_error - enter HALTED state for this PHY device + * @phydev: target phy_device struct * * Moves the PHY to the HALTED state in response to a read * or write error, and tells the controller the link is down. @@ -478,9 +535,12 @@ void phy_error(struct phy_device *phydev) spin_unlock(&phydev->lock); } -/* phy_interrupt +/** + * phy_interrupt - PHY interrupt handler + * @irq: interrupt line + * @phy_dat: phy_device pointer * - * description: When a PHY interrupt occurs, the handler disables + * Description: When a PHY interrupt occurs, the handler disables * interrupts, and schedules a work task to clear the interrupt. */ static irqreturn_t phy_interrupt(int irq, void *phy_dat) @@ -501,7 +561,10 @@ static irqreturn_t phy_interrupt(int irq, void *phy_dat) return IRQ_HANDLED; } -/* Enable the interrupts from the PHY side */ +/** + * phy_enable_interrupts - Enable the interrupts from the PHY side + * @phydev: target phy_device struct + */ int phy_enable_interrupts(struct phy_device *phydev) { int err; @@ -517,7 +580,10 @@ int phy_enable_interrupts(struct phy_device *phydev) } EXPORT_SYMBOL(phy_enable_interrupts); -/* Disable the PHY interrupts from the PHY side */ +/** + * phy_disable_interrupts - Disable the PHY interrupts from the PHY side + * @phydev: target phy_device struct + */ int phy_disable_interrupts(struct phy_device *phydev) { int err; @@ -543,13 +609,15 @@ phy_err: } EXPORT_SYMBOL(phy_disable_interrupts); -/* phy_start_interrupts +/** + * phy_start_interrupts - request and enable interrupts for a PHY device + * @phydev: target phy_device struct * - * description: Request the interrupt for the given PHY. If - * this fails, then we set irq to PHY_POLL. + * Description: Request the interrupt for the given PHY. + * If this fails, then we set irq to PHY_POLL. * Otherwise, we enable the interrupts in the PHY. - * Returns 0 on success. * This should only be called with a valid IRQ number. + * Returns 0 on success or < 0 on error. */ int phy_start_interrupts(struct phy_device *phydev) { @@ -574,6 +642,10 @@ int phy_start_interrupts(struct phy_device *phydev) } EXPORT_SYMBOL(phy_start_interrupts); +/** + * phy_stop_interrupts - disable interrupts from a PHY device + * @phydev: target phy_device struct + */ int phy_stop_interrupts(struct phy_device *phydev) { int err; @@ -596,7 +668,10 @@ int phy_stop_interrupts(struct phy_device *phydev) EXPORT_SYMBOL(phy_stop_interrupts); -/* Scheduled by the phy_interrupt/timer to handle PHY changes */ +/** + * phy_change - Scheduled by the phy_interrupt/timer to handle PHY changes + * @work: work_struct that describes the work to be done + */ static void phy_change(struct work_struct *work) { int err; @@ -630,7 +705,10 @@ phy_err: phy_error(phydev); } -/* Bring down the PHY link, and stop checking the status. */ +/** + * phy_stop - Bring down the PHY link, and stop checking the status + * @phydev: target phy_device struct + */ void phy_stop(struct phy_device *phydev) { spin_lock(&phydev->lock); @@ -659,9 +737,11 @@ out_unlock: } -/* phy_start +/** + * phy_start - start or restart a PHY device + * @phydev: target phy_device struct * - * description: Indicates the attached device's readiness to + * Description: Indicates the attached device's readiness to * handle PHY-related work. Used during startup to start the * PHY, and after a call to phy_stop() to resume operation. * Also used to indicate the MDIO bus has cleared an error diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index 8f01952c4850..a8b74cdab1ea 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -74,11 +74,13 @@ struct phy_device* phy_device_create(struct mii_bus *bus, int addr, int phy_id) } EXPORT_SYMBOL(phy_device_create); -/* get_phy_device +/** + * get_phy_device - reads the specified PHY device and returns its @phy_device struct + * @bus: the target MII bus + * @addr: PHY address on the MII bus * - * description: Reads the ID registers of the PHY at addr on the - * bus, then allocates and returns the phy_device to - * represent it. + * Description: Reads the ID registers of the PHY at @addr on the + * @bus, then allocates and returns the phy_device to represent it. */ struct phy_device * get_phy_device(struct mii_bus *bus, int addr) { @@ -112,23 +114,33 @@ struct phy_device * get_phy_device(struct mii_bus *bus, int addr) return dev; } -/* phy_prepare_link: +/** + * phy_prepare_link - prepares the PHY layer to monitor link status + * @phydev: target phy_device struct + * @handler: callback function for link status change notifications * - * description: Tells the PHY infrastructure to handle the + * Description: Tells the PHY infrastructure to handle the * gory details on monitoring link status (whether through * polling or an interrupt), and to call back to the * connected device driver when the link status changes. * If you want to monitor your own link state, don't call - * this function */ + * this function. + */ void phy_prepare_link(struct phy_device *phydev, void (*handler)(struct net_device *)) { phydev->adjust_link = handler; } -/* phy_connect: +/** + * phy_connect - connect an ethernet device to a PHY device + * @dev: the network device to connect + * @phy_id: the PHY device to connect + * @handler: callback function for state change notifications + * @flags: PHY device's dev_flags + * @interface: PHY device's interface * - * description: Convenience function for connecting ethernet + * Description: Convenience function for connecting ethernet * devices to PHY devices. The default behavior is for * the PHY infrastructure to handle everything, and only notify * the connected driver when the link status changes. If you @@ -158,6 +170,10 @@ struct phy_device * phy_connect(struct net_device *dev, const char *phy_id, } EXPORT_SYMBOL(phy_connect); +/** + * phy_disconnect - disable interrupts, stop state machine, and detach a PHY device + * @phydev: target phy_device struct + */ void phy_disconnect(struct phy_device *phydev) { if (phydev->irq > 0) @@ -171,21 +187,25 @@ void phy_disconnect(struct phy_device *phydev) } EXPORT_SYMBOL(phy_disconnect); -/* phy_attach: +static int phy_compare_id(struct device *dev, void *data) +{ + return strcmp((char *)data, dev->bus_id) ? 0 : 1; +} + +/** + * phy_attach - attach a network device to a particular PHY device + * @dev: network device to attach + * @phy_id: PHY device to attach + * @flags: PHY device's dev_flags + * @interface: PHY device's interface * - * description: Called by drivers to attach to a particular PHY + * Description: Called by drivers to attach to a particular PHY * device. The phy_device is found, and properly hooked up * to the phy_driver. If no driver is attached, then the * genphy_driver is used. The phy_device is given a ptr to * the attaching device, and given a callback for link status - * change. The phy_device is returned to the attaching - * driver. + * change. The phy_device is returned to the attaching driver. */ -static int phy_compare_id(struct device *dev, void *data) -{ - return strcmp((char *)data, dev->bus_id) ? 0 : 1; -} - struct phy_device *phy_attach(struct net_device *dev, const char *phy_id, u32 flags, phy_interface_t interface) { @@ -246,6 +266,10 @@ struct phy_device *phy_attach(struct net_device *dev, } EXPORT_SYMBOL(phy_attach); +/** + * phy_detach - detach a PHY device from its network device + * @phydev: target phy_device struct + */ void phy_detach(struct phy_device *phydev) { phydev->attached_dev = NULL; @@ -262,11 +286,13 @@ EXPORT_SYMBOL(phy_detach); /* Generic PHY support and helper functions */ -/* genphy_config_advert +/** + * genphy_config_advert - sanitize and advertise auto-negotation parameters + * @phydev: target phy_device struct * - * description: Writes MII_ADVERTISE with the appropriate values, + * Description: Writes MII_ADVERTISE with the appropriate values, * after sanitizing the values to make sure we only advertise - * what is supported + * what is supported. */ int genphy_config_advert(struct phy_device *phydev) { @@ -328,11 +354,14 @@ int genphy_config_advert(struct phy_device *phydev) } EXPORT_SYMBOL(genphy_config_advert); -/* genphy_setup_forced +/** + * genphy_setup_forced - configures/forces speed/duplex from @phydev + * @phydev: target phy_device struct * - * description: Configures MII_BMCR to force speed/duplex + * Description: Configures MII_BMCR to force speed/duplex * to the values in phydev. Assumes that the values are valid. - * Please see phy_sanitize_settings() */ + * Please see phy_sanitize_settings(). + */ int genphy_setup_forced(struct phy_device *phydev) { int ctl = BMCR_RESET; @@ -361,7 +390,10 @@ int genphy_setup_forced(struct phy_device *phydev) } -/* Enable and Restart Autonegotiation */ +/** + * genphy_restart_aneg - Enable and Restart Autonegotiation + * @phydev: target phy_device struct + */ int genphy_restart_aneg(struct phy_device *phydev) { int ctl; @@ -382,11 +414,13 @@ int genphy_restart_aneg(struct phy_device *phydev) } -/* genphy_config_aneg +/** + * genphy_config_aneg - restart auto-negotiation or write BMCR + * @phydev: target phy_device struct * - * description: If auto-negotiation is enabled, we configure the + * Description: If auto-negotiation is enabled, we configure the * advertising, and then restart auto-negotiation. If it is not - * enabled, then we write the BMCR + * enabled, then we write the BMCR. */ int genphy_config_aneg(struct phy_device *phydev) { @@ -406,11 +440,13 @@ int genphy_config_aneg(struct phy_device *phydev) } EXPORT_SYMBOL(genphy_config_aneg); -/* genphy_update_link +/** + * genphy_update_link - update link status in @phydev + * @phydev: target phy_device struct * - * description: Update the value in phydev->link to reflect the + * Description: Update the value in phydev->link to reflect the * current link value. In order to do this, we need to read - * the status register twice, keeping the second value + * the status register twice, keeping the second value. */ int genphy_update_link(struct phy_device *phydev) { @@ -437,9 +473,11 @@ int genphy_update_link(struct phy_device *phydev) } EXPORT_SYMBOL(genphy_update_link); -/* genphy_read_status +/** + * genphy_read_status - check the link status and update current link state + * @phydev: target phy_device struct * - * description: Check the link, then figure out the current state + * Description: Check the link, then figure out the current state * by comparing what we advertise with what the link partner * advertises. Start by checking the gigabit possibilities, * then move on to 10/100. @@ -579,9 +617,11 @@ static int genphy_config_init(struct phy_device *phydev) } -/* phy_probe +/** + * phy_probe - probe and init a PHY device + * @dev: device to probe and init * - * description: Take care of setting up the phy_device structure, + * Description: Take care of setting up the phy_device structure, * set the state to READY (the driver's init function should * set it to STARTING if needed). */ @@ -643,6 +683,10 @@ static int phy_remove(struct device *dev) return 0; } +/** + * phy_driver_register - register a phy_driver with the PHY layer + * @new_driver: new phy_driver to register + */ int phy_driver_register(struct phy_driver *new_driver) { int retval; -- cgit v1.2.3 From ecf2a80a97b3d38ae008fa8a3cb98cd540ac1eae Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Fri, 27 Apr 2007 22:00:09 -0300 Subject: ACPI: thinkpad-acpi: add a fan-control feature master toggle Len Brown considers that an active by default fan control interface in laptops may be too close to giving users enough rope. There is a good chance he is quite correct on this, especially if someone decides to use that interface in applets and users are not aware of its risks. This patch adds a master switch to thinkpad-acpi that enables or disables the entire fan-control feature as a module parameter: "fan_control". It defaults to disabled. Set it to non-zero to enable fan control. Also, the patch removes the expermiental status from fan control, since it is stable enough to not be called experimental, and the master switch makes it safe enough to do so. Signed-off-by: Henrique de Moraes Holschuh Signed-off-by: Len Brown --- Documentation/thinkpad-acpi.txt | 15 +++++++-------- drivers/misc/thinkpad_acpi.c | 30 +++++++++++++++++++++++++++++- drivers/misc/thinkpad_acpi.h | 2 ++ 3 files changed, 38 insertions(+), 9 deletions(-) (limited to 'Documentation') diff --git a/Documentation/thinkpad-acpi.txt b/Documentation/thinkpad-acpi.txt index eab4997efc0f..bca50d78a425 100644 --- a/Documentation/thinkpad-acpi.txt +++ b/Documentation/thinkpad-acpi.txt @@ -38,7 +38,7 @@ detailed description): - Experimental: embedded controller register dump - LCD brightness control - Volume control - - Experimental: fan speed, fan enable/disable + - Fan control and monitoring: fan speed, fan enable/disable - Experimental: WAN enable and disable A compatibility table by model and feature is maintained on the web @@ -681,21 +681,20 @@ distinct. The unmute the volume after the mute command, use either the up or down command (the level command will not unmute the volume). The current volume level and mute state is shown in the file. -EXPERIMENTAL: fan speed, fan enable/disable -------------------------------------------- +Fan control and monitoring: fan speed, fan enable/disable +--------------------------------------------------------- procfs: /proc/acpi/ibm/fan sysfs device attributes: (hwmon) fan_input, pwm1, pwm1_enable -This feature is marked EXPERIMENTAL because the implementation -directly accesses hardware registers and may not work as expected. USE -WITH CAUTION! To use this feature, you need to supply the -experimental=1 parameter when loading the module. +NOTE NOTE NOTE: fan control operations are disabled by default for +safety reasons. To enable them, the module parameter "fan_control=1" +must be given to thinkpad-acpi. This feature attempts to show the current fan speed, control mode and other fan data that might be available. The speed is read directly from the hardware registers of the embedded controller. This is known -to work on later R, T and X series ThinkPads but may show a bogus +to work on later R, T, X and Z series ThinkPads but may show a bogus value on other models. Fan levels: diff --git a/drivers/misc/thinkpad_acpi.c b/drivers/misc/thinkpad_acpi.c index c0a023cc5ded..7dc3a2206195 100644 --- a/drivers/misc/thinkpad_acpi.c +++ b/drivers/misc/thinkpad_acpi.c @@ -2935,6 +2935,9 @@ static ssize_t fan_fan_watchdog_store(struct device_driver *drv, if (parse_strtoul(buf, 120, &t)) return -EINVAL; + if (!fan_control_allowed) + return -EPERM; + fan_watchdog_maxinterval = t; fan_watchdog_reset(); @@ -3046,6 +3049,14 @@ static int __init fan_init(struct ibm_init_struct *iibm) fan_control_access_mode != TPACPI_FAN_WR_NONE), fan_status_access_mode, fan_control_access_mode); + /* fan control master switch */ + if (!fan_control_allowed) { + fan_control_access_mode = TPACPI_FAN_WR_NONE; + fan_control_commands = 0; + dbg_printk(TPACPI_DBG_INIT, + "fan control features disabled by parameter\n"); + } + /* update fan_control_desired_level */ if (fan_status_access_mode != TPACPI_FAN_NONE) fan_get_status_safe(NULL); @@ -3203,6 +3214,9 @@ static void fan_watchdog_reset(void) static int fan_set_level(int level) { + if (!fan_control_allowed) + return -EPERM; + switch (fan_control_access_mode) { case TPACPI_FAN_WR_ACPI_SFAN: if (level >= 0 && level <= 7) { @@ -3242,6 +3256,9 @@ static int fan_set_level_safe(int level) { int rc; + if (!fan_control_allowed) + return -EPERM; + rc = mutex_lock_interruptible(&fan_mutex); if (rc < 0) return rc; @@ -3262,6 +3279,9 @@ static int fan_set_enable(void) u8 s; int rc; + if (!fan_control_allowed) + return -EPERM; + rc = mutex_lock_interruptible(&fan_mutex); if (rc < 0) return rc; @@ -3315,6 +3335,9 @@ static int fan_set_disable(void) { int rc; + if (!fan_control_allowed) + return -EPERM; + rc = mutex_lock_interruptible(&fan_mutex); if (rc < 0) return rc; @@ -3351,6 +3374,9 @@ static int fan_set_speed(int speed) { int rc; + if (!fan_control_allowed) + return -EPERM; + rc = mutex_lock_interruptible(&fan_mutex); if (rc < 0) return rc; @@ -3558,7 +3584,6 @@ static struct ibm_struct fan_driver_data = { .read = fan_read, .write = fan_write, .exit = fan_exit, - .flags.experimental = 1, }; /**************************************************************************** @@ -3879,6 +3904,9 @@ module_param_named(debug, dbg_level, uint, 0); static int force_load; module_param(force_load, int, 0); +static int fan_control_allowed; +module_param_named(fan_control, fan_control_allowed, int, 0); + #define IBM_PARAM(feature) \ module_param_call(feature, set_ibm_param, NULL, NULL, 0) diff --git a/drivers/misc/thinkpad_acpi.h b/drivers/misc/thinkpad_acpi.h index 8348fc653009..a9e709368256 100644 --- a/drivers/misc/thinkpad_acpi.h +++ b/drivers/misc/thinkpad_acpi.h @@ -375,6 +375,8 @@ enum fan_control_commands { * and also watchdog cmd */ }; +static int fan_control_allowed; + static enum fan_status_access_mode fan_status_access_mode; static enum fan_control_access_mode fan_control_access_mode; static enum fan_control_commands fan_control_commands; -- cgit v1.2.3 From b39fe582eb9252dca9a62f7135bcad2e486083e5 Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Fri, 27 Apr 2007 22:00:13 -0300 Subject: ACPI: thinkpad-acpi: improve fan control documentation Improve fan control documentation and fix one mistake. Signed-off-by: Henrique de Moraes Holschuh Signed-off-by: Len Brown --- Documentation/thinkpad-acpi.txt | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) (limited to 'Documentation') diff --git a/Documentation/thinkpad-acpi.txt b/Documentation/thinkpad-acpi.txt index bca50d78a425..e3ad7a4f7402 100644 --- a/Documentation/thinkpad-acpi.txt +++ b/Documentation/thinkpad-acpi.txt @@ -795,15 +795,23 @@ Sysfs notes: The sysfs interface follows the hwmon subsystem guidelines for the most part, and the exception is the fan safety watchdog. +Writes to any of the sysfs attributes may return the EINVAL error if +that operation is not supported in a given ThinkPad or if the parameter +is out-of-bounds, and EPERM if it is forbidden. They may also return +EINTR (interrupted system call), and EIO (I/O error while trying to talk +to the firmware). + +Features not yet implemented by the driver return ENOSYS. + hwmon device attribute pwm1_enable: 0: PWM offline (fan is set to full-speed mode) 1: Manual PWM control (use pwm1 to set fan level) 2: Hardware PWM control (EC "auto" mode) 3: reserved (Software PWM control, not implemented yet) - Modes 0 and 2 are not supported by all ThinkPads, and the driver - is not always able to detect this. If it does know a mode is - unsupported, it will return -EINVAL. + Modes 0 and 2 are not supported by all ThinkPads, and the + driver is not always able to detect this. If it does know a + mode is unsupported, it will return -EINVAL. hwmon device attribute pwm1: Fan level, scaled from the firmware values of 0-7 to the hwmon @@ -826,8 +834,8 @@ driver attribute fan_watchdog: To stop the fan: set pwm1 to zero, and pwm1_enable to 1. To start the fan in a safe mode: set pwm1_enable to 2. If that fails -with ENOTSUP, set it to 1 and set pwm1 to at least 128 (255 would be the -safest choice, though). +with EINVAL, try to set pwm1_enable to 1 and pwm1 to at least 128 (255 +would be the safest choice, though). EXPERIMENTAL: WAN -- /proc/acpi/ibm/wan -- cgit v1.2.3 From a0416420e2c6244792d6f308183ad57c40532078 Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Fri, 27 Apr 2007 22:00:16 -0300 Subject: ACPI: thinkpad-acpi: add sysfs support to hotkey subdriver Add the hotkey sysfs support. Signed-off-by: Henrique de Moraes Holschuh Signed-off-by: Len Brown --- Documentation/thinkpad-acpi.txt | 58 ++++++++++++++---- drivers/misc/thinkpad_acpi.c | 127 ++++++++++++++++++++++++++++++++++++++++ drivers/misc/thinkpad_acpi.h | 2 + 3 files changed, 176 insertions(+), 11 deletions(-) (limited to 'Documentation') diff --git a/Documentation/thinkpad-acpi.txt b/Documentation/thinkpad-acpi.txt index e3ad7a4f7402..ebeed589f6d7 100644 --- a/Documentation/thinkpad-acpi.txt +++ b/Documentation/thinkpad-acpi.txt @@ -134,8 +134,11 @@ end of this document. Changes to the sysfs interface done by the kernel subsystems are not documented here, nor are they tracked by this attribute. -Hot keys -- /proc/acpi/ibm/hotkey ---------------------------------- +Hot keys +-------- + +procfs: /proc/acpi/ibm/hotkey +sysfs device attribute: hotkey/* Without this driver, only the Fn-F4 key (sleep button) generates an ACPI event. With the driver loaded, the hotkey feature enabled and the @@ -149,15 +152,6 @@ All labeled Fn-Fx key combinations generate distinct events. In addition, the lid microswitch and some docking station buttons may also generate such events. -The following commands can be written to this file: - - echo enable > /proc/acpi/ibm/hotkey -- enable the hot keys feature - echo disable > /proc/acpi/ibm/hotkey -- disable the hot keys feature - echo 0xffff > /proc/acpi/ibm/hotkey -- enable all possible hot keys - echo 0x0000 > /proc/acpi/ibm/hotkey -- disable all possible hot keys - ... any other 4-hex-digit mask ... - echo reset > /proc/acpi/ibm/hotkey -- restore the original mask - The bit mask allows some control over which hot keys generate ACPI events. Not all bits in the mask can be modified. Not all bits that can be modified do anything. Not all hot keys can be individually @@ -189,6 +183,48 @@ buttons do not generate ACPI events even with this driver. They *can* be used through the "ThinkPad Buttons" utility, see http://www.nongnu.org/tpb/ +procfs notes: + +The following commands can be written to the /proc/acpi/ibm/hotkey file: + + echo enable > /proc/acpi/ibm/hotkey -- enable the hot keys feature + echo disable > /proc/acpi/ibm/hotkey -- disable the hot keys feature + echo 0xffff > /proc/acpi/ibm/hotkey -- enable all possible hot keys + echo 0x0000 > /proc/acpi/ibm/hotkey -- disable all possible hot keys + ... any other 4-hex-digit mask ... + echo reset > /proc/acpi/ibm/hotkey -- restore the original mask + +sysfs notes: + + The hot keys attributes are in a hotkey/ subdirectory off the + thinkpad device. + + bios_enabled: + Returns the status of the hot keys feature when + thinkpad-acpi was loaded. Upon module unload, the hot + key feature status will be restored to this value. + + 0: hot keys were disabled + 1: hot keys were enabled + + bios_mask: + Returns the hot keys mask when thinkpad-acpi was loaded. + Upon module unload, the hot keys mask will be restored + to this value. + + enable: + Enables/disables the hot keys feature, and reports + current status of the hot keys feature. + + 0: disables the hot keys feature / feature disabled + 1: enables the hot keys feature / feature enabled + + mask: + bit mask to enable ACPI event generation for each hot + key (see above). Returns the current status of the hot + keys mask, and allows one to modify it. + + Bluetooth -- /proc/acpi/ibm/bluetooth ------------------------------------- diff --git a/drivers/misc/thinkpad_acpi.c b/drivers/misc/thinkpad_acpi.c index a56526500c18..83a8d984e709 100644 --- a/drivers/misc/thinkpad_acpi.c +++ b/drivers/misc/thinkpad_acpi.c @@ -706,6 +706,108 @@ static struct ibm_struct thinkpad_acpi_driver_data = { static int hotkey_orig_status; static int hotkey_orig_mask; +static struct attribute_set *hotkey_dev_attributes = NULL; + +/* sysfs hotkey enable ------------------------------------------------- */ +static ssize_t hotkey_enable_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int res, status, mask; + + res = hotkey_get(&status, &mask); + if (res) + return res; + + return snprintf(buf, PAGE_SIZE, "%d\n", status); +} + +static ssize_t hotkey_enable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long t; + int res, status, mask; + + if (parse_strtoul(buf, 1, &t)) + return -EINVAL; + + res = hotkey_get(&status, &mask); + if (!res) + res = hotkey_set(t, mask); + + return (res) ? res : count; +} + +static struct device_attribute dev_attr_hotkey_enable = + __ATTR(enable, S_IWUSR | S_IRUGO, + hotkey_enable_show, hotkey_enable_store); + +/* sysfs hotkey mask --------------------------------------------------- */ +static ssize_t hotkey_mask_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int res, status, mask; + + res = hotkey_get(&status, &mask); + if (res) + return res; + + return snprintf(buf, PAGE_SIZE, "0x%04x\n", mask); +} + +static ssize_t hotkey_mask_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long t; + int res, status, mask; + + if (parse_strtoul(buf, 0xffff, &t)) + return -EINVAL; + + res = hotkey_get(&status, &mask); + if (!res) + hotkey_set(status, t); + + return (res) ? res : count; +} + +static struct device_attribute dev_attr_hotkey_mask = + __ATTR(mask, S_IWUSR | S_IRUGO, + hotkey_mask_show, hotkey_mask_store); + +/* sysfs hotkey bios_enabled ------------------------------------------- */ +static ssize_t hotkey_bios_enabled_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%d\n", hotkey_orig_status); +} + +static struct device_attribute dev_attr_hotkey_bios_enabled = + __ATTR(bios_enabled, S_IRUGO, hotkey_bios_enabled_show, NULL); + +/* sysfs hotkey bios_mask ---------------------------------------------- */ +static ssize_t hotkey_bios_mask_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return snprintf(buf, PAGE_SIZE, "0x%04x\n", hotkey_orig_mask); +} + +static struct device_attribute dev_attr_hotkey_bios_mask = + __ATTR(bios_mask, S_IRUGO, hotkey_bios_mask_show, NULL); + +/* --------------------------------------------------------------------- */ + +static struct attribute *hotkey_mask_attributes[] = { + &dev_attr_hotkey_mask.attr, + &dev_attr_hotkey_bios_enabled.attr, + &dev_attr_hotkey_bios_mask.attr, +}; + static int __init hotkey_init(struct ibm_init_struct *iibm) { int res; @@ -722,6 +824,15 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) str_supported(tp_features.hotkey)); if (tp_features.hotkey) { + hotkey_dev_attributes = create_attr_set(4, + TPACPI_HOTKEY_SYSFS_GROUP); + if (!hotkey_dev_attributes) + return -ENOMEM; + res = add_to_attr_set(hotkey_dev_attributes, + &dev_attr_hotkey_enable.attr); + if (res) + return res; + /* mask not supported on 570, 600e/x, 770e, 770x, A21e, A2xm/p, A30, R30, R31, T20-22, X20-21, X22-24 */ tp_features.hotkey_mask = @@ -731,6 +842,16 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) str_supported(tp_features.hotkey_mask)); res = hotkey_get(&hotkey_orig_status, &hotkey_orig_mask); + if (!res && tp_features.hotkey_mask) { + res = add_many_to_attr_set(hotkey_dev_attributes, + hotkey_mask_attributes, + ARRAY_SIZE(hotkey_mask_attributes)); + } + if (!res) + res = register_attr_set_with_sysfs( + hotkey_dev_attributes, + &tpacpi_pdev->dev.kobj); + if (res) return res; } @@ -748,6 +869,11 @@ static void hotkey_exit(void) if (res) printk(IBM_ERR "failed to restore hotkey to BIOS defaults\n"); } + + if (hotkey_dev_attributes) { + delete_attr_set(hotkey_dev_attributes, &tpacpi_pdev->dev.kobj); + hotkey_dev_attributes = NULL; + } } static void hotkey_notify(struct ibm_struct *ibm, u32 event) @@ -798,6 +924,7 @@ static int hotkey_set(int status, int mask) return 0; } +/* procfs -------------------------------------------------------------- */ static int hotkey_read(char *p) { int res, status, mask; diff --git a/drivers/misc/thinkpad_acpi.h b/drivers/misc/thinkpad_acpi.h index a9e709368256..7615adb381c8 100644 --- a/drivers/misc/thinkpad_acpi.h +++ b/drivers/misc/thinkpad_acpi.h @@ -414,6 +414,8 @@ static int fan_write_cmd_watchdog(const char *cmd, int *rc); * Hotkey subdriver */ +#define TPACPI_HOTKEY_SYSFS_GROUP "hotkey" + static int hotkey_orig_status; static int hotkey_orig_mask; -- cgit v1.2.3 From d3a6ade4f84416d774c3e5db5faae1840d55bd97 Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Fri, 27 Apr 2007 22:00:17 -0300 Subject: ACPI: thinkpad-acpi: add sysfs support to wan and bluetooth subdrivers Add support to sysfs to the wan and bluetooth subdrivers. Signed-off-by: Henrique de Moraes Holschuh Signed-off-by: Len Brown --- Documentation/thinkpad-acpi.txt | 61 ++++++++++++++--- drivers/misc/thinkpad_acpi.c | 144 ++++++++++++++++++++++++++++++++++++---- drivers/misc/thinkpad_acpi.h | 4 ++ 3 files changed, 186 insertions(+), 23 deletions(-) (limited to 'Documentation') diff --git a/Documentation/thinkpad-acpi.txt b/Documentation/thinkpad-acpi.txt index ebeed589f6d7..2d4803359a04 100644 --- a/Documentation/thinkpad-acpi.txt +++ b/Documentation/thinkpad-acpi.txt @@ -225,15 +225,35 @@ sysfs notes: keys mask, and allows one to modify it. -Bluetooth -- /proc/acpi/ibm/bluetooth -------------------------------------- +Bluetooth +--------- -This feature shows the presence and current state of a Bluetooth -device. If Bluetooth is installed, the following commands can be used: +procfs: /proc/acpi/ibm/bluetooth +sysfs device attribute: bluetooth/enable + +This feature shows the presence and current state of a ThinkPad +Bluetooth device in the internal ThinkPad CDC slot. + +Procfs notes: + +If Bluetooth is installed, the following commands can be used: echo enable > /proc/acpi/ibm/bluetooth echo disable > /proc/acpi/ibm/bluetooth +Sysfs notes: + + If the Bluetooth CDC card is installed, it can be enabled / + disabled through the "bluetooth/enable" thinkpad-acpi device + attribute, and its current status can also be queried. + + enable: + 0: disables Bluetooth / Bluetooth is disabled + 1: enables Bluetooth / Bluetooth is enabled. + + Note: this interface will be probably be superseeded by the + generic rfkill class. + Video output control -- /proc/acpi/ibm/video -------------------------------------------- @@ -874,23 +894,42 @@ with EINVAL, try to set pwm1_enable to 1 and pwm1 to at least 128 (255 would be the safest choice, though). -EXPERIMENTAL: WAN -- /proc/acpi/ibm/wan ---------------------------------------- +EXPERIMENTAL: WAN +----------------- + +procfs: /proc/acpi/ibm/wan +sysfs device attribute: wwan/enable This feature is marked EXPERIMENTAL because the implementation directly accesses hardware registers and may not work as expected. USE WITH CAUTION! To use this feature, you need to supply the experimental=1 parameter when loading the module. -This feature shows the presence and current state of a WAN (Sierra -Wireless EV-DO) device. If WAN is installed, the following commands can -be used: +This feature shows the presence and current state of a W-WAN (Sierra +Wireless EV-DO) device. + +It was tested on a Lenovo Thinkpad X60. It should probably work on other +Thinkpad models which come with this module installed. + +Procfs notes: + +If the W-WAN card is installed, the following commands can be used: echo enable > /proc/acpi/ibm/wan echo disable > /proc/acpi/ibm/wan -It was tested on a Lenovo Thinkpad X60. It should probably work on other -Thinkpad models which come with this module installed. +Sysfs notes: + + If the W-WAN card is installed, it can be enabled / + disabled through the "wwan/enable" thinkpad-acpi device + attribute, and its current status can also be queried. + + enable: + 0: disables WWAN card / WWAN card is disabled + 1: enables WWAN card / WWAN card is enabled. + + Note: this interface will be probably be superseeded by the + generic rfkill class. Multiple Commands, Module Parameters ------------------------------------ diff --git a/drivers/misc/thinkpad_acpi.c b/drivers/misc/thinkpad_acpi.c index 83a8d984e709..6c36a55cb3d1 100644 --- a/drivers/misc/thinkpad_acpi.c +++ b/drivers/misc/thinkpad_acpi.c @@ -1020,8 +1020,54 @@ static struct ibm_struct hotkey_driver_data = { * Bluetooth subdriver */ +/* sysfs bluetooth enable ---------------------------------------------- */ +static ssize_t bluetooth_enable_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int status; + + status = bluetooth_get_radiosw(); + if (status < 0) + return status; + + return snprintf(buf, PAGE_SIZE, "%d\n", status ? 1 : 0); +} + +static ssize_t bluetooth_enable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long t; + int res; + + if (parse_strtoul(buf, 1, &t)) + return -EINVAL; + + res = bluetooth_set_radiosw(t); + + return (res) ? res : count; +} + +static struct device_attribute dev_attr_bluetooth_enable = + __ATTR(enable, S_IWUSR | S_IRUGO, + bluetooth_enable_show, bluetooth_enable_store); + +/* --------------------------------------------------------------------- */ + +static struct attribute *bluetooth_attributes[] = { + &dev_attr_bluetooth_enable.attr, + NULL +}; + +static const struct attribute_group bluetooth_attr_group = { + .name = TPACPI_BLUETH_SYSFS_GROUP, + .attrs = bluetooth_attributes, +}; + static int __init bluetooth_init(struct ibm_init_struct *iibm) { + int res; int status = 0; vdbg_printk(TPACPI_DBG_INIT, "initializing bluetooth subdriver\n"); @@ -1037,17 +1083,29 @@ static int __init bluetooth_init(struct ibm_init_struct *iibm) str_supported(tp_features.bluetooth), status); - if (tp_features.bluetooth && - !(status & TP_ACPI_BLUETOOTH_HWPRESENT)) { - /* no bluetooth hardware present in system */ - tp_features.bluetooth = 0; - dbg_printk(TPACPI_DBG_INIT, - "bluetooth hardware not installed\n"); + if (tp_features.bluetooth) { + if (!(status & TP_ACPI_BLUETOOTH_HWPRESENT)) { + /* no bluetooth hardware present in system */ + tp_features.bluetooth = 0; + dbg_printk(TPACPI_DBG_INIT, + "bluetooth hardware not installed\n"); + } else { + res = sysfs_create_group(&tpacpi_pdev->dev.kobj, + &bluetooth_attr_group); + if (res) + return res; + } } return (tp_features.bluetooth)? 0 : 1; } +static void bluetooth_exit(void) +{ + sysfs_remove_group(&tpacpi_pdev->dev.kobj, + &bluetooth_attr_group); +} + static int bluetooth_get_radiosw(void) { int status; @@ -1080,6 +1138,7 @@ static int bluetooth_set_radiosw(int radio_on) return 0; } +/* procfs -------------------------------------------------------------- */ static int bluetooth_read(char *p) { int len = 0; @@ -1119,14 +1178,61 @@ static struct ibm_struct bluetooth_driver_data = { .name = "bluetooth", .read = bluetooth_read, .write = bluetooth_write, + .exit = bluetooth_exit, }; /************************************************************************* * Wan subdriver */ +/* sysfs wan enable ---------------------------------------------------- */ +static ssize_t wan_enable_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int status; + + status = wan_get_radiosw(); + if (status < 0) + return status; + + return snprintf(buf, PAGE_SIZE, "%d\n", status ? 1 : 0); +} + +static ssize_t wan_enable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long t; + int res; + + if (parse_strtoul(buf, 1, &t)) + return -EINVAL; + + res = wan_set_radiosw(t); + + return (res) ? res : count; +} + +static struct device_attribute dev_attr_wan_enable = + __ATTR(enable, S_IWUSR | S_IRUGO, + wan_enable_show, wan_enable_store); + +/* --------------------------------------------------------------------- */ + +static struct attribute *wan_attributes[] = { + &dev_attr_wan_enable.attr, + NULL +}; + +static const struct attribute_group wan_attr_group = { + .name = TPACPI_WAN_SYSFS_GROUP, + .attrs = wan_attributes, +}; + static int __init wan_init(struct ibm_init_struct *iibm) { + int res; int status = 0; vdbg_printk(TPACPI_DBG_INIT, "initializing wan subdriver\n"); @@ -1140,17 +1246,29 @@ static int __init wan_init(struct ibm_init_struct *iibm) str_supported(tp_features.wan), status); - if (tp_features.wan && - !(status & TP_ACPI_WANCARD_HWPRESENT)) { - /* no wan hardware present in system */ - tp_features.wan = 0; - dbg_printk(TPACPI_DBG_INIT, - "wan hardware not installed\n"); + if (tp_features.wan) { + if (!(status & TP_ACPI_WANCARD_HWPRESENT)) { + /* no wan hardware present in system */ + tp_features.wan = 0; + dbg_printk(TPACPI_DBG_INIT, + "wan hardware not installed\n"); + } else { + res = sysfs_create_group(&tpacpi_pdev->dev.kobj, + &wan_attr_group); + if (res) + return res; + } } return (tp_features.wan)? 0 : 1; } +static void wan_exit(void) +{ + sysfs_remove_group(&tpacpi_pdev->dev.kobj, + &wan_attr_group); +} + static int wan_get_radiosw(void) { int status; @@ -1183,6 +1301,7 @@ static int wan_set_radiosw(int radio_on) return 0; } +/* procfs -------------------------------------------------------------- */ static int wan_read(char *p) { int len = 0; @@ -1222,6 +1341,7 @@ static struct ibm_struct wan_driver_data = { .name = "wan", .read = wan_read, .write = wan_write, + .exit = wan_exit, .flags.experimental = 1, }; diff --git a/drivers/misc/thinkpad_acpi.h b/drivers/misc/thinkpad_acpi.h index 7615adb381c8..a6c285585863 100644 --- a/drivers/misc/thinkpad_acpi.h +++ b/drivers/misc/thinkpad_acpi.h @@ -278,6 +278,8 @@ static int beep_write(char *buf); * Bluetooth subdriver */ +#define TPACPI_BLUETH_SYSFS_GROUP "bluetooth" + enum { /* ACPI GBDC/SBDC bits */ TP_ACPI_BLUETOOTH_HWPRESENT = 0x01, /* Bluetooth hw available */ @@ -551,6 +553,8 @@ static int volume_write(char *buf); * Wan subdriver */ +#define TPACPI_WAN_SYSFS_GROUP "wwan" + enum { /* ACPI GWAN/SWAN bits */ TP_ACPI_WANCARD_HWPRESENT = 0x01, /* Wan hw available */ -- cgit v1.2.3 From cbefb762b67fa6d3eb2a48ae3380358a940e8c9d Mon Sep 17 00:00:00 2001 From: "malattia@linux.it" Date: Sat, 28 Apr 2007 23:36:26 +0900 Subject: meye: make meye use sony-laptop instead of sonypi Change sonypi_camera_command() calls to sony_pic_camera_command() and use the renamed macros. Signed-off-by: Mattia Dongili Signed-off-by: Len Brown --- Documentation/video4linux/meye.txt | 7 ++--- drivers/media/video/Kconfig | 6 ++-- drivers/media/video/meye.c | 62 +++++++++++++++++++------------------- drivers/media/video/meye.h | 2 +- 4 files changed, 38 insertions(+), 39 deletions(-) (limited to 'Documentation') diff --git a/Documentation/video4linux/meye.txt b/Documentation/video4linux/meye.txt index ecb34160e61d..5e51c59bf2b0 100644 --- a/Documentation/video4linux/meye.txt +++ b/Documentation/video4linux/meye.txt @@ -5,10 +5,9 @@ Vaio Picturebook Motion Eye Camera Driver Readme Copyright (C) 2000 Andrew Tridgell This driver enable the use of video4linux compatible applications with the -Motion Eye camera. This driver requires the "Sony Vaio Programmable I/O -Control Device" driver (which can be found in the "Character drivers" -section of the kernel configuration utility) to be compiled and installed -(using its "camera=1" parameter). +Motion Eye camera. This driver requires the "Sony Laptop Extras" driver (which +can be found in the "Misc devices" section of the kernel configuration utility) +to be compiled and installed (using its "camera=1" parameter). It can do at maximum 30 fps @ 320x240 or 15 fps @ 640x480. diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index 7a6105153f23..ff6aefde10df 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -577,14 +577,14 @@ config VIDEO_ZORAN_AVS6EYES config VIDEO_MEYE tristate "Sony Vaio Picturebook Motion Eye Video For Linux" - depends on PCI && SONYPI && VIDEO_V4L1 + depends on PCI && SONY_LAPTOP && VIDEO_V4L1 ---help--- This is the video4linux driver for the Motion Eye camera found in the Vaio Picturebook laptops. Please read the material in for more information. - If you say Y or M here, you need to say Y or M to "Sony Programmable - I/O Control Device" in the character device section. + If you say Y or M here, you need to say Y or M to "Sony Laptop + Extras" in the misc device section. To compile this driver as a module, choose M here: the module will be called meye. diff --git a/drivers/media/video/meye.c b/drivers/media/video/meye.c index 98681da5e3b9..664aba8b4d85 100644 --- a/drivers/media/video/meye.c +++ b/drivers/media/video/meye.c @@ -925,13 +925,13 @@ static int meye_do_ioctl(struct inode *inode, struct file *file, if (p->palette != VIDEO_PALETTE_YUV422 && p->palette != VIDEO_PALETTE_YUYV) return -EINVAL; mutex_lock(&meye.lock); - sonypi_camera_command(SONYPI_COMMAND_SETCAMERABRIGHTNESS, + sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERABRIGHTNESS, p->brightness >> 10); - sonypi_camera_command(SONYPI_COMMAND_SETCAMERAHUE, + sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERAHUE, p->hue >> 10); - sonypi_camera_command(SONYPI_COMMAND_SETCAMERACOLOR, + sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERACOLOR, p->colour >> 10); - sonypi_camera_command(SONYPI_COMMAND_SETCAMERACONTRAST, + sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERACONTRAST, p->contrast >> 10); meye.picture = *p; mutex_unlock(&meye.lock); @@ -1043,11 +1043,11 @@ static int meye_do_ioctl(struct inode *inode, struct file *file, meye.params.quality != jp->quality) mchip_hic_stop(); /* need restart */ meye.params = *jp; - sonypi_camera_command(SONYPI_COMMAND_SETCAMERASHARPNESS, + sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERASHARPNESS, meye.params.sharpness); - sonypi_camera_command(SONYPI_COMMAND_SETCAMERAAGC, + sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERAAGC, meye.params.agc); - sonypi_camera_command(SONYPI_COMMAND_SETCAMERAPICTURE, + sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERAPICTURE, meye.params.picture); mutex_unlock(&meye.lock); break; @@ -1287,38 +1287,38 @@ static int meye_do_ioctl(struct inode *inode, struct file *file, mutex_lock(&meye.lock); switch (c->id) { case V4L2_CID_BRIGHTNESS: - sonypi_camera_command( - SONYPI_COMMAND_SETCAMERABRIGHTNESS, c->value); + sony_pic_camera_command( + SONY_PIC_COMMAND_SETCAMERABRIGHTNESS, c->value); meye.picture.brightness = c->value << 10; break; case V4L2_CID_HUE: - sonypi_camera_command( - SONYPI_COMMAND_SETCAMERAHUE, c->value); + sony_pic_camera_command( + SONY_PIC_COMMAND_SETCAMERAHUE, c->value); meye.picture.hue = c->value << 10; break; case V4L2_CID_CONTRAST: - sonypi_camera_command( - SONYPI_COMMAND_SETCAMERACONTRAST, c->value); + sony_pic_camera_command( + SONY_PIC_COMMAND_SETCAMERACONTRAST, c->value); meye.picture.contrast = c->value << 10; break; case V4L2_CID_SATURATION: - sonypi_camera_command( - SONYPI_COMMAND_SETCAMERACOLOR, c->value); + sony_pic_camera_command( + SONY_PIC_COMMAND_SETCAMERACOLOR, c->value); meye.picture.colour = c->value << 10; break; case V4L2_CID_AGC: - sonypi_camera_command( - SONYPI_COMMAND_SETCAMERAAGC, c->value); + sony_pic_camera_command( + SONY_PIC_COMMAND_SETCAMERAAGC, c->value); meye.params.agc = c->value; break; case V4L2_CID_SHARPNESS: - sonypi_camera_command( - SONYPI_COMMAND_SETCAMERASHARPNESS, c->value); + sony_pic_camera_command( + SONY_PIC_COMMAND_SETCAMERASHARPNESS, c->value); meye.params.sharpness = c->value; break; case V4L2_CID_PICTURE: - sonypi_camera_command( - SONYPI_COMMAND_SETCAMERAPICTURE, c->value); + sony_pic_camera_command( + SONY_PIC_COMMAND_SETCAMERAPICTURE, c->value); meye.params.picture = c->value; break; case V4L2_CID_JPEGQUAL: @@ -1848,7 +1848,7 @@ static int __devinit meye_probe(struct pci_dev *pcidev, memcpy(meye.video_dev, &meye_template, sizeof(meye_template)); meye.video_dev->dev = &meye.mchip_dev->dev; - if ((ret = sonypi_camera_command(SONYPI_COMMAND_SETCAMERA, 1))) { + if ((ret = sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERA, 1))) { printk(KERN_ERR "meye: unable to power on the camera\n"); printk(KERN_ERR "meye: did you enable the camera in " "sonypi using the module options ?\n"); @@ -1928,13 +1928,13 @@ static int __devinit meye_probe(struct pci_dev *pcidev, meye.params.picture = 0; meye.params.framerate = 0; - sonypi_camera_command(SONYPI_COMMAND_SETCAMERABRIGHTNESS, 32); - sonypi_camera_command(SONYPI_COMMAND_SETCAMERAHUE, 32); - sonypi_camera_command(SONYPI_COMMAND_SETCAMERACOLOR, 32); - sonypi_camera_command(SONYPI_COMMAND_SETCAMERACONTRAST, 32); - sonypi_camera_command(SONYPI_COMMAND_SETCAMERASHARPNESS, 32); - sonypi_camera_command(SONYPI_COMMAND_SETCAMERAPICTURE, 0); - sonypi_camera_command(SONYPI_COMMAND_SETCAMERAAGC, 48); + sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERABRIGHTNESS, 32); + sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERAHUE, 32); + sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERACOLOR, 32); + sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERACONTRAST, 32); + sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERASHARPNESS, 32); + sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERAPICTURE, 0); + sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERAAGC, 48); printk(KERN_INFO "meye: Motion Eye Camera Driver v%s.\n", MEYE_DRIVER_VERSION); @@ -1953,7 +1953,7 @@ outremap: outregions: pci_disable_device(meye.mchip_dev); outenabledev: - sonypi_camera_command(SONYPI_COMMAND_SETCAMERA, 0); + sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERA, 0); outsonypienable: kfifo_free(meye.doneq); outkfifoalloc2: @@ -1986,7 +1986,7 @@ static void __devexit meye_remove(struct pci_dev *pcidev) pci_disable_device(meye.mchip_dev); - sonypi_camera_command(SONYPI_COMMAND_SETCAMERA, 0); + sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERA, 0); kfifo_free(meye.doneq); kfifo_free(meye.grabq); diff --git a/drivers/media/video/meye.h b/drivers/media/video/meye.h index ea107cb5c845..323d0074120d 100644 --- a/drivers/media/video/meye.h +++ b/drivers/media/video/meye.h @@ -255,7 +255,7 @@ /****************************************************************************/ /* Sony Programmable I/O Controller for accessing the camera commands */ -#include +#include /* private API definitions */ #include -- cgit v1.2.3 From 11d77d0c01b80e44c7aceb21928508dafce774f9 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 30 Apr 2007 15:09:53 -0700 Subject: power management: remove firmware disk mode This patch removes the firmware disk suspend mode which is the wrong approach, it is supposed to be used for implementing firmware-based disk suspend but cannot actually be used for that. Signed-off-by: Johannes Berg Acked-by: Pavel Machek Cc: Cc: David Brownell Cc: Len Brown Acked-by: Russell King Cc: Greg KH Cc: "Rafael J. Wysocki" Cc: Paul Mundt Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/power/interface.txt | 21 +++++---------------- Documentation/power/states.txt | 13 +++++++------ Documentation/power/swsusp.txt | 14 +++++--------- include/linux/pm.h | 13 ++++++------- kernel/power/disk.c | 27 +++++++++++---------------- 5 files changed, 34 insertions(+), 54 deletions(-) (limited to 'Documentation') diff --git a/Documentation/power/interface.txt b/Documentation/power/interface.txt index 74311d7e0f3c..8c5b41bf3f36 100644 --- a/Documentation/power/interface.txt +++ b/Documentation/power/interface.txt @@ -18,17 +18,10 @@ states. /sys/power/disk controls the operating mode of the suspend-to-disk -mechanism. Suspend-to-disk can be handled in several ways. The -greatest distinction is who writes memory to disk - the firmware or -the kernel. If the firmware does it, we assume that it also handles -suspending the system. - -If the kernel does it, then we have three options for putting the system -to sleep - using the platform driver (e.g. ACPI or other PM -registers), powering off the system or rebooting the system (for -testing). The system will support either 'firmware' or 'platform', and -that is known a priori. But, the user may choose 'shutdown' or -'reboot' as alternatives. +mechanism. Suspend-to-disk can be handled in several ways. We have a +few options for putting the system to sleep - using the platform driver +(e.g. ACPI or other pm_ops), powering off the system or rebooting the +system (for testing). Additionally, /sys/power/disk can be used to turn on one of the two testing modes of the suspend-to-disk mechanism: 'testproc' or 'test'. If the @@ -44,16 +37,12 @@ is being slow and which device drivers are misbehaving. Reading from this file will display what the mode is currently set to. Writing to this file will accept one of - 'firmware' - 'platform' + 'platform' (only if the platform supports it) 'shutdown' 'reboot' 'testproc' 'test' -It will only change to 'firmware' or 'platform' if the system supports -it. - /sys/power/image_size controls the size of the image created by the suspend-to-disk mechanism. It can be written a string representing a non-negative integer that will be used as an upper diff --git a/Documentation/power/states.txt b/Documentation/power/states.txt index 0931a330d362..34800cc521bf 100644 --- a/Documentation/power/states.txt +++ b/Documentation/power/states.txt @@ -62,17 +62,18 @@ setup via another operating system for it to use. Despite the inconvenience, this method requires minimal work by the kernel, since the firmware will also handle restoring memory contents on resume. -If the kernel is responsible for persistently saving state, a mechanism -called 'swsusp' (Swap Suspend) is used to write memory contents to -free swap space. swsusp has some restrictive requirements, but should -work in most cases. Some, albeit outdated, documentation can be found -in Documentation/power/swsusp.txt. +For suspend-to-disk, a mechanism called swsusp called 'swsusp' (Swap +Suspend) is used to write memory contents to free swap space. +swsusp has some restrictive requirements, but should work in most +cases. Some, albeit outdated, documentation can be found in +Documentation/power/swsusp.txt. Alternatively, userspace can do most +of the actual suspend to disk work, see userland-swsusp.txt. Once memory state is written to disk, the system may either enter a low-power state (like ACPI S4), or it may simply power down. Powering down offers greater savings, and allows this mechanism to work on any system. However, entering a real low-power state allows the user to -trigger wake up events (e.g. pressing a key or opening a laptop lid). +trigger wake up events (e.g. pressing a key or opening a laptop lid). A transition from Suspend-to-Disk to the On state should take about 30 seconds, though it's typically a bit more with the current diff --git a/Documentation/power/swsusp.txt b/Documentation/power/swsusp.txt index 0761ff6c57ed..c55bd5079b90 100644 --- a/Documentation/power/swsusp.txt +++ b/Documentation/power/swsusp.txt @@ -156,8 +156,7 @@ instead set the PF_NOFREEZE process flag when creating the thread (and be very careful). -Q: What is the difference between "platform", "shutdown" and -"firmware" in /sys/power/disk? +Q: What is the difference between "platform" and "shutdown"? A: @@ -166,11 +165,8 @@ shutdown: save state in linux, then tell bios to powerdown platform: save state in linux, then tell bios to powerdown and blink "suspended led" -firmware: tell bios to save state itself [needs BIOS-specific suspend - partition, and has very little to do with swsusp] - -"platform" is actually right thing to do, but "shutdown" is most -reliable. +"platform" is actually right thing to do where supported, but +"shutdown" is most reliable (except on ACPI systems). Q: I do not understand why you have such strong objections to idea of selective suspend. @@ -388,8 +384,8 @@ while the system is asleep, maintaining the connection, using true sleep modes like "suspend-to-RAM" or "standby". (Don't write "disk" to the /sys/power/state file; write "standby" or "mem".) We've not seen any hardware that can use these modes through software suspend, although in -theory some systems might support "platform" or "firmware" modes that -won't break the USB connections. +theory some systems might support "platform" modes that won't break the +USB connections. Remember that it's always a bad idea to unplug a disk drive containing a mounted filesystem. That's true even when your system is asleep! The diff --git a/include/linux/pm.h b/include/linux/pm.h index dfced9188bdc..c2a55f94c29a 100644 --- a/include/linux/pm.h +++ b/include/linux/pm.h @@ -114,13 +114,12 @@ typedef int __bitwise suspend_disk_method_t; /* invalid must be 0 so struct pm_ops initialisers can leave it out */ #define PM_DISK_INVALID ((__force suspend_disk_method_t) 0) -#define PM_DISK_FIRMWARE ((__force suspend_disk_method_t) 1) -#define PM_DISK_PLATFORM ((__force suspend_disk_method_t) 2) -#define PM_DISK_SHUTDOWN ((__force suspend_disk_method_t) 3) -#define PM_DISK_REBOOT ((__force suspend_disk_method_t) 4) -#define PM_DISK_TEST ((__force suspend_disk_method_t) 5) -#define PM_DISK_TESTPROC ((__force suspend_disk_method_t) 6) -#define PM_DISK_MAX ((__force suspend_disk_method_t) 7) +#define PM_DISK_PLATFORM ((__force suspend_disk_method_t) 1) +#define PM_DISK_SHUTDOWN ((__force suspend_disk_method_t) 2) +#define PM_DISK_REBOOT ((__force suspend_disk_method_t) 3) +#define PM_DISK_TEST ((__force suspend_disk_method_t) 4) +#define PM_DISK_TESTPROC ((__force suspend_disk_method_t) 5) +#define PM_DISK_MAX ((__force suspend_disk_method_t) 6) /** * struct pm_ops - Callbacks for managing platform dependent suspend states. diff --git a/kernel/power/disk.c b/kernel/power/disk.c index 4de2f69fe095..02e4fb69111a 100644 --- a/kernel/power/disk.c +++ b/kernel/power/disk.c @@ -122,8 +122,6 @@ static int prepare_processes(void) /** * pm_suspend_disk - The granpappy of hibernation power management. * - * If we're going through the firmware, then get it over with quickly. - * * If not, then call swsusp to do its thing, then figure out how * to power down the system. */ @@ -292,7 +290,6 @@ late_initcall(software_resume); static const char * const pm_disk_modes[] = { - [PM_DISK_FIRMWARE] = "firmware", [PM_DISK_PLATFORM] = "platform", [PM_DISK_SHUTDOWN] = "shutdown", [PM_DISK_REBOOT] = "reboot", @@ -303,27 +300,25 @@ static const char * const pm_disk_modes[] = { /** * disk - Control suspend-to-disk mode * - * Suspend-to-disk can be handled in several ways. The greatest - * distinction is who writes memory to disk - the firmware or the OS. - * If the firmware does it, we assume that it also handles suspending - * the system. - * If the OS does it, then we have three options for putting the system - * to sleep - using the platform driver (e.g. ACPI or other PM registers), - * powering off the system or rebooting the system (for testing). + * Suspend-to-disk can be handled in several ways. We have a few options + * for putting the system to sleep - using the platform driver (e.g. ACPI + * or other pm_ops), powering off the system or rebooting the system + * (for testing) as well as the two test modes. * - * The system will support either 'firmware' or 'platform', and that is - * known a priori (and encoded in pm_ops). But, the user may choose - * 'shutdown' or 'reboot' as alternatives. + * The system can support 'platform', and that is known a priori (and + * encoded in pm_ops). However, the user may choose 'shutdown' or 'reboot' + * as alternatives, as well as the test modes 'test' and 'testproc'. * * show() will display what the mode is currently set to. * store() will accept one of * - * 'firmware' * 'platform' * 'shutdown' * 'reboot' + * 'test' + * 'testproc' * - * It will only change to 'firmware' or 'platform' if the system + * It will only change to 'platform' if the system * supports it (as determined from pm_ops->pm_disk_mode). */ @@ -345,7 +340,7 @@ static ssize_t disk_store(struct subsystem * s, const char * buf, size_t n) len = p ? p - buf : n; mutex_lock(&pm_mutex); - for (i = PM_DISK_FIRMWARE; i < PM_DISK_MAX; i++) { + for (i = PM_DISK_PLATFORM; i < PM_DISK_MAX; i++) { if (!strncmp(buf, pm_disk_modes[i], len)) { mode = i; break; -- cgit v1.2.3