S: 70110 Kuopio
S: Finland
+N: Luca Risolia
+E: luca_ing@libero.it
+D: V4L driver for W996[87]CF JPEG USB Dual Mode Camera Chip
+S: Via Libertà 41/a
+S: Osio Sotto, 24046, Bergamo
+S: Italy
+
N: William E. Roadcap
E: roadcapw@cfw.com
W: http://www.cfw.com/~roadcapw
# alphabetically. Leonard used to be very proud of being the
# last entry, and he'll get positively pissed if he can't even
# be second-to-last. (and this file really _is_ supposed to be
-# in alphabetic order)
+# in alphabetic order)
0 = /dev/usb/lp0 First USB printer
...
15 = /dev/usb/lp15 16th USB printer
- 16 = /dev/usb/mouse0 First USB mouse
- ...
- 31 = /dev/usb/mouse15 16th USB mouse
- 32 = /dev/usb/ez0 First USB firmware loader
- ...
- 47 = /dev/usb/ez15 16th USB firmware loader
+ 32 = /dev/usb/mdc800 MDC800 USB camera
48 = /dev/usb/scanner0 First USB scanner
...
63 = /dev/usb/scanner15 16th USB scanner
64 = /dev/usb/rio500 Diamond Rio 500
+ 96 = /dev/usb/hiddev0 1st USB HID device
+ ...
+ 111 = /dev/usb/hiddev15 16th USB HID device
+ 112 = /dev/usb/auer0 1st auerswald ISDN device
+ ...
+ 127 = /dev/usb/auer15 16th auerswald ISDN device
+ 128 = /dev/usb/brlvgr0 First Braille Voyager device
+ ...
+ 131 = /dev/usb/brlvgr3 Fourth Braille Voyager device
+ 144 = /dev/usb/lcd USB LCD device
+ 160 = /dev/usb/legousbtower0 1st USB Legotower device
+ ...
+ 175 = /dev/usb/legousbtower15 16th USB Legotower device
+ 240 = /dev/usb/dabusb0 First daubusb device
+ ...
+ 243 = /dev/usb/dabusb3 Fourth dabusb device
181 char Conrad Electronic parallel port radio clocks
0 = /dev/pcfclock0 First Conrad radio clock
--- /dev/null
+Revision 3, 2003-10-04
+Jean Delvare <khali@linux-fr.org>
+Greg KH <greg@kroah.com>
+
+This is a guide on how to convert I2C chip drivers from Linux 2.4 to
+Linux 2.6. I have been using existing drivers (lm75, lm78) as examples.
+Then I converted a driver myself (lm83) and updated this document.
+
+There are two sets of points below. The first set concerns technical
+changes. The second set concerns coding policy. Both are mandatory.
+
+Although reading this guide will help you porting drivers, I suggest
+you keep an eye on an already ported driver while porting your own
+driver. This will help you a lot understanding what this guide
+exactly means. Choose the chip driver that is the more similar to
+yours for best results.
+
+Technical changes:
+
+* [Includes] Get rid of "version.h". Replace <linux/i2c-proc.h> with
+ <linux/i2c-sensor.h>. Includes typically look like that:
+ #include <linux/module.h>
+ #include <linux/init.h>
+ #include <linux/slab.h>
+ #include <linux/i2c.h>
+ #include <linux/i2c-sensor.h>
+ #include <linux/i2c-vid.h> /* if you need VRM support */
+ #include <asm/io.h> /* if you have I/O operations */
+ Some extra headers may be required for a given driver.
+
+* [Addresses] SENSORS_I2C_END becomes I2C_CLIENT_END, SENSORS_ISA_END
+ becomes I2C_CLIENT_ISA_END.
+
+* [Client data] Get rid of sysctl_id. Try using standard names for
+ register values (for example, temp_os becomes temp_max). You're
+ still relatively free here, but you *have* to follow the standard
+ names for sysfs files (see the Sysctl section below).
+
+* [Function prototypes] The detect functions loses its flags
+ parameter. Sysctl (e.g. lm75_temp) and miscellaneous (e.g.
+ swap_bytes) functions are off the list of prototypes. This
+ usually leaves five prototypes:
+ static int lm75_attach_adapter(struct i2c_adapter *adapter);
+ static int lm75_detect(struct i2c_adapter *adapter, int address,
+ int kind);
+ static void lm75_init_client(struct i2c_client *client);
+ static int lm75_detach_client(struct i2c_client *client);
+ static void lm75_update_client(struct i2c_client *client);
+
+* [Sysctl] All sysctl stuff is of course gone (defines, ctl_table
+ and functions). Instead, right after the static id definition
+ line, you have to define show and set functions for each sysfs
+ file. Only define set for writable values. Take a look at an
+ existing 2.6 driver for details (lm78 for example). Don't forget
+ to define the attributes for each file (this is that step that
+ links callback functions). Use the file names specified in
+ Documentation/i2c/sysfs-interface for the individual files. Also
+ convert the units these files read and write to the specified ones.
+ If you need to add a new type of file, please discuss it on the
+ sensors mailing list <sensors@stimpy.netroedge.com> by providing a
+ patch to the Documentation/i2c/sysfs-interface file.
+
+* [Attach] For I2C drivers, the attach function should make sure
+ that the adapter's class has I2C_ADAP_CLASS_SMBUS, using the
+ following construct:
+ if (!(adapter->class & I2C_ADAP_CLASS_SMBUS))
+ return 0;
+ ISA-only drivers of course don't need this.
+
+* [Detect] As mentioned earlier, the flags parameter is gone.
+ The type_name and client_name strings are replaced by a single
+ name string, which will be filled with a lowercase, short string
+ (typically the driver name, e.g. "lm75"). The errorN labels are
+ reduced to the number needed. If that number is 2 (i2c-only
+ drivers), it is advised that the labels are named exit and
+ exit_free. For i2c+isa drivers, labels should be named ERROR0,
+ ERROR1 and ERROR2. Don't forget to properly set err before
+ jumping to error labels. By the way, labels should be
+ left-aligned.
+ Use memset to fill the client and data area with 0x00.
+ Use i2c_set_clientdata to set the client data (as opposed to
+ a direct access to client->data).
+ Use strlcpy instead of strcpy to copy the client name.
+ Replace the sysctl directory registration by calls to
+ device_create_file. Move the driver initialization before any
+ sysfs file creation.
+
+* [Detach] Get rid of data, remove the call to
+ i2c_deregister_entry.
+
+* [Update] Don't access client->data directly, use
+ i2c_get_clientdata(client) instead.
+
+* [Interface] Init function should not print anything. Make sure
+ there is a MODULE_LICENSE() line.
+
+Coding policy:
+
+* [Copyright] Use (C), not (c), for copyright.
+
+* [Debug/log] Get rid of #ifdef DEBUG/#endif constructs whenever you
+ can. Calls to printk/pr_debug for debugging purposes are replaced
+ by calls to dev_dbg. Here is an example on how to call it (taken
+ from lm75_detect):
+ dev_dbg(&adapter->dev,
+ "lm75_detect called for an ISA bus adapter?!?\n");
+ Replace other printk calls with the dev_info, dev_err or dev_warn
+ function, as appropriate.
+
+* [Constants] Constants defines (registers, conversions, initial
+ values) should be aligned. This greatly improves readability.
+ Same goes for variables declarations. Alignments are achieved by the
+ means of tabs, not spaces. Remember that tabs are set to 8 in the
+ Linux kernel code.
+
+* [Structure definition] The name field should be standardized. All
+ lowercase and as simple as the driver name itself (e.g. "lm75").
+
+* [Layout] Avoid extra empty lines between comments and what they
+ comment. Respect the coding style (see Documentation/CodingStyle),
+ in particular when it comes to placing curly braces.
Fixed point XXXXX, divide by 1000 to get Amps.
Read/Write.
-curr_min[1-n] Current min or hysteresis value.
- Preferably a hysteresis value, reported as a absolute
- current, NOT a delta from the max value.
+curr_min[1-n] Current min value.
Fixed point XXXXX, divide by 1000 to get Amps.
Read/Write.
Integers 1,2,3, or thermistor Beta value (3435)
Read/Write.
-temp_max[1-3] Temperature max value.
+temp_max[1-4] Temperature max value.
Fixed point value in form XXXXX and should be divided by
1000 to get degrees Celsius.
Read/Write value.
-temp_min[1-3] Temperature min or hysteresis value.
+temp_min[1-3] Temperature min value.
Fixed point value in form XXXXX and should be divided by
- 1000 to get degrees Celsius. This is preferably a
- hysteresis value, reported as a absolute temperature,
- NOT a delta from the max value.
+ 1000 to get degrees Celsius.
Read/Write value.
-temp_input[1-3] Temperature input value.
+temp_hyst[1-3] Temperature hysteresis value.
+ Fixed point value in form XXXXX and should be divided by
+ 1000 to get degrees Celsius. Must be reported as an
+ absolute temperature, NOT a delta from the max value.
+ Read/Write value.
+
+temp_input[1-4] Temperature input value.
+ Fixed point value in form XXXXX and should be divided by
+ 1000 to get degrees Celsius.
Read only value.
+temp_crit Temperature critical value, typically greater than all
+ temp_max values.
+ Fixed point value in form XXXXX and should be divided by
+ 1000 to get degrees Celsius.
+ Common to all temperature channels.
+ Read/Write value.
+
If there are multiple temperature sensors, temp_*1 is
generally the sensor inside the chip itself, generally
- reported as "motherboard temperature". temp_*2 and
- temp_*3 are generally sensors external to the chip
+ reported as "motherboard temperature". temp_*2 to
+ temp_*4 are generally sensors external to the chip
itself, for example the thermal diode inside the CPU or
a thermistor nearby.
routines, a client structure specific information like the actual I2C
address.
- struct i2c_driver foo_driver
- {
- /* name */ "Foo version 2.3 and later driver",
- /* id */ I2C_DRIVERID_FOO,
- /* flags */ I2C_DF_NOTIFY,
- /* attach_adapter */ &foo_attach_adapter,
- /* detach_client */ &foo_detach_client,
- /* command */ &foo_command, /* May be NULL */
- /* inc_use */ &foo_inc_use, /* May be NULL */
- /* dec_use */ &foo_dec_use /* May be NULL */
+ static struct i2c_driver foo_driver = {
+ .owner = THIS_MODULE,
+ .name = "Foo version 2.3 driver",
+ .id = I2C_DRIVERID_FOO, /* usually from i2c-id.h */
+ .flags = I2C_DF_NOTIFY,
+ .attach_adapter = &foo_attach_adapter,
+ .detach_client = &foo_detach_client,
+ .command = &foo_command /* may be NULL */
}
The name can be chosen freely, and may be upto 40 characters long. Please
All other fields are for call-back functions which will be explained
below.
-
-Module usage count
-==================
-
-If your driver can also be compiled as a module, there are moments at
-which the module can not be removed from memory. For example, when you
-are doing a lengthy transaction, or when you create a /proc directory,
-and some process has entered that directory (this last case is the
-main reason why these call-backs were introduced).
-
-To increase or decrease the module usage count, you can use the
-MOD_{INC,DEC}_USE_COUNT macros. They must be called from the module
-which needs to get its usage count changed; that is why each driver
-module has to implement its own callback.
-
- void foo_inc_use (struct i2c_client *client)
- {
- #ifdef MODULE
- MOD_INC_USE_COUNT;
- #endif
- }
-
- void foo_dec_use (struct i2c_client *client)
- {
- #ifdef MODULE
- MOD_DEC_USE_COUNT;
- #endif
- }
-
-Do not call these call-back functions directly; instead, use one of the
-following functions defined in i2c.h:
- void i2c_inc_use_client(struct i2c_client *);
- void i2c_dec_use_client(struct i2c_client *);
-
-You should *not* increase the module count just because a device is
-detected and a client created. This would make it impossible to remove
-an adapter driver!
+There use to be two additional fields in this structure, inc_use et dec_use,
+for module usage count, but these fields were obsoleted and removed.
Extra client data
--- /dev/null
+
+ W996[87]CF JPEG USB Dual Mode Camera Chip driver for Linux 2.6
+ ==============================================================
+
+ - Documentation -
+
+
+Index
+=====
+1. Copyright
+2. License
+3. Overview
+4. Supported devices
+5. Kernel configuration and third-part module compilation
+6. Module loading
+7. Module paramaters
+8. Credits
+
+
+1. Copyright
+============
+Copyright (C) 2002 2003 by Luca Risolia <luca_ing@libero.it>
+
+
+2. License
+==========
+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.
+
+
+3. Overview
+===========
+This driver supports the video streaming capabilities of the devices mounting
+Winbond W9967CF and Winbond W9968CF JPEG USB Dual Mode Camera Chips, when they
+are being commanded by USB.
+
+The driver relies on the Video4Linux, USB and I2C core modules of the Linux
+kernel, version 2.6.0 or greater, and is not compatible in any way with
+previous versions. It has been designed to run properly on SMP systems
+as well. At the moment, an additional module, "ovcamchip", is mandatory; it
+provides support for some OmniVision CMOS sensors connected to the W996[87]CF
+chips.
+
+The driver is split into two modules: the basic one, "w9968cf", is needed for
+the supported devices to work; the second one, "w9968cf-vpp", is an optional
+module, which provides some useful video post-processing functions like video
+decoding, up-scaling and colour conversions. These routines can't be included
+into official kernels for performance purposes. Once the driver is installed,
+every time an application tries to open a recognized device, "w9968cf" checks
+the presence of the "w9968cf-vpp" module and loads it automatically by default.
+
+Up to 32 cameras can be handled at the same time. They can be connected and
+disconnected from the host many times without turning off the computer, if
+your system supports the hotplug facility.
+
+To change the default settings for each camera, many paramaters can be passed
+through command line when the module is loaded into memory.
+
+The latest and full featured version of the W996[87]CF driver can be found at:
+http://go.lamarinapunto.com/
+
+The "ovcamchip" module is part of the OV511 driver, version 2.25, which can be
+downloaded from internet:
+http://alpha.dyndns.org/ov511/
+To know how to patch, compile and load it, read the paragraphs below.
+
+
+4. Supported devices
+====================
+At the moment, known W996[87]CF based devices are:
+- Aroma Digi Pen ADG-5000 Refurbished
+- AVerTV USB
+- Creative Labs Video Blaster WebCam Go
+- Creative Labs Video Blaster WebCam Go Plus
+- Die Lebon LDC-D35A Digital Kamera
+- Ezonics EZ-802 EZMega Cam
+- OPCOM Digi Pen VGA Dual Mode Pen Camera
+
+If you know any other W996[87]CF based cameras, please contact me.
+
+The list above does NOT imply that all those devices work with this driver: up
+until now only webcams that have a CMOS sensor supported by the "ovcamchip"
+module work.
+For a list of supported CMOS sensors, please visit the module author homepage:
+http://alpha.dyndns.org/ov511/
+
+Possible external microcontrollers of those webcams are not supported: this
+means that still images can't be downloaded from the device memory.
+
+Furthermore, it's worth to note that I was only able to run tests on my
+"Creative Labs Video Blaster WebCam Go". Donations of other models, for
+additional testing and full support, would be much appreciated.
+
+
+5. Kernel configuration and third-part module compilation
+=========================================================
+As noted above, kernel 2.6.0 is the minimum for this driver; for it to work
+properly, the driver needs kernel support for Video4Linux, USB and I2C, and a
+third-part module for the CMOS sensor.
+
+The following options of the kernel configuration file must be enabled and
+corresponding modules must be compiled:
+
+ # Multimedia devices
+ #
+ CONFIG_VIDEO_DEV=m
+
+ # I2C support
+ #
+ CONFIG_I2C=m
+
+The I2C core module can be compiled statically in the kernel as well.
+
+ # USB support
+ #
+ CONFIG_USB=m
+
+In addition, depending on the hardware being used, just one of the modules
+below is necessary:
+
+ # USB Host Controller Drivers
+ #
+ CONFIG_USB_EHCI_HCD=m
+ CONFIG_USB_UHCI_HCD=m
+ CONFIG_USB_OHCI_HCD=m
+
+Also, make sure "Enforce bandwidth allocation" is NOT enabled.
+
+ # USB Multimedia devices
+ #
+ CONFIG_USB_W9968CF=m
+
+The last module we need is "ovcamchip.o". To obtain it, you have to download
+the OV511 driver, version 2.25 - don't use other versions - which is available
+at http://alpha.dyndns.org/ov511/ . Then you have to download the latest
+version of the full featured W996[87]CF driver, which contains a patch for the
+"ovcamchip" module; it is available at http://go.lamarinapunto.com .
+Once you have obtained the packages, decompress, patch and compile the
+"ovcamchip" module. In other words:
+
+ [user@localhost home]$ tar xvzf w9968cf-x.x.tar.gz
+ [user@localhost home]$ tar xvjf ov511-2.25.tar.bz2
+ [user@localhost home]$ cd ov511-2.25
+ [user@localhost ov511-2.25]$ patch -p1 < \
+ /path/to/w9968cf-x.x/ov511-2.25.patch
+ [user@localhost ov511-2.25]$ make
+
+It's worth to note that the full featured version of the W996[87]CF driver
+can also be installed overwriting the one in the kernel; in this case, read the
+documentation included in the package.
+
+If everything went well, the W996[87]CF driver can be immediatly used (see next
+paragraph).
+
+
+6. Module loading
+=================
+To use the driver, it is necessary to load the "w9968cf" module into memory
+after every other module required.
+
+For example, loading can be done this way, as root:
+
+ [root@localhost home]# modprobe usbcore
+ [root@localhost home]# modprobe i2c-core
+ [root@localhost ov511-x.xx]# insmod ./ovcamchip.ko
+ [root@localhost home]# modprobe w9968cf
+
+At this point the devices should be recognized: "dmesg" can be used to analyze
+kernel messages:
+
+ [user@localhost home]$ dmesg
+
+There are a lot of parameters the module can use to change the default
+settings for each device. To list every possible parameter with a brief
+explanation about them and which syntax to use, it is recommended to run the
+"modinfo" command:
+
+ [root@locahost home]# modinfo w9968cf
+
+
+7. Module paramaters
+====================
+
+Module paramaters are listed below:
+-------------------------------------------------------------------------------
+Name: vppmod_load
+Type: int
+Syntax: <0|1>
+Description: Automatic 'w9968cf-vpp' module loading: 0 disabled, 1 enabled.
+ If enabled, every time an application attempts to open a
+ camera, 'insmod' searches for the video post-processing module
+ in the system and loads it automatically (if present).
+ The 'w9968cf-vpp' module adds extra image manipulation
+ capabilities to the 'w9968cf' module,like software up-scaling,
+ colour conversions and video decoding.
+Default: 1
+-------------------------------------------------------------------------------
+Name: simcams
+Type: int
+Syntax: <n>
+Description: Number of cameras allowed to stream simultaneously.
+ n may vary from 0 to 32.
+Default: 32
+-------------------------------------------------------------------------------
+Name: video_nr
+Type: int array (min = 0, max = 32)
+Syntax: <-1|n[,...]>
+Description: Specify V4L minor mode number.
+ -1 = use next available
+ n = use minor number n
+ You can specify 32 cameras this way.
+ For example:
+ video_nr=-1,2,-1 would assign minor number 2 to the second
+ recognized camera and use auto for the first one and for every
+ other camera.
+Default: -1
+-------------------------------------------------------------------------------
+Name: packet_size
+Type: int array (min = 0, max = 32)
+Syntax: <n[,...]>
+Description: Specify the maximum data payload size in bytes for alternate
+ settings, for each device. n is scaled between 63 and 1023.
+Default: 1023
+-------------------------------------------------------------------------------
+Name: max_buffers
+Type: int array (min = 0, max = 32)
+Syntax: <n[,...]>
+Description: Only for advanced users.
+ Specify the maximum number of video frame buffers to allocate
+ for each device, from 2 to 32.
+Default: 2
+-------------------------------------------------------------------------------
+Name: double_buffer
+Type: int array (min = 0, max = 32)
+Syntax: <0|1[,...]>
+Description: Hardware double buffering: 0 disabled, 1 enabled.
+ It should be enabled if you want smooth video output: if you
+ obtain out of sync. video, disable it at all, or try to
+ decrease the 'clockdiv' module paramater value.
+Default: 1 for every device.
+-------------------------------------------------------------------------------
+Name: clamping
+Type: int array (min = 0, max = 32)
+Syntax: <0|1[,...]>
+Description: Video data clamping: 0 disabled, 1 enabled.
+Default: 0 for every device.
+-------------------------------------------------------------------------------
+Name: filter_type
+Type: int array (min = 0, max = 32)
+Syntax: <0|1|2[,...]>
+Description: Video filter type.
+ 0 none, 1 (1-2-1) 3-tap filter, 2 (2-3-6-3-2) 5-tap filter.
+ The filter is used to reduce noise and aliasing artifacts
+ produced by the CCD or CMOS sensor.
+Default: 0 for every device.
+-------------------------------------------------------------------------------
+Name: largeview
+Type: int array (min = 0, max = 32)
+Syntax: <0|1[,...]>
+Description: Large view: 0 disabled, 1 enabled.
+Default: 1 for every device.
+-------------------------------------------------------------------------------
+Name: upscaling
+Type: int array (min = 0, max = 32)
+Syntax: <0|1[,...]>
+Description: Software scaling (for non-compressed video only):
+ 0 disabled, 1 enabled.
+ Disable it if you have a slow CPU or you don't have enough
+ memory.
+Default: 0 for every device.
+Note: If 'w9968cf-vpp' is not loaded, this paramater is set to 0.
+-------------------------------------------------------------------------------
+Name: decompression
+Type: int array (min = 0, max = 32)
+Syntax: <0|1|2[,...]>
+Description: Software video decompression:
+ 0 = disables decompression
+ (doesn't allow formats needing decompression).
+ 1 = forces decompression
+ (allows formats needing decompression only).
+ 2 = allows any permitted formats.
+ Formats supporting (de)compressed video are YUV422P and
+ YUV420P/YUV420 in any resolutions where width and height are
+ multiples of 16.
+Default: 2 for every device.
+Note: If 'w9968cf-vpp' is not loaded, forcing decompression is not
+ allowed; in this case this paramater is set to 2.
+-------------------------------------------------------------------------------
+Name: force_palette
+Type: int array (min = 0, max = 32)
+Syntax: <0|9|10|13|15|8|7|1|6|3|4|5[,...]>
+Description: Force picture palette.
+ In order:
+ 0 = Off - allows any of the following formats:
+ 9 = UYVY 16 bpp - Original video, compression disabled
+ 10 = YUV420 12 bpp - Original video, compression enabled
+ 13 = YUV422P 16 bpp - Original video, compression enabled
+ 15 = YUV420P 12 bpp - Original video, compression enabled
+ 8 = YUVY 16 bpp - Software conversion from UYVY
+ 7 = YUV422 16 bpp - Software conversion from UYVY
+ 1 = GREY 8 bpp - Software conversion from UYVY
+ 6 = RGB555 16 bpp - Software conversion from UYVY
+ 3 = RGB565 16 bpp - Software conversion from UYVY
+ 4 = RGB24 24 bpp - Software conversion from UYVY
+ 5 = RGB32 32 bpp - Software conversion from UYVY
+ When not 0, this paramater will override 'decompression'.
+Default: 0 for every device. Initial palette is 9 (UYVY).
+Note: If 'w9968cf-vpp' is not loaded, this paramater is set to 9.
+-------------------------------------------------------------------------------
+Name: force_rgb
+Type: int array (min = 0, max = 32)
+Syntax: <0|1[,...]>
+Description: Read RGB video data instead of BGR:
+ 1 = use RGB component ordering.
+ 0 = use BGR component ordering.
+ This parameter has effect when using RGBX palettes only.
+Default: 0 for every device.
+-------------------------------------------------------------------------------
+Name: autobright
+Type: long array (min = 0, max = 32)
+Syntax: <0|1[,...]>
+Description: CMOS sensor automatically changes brightness:
+ 0 = no, 1 = yes
+Default: 0 for every device.
+-------------------------------------------------------------------------------
+Name: autoexp
+Type: long array (min = 0, max = 32)
+Syntax: <0|1[,...]>
+Description: CMOS sensor automatically changes exposure:
+ 0 = no, 1 = yes
+Default: 1 for every device.
+-------------------------------------------------------------------------------
+Name: lightfreq
+Type: long array (min = 0, max = 32)
+Syntax: <50|60[,...]>
+Description: Light frequency in Hz:
+ 50 for European and Asian lighting, 60 for American lighting.
+Default: 50 for every device.
+-------------------------------------------------------------------------------
+Name: bandingfilter
+Type: long array (min = 0, max = 32)
+Syntax: <0|1[,...]>
+Description: Banding filter to reduce effects of fluorescent
+ lighting:
+ 0 disabled, 1 enabled.
+ This filter tries to reduce the pattern of horizontal
+ light/dark bands caused by some (usually fluorescent) lighting.
+Default: 0 for every device.
+-------------------------------------------------------------------------------
+Name: clockdiv
+Type: long array (min = 0, max = 32)
+Syntax: <-1|n[,...]>
+Description: Force pixel clock divisor to a specific value (for experts):
+ n may vary from 0 to 127.
+ -1 for automatic value.
+ See also the 'double_buffer' module paramater.
+Default: -1 for every device.
+-------------------------------------------------------------------------------
+Name: backlight
+Type: long array (min = 0, max = 32)
+Syntax: <0|1[,...]>
+Description: Objects are lit from behind:
+ 0 = no, 1 = yes
+Default: 0 for every device.
+-------------------------------------------------------------------------------
+Name: mirror
+Type: long array (min = 0, max = 32)
+Syntax: <0|1[,...]>
+Description: Reverse image horizontally:
+ 0 = no, 1 = yes
+Default: 0 for every device.
+-------------------------------------------------------------------------------
+Name: sensor_mono
+Type: long array (min = 0, max = 32)
+Syntax: <0|1[,...]>
+Description: The CMOS sensor is monochrome:
+ 0 = no, 1 = yes
+Default: 0 for every device.
+-------------------------------------------------------------------------------
+Name: brightness
+Type: long array (min = 0, max = 32)
+Syntax: <n[,...]>
+Description: Set picture brightness (0-65535).
+ This parameter has no effect if 'autobright' is enabled.
+Default: 31000 for every device.
+-------------------------------------------------------------------------------
+Name: hue
+Type: long array (min = 0, max = 32)
+Syntax: <n[,...]>
+Description: Set picture hue (0-65535).
+Default: 32768 for every device.
+-------------------------------------------------------------------------------
+Name: colour
+Type: long array (min = 0, max = 32)
+Syntax: <n[,...]>
+Description: Set picture saturation (0-65535).
+Default: 32768 for every device.
+-------------------------------------------------------------------------------
+Name: contrast
+Type: long array (min = 0, max = 32)
+Syntax: <n[,...]>
+Description: Set picture contrast (0-65535).
+Default: 50000 for every device.
+-------------------------------------------------------------------------------
+Name: whiteness
+Type: long array (min = 0, max = 32)
+Syntax: <n[,...]>
+Description: Set picture whiteness (0-65535).
+Default: 32768 for every device.
+-------------------------------------------------------------------------------
+Name: debug
+Type: int
+Syntax: <n>
+Description: Debugging information level, from 0 to 6:
+ 0 = none (be cautious)
+ 1 = critical errors
+ 2 = significant informations
+ 3 = configuration or general messages
+ 4 = warnings
+ 5 = called functions
+ 6 = function internals
+ Level 5 and 6 are useful for testing only, when just one
+ device is used.
+Default: 2
+-------------------------------------------------------------------------------
+Name: specific_debug
+Type: int
+Syntax: <0|1>
+Description: Enable or disable specific debugging messages:
+ 0 = print messages concerning every level <= 'debug' level.
+ 1 = print messages concerning the level indicated by 'debug'.
+Default: 0
+-------------------------------------------------------------------------------
+
+
+8. Credits
+==========
+The development would not have proceed much further without having looked at
+the source code of other drivers and without the help of several persons; in
+particular:
+
+- the I2C interface to kernel and high-level CMOS sensor control routines have
+ been taken from the OV511 driver by Mark McClelland;
+
+- memory management code has been copied from the bttv driver by Ralph Metzler,
+ Marcus Metzler and Gerd Knorr;
+
+- the low-level I2C read function has been written by Frédéric Jouault, who
+ also gave me commented logs about sniffed USB traffic taken from another
+ driver for another system;
+
+- the low-level I2C fast write function has been written by Piotr Czerczak;
L: linux-usb-devel@lists.sourceforge.net
S: Maintained
+USB W996[87]CF DRIVER
+P: Luca Risolia
+M: luca_ing@libero.it
+L: linux-usb-devel@lists.sourceforge.net
+W: http://go.lamarinapunto.com
+S: Maintained
+
USER-MODE LINUX
P: Jeff Dike
M: jdike@karaya.com
Say Y here only if you plan to use gdb to debug the kernel.
If you don't debug the kernel, you can say N.
+config DEBUG_DEV_PRINTK
+ bool "Debug dev_printk"
+ depends on DEBUG_KERNEL
+ help
+ If you say Y here, all dev_printk() parameters will be checked before
+ dev_printk() is called. This is useful when debugging new drivers.
+
config DEBUG_SPINLOCK_SLEEP
bool "Sleep-inside-spinlock checking"
help
--- /dev/null
+echo Setting up the environment for debugging vmlinux...\n
+echo set remotedebug 0 \n
+set remotedebug 0
+echo cd arch/mips/kernel \n
+cd arch/mips/kernel
+echo target remote /dev/ttyS0 \n
+target remote /dev/ttyS0
static void __init mixer_init(void)
{
- mixer_unit = register_sound_mixer(&mixer_fops, -1);
+ mixer_unit = register_sound_mixer(&mixer_fops, -1, NULL);
if (mixer_unit < 0)
return;
static void __init sq_init(void)
{
- sq_unit = register_sound_dsp(&sq_fops, -1);
+ sq_unit = register_sound_dsp(&sq_fops, -1, NULL);
if (sq_unit < 0)
return;
static void __init state_init(void)
{
- state_unit = register_sound_special(&state_fops, SND_DEV_STATUS);
+ state_unit = register_sound_special(&state_fops, SND_DEV_STATUS, NULL);
if (state_unit < 0)
return;
state.busy = 0;
printk(KERN_INFO "UML Audio Relay (host dsp = %s, host mixer = %s)\n",
dsp, mixer);
- module_data.dev_audio = register_sound_dsp(&hostaudio_fops, -1);
+ module_data.dev_audio = register_sound_dsp(&hostaudio_fops, -1, NULL);
if(module_data.dev_audio < 0){
printk(KERN_ERR "hostaudio: couldn't register DSP device!\n");
return -ENODEV;
}
- module_data.dev_mixer = register_sound_mixer(&hostmixer_fops, -1);
+ module_data.dev_mixer = register_sound_mixer(&hostmixer_fops, -1, NULL);
if(module_data.dev_mixer < 0){
printk(KERN_ERR "hostmixer: couldn't register mixer "
"device!\n");
*
*/
+#ifdef DEBUG_KOBJECT
+#define DEBUG 1
+#else
#undef DEBUG
+#endif
#include <linux/device.h>
#include <linux/module.h>
*
*/
+#ifdef DEBUG_KOBJECT
+#define DEBUG 1
+#else
#undef DEBUG
+#endif
#include <linux/device.h>
#include <linux/module.h>
*
*/
+#ifdef DEBUG_KOBJECT
+#define DEBUG 1
+#else
#undef DEBUG
+#endif
#include <linux/device.h>
#include <linux/err.h>
*
*/
+#ifdef DEBUG_KOBJECT
+#define DEBUG 1
+#else
#undef DEBUG
+#endif
#include <linux/device.h>
#include <linux/module.h>
* add themselves as children of the system bus.
*/
+#ifdef DEBUG_KOBJECT
+#define DEBUG 1
+#else
#undef DEBUG
+#endif
#include <linux/sysdev.h>
#include <linux/err.h>
If compiled as a module, it will be called scx200_gpio.
+config FJKEYINFO
+ tristate "Fujitsu Lifebook Application Key Support"
+ help
+ Provide support for Fujitsu Lifebook application keys.
+
+ If compiled as a module, it will be called fjkeyinf.
+
config RAW_DRIVER
tristate "RAW driver (/dev/raw/rawN)"
help
obj-$(CONFIG_NWBUTTON) += nwbutton.o
obj-$(CONFIG_NWFLASH) += nwflash.o
obj-$(CONFIG_SCx200_GPIO) += scx200_gpio.o
+obj-$(CONFIG_FJKEYINFO) += fjkeyinf.o
obj-$(CONFIG_WATCHDOG) += watchdog/
obj-$(CONFIG_MWAVE) += mwave/
--- /dev/null
+/*
+ SMBus client for the Fujitsu Siemens Lifebook C-6535 Application Panel
+
+ Copyright (C) 2001 Jochen Eisinger <jochen.eisinger@gmx.net>
+
+ 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 <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/ctype.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/ioport.h>
+#include <linux/types.h>
+#include <linux/mm.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <linux/i2c.h>
+#include <linux/i2c-id.h>
+#include <linux/miscdevice.h>
+
+/* Pulled what was needed from the userspace apanel.h file here */
+/* Make sure they match what the userspace tools were build against */
+
+/* Describes to which subsystem the ioctl applies. 'P' for proprietary? */
+#define APANEL_IOCTL_GROUP 'P'
+
+
+
+/*
+ * Use one ioctl for all devices
+ * This has the advantage that one interface is defined
+ * for all devices. The caps can tell what is possible on
+ * a certain device (with some defaults for each device
+ * of course which the programmer doesn't have to check
+ * explicitly like on/off for led and read for buttons).
+ */
+
+struct apanelcmd {
+ int device; /* Device to operate command on (APANEL_DEV_GEN_*) */
+ int cmd; /* Command to execute (APANEL_CMD_*) */
+ int data; /* Data for command */
+};
+
+
+struct fj_device {
+ __u8 type;
+ __u8 access_method;
+ __u8 chip_type;
+ __u8 number;
+} __attribute__((packed));
+
+#define DEVICE_APPLICATION_BUTTONS 1
+#define DEVICE_CD_BUTTONS 2
+#define DEVICE_LCD 3
+#define DEVICE_LED_1 4
+#define DEVICE_LED_2 6
+#define DEVICE_APPLICATION_3_BUTTONS 7
+
+
+#define IOCTL_APANEL_CMD _IOR(APANEL_IOCTL_GROUP,202,struct apanelcmd)
+
+
+/* Devices */
+#define APANEL_DEV_LED 1
+#define APANEL_DEV_LCD 2
+#define APANEL_DEV_CDBTN 3
+#define APANEL_DEV_APPBTN 4
+
+/*
+ * Commands
+ */
+
+/* Device capabilities */
+#define APANEL_CAP_ONOFF 1 /* Turn on/off */
+#define APANEL_CAP_GET 2 /* General get command */
+#define APANEL_CAP_SET 4 /* General set command */
+#define APANEL_CAP_BLINK 8 /* Blinking */
+#define APANEL_CAP_DUTY 0x10 /* Duty cycle of blinking */
+#define APANEL_CAP_RESET 0x20 /* Reset device */
+#define APANEL_CAP_MAX 0x40 /* Get highest possible value for */
+ /* set/get */
+#define APANEL_CAP_MIN 0x80 /* Get lowest possible value */
+#define APANEL_CMD_ONOFF 2
+#define APANEL_ONOFF_ON 1 /* Or is APANEL_ON nicer? */
+#define APANEL_ONOFF_OFF 0
+
+#define APANEL_CMD_SET 3
+#define APANEL_CMD_GET 4
+
+#define APANEL_CMD_BLINK 5 /* data=Blink frequency *0.01Hz or so */
+#define APANEL_CMD_DUTY 6 /* data=percentage high */
+
+#define APANEL_CMD_RESET 7 /* If this is useful at all */
+
+#define APANEL_CMD_MAX 8
+#define APANEL_CMD_MIN 9
+
+/*
+ * Button masks
+ */
+/* Masks for application buttons */
+#define APANEL_APPBTN_A 1
+#define APANEL_APPBTN_B 2
+#define APANEL_APPBTN_INTERNET 4
+#define APANEL_APPBTN_EMAIL 8
+
+/* Masks for cd buttons */
+#define APANEL_CDBTN_STOP 1
+#define APANEL_CDBTN_EJECT 2
+#define APANEL_CDBTN_PLAY 4
+#define APANEL_CDBTN_PAUSE 8
+#define APANEL_CDBTN_BACK 0x10
+#define APANEL_CDBTN_FORW 0x20
+
+
+
+/* print lots of useless debug infos */
+#define DEBUG
+
+#define MY_NAME "fjkeyinf"
+
+#define dbg(format, arg...) \
+ do { \
+ if (debug) \
+ printk(KERN_DEBUG "%s: " format, \
+ MY_NAME , ## arg); \
+ } while (0)
+
+static int debug = 1;
+
+/*
+ * this is the internal driver version number. There is no direct relationship
+ * between the hardware, the apanel protocol, or the driver versions
+ * However, this version number should be increased for every change
+ */
+/* current driver version */
+#define FJKEYINF_VERSION_MAJOR 0
+#define FJKEYINF_VERSION_MINOR 4
+
+/*
+ * This is the apanel version this driver implements. This _must_ be the
+ * same as your apanel.h file
+ */
+/* protocol version */
+#define FJKEYINF_APANEL_MAJOR 1
+#define FJKEYINF_APANEL_MINOR 0
+
+/*
+ * Some real leet m4cr0s
+ */
+#define STRINGIFY1(x) #x
+#define STRINGIFY(x) STRINGIFY1(x)
+
+#define PASTE1(x,y) x##y
+#define PASTE(x,y) PASTE1(x,y)
+
+/*
+ * This is the version of the driver as a string
+ */
+#define FJKEYINF_VERSION_STRING PASTE("v", \
+ PASTE(STRINGIFY(FJKEYINF_VERSION_MAJOR),\
+ PASTE(".",STRINGIFY(FJKEYINF_VERSION_MINOR))))
+
+/*
+ * This is the version of the protocol as a string
+ */
+#define FJKEYINF_APANEL_VERSION_STRING PASTE("v", \
+ PASTE(STRINGIFY(FJKEYINF_APANEL_MAJOR),\
+ PASTE(".", \
+ STRINGIFY(FJKEYINF_APANEL_MINOR))))
+
+/*
+ * every i2c device has to register to the i2c sub-system. Therefor it needs
+ * a driver id. We should be I2C_DRIVERID_FJKEYINF. However, if this isn't
+ * defined in i2c-id.h, we'll use a generic id.
+ */
+#ifndef I2C_DRIVERID_FJKEYINF
+#define I2C_DRIVERID_FJKEYINF 0xF000
+#endif
+
+/*
+ * Yes, it's unbelievable, but me crappy driver has an official devices
+ * entry. It's register as a misc-device (char-major-10) minor 216. The
+ * location should be /dev/fujitsu/apanel... after all, it seems to be
+ * a quite c00l driver ;>
+ */
+#define FJKEYINF_CHAR_MINOR 216
+
+/*
+ * Modules can store nice extra infos...
+ */
+MODULE_AUTHOR("Jochen Eisinger <jochen.eisinger@gmx.net>");
+MODULE_DESCRIPTION("Application Panel driver for Lifebook C-series");
+MODULE_LICENSE("GPL");
+
+/*
+ * So, let's start the hackin'
+ *
+ * we first define the interface for the i2c driver, cuz the misc device
+ * part uses the struct fjkeyinf_client. also note that this struct is
+ * static where it would be better to dynamically allocate one... but what
+ * fsck, more than one apanel your crappy laptop won't have...
+ */
+
+/* definitions for i2c (smbus) interface */
+
+/* forward declaration of the interface procedures */
+
+/* detach removes the smbus driver from the system */
+ static int fjkeyinf_detach(struct i2c_client *client);
+/* attach tries to attach to a given smbus */
+ static int fjkeyinf_attach(struct i2c_adapter *adap);
+
+/* this structur defines the interface to the i2c sub-system */
+static struct i2c_driver fjkeyinf_driver = {
+ .owner = THIS_MODULE,
+ .name = "fujitsu_panel" /* FJKEYINF_VERSION_STRING */,
+ .id = I2C_DRIVERID_FJKEYINF,
+ .flags = I2C_DF_NOTIFY,
+ .attach_adapter = &fjkeyinf_attach,
+ .detach_client = &fjkeyinf_detach,
+ .command = NULL,
+};
+
+
+/* Addresses to scan. afaik the device is at id 0x18. so we only scan this */
+static unsigned short normal_i2c[] = {0x18,I2C_CLIENT_END};
+static unsigned short normal_i2c_range[] = {I2C_CLIENT_END};
+
+/*
+ * generate some stupid additional structures. we don't really care about
+ * this
+ */
+I2C_CLIENT_INSMOD;
+
+
+/*
+ * If we've found the device, we have to provide an i2c_client structure
+ * to the i2c subsystem with which this devices can be accessed. as I stated
+ * above, there's max. 1 device, so I use _one static_ entry
+ */
+static struct i2c_client fjkeyinf_client =
+{
+ .id = -1,
+ .flags = 0,
+ .addr = 0,
+ .adapter = NULL,
+ .driver = &fjkeyinf_driver,
+ .name = "fjkeyinf",
+};
+
+
+/*
+ * luckily, the c-series laptops have a configuration block in there BIOS.
+ * so we can use it to detect the presence of the apanel device. There's
+ * also a checksum somewhere, but we don't care about it.
+ *
+ * Note the first 8 characters. that's where this strange driver name comes
+ * from.
+ *
+ * The configuration block can be found at 0x000FFA30
+ *
+ * There should also be an access method 3, but I don't know much about it.
+ * Basically there should also be an LCD device. but my notebook hasn't one
+ * so I don't know what the configuration block looks like
+ *
+ * type 1 is LED type 4 ist BUTTONS, probably 2 is LCD.
+ */
+static const unsigned char fjkeyinf_info[16] = {
+ 'F', 'J', 'K', 'E', 'Y', 'I', 'N', 'F',
+ /* device 0 */ /* type */ 1,
+ /* access method*/ 1,
+ /* enabled */ 1,
+ /* smbus device */ 0x30,
+ /* device 1 */ /* type */ 4,
+ /* access method*/ 1,
+ /* enabled */ 1,
+ /* smbus device */ 0x30};
+
+#define FJKEYINF_INFO_ADDR 0x000FFA30
+#define FJKEYINF_INFO_ADDR 0x000F6f20 /* Address for the P2120 */
+
+/*
+ * the following functions implement the ioctls. Note however, that not
+ * much is implemented yet.
+ */
+
+/* turn a device on or off */
+
+int fjkeyinf_onoff(int dev, int state)
+{
+
+ switch (dev) {
+
+ case APANEL_DEV_LED:
+
+ if (state) {
+ dbg("turning LED on...\n");
+
+ i2c_smbus_write_word_data(&fjkeyinf_client,
+ 0x10, 0x8080);
+ i2c_smbus_write_word_data(&fjkeyinf_client,
+ 0x0f, 0x100);
+ i2c_smbus_write_word_data(&fjkeyinf_client,
+ 0x11, 1);
+
+ } else {
+
+ dbg("turning LED off...\n");
+
+ i2c_smbus_write_word_data(&fjkeyinf_client,
+ 0x10, 0);
+ i2c_smbus_write_word_data(&fjkeyinf_client,
+ 0x0f, 0x100);
+ i2c_smbus_write_word_data(&fjkeyinf_client,
+ 0x11, 0);
+
+
+ }
+
+ return state;
+
+ default:
+
+ printk(KERN_NOTICE
+ "fjkeyinf: ONOFF called for invalid device"
+ " (dev %d, state %d)\n", dev, state);
+
+ return -EINVAL;
+
+ }
+
+}
+
+/* gets the current value from a device */
+int fjkeyinf_get(int dev)
+{
+ switch (dev) {
+ case APANEL_DEV_LED:
+ return ((i2c_smbus_read_word_data
+ (&fjkeyinf_client, 0x00) >> 7) & 1);
+
+ case APANEL_DEV_APPBTN:
+ {
+ int state;
+
+ state = i2c_smbus_read_word_data
+ (&fjkeyinf_client, 0x00);
+
+ state = (state >> 8) & 0x0f;
+
+ state ^= 0x0f;
+
+ return (((state & 1) ? APANEL_APPBTN_EMAIL : 0)
+ + ((state & 2) ? APANEL_APPBTN_INTERNET : 0)
+ + ((state & 4) ? APANEL_APPBTN_A : 0)
+ + ((state & 8) ? APANEL_APPBTN_B : 0));
+ }
+
+ default:
+ printk(KERN_NOTICE "fjkeyinf: GET called for invalid"
+ " device (dev %d)\n", dev);
+ return -EINVAL;
+ }
+}
+
+
+
+/*
+ * file operations for /dev/fujitsu/apanel
+ *
+ * That what the name says: file operation. pretty basic stuff follows
+ */
+
+/*
+ * if somebody opens us, we just realize and let him pass
+ */
+static int fjkeyinf_open (struct inode *inode, struct file *filp)
+{
+ dbg("open called for /dev/fujitsu/apanel\n");
+ return 0;
+}
+
+/* same with close */
+static int fjkeyinf_close (struct inode *inode, struct file *filp)
+{
+ dbg("close called for /dev/fujitsu/apanel\n");
+ return 0;
+}
+
+/* all commands are passed using the ioctl interface. so we have to decide
+ * here, what we have to do */
+int fjkeyinf_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
+ unsigned long args)
+{
+ struct apanelcmd *acmd;
+
+ dbg("ioctl (cmd: %u, args: 0x%08lx)\n", cmd, args);
+
+ /* Let's see, what they want from us... */
+
+ switch (cmd) {
+ case IOCTL_APANEL_CMD:
+ /* the actual command is passed in a apanelcmd
+ * structure */
+
+ if (!(args)) {
+
+ printk(KERN_NOTICE "fjkeyinf: invalid apanel command "
+ "(NULL pointer)\n");
+ return -EINVAL;
+
+ }
+
+ acmd = (struct apanelcmd *)args;
+
+ /* although not all commands are implemented, we
+ * understand all... */
+
+ dbg("apanel command %d\n", acmd->cmd);
+
+
+ switch (acmd->cmd) {
+ case APANEL_CMD_ONOFF:
+ return fjkeyinf_onoff(acmd->device,
+ acmd->data);
+
+ case APANEL_CMD_GET:
+ return fjkeyinf_get(acmd->device);
+
+ default:
+ printk(KERN_NOTICE "fjkeyinf: unknown "
+ "device/command %d/%d\n",
+ acmd->device, acmd->cmd);
+ return -EINVAL;
+ }
+
+ default:
+ printk(KERN_NOTICE "fjkeyinf: unknown ioctl code %u\n", cmd);
+ return -EINVAL;
+ }
+}
+
+/* now we tell the misc_device what nice functions we've implemented */
+static struct file_operations fjkeyinf_fops = {
+ .owner = THIS_MODULE,
+ .ioctl = fjkeyinf_ioctl,
+ .open = fjkeyinf_open,
+ .release = fjkeyinf_close,
+};
+
+/* misc dev entry. We need this to register to the misc dev driver */
+static struct miscdevice fjkeyinf_dev = {
+ FJKEYINF_CHAR_MINOR,
+ "apanel",
+ &fjkeyinf_fops
+};
+
+
+/* Now comes the i2c driver stuff. This is pretty straight forward, just as they
+ * described it in their docu.
+ */
+
+/* basically this function should probe the i2c client, but we know that it has
+ * to be the one we're looking for - and I have no idea how I should probe for
+ * it, so we just register... */
+static int fjkeyinf_probe(struct i2c_adapter *adap, int addr, int kind)
+{
+ int result;
+ fjkeyinf_client.adapter = adap;
+ fjkeyinf_client.addr = addr;
+
+ dbg("%s\n", __FUNCTION__);
+ if ((result = misc_register(&fjkeyinf_dev)) < 0) {
+ printk(KERN_NOTICE "fjkeyinf: could not register misc device (%d)\n",
+ result);
+ return result;
+ }
+
+ i2c_attach_client(&fjkeyinf_client);
+
+ printk(KERN_INFO "fjkeyinf: Application Panel Driver "
+ /*FJKEYINF_VERSION_STRING*/ "\n");
+ printk(KERN_INFO "fjkeyinf: Copyright (c) 2001 by "
+ "Jochen Eisinger <jochen.eisinger@gmx.net>\n");
+
+
+ dbg("driver loaded at address 0x%02x...\n", addr);
+
+ return 0;
+}
+
+
+/* this function is invoked, when we should release our resource... */
+static int fjkeyinf_detach(struct i2c_client *client)
+{
+ dbg("driver detached...\n");
+
+ misc_deregister(&fjkeyinf_dev);
+
+ i2c_detach_client(client);
+
+ return 0;
+}
+
+/* this function is invoked for every i2c adapter, that has a device at the
+ * address we've specified */
+static int fjkeyinf_attach(struct i2c_adapter *adap)
+{
+ dbg("%s\n", __FUNCTION__);
+ return i2c_probe(adap, &addr_data, fjkeyinf_probe);
+}
+
+
+
+/* startup */
+static int __init fjkeyinf_init(void)
+{
+ unsigned char *fujitsu_bios = __va(FJKEYINF_INFO_ADDR);
+// int ctr;
+ struct fj_device *dev;
+
+ if (__pa(high_memory) < (FJKEYINF_INFO_ADDR - 16)) {
+ dbg("Fujitsu BIOS not found...\n");
+ return -ENODEV;
+ }
+
+ dbg("Configuration block [%c%c%c%c%c%c%c%c] "
+ "(%d, %d, %d, 0x%02x) (%d, %d, %d, 0x%02x)\n",
+ fujitsu_bios[0],
+ fujitsu_bios[1],
+ fujitsu_bios[2],
+ fujitsu_bios[3],
+ fujitsu_bios[4],
+ fujitsu_bios[5],
+ fujitsu_bios[6],
+ fujitsu_bios[7],
+ (int)fujitsu_bios[8],
+ (int)fujitsu_bios[9],
+ (int)fujitsu_bios[10],
+ (int)fujitsu_bios[11],
+ (int)fujitsu_bios[12],
+ (int)fujitsu_bios[13],
+ (int)fujitsu_bios[14],
+ (int)fujitsu_bios[15]);
+
+ dev = (struct fj_device *)&fujitsu_bios[8];
+ while (1) {
+ if (dev->type == 0)
+ break;
+ dbg("type = %d, access_method = %d, chip_type = %d, number = %d\n",
+ dev->type, dev->access_method, dev->chip_type, dev->number);
+ ++dev;
+ }
+
+// for (ctr=0 ; ctr<16 ; ctr++)
+// if (fujitsu_bios[ctr] != fjkeyinf_info[ctr]) {
+// dbg("device not found...\n");
+// return -ENODEV;
+// }
+
+ dbg("device found...\n");
+
+ i2c_add_driver(&fjkeyinf_driver);
+ return 0;
+}
+
+static void __exit fjkeyinf_exit(void)
+{
+ i2c_del_driver(&fjkeyinf_driver);
+}
+
+module_init(fjkeyinf_init);
+module_exit(fjkeyinf_exit);
+
#include <linux/smp_lock.h>
#include <linux/devfs_fs_kernel.h>
#include <linux/ptrace.h>
+#include <linux/device.h>
#include <asm/uaccess.h>
#include <asm/io.h>
.open = memory_open, /* just a selector for the real open */
};
-static const struct {
+static const struct mem_dev {
unsigned int minor;
char *name;
umode_t mode;
{11,"kmsg", S_IRUGO | S_IWUSR, &kmsg_fops},
};
+static void release_mem_dev(struct class_device *class_dev)
+{
+ kfree(class_dev);
+}
+
+static struct class mem_class = {
+ .name = "mem",
+ .release = &release_mem_dev,
+};
+
+static ssize_t show_dev(struct class_device *class_dev, char *buf)
+{
+ struct mem_dev *mem_dev = class_get_devdata(class_dev);
+ return print_dev_t(buf, MKDEV(MEM_MAJOR, mem_dev->minor));
+}
+static CLASS_DEVICE_ATTR(dev, S_IRUGO, show_dev, NULL);
+
static int __init chr_dev_init(void)
{
int i;
if (register_chrdev(MEM_MAJOR,"mem",&memory_fops))
printk("unable to get major %d for memory devs\n", MEM_MAJOR);
+ class_register(&mem_class);
for (i = 0; i < ARRAY_SIZE(devlist); i++) {
+ struct class_device *class_dev;
+
+ class_dev = kmalloc(sizeof(*class_dev), GFP_KERNEL);
+ if (class_dev) {
+ memset(class_dev, 0x00, sizeof(*class_dev));
+ class_dev->class = &mem_class;
+ strncpy(class_dev->class_id, devlist[i].name, BUS_ID_SIZE);
+ class_set_devdata(class_dev, (void *)&devlist[i]);
+ if (!class_device_register(class_dev));
+ class_device_create_file(class_dev, &class_device_attr_dev);
+ }
+
devfs_mk_cdev(MKDEV(MEM_MAJOR, devlist[i].minor),
S_IFCHR | devlist[i].mode, devlist[i].name);
}
#include <linux/devfs_fs_kernel.h>
#include <linux/stat.h>
#include <linux/init.h>
-
+#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
return err;
}
+/* Misc class implementation */
+
+/*
+ * TODO for 2.7:
+ * - add a struct class_device to struct miscdevice and make all usages of
+ * them dynamic. This will let us get rid of struct misc_dev below.
+ */
+struct misc_dev {
+ struct list_head node;
+ dev_t dev;
+ struct class_device class_dev;
+};
+#define to_misc_dev(d) container_of(d, struct misc_dev, class_dev)
+
+static LIST_HEAD(misc_dev_list);
+static spinlock_t misc_dev_list_lock = SPIN_LOCK_UNLOCKED;
+
+static void release_misc_dev(struct class_device *class_dev)
+{
+ struct misc_dev *misc_dev = to_misc_dev(class_dev);
+ kfree(misc_dev);
+}
+
+static struct class misc_class = {
+ .name = "misc",
+ .release = &release_misc_dev,
+};
+
+static ssize_t show_dev(struct class_device *class_dev, char *buf)
+{
+ struct misc_dev *misc_dev = to_misc_dev(class_dev);
+ return print_dev_t(buf, misc_dev->dev);
+}
+static CLASS_DEVICE_ATTR(dev, S_IRUGO, show_dev, NULL);
+
+static void misc_add_class_device(struct miscdevice *misc)
+{
+ struct misc_dev *misc_dev = NULL;
+ int retval;
+
+ misc_dev = kmalloc(sizeof(*misc_dev), GFP_KERNEL);
+ if (!misc_dev)
+ return;
+ memset(misc_dev, 0x00, sizeof(*misc_dev));
+
+ misc_dev->dev = MKDEV(MISC_MAJOR, misc->minor);
+ misc_dev->class_dev.dev = misc->dev;
+ misc_dev->class_dev.class = &misc_class;
+ snprintf(misc_dev->class_dev.class_id, BUS_ID_SIZE, "%s", misc->name);
+ retval = class_device_register(&misc_dev->class_dev);
+ if (retval)
+ goto error;
+ class_device_create_file(&misc_dev->class_dev, &class_device_attr_dev);
+ spin_lock(&misc_dev_list_lock);
+ list_add(&misc_dev->node, &misc_dev_list);
+ spin_unlock(&misc_dev_list_lock);
+ return;
+error:
+ kfree(misc_dev);
+}
+
+static void misc_remove_class_device(struct miscdevice *misc)
+{
+ struct misc_dev *misc_dev = NULL;
+ struct list_head *tmp;
+ int found = 0;
+
+ spin_lock(&misc_dev_list_lock);
+ list_for_each(tmp, &misc_dev_list) {
+ misc_dev = list_entry(tmp, struct misc_dev, node);
+ if ((MINOR(misc_dev->dev) == misc->minor)) {
+ found = 1;
+ break;
+ }
+ }
+ if (found) {
+ list_del(&misc_dev->node);
+ spin_unlock(&misc_dev_list_lock);
+ class_device_unregister(&misc_dev->class_dev);
+ } else {
+ spin_unlock(&misc_dev_list_lock);
+ }
+}
+
+
static struct file_operations misc_fops = {
.owner = THIS_MODULE,
.open = misc_open,
devfs_mk_cdev(MKDEV(MISC_MAJOR, misc->minor),
S_IFCHR|S_IRUSR|S_IWUSR|S_IRGRP, misc->devfs_name);
+ misc_add_class_device(misc);
/*
* Add it to the front, so that later devices can "override"
down(&misc_sem);
list_del(&misc->list);
+ misc_remove_class_device(misc);
devfs_remove(misc->devfs_name);
if (i < DYNAMIC_MINORS && i>0) {
misc_minors[i>>3] &= ~(1 << (misc->minor & 7));
if (ent)
ent->proc_fops = &misc_proc_fops;
#endif
+ class_register(&misc_class);
#ifdef CONFIG_MVME16x
rtc_MK48T08_init();
#endif
/* v */ NULL, /* May be assigned at init time by SMP VOYAGER */
/* w */ NULL,
/* x */ NULL,
-/* y */ NULL,
+/* y */ &sysrq_mountro_op, /* stupid fujitsu laptop, can't hit the U key */
/* z */ NULL
};
#include <linux/kbd_kern.h>
#include <linux/console.h>
#include <linux/smp_lock.h>
+#include <linux/device.h>
#include <asm/uaccess.h>
#include <asm/byteorder.h>
#include <asm/unaligned.h>
.open = vcs_open,
};
+/* vc class implementation */
+
+struct vc_dev {
+ struct list_head node;
+ dev_t dev;
+ struct class_device class_dev;
+};
+#define to_vc_dev(d) container_of(d, struct vc_dev, class_dev)
+
+static LIST_HEAD(vc_dev_list);
+static spinlock_t vc_dev_list_lock = SPIN_LOCK_UNLOCKED;
+
+static void release_vc_dev(struct class_device *class_dev)
+{
+ struct vc_dev *vc_dev = to_vc_dev(class_dev);
+ kfree(vc_dev);
+}
+
+static struct class vc_class = {
+ .name = "vc",
+ .release = &release_vc_dev,
+};
+
+static ssize_t show_dev(struct class_device *class_dev, char *buf)
+{
+ struct vc_dev *vc_dev = to_vc_dev(class_dev);
+ return print_dev_t(buf, vc_dev->dev);
+}
+static CLASS_DEVICE_ATTR(dev, S_IRUGO, show_dev, NULL);
+
+static int vc_add_class_device(dev_t dev, char *name, int minor)
+{
+ struct vc_dev *vc_dev = NULL;
+ int retval;
+
+ vc_dev = kmalloc(sizeof(*vc_dev), GFP_KERNEL);
+ if (!vc_dev)
+ return -ENOMEM;
+ memset(vc_dev, 0x00, sizeof(*vc_dev));
+
+ vc_dev->dev = dev;
+ vc_dev->class_dev.class = &vc_class;
+ snprintf(vc_dev->class_dev.class_id, BUS_ID_SIZE, name, minor);
+ retval = class_device_register(&vc_dev->class_dev);
+ if (retval)
+ goto error;
+ class_device_create_file(&vc_dev->class_dev, &class_device_attr_dev);
+ spin_lock(&vc_dev_list_lock);
+ list_add(&vc_dev->node, &vc_dev_list);
+ spin_unlock(&vc_dev_list_lock);
+ return 0;
+error:
+ kfree(vc_dev);
+ return retval;
+}
+
+static void vc_remove_class_device(int minor)
+{
+ struct vc_dev *vc_dev = NULL;
+ struct list_head *tmp;
+ int found = 0;
+
+ spin_lock(&vc_dev_list_lock);
+ list_for_each(tmp, &vc_dev_list) {
+ vc_dev = list_entry(tmp, struct vc_dev, node);
+ if (MINOR(vc_dev->dev) == minor) {
+ found = 1;
+ break;
+ }
+ }
+ if (found) {
+ list_del(&vc_dev->node);
+ spin_unlock(&vc_dev_list_lock);
+ class_device_unregister(&vc_dev->class_dev);
+ } else {
+ spin_unlock(&vc_dev_list_lock);
+ }
+}
+
void vcs_make_devfs(struct tty_struct *tty)
{
devfs_mk_cdev(MKDEV(VCS_MAJOR, tty->index + 1),
devfs_mk_cdev(MKDEV(VCS_MAJOR, tty->index + 129),
S_IFCHR|S_IRUSR|S_IWUSR,
"vcc/a%u", tty->index + 1);
+ vc_add_class_device(MKDEV(VCS_MAJOR, tty->index + 1), "vcs%u", tty->index + 1);
+ vc_add_class_device(MKDEV(VCS_MAJOR, tty->index + 129), "vcsa%u", tty->index + 1);
}
void vcs_remove_devfs(struct tty_struct *tty)
{
devfs_remove("vcc/%u", tty->index + 1);
devfs_remove("vcc/a%u", tty->index + 1);
+ vc_remove_class_device(tty->index + 1);
+ vc_remove_class_device(tty->index + 129);
}
int __init vcs_init(void)
{
if (register_chrdev(VCS_MAJOR, "vcs", &vcs_fops))
panic("unable to get major %d for vcs device", VCS_MAJOR);
+ class_register(&vc_class);
devfs_mk_cdev(MKDEV(VCS_MAJOR, 0), S_IFCHR|S_IRUSR|S_IWUSR, "vcc/0");
devfs_mk_cdev(MKDEV(VCS_MAJOR, 128), S_IFCHR|S_IRUSR|S_IWUSR, "vcc/a0");
+ vc_add_class_device(MKDEV(VCS_MAJOR, 0), "vcs", 0);
+ vc_add_class_device(MKDEV(VCS_MAJOR, 128), "vcsa", 128);
return 0;
}
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
/* ------------------------------------------------------------------------- */
-/* With some changes from Kyösti Mälkki <kmalkki@cc.hut.fi> and even
- Frodo Looijaard <frodol@dds.nl> */
-
-/* $Id: i2c-algo-bit.c,v 1.44 2003/01/21 08:08:16 kmalkki Exp $ */
+/* With some changes from Frodo Looijaard <frodol@dds.nl>, Kyösti Mälkki
+ <kmalkki@cc.hut.fi> and Jean Delvare <khali@linux-fr.org> */
/* #define DEBUG 1 */
setscl(adap,1);
/* Not all adapters have scl sense line... */
- if (adap->getscl == NULL )
+ if (adap->getscl == NULL ) {
+ udelay(adap->udelay);
return 0;
+ }
start=jiffies;
while (! getscl(adap) ) {
*/
static int test_bus(struct i2c_algo_bit_data *adap, char* name) {
int scl,sda;
+
+ if (adap->getscl==NULL)
+ printk(KERN_INFO "i2c-algo-bit.o: Testing SDA only, "
+ "SCL is not readable.\n");
+
sda=getsda(adap);
- if (adap->getscl==NULL) {
- printk(KERN_WARNING "i2c-algo-bit.o: Warning: Adapter can't read from clock line - skipping test.\n");
- return 0;
- }
- scl=getscl(adap);
- printk(KERN_INFO "i2c-algo-bit.o: Adapter: %s scl: %d sda: %d -- testing...\n",
- name,getscl(adap),getsda(adap));
+ scl=(adap->getscl==NULL?1:getscl(adap));
+ printk(KERN_DEBUG "i2c-algo-bit.o: (0) scl=%d, sda=%d\n",scl,sda);
if (!scl || !sda ) {
- printk(KERN_INFO " i2c-algo-bit.o: %s seems to be busy.\n",name);
+ printk(KERN_WARNING "i2c-algo-bit.o: %s seems to be busy.\n", name);
goto bailout;
}
+
sdalo(adap);
- printk(KERN_DEBUG "i2c-algo-bit.o:1 scl: %d sda: %d \n",getscl(adap),
- getsda(adap));
- if ( 0 != getsda(adap) ) {
- printk(KERN_WARNING "i2c-algo-bit.o: %s SDA stuck high!\n",name);
- sdahi(adap);
+ sda=getsda(adap);
+ scl=(adap->getscl==NULL?1:getscl(adap));
+ printk(KERN_DEBUG "i2c-algo-bit.o: (1) scl=%d, sda=%d\n",scl,sda);
+ if ( 0 != sda ) {
+ printk(KERN_WARNING "i2c-algo-bit.o: SDA stuck high!\n");
goto bailout;
}
- if ( 0 == getscl(adap) ) {
- printk(KERN_WARNING "i2c-algo-bit.o: %s SCL unexpected low while pulling SDA low!\n",
- name);
+ if ( 0 == scl ) {
+ printk(KERN_WARNING "i2c-algo-bit.o: SCL unexpected low "
+ "while pulling SDA low!\n");
goto bailout;
}
+
sdahi(adap);
- printk(KERN_DEBUG "i2c-algo-bit.o:2 scl: %d sda: %d \n",getscl(adap),
- getsda(adap));
- if ( 0 == getsda(adap) ) {
- printk(KERN_WARNING "i2c-algo-bit.o: %s SDA stuck low!\n",name);
- sdahi(adap);
+ sda=getsda(adap);
+ scl=(adap->getscl==NULL?1:getscl(adap));
+ printk(KERN_DEBUG "i2c-algo-bit.o: (2) scl=%d, sda=%d\n",scl,sda);
+ if ( 0 == sda ) {
+ printk(KERN_WARNING "i2c-algo-bit.o: SDA stuck low!\n");
goto bailout;
}
- if ( 0 == getscl(adap) ) {
- printk(KERN_WARNING "i2c-algo-bit.o: %s SCL unexpected low while SDA high!\n",
- name);
- goto bailout;
+ if ( 0 == scl ) {
+ printk(KERN_WARNING "i2c-algo-bit.o: SCL unexpected low "
+ "while pulling SDA high!\n");
+ goto bailout;
}
+
scllo(adap);
- printk(KERN_DEBUG "i2c-algo-bit.o:3 scl: %d sda: %d \n",getscl(adap),
- getsda(adap));
- if ( 0 != getscl(adap) ) {
- printk(KERN_WARNING "i2c-algo-bit.o: %s SCL stuck high!\n",name);
- sclhi(adap);
+ sda=getsda(adap);
+ scl=(adap->getscl==NULL?0:getscl(adap));
+ printk(KERN_DEBUG "i2c-algo-bit.o: (3) scl=%d, sda=%d\n",scl,sda);
+ if ( 0 != scl ) {
+ printk(KERN_WARNING "i2c-algo-bit.o: SCL stuck high!\n");
goto bailout;
}
- if ( 0 == getsda(adap) ) {
- printk(KERN_WARNING "i2c-algo-bit.o: %s SDA unexpected low while pulling SCL low!\n",
- name);
+ if ( 0 == sda ) {
+ printk(KERN_WARNING "i2c-algo-bit.o: SDA unexpected low "
+ "while pulling SCL low!\n");
goto bailout;
}
+
sclhi(adap);
- printk(KERN_DEBUG "i2c-algo-bit.o:4 scl: %d sda: %d \n",getscl(adap),
- getsda(adap));
- if ( 0 == getscl(adap) ) {
- printk(KERN_WARNING "i2c-algo-bit.o: %s SCL stuck low!\n",name);
- sclhi(adap);
+ sda=getsda(adap);
+ scl=(adap->getscl==NULL?1:getscl(adap));
+ printk(KERN_DEBUG "i2c-algo-bit.o: (4) scl=%d, sda=%d\n",scl,sda);
+ if ( 0 == scl ) {
+ printk(KERN_WARNING "i2c-algo-bit.o: SCL stuck low!\n");
goto bailout;
}
- if ( 0 == getsda(adap) ) {
- printk(KERN_WARNING "i2c-algo-bit.o: %s SDA unexpected low while SCL high!\n",
- name);
+ if ( 0 == sda ) {
+ printk(KERN_WARNING "i2c-algo-bit.o: SDA unexpected low "
+ "while pulling SCL high!\n");
goto bailout;
}
printk(KERN_INFO "i2c-algo-bit.o: %s passed test.\n",name);
help
If you say yes to this option, support will be included for the Intel
PIIX4 family of mainboard I2C interfaces. Specifically, the following
- versions of the chipset is supported:
+ versions of the chipset are supported:
Intel PIIX4
Intel 440MX
Serverworks OSB4
Serverworks CSB5
+ Serverworks CSB6
SMSC Victory66
This driver can also be built as a module. If so, the module
8233
8233A
8235
+ 8237
This driver can also be built as a module. If so, the module
will be called i2c-viapro.
int timeout = 2;
if (irq > 0) {
- cli();
+ /* This cli() stuff has to get cleaned up */
+ #if defined(CONFIG_SMP)
+ WARN_ON(1);
+ #endif
+ /* cli(); */
if (pcf_pending == 0) {
interruptible_sleep_on_timeout(&pcf_wait, timeout*HZ );
} else
pcf_pending = 0;
- sti();
+ /* sti(); */
} else {
udelay(100);
}
#include <linux/i2c.h>
#include <linux/i2c-algo-ite.h>
#include <linux/i2c-adap-ite.h>
-#include "../i2c-ite.h"
#define DEFAULT_BASE 0x14014030
#define ITE_IIC_IO_SIZE 0x40
/*
Supports:
Intel PIIX4, 440MX
- Serverworks OSB4, CSB5
+ Serverworks OSB4, CSB5, CSB6
SMSC Victory66
Note: we assume there can only be one device, with one SMBus interface.
.driver_data = 0,
},
{
+ .vendor = PCI_VENDOR_ID_SERVERWORKS,
+ .device = PCI_DEVICE_ID_SERVERWORKS_CSB6,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .driver_data = 0,
+ },
+ {
.vendor = PCI_VENDOR_ID_INTEL,
.device = PCI_DEVICE_ID_INTEL_82443MX_3,
.subvendor = PCI_ANY_ID,
/*
i2c-savage4.c - Part of lm_sensors, Linux kernel modules for hardware
monitoring
- Copyright (c) 1998, 1999 Frodo Looijaard <frodol@dds.nl>,
- Philip Edelbrock <phil@netroedge.com>,
- Ralph Metzler <rjkm@thp.uni-koeln.de>, and
+ Copyright (C) 1998-2003 The LM Sensors Team
+ Alexander Wold <awold@bigfoot.com>
Mark D. Studebaker <mdsxyz123@yahoo.com>
- Based on code written by Ralph Metzler <rjkm@thp.uni-koeln.de> and
- Simon Vogl
+ Based on i2c-voodoo3.c.
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
pci_unregister_driver(&savage4_driver);
}
-MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>, "
- "Philip Edelbrock <phil@netroedge.com>, "
- "Ralph Metzler <rjkm@thp.uni-koeln.de>, "
+MODULE_AUTHOR("Alexander Wold <awold@bigfoot.com> "
"and Mark D. Studebaker <mdsxyz123@yahoo.com>");
MODULE_DESCRIPTION("Savage4 I2C/SMBus driver");
MODULE_LICENSE("GPL");
8233
8233A (0x3147 and 0x3177)
8235
+ 8237
Note: we assume there can only be one device, with one SMBus interface.
*/
},
{
.vendor = PCI_VENDOR_ID_VIA,
+ .device = PCI_DEVICE_ID_VIA_8237,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .driver_data = SMBBA3
+ },
+ {
+ .vendor = PCI_VENDOR_ID_VIA,
.device = PCI_DEVICE_ID_VIA_8231_4,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
This driver can also be built as a module. If so, the module
will be called lm78.
+config SENSORS_LM83
+ tristate "National Semiconductor LM83"
+ depends on I2C && EXPERIMENTAL
+ select I2C_SENSOR
+ help
+ If you say yes here you get support for National Semiconductor
+ LM83 sensor chips.
+
+ This driver can also be built as a module. If so, the module
+ will be called lm83.
+
config SENSORS_LM85
tristate "National Semiconductor LM85 and compatibles"
depends on I2C && EXPERIMENTAL
of sensor chips: the W83781D, W83782D, W83783S and W83682HF,
and the similar Asus AS99127F.
- This driver can also be built as a module. If so, the module
- will be called w83781d.
+
+
+config SENSORS_SMBUS_ARP
+ tristate "SMBus ARP 2.0 support"
+ depends on I2C && EXPERIMENTAL
+ select I2C_SENSOR
+
endmenu
obj-$(CONFIG_SENSORS_IT87) += it87.o
obj-$(CONFIG_SENSORS_LM75) += lm75.o
obj-$(CONFIG_SENSORS_LM78) += lm78.o
+obj-$(CONFIG_SENSORS_LM83) += lm83.o
obj-$(CONFIG_SENSORS_LM85) += lm85.o
obj-$(CONFIG_SENSORS_VIA686A) += via686a.o
+
+
+
+obj-$(CONFIG_SENSORS_SMBUS_ARP) += smbus-arp.o
it87_update_client(client);
return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp[nr])*100 );
}
-/* more like overshoot temperature */
static ssize_t show_temp_max(struct device *dev, char *buf, int nr)
{
struct i2c_client *client = to_i2c_client(dev);
it87_update_client(client);
return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_high[nr])*100);
}
-/* more like hysteresis temperature */
static ssize_t show_temp_min(struct device *dev, char *buf, int nr)
{
struct i2c_client *client = to_i2c_client(dev);
show_temp_offset(2);
show_temp_offset(3);
-/* more like overshoot temperature */
static ssize_t show_sensor(struct device *dev, char *buf, int nr)
{
struct i2c_client *client = to_i2c_client(dev);
show_fan_offset(2);
show_fan_offset(3);
-/* Alarm */
-static ssize_t show_alarm(struct device *dev, char *buf)
+/* Alarms */
+static ssize_t show_alarms(struct device *dev, char *buf)
{
struct i2c_client *client = to_i2c_client(dev);
struct it87_data *data = i2c_get_clientdata(client);
it87_update_client(client);
return sprintf(buf,"%d\n", ALARMS_FROM_REG(data->alarms));
}
-static DEVICE_ATTR(alarm, S_IRUGO | S_IWUSR, show_alarm, NULL);
+static DEVICE_ATTR(alarms, S_IRUGO | S_IWUSR, show_alarms, NULL);
/* This function is called when:
* it87_driver is inserted (when this module is loaded), for each
device_create_file(&new_client->dev, &dev_attr_fan_div1);
device_create_file(&new_client->dev, &dev_attr_fan_div2);
device_create_file(&new_client->dev, &dev_attr_fan_div3);
- device_create_file(&new_client->dev, &dev_attr_alarm);
+ device_create_file(&new_client->dev, &dev_attr_alarms);
return 0;
set(temp_hyst, LM75_REG_TEMP_HYST);
static DEVICE_ATTR(temp_max, S_IWUSR | S_IRUGO, show_temp_max, set_temp_max);
-static DEVICE_ATTR(temp_min, S_IWUSR | S_IRUGO, show_temp_hyst, set_temp_hyst);
+static DEVICE_ATTR(temp_hyst, S_IWUSR | S_IRUGO, show_temp_hyst, set_temp_hyst);
static DEVICE_ATTR(temp_input, S_IRUGO, show_temp_input, NULL);
static int lm75_attach_adapter(struct i2c_adapter *adapter)
/* Register sysfs hooks */
device_create_file(&new_client->dev, &dev_attr_temp_max);
- device_create_file(&new_client->dev, &dev_attr_temp_min);
+ device_create_file(&new_client->dev, &dev_attr_temp_hyst);
device_create_file(&new_client->dev, &dev_attr_temp_input);
return 0;
static DEVICE_ATTR(temp_input, S_IRUGO, show_temp, NULL)
static DEVICE_ATTR(temp_max, S_IRUGO | S_IWUSR,
show_temp_over, set_temp_over)
-static DEVICE_ATTR(temp_min, S_IRUGO | S_IWUSR,
+static DEVICE_ATTR(temp_hyst, S_IRUGO | S_IWUSR,
show_temp_hyst, set_temp_hyst)
/* 3 Fans */
device_create_file(&new_client->dev, &dev_attr_in_min6);
device_create_file(&new_client->dev, &dev_attr_in_max6);
device_create_file(&new_client->dev, &dev_attr_temp_input);
- device_create_file(&new_client->dev, &dev_attr_temp_min);
device_create_file(&new_client->dev, &dev_attr_temp_max);
+ device_create_file(&new_client->dev, &dev_attr_temp_hyst);
device_create_file(&new_client->dev, &dev_attr_fan_input1);
device_create_file(&new_client->dev, &dev_attr_fan_min1);
device_create_file(&new_client->dev, &dev_attr_fan_div1);
--- /dev/null
+/*
+ * lm83.c - Part of lm_sensors, Linux kernel modules for hardware
+ * monitoring
+ * Copyright (C) 2003 Jean Delvare <khali@linux-fr.org>
+ *
+ * Heavily inspired from the lm78, lm75 and adm1021 drivers. The LM83 is
+ * a sensor chip made by National Semiconductor. It reports up to four
+ * temperatures (its own plus up to three external ones) with a 1 deg
+ * resolution and a 3-4 deg accuracy. Complete datasheet can be obtained
+ * from National's website at:
+ * http://www.national.com/pf/LM/LM83.html
+ * Since the datasheet omits to give the chip stepping code, I give it
+ * here: 0x03 (at register 0xff).
+ *
+ * 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 <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/i2c-sensor.h>
+
+/*
+ * Addresses to scan
+ * Address is selected using 2 three-level pins, resulting in 9 possible
+ * addresses.
+ */
+
+static unsigned short normal_i2c[] = { I2C_CLIENT_END };
+static unsigned short normal_i2c_range[] = { 0x18, 0x1a, 0x29, 0x2b,
+ 0x4c, 0x4e, I2C_CLIENT_END };
+static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END };
+static unsigned int normal_isa_range[] = { I2C_CLIENT_ISA_END };
+
+/*
+ * Insmod parameters
+ */
+
+SENSORS_INSMOD_1(lm83);
+
+/*
+ * The LM83 registers
+ * Manufacturer ID is 0x01 for National Semiconductor.
+ */
+
+#define LM83_REG_R_MAN_ID 0xFE
+#define LM83_REG_R_CHIP_ID 0xFF
+#define LM83_REG_R_CONFIG 0x03
+#define LM83_REG_W_CONFIG 0x09
+#define LM83_REG_R_STATUS1 0x02
+#define LM83_REG_R_STATUS2 0x35
+#define LM83_REG_R_LOCAL_TEMP 0x00
+#define LM83_REG_R_LOCAL_HIGH 0x05
+#define LM83_REG_W_LOCAL_HIGH 0x0B
+#define LM83_REG_R_REMOTE1_TEMP 0x30
+#define LM83_REG_R_REMOTE1_HIGH 0x38
+#define LM83_REG_W_REMOTE1_HIGH 0x50
+#define LM83_REG_R_REMOTE2_TEMP 0x01
+#define LM83_REG_R_REMOTE2_HIGH 0x07
+#define LM83_REG_W_REMOTE2_HIGH 0x0D
+#define LM83_REG_R_REMOTE3_TEMP 0x31
+#define LM83_REG_R_REMOTE3_HIGH 0x3A
+#define LM83_REG_W_REMOTE3_HIGH 0x52
+#define LM83_REG_R_TCRIT 0x42
+#define LM83_REG_W_TCRIT 0x5A
+
+/*
+ * Conversions, initial values and various macros
+ * The LM83 uses signed 8-bit values.
+ */
+
+#define TEMP_FROM_REG(val) ((val > 127 ? val-256 : val) * 1000)
+#define TEMP_TO_REG(val) ((val < 0 ? val+256 : val) / 1000)
+
+#define LM83_INIT_HIGH 100
+#define LM83_INIT_CRIT 120
+
+static const u8 LM83_REG_R_TEMP[] = {
+ LM83_REG_R_LOCAL_TEMP,
+ LM83_REG_R_REMOTE1_TEMP,
+ LM83_REG_R_REMOTE2_TEMP,
+ LM83_REG_R_REMOTE3_TEMP
+};
+
+static const u8 LM83_REG_R_HIGH[] = {
+ LM83_REG_R_LOCAL_HIGH,
+ LM83_REG_R_REMOTE1_HIGH,
+ LM83_REG_R_REMOTE2_HIGH,
+ LM83_REG_R_REMOTE3_HIGH
+};
+
+static const u8 LM83_REG_W_HIGH[] = {
+ LM83_REG_W_LOCAL_HIGH,
+ LM83_REG_W_REMOTE1_HIGH,
+ LM83_REG_W_REMOTE2_HIGH,
+ LM83_REG_W_REMOTE3_HIGH
+};
+
+/*
+ * Functions declaration
+ */
+
+static int lm83_attach_adapter(struct i2c_adapter *adapter);
+static int lm83_detect(struct i2c_adapter *adapter, int address,
+ int kind);
+static void lm83_init_client(struct i2c_client *client);
+static int lm83_detach_client(struct i2c_client *client);
+static void lm83_update_client(struct i2c_client *client);
+
+/*
+ * Driver data (common to all clients)
+ */
+
+static struct i2c_driver lm83_driver = {
+ .owner = THIS_MODULE,
+ .name = "lm83",
+ .id = I2C_DRIVERID_LM83,
+ .flags = I2C_DF_NOTIFY,
+ .attach_adapter = lm83_attach_adapter,
+ .detach_client = lm83_detach_client,
+};
+
+/*
+ * Client data (each client gets its own)
+ */
+
+struct lm83_data
+{
+ struct semaphore update_lock;
+ char valid; /* zero until following fields are valid */
+ unsigned long last_updated; /* in jiffies */
+
+ /* registers values */
+ u8 temp_input[4];
+ u8 temp_high[4];
+ u8 temp_crit;
+ u16 alarms; /* bitvector, combined */
+};
+
+/*
+ * Internal variables
+ */
+
+static int lm83_id = 0;
+
+/*
+ * Sysfs stuff
+ */
+
+#define show_temp(suffix, value) \
+static ssize_t show_temp_##suffix(struct device *dev, char *buf) \
+{ \
+ struct i2c_client *client = to_i2c_client(dev); \
+ struct lm83_data *data = i2c_get_clientdata(client); \
+ lm83_update_client(client); \
+ return sprintf(buf, "%d\n", TEMP_FROM_REG(data->value)); \
+}
+show_temp(input1, temp_input[0]);
+show_temp(input2, temp_input[1]);
+show_temp(input3, temp_input[2]);
+show_temp(input4, temp_input[3]);
+show_temp(high1, temp_high[0]);
+show_temp(high2, temp_high[1]);
+show_temp(high3, temp_high[2]);
+show_temp(high4, temp_high[3]);
+show_temp(crit, temp_crit);
+
+#define set_temp(suffix, value, reg) \
+static ssize_t set_temp_##suffix(struct device *dev, const char *buf, \
+ size_t count) \
+{ \
+ struct i2c_client *client = to_i2c_client(dev); \
+ struct lm83_data *data = i2c_get_clientdata(client); \
+ data->value = TEMP_TO_REG(simple_strtoul(buf, NULL, 10)); \
+ i2c_smbus_write_byte_data(client, reg, data->value); \
+ return count; \
+}
+set_temp(high1, temp_high[0], LM83_REG_W_LOCAL_HIGH);
+set_temp(high2, temp_high[1], LM83_REG_W_REMOTE1_HIGH);
+set_temp(high3, temp_high[2], LM83_REG_W_REMOTE2_HIGH);
+set_temp(high4, temp_high[3], LM83_REG_W_REMOTE3_HIGH);
+set_temp(crit, temp_crit, LM83_REG_W_TCRIT);
+
+static ssize_t show_alarms(struct device *dev, char *buf)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct lm83_data *data = i2c_get_clientdata(client);
+ lm83_update_client(client);
+ return sprintf(buf, "%d\n", data->alarms);
+}
+
+static DEVICE_ATTR(temp_input1, S_IRUGO, show_temp_input1, NULL);
+static DEVICE_ATTR(temp_input2, S_IRUGO, show_temp_input2, NULL);
+static DEVICE_ATTR(temp_input3, S_IRUGO, show_temp_input3, NULL);
+static DEVICE_ATTR(temp_input4, S_IRUGO, show_temp_input4, NULL);
+static DEVICE_ATTR(temp_max1, S_IWUSR | S_IRUGO, show_temp_high1,
+ set_temp_high1);
+static DEVICE_ATTR(temp_max2, S_IWUSR | S_IRUGO, show_temp_high2,
+ set_temp_high2);
+static DEVICE_ATTR(temp_max3, S_IWUSR | S_IRUGO, show_temp_high3,
+ set_temp_high3);
+static DEVICE_ATTR(temp_max4, S_IWUSR | S_IRUGO, show_temp_high4,
+ set_temp_high4);
+static DEVICE_ATTR(temp_crit, S_IWUSR | S_IRUGO, show_temp_crit,
+ set_temp_crit);
+static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);
+
+/*
+ * Real code
+ */
+
+static int lm83_attach_adapter(struct i2c_adapter *adapter)
+{
+ if (!(adapter->class & I2C_ADAP_CLASS_SMBUS))
+ return 0;
+ return i2c_detect(adapter, &addr_data, lm83_detect);
+}
+
+/*
+ * The following function does more than just detection. If detection
+ * succeeds, it also registers the new chip.
+ */
+static int lm83_detect(struct i2c_adapter *adapter, int address,
+ int kind)
+{
+ struct i2c_client *new_client;
+ struct lm83_data *data;
+ int err = 0;
+ const char *name = "";
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ goto exit;
+
+ if (!(new_client = kmalloc(sizeof(struct i2c_client) +
+ sizeof(struct lm83_data), GFP_KERNEL))) {
+ err = -ENOMEM;
+ goto exit;
+ }
+ memset(new_client, 0x00, sizeof(struct i2c_client) +
+ sizeof(struct lm83_data));
+
+ /* The LM83-specific data is placed right after the common I2C
+ * client data. */
+ data = (struct lm83_data *) (new_client + 1);
+ i2c_set_clientdata(new_client, data);
+ new_client->addr = address;
+ new_client->adapter = adapter;
+ new_client->driver = &lm83_driver;
+ new_client->flags = 0;
+
+ /* Now we do the detection and identification. A negative kind
+ * means that the driver was loaded with no force parameter
+ * (default), so we must both detect and identify the chip
+ * (actually there is only one possible kind of chip for now, LM83).
+ * A zero kind means that the driver was loaded with the force
+ * parameter, the detection step shall be skipped. A positive kind
+ * means that the driver was loaded with the force parameter and a
+ * given kind of chip is requested, so both the detection and the
+ * identification steps are skipped. */
+ if (kind < 0) { /* detection */
+ if (((i2c_smbus_read_byte_data(new_client, LM83_REG_R_STATUS1)
+ & 0xA8) != 0x00) ||
+ ((i2c_smbus_read_byte_data(new_client, LM83_REG_R_STATUS2)
+ & 0x48) != 0x00) ||
+ ((i2c_smbus_read_byte_data(new_client, LM83_REG_R_CONFIG)
+ & 0x41) != 0x00)) {
+ dev_dbg(&client->dev,
+ "LM83 detection failed at 0x%02x.\n", address);
+ goto exit_free;
+ }
+ }
+
+ if (kind <= 0) { /* identification */
+ u8 man_id, chip_id;
+
+ man_id = i2c_smbus_read_byte_data(new_client,
+ LM83_REG_R_MAN_ID);
+ chip_id = i2c_smbus_read_byte_data(new_client,
+ LM83_REG_R_CHIP_ID);
+ if (man_id == 0x01) { /* National Semiconductor */
+ if (chip_id == 0x03) {
+ kind = lm83;
+ name = "lm83";
+ }
+ }
+
+ if (kind <= 0) { /* identification failed */
+ dev_info(&adapter->dev,
+ "Unsupported chip (man_id=0x%02X, "
+ "chip_id=0x%02X).\n", man_id, chip_id);
+ goto exit_free;
+ }
+ }
+
+ /* We can fill in the remaining client fields */
+ strlcpy(new_client->name, name, I2C_NAME_SIZE);
+ new_client->id = lm83_id++;
+ data->valid = 0;
+ init_MUTEX(&data->update_lock);
+
+ /* Tell the I2C layer a new client has arrived */
+ if ((err = i2c_attach_client(new_client)))
+ goto exit_free;
+
+ /* Initialize the LM83 chip */
+ lm83_init_client(new_client);
+
+ /* Register sysfs hooks */
+ device_create_file(&new_client->dev, &dev_attr_temp_input1);
+ device_create_file(&new_client->dev, &dev_attr_temp_input2);
+ device_create_file(&new_client->dev, &dev_attr_temp_input3);
+ device_create_file(&new_client->dev, &dev_attr_temp_input4);
+ device_create_file(&new_client->dev, &dev_attr_temp_max1);
+ device_create_file(&new_client->dev, &dev_attr_temp_max2);
+ device_create_file(&new_client->dev, &dev_attr_temp_max3);
+ device_create_file(&new_client->dev, &dev_attr_temp_max4);
+ device_create_file(&new_client->dev, &dev_attr_temp_crit);
+ device_create_file(&new_client->dev, &dev_attr_alarms);
+
+ return 0;
+
+exit_free:
+ kfree(new_client);
+exit:
+ return err;
+}
+
+static void lm83_init_client(struct i2c_client *client)
+{
+ int nr;
+
+ for (nr = 0; nr < 4; nr++)
+ i2c_smbus_write_byte_data(client, LM83_REG_W_HIGH[nr],
+ TEMP_TO_REG(LM83_INIT_HIGH));
+ i2c_smbus_write_byte_data(client, LM83_REG_W_TCRIT,
+ TEMP_TO_REG(LM83_INIT_CRIT));
+}
+
+static int lm83_detach_client(struct i2c_client *client)
+{
+ int err;
+
+ if ((err = i2c_detach_client(client))) {
+ dev_err(&client->dev,
+ "Client deregistration failed, client not detached.\n");
+ return err;
+ }
+
+ kfree(client);
+ return 0;
+}
+
+static void lm83_update_client(struct i2c_client *client)
+{
+ struct lm83_data *data = i2c_get_clientdata(client);
+
+ down(&data->update_lock);
+
+ if ((jiffies - data->last_updated > HZ * 2) ||
+ (jiffies < data->last_updated) ||
+ !data->valid) {
+ int nr;
+ dev_dbg(&client->dev, "Updating lm83 data.\n");
+ for (nr = 0; nr < 4 ; nr++) {
+ data->temp_input[nr] =
+ i2c_smbus_read_byte_data(client,
+ LM83_REG_R_TEMP[nr]);
+ data->temp_high[nr] =
+ i2c_smbus_read_byte_data(client,
+ LM83_REG_R_HIGH[nr]);
+ }
+ data->temp_crit =
+ i2c_smbus_read_byte_data(client, LM83_REG_R_TCRIT);
+ data->alarms =
+ i2c_smbus_read_byte_data(client, LM83_REG_R_STATUS1)
+ + (i2c_smbus_read_byte_data(client, LM83_REG_R_STATUS2)
+ << 8);
+ data->last_updated = jiffies;
+ data->valid = 1;
+ }
+
+ up(&data->update_lock);
+}
+
+static int __init sensors_lm83_init(void)
+{
+ return i2c_add_driver(&lm83_driver);
+}
+
+static void __exit sensors_lm83_exit(void)
+{
+ i2c_del_driver(&lm83_driver);
+}
+
+MODULE_AUTHOR("Jean Delvare <khali@linux-fr.org>");
+MODULE_DESCRIPTION("LM83 driver");
+MODULE_LICENSE("GPL");
+
+module_init(sensors_lm83_init);
+module_exit(sensors_lm83_exit);
size_t count, int nr)
{
struct i2c_client *client = to_i2c_client(dev);
- struct lm85_data *data = i2c_get_clientdata(client);
int val;
- down(&data->update_lock);
val = simple_strtol(buf, NULL, 10);
- data->fan_min[nr] = FAN_TO_REG(val);
- lm85_write_value(client, LM85_REG_FAN_MIN(nr), data->fan_min[nr]);
- up(&data->update_lock);
+ val = FAN_TO_REG(val);
+ lm85_write_value(client, LM85_REG_FAN_MIN(nr), val);
return count;
}
size_t count, int nr)
{
struct i2c_client *client = to_i2c_client(dev);
- struct lm85_data *data = i2c_get_clientdata(client);
int val;
- down(&data->update_lock);
val = simple_strtol(buf, NULL, 10);
- data->pwm[nr] = PWM_TO_REG(val);
- lm85_write_value(client, LM85_REG_PWM(nr), data->pwm[nr]);
- up(&data->update_lock);
+ val = PWM_TO_REG(val);
+ lm85_write_value(client, LM85_REG_PWM(nr), val);
return count;
}
static ssize_t show_pwm_enable(struct device *dev, char *buf, int nr)
size_t count, int nr)
{
struct i2c_client *client = to_i2c_client(dev);
- struct lm85_data *data = i2c_get_clientdata(client);
int val;
- down(&data->update_lock);
val = simple_strtol(buf, NULL, 10);
- data->in_min[nr] = INS_TO_REG(nr, val);
- lm85_write_value(client, LM85_REG_IN_MIN(nr), data->in_min[nr]);
- up(&data->update_lock);
+ val = INS_TO_REG(nr, val);
+ lm85_write_value(client, LM85_REG_IN_MIN(nr), val);
return count;
}
static ssize_t show_in_max(struct device *dev, char *buf, int nr)
size_t count, int nr)
{
struct i2c_client *client = to_i2c_client(dev);
- struct lm85_data *data = i2c_get_clientdata(client);
int val;
- down(&data->update_lock);
val = simple_strtol(buf, NULL, 10);
- data->in_max[nr] = INS_TO_REG(nr, val);
- lm85_write_value(client, LM85_REG_IN_MAX(nr), data->in_max[nr]);
- up(&data->update_lock);
+ val = INS_TO_REG(nr, val);
+ lm85_write_value(client, LM85_REG_IN_MAX(nr), val);
return count;
}
#define show_in_reg(offset) \
size_t count, int nr)
{
struct i2c_client *client = to_i2c_client(dev);
- struct lm85_data *data = i2c_get_clientdata(client);
int val;
- down(&data->update_lock);
val = simple_strtol(buf, NULL, 10);
- data->temp_min[nr] = TEMP_TO_REG(val);
- lm85_write_value(client, LM85_REG_TEMP_MIN(nr), data->temp_min[nr]);
- up(&data->update_lock);
+ val = TEMP_TO_REG(val);
+ lm85_write_value(client, LM85_REG_TEMP_MIN(nr), val);
return count;
}
static ssize_t show_temp_max(struct device *dev, char *buf, int nr)
size_t count, int nr)
{
struct i2c_client *client = to_i2c_client(dev);
- struct lm85_data *data = i2c_get_clientdata(client);
int val;
- down(&data->update_lock);
val = simple_strtol(buf, NULL, 10);
- data->temp_max[nr] = TEMP_TO_REG(val);
- lm85_write_value(client, LM85_REG_TEMP_MAX(nr), data->temp_max[nr]);
- up(&data->update_lock);
+ val = TEMP_TO_REG(val);
+ lm85_write_value(client, LM85_REG_TEMP_MAX(nr), val);
return count;
}
#define show_temp_reg(offset) \
int lm85_read_value(struct i2c_client *client, u8 reg)
{
+ struct lm85_data *data = i2c_get_clientdata(client);
int res;
+ /* serialize access to the hardware */
+ down(&data->update_lock);
+
/* What size location is it? */
switch( reg ) {
case LM85_REG_FAN(0) : /* Read WORD data */
res = i2c_smbus_read_byte_data(client, reg);
break ;
}
+ up(&data->update_lock);
return res ;
}
int lm85_write_value(struct i2c_client *client, u8 reg, int value)
{
+ struct lm85_data *data = i2c_get_clientdata(client);
int res ;
+ /* serialize access to the hardware */
+ down(&data->update_lock);
+
switch( reg ) {
case LM85_REG_FAN(0) : /* Write WORD data */
case LM85_REG_FAN(1) :
res = i2c_smbus_write_byte_data(client, reg, value);
break ;
}
+ up(&data->update_lock);
return res ;
}
struct lm85_data *data = i2c_get_clientdata(client);
int i;
- down(&data->update_lock);
-
if ( !data->valid ||
(jiffies - data->last_reading > LM85_DATA_INTERVAL ) ) {
/* Things that change quickly */
}; /* last_config */
data->valid = 1;
-
- up(&data->update_lock);
}
--- /dev/null
+/*
+ vrm.c - Part of lm_sensors, Linux kernel modules for hardware
+ monitoring
+ Copyright (c) 2002 Mark D. Studebaker <mdsxyz123@yahoo.com>
+ With assistance from Trent Piepho <xyzzy@speakeasy.org>
+
+ 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.
+*/
+
+/*
+ This file contains common code for decoding VID pins.
+ This file is #included in various chip drivers in this directory.
+ As the user is unlikely to load more than one driver which
+ includes this code we don't worry about the wasted space.
+ Reference: VRM x.y DC-DC Converter Design Guidelines,
+ available at http://developer.intel.com
+*/
+
+/*
+ Legal val values 00 - 1F.
+ vrm is the Intel VRM document version.
+ Note: vrm version is scaled by 10 and the return value is scaled by 1000
+ to avoid floating point in the kernel.
+*/
+
+#define DEFAULT_VRM 82
+
+static inline int vid_from_reg(int val, int vrm)
+{
+ switch(vrm) {
+
+ case 91: /* VRM 9.1 */
+ case 90: /* VRM 9.0 */
+ return(val == 0x1f ? 0 :
+ 1850 - val * 25);
+
+ case 85: /* VRM 8.5 */
+ return((val & 0x10 ? 25 : 0) +
+ ((val & 0x0f) > 0x04 ? 2050 : 1250) -
+ ((val & 0x0f) * 50));
+
+ case 84: /* VRM 8.4 */
+ val &= 0x0f;
+ /* fall through */
+ default: /* VRM 8.2 */
+ return(val == 0x1f ? 0 :
+ val & 0x10 ? 5100 - (val) * 100 :
+ 2050 - (val) * 50);
+ }
+}
--- /dev/null
+/*
+ smbus-arp.c - Part of lm_sensors, Linux kernel modules for hardware
+ monitoring
+ Copyright (c) 2002 Mark D. Studebaker <mdsxyz123@yahoo.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.
+
+ 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 <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/i2c-sensor.h>
+#include <linux/init.h>
+
+#define DEBUG 1
+
+/* Addresses to scan */
+#define ARP_ADDRESS 0x61
+static unsigned short normal_i2c[] = { ARP_ADDRESS, I2C_CLIENT_END };
+static unsigned short normal_i2c_range[] = { I2C_CLIENT_END };
+static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END };
+static unsigned int normal_isa_range[] = { I2C_CLIENT_ISA_END };
+
+/* Insmod parameters */
+SENSORS_INSMOD_1(arp);
+
+/* ARP Commands */
+#define ARP_PREPARE 0x01
+#define ARP_RESET_DEV 0x02
+#define ARP_GET_UDID_GEN 0x03
+#define ARP_ASSIGN_ADDR 0x04
+
+/* UDID Fields */
+#define ARP_CAPAB 0
+#define ARP_VER 1
+#define ARP_VEND 2
+#define ARP_DEV 4
+#define ARP_INT 6
+#define ARP_SUBVEND 8
+#define ARP_SUBDEV 10
+#define ARP_SPECID 12
+
+#define UDID_LENGTH 0x11
+
+static u8 reserved[] =
+/* As defined by SMBus Spec. Appendix C */
+ {0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x28,
+ 0x37, ARP_ADDRESS,
+/* As defined by SMBus Spec. Sect. 5.2 */
+ 0x01, 0x02, 0x03, 0x04, 0x05,
+ 0x06, 0x07, 0x78, 0x79, 0x7a, 0x7b,
+ 0x7c, 0x7d, 0x7e, 0x7f,
+/* Common PC addresses (bad idea) */
+ 0x2d, 0x48, 0x49, /* sensors */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* eeproms */
+ 0x69, /* clock chips */
+/* Must end in 0 which is also reserved */
+ 0x00};
+
+#define SMBUS_ADDRESS_SIZE 0x80
+#define ARP_FREE 0
+#define ARP_RESERVED 1
+#define ARP_BUSY 2
+
+#define ARP_MAX_DEVICES 8
+struct arp_device {
+ int status;
+ u8 udid[UDID_LENGTH];
+ u8 dev_cap;
+ u8 dev_ver;
+ u16 dev_vid;
+ u16 dev_did;
+ u16 dev_int;
+ u16 dev_svid;
+ u16 dev_sdid;
+ u32 dev_vsid;
+ u8 saddr;
+};
+
+/* Each client has this additional data */
+struct arp_data {
+ struct semaphore update_lock;
+ char valid; /* !=0 if following fields are valid */
+ unsigned long last_updated; /* In jiffies */
+
+ u8 address_pool[SMBUS_ADDRESS_SIZE];
+ struct arp_device dev[ARP_MAX_DEVICES];
+};
+
+
+static int smbusarp_attach_adapter(struct i2c_adapter *adapter);
+static int smbusarp_detect(struct i2c_adapter *adapter, int address, int kind);
+static int smbusarp_detach_client(struct i2c_client *client);
+
+static int smbusarp_init_client(struct i2c_client *client);
+
+static struct i2c_driver smbusarp_driver = {
+ .owner = THIS_MODULE,
+ .name = "SMBus_ARP",
+ .id = I2C_DRIVERID_ARP,
+ .flags = I2C_DF_NOTIFY,
+ .attach_adapter = smbusarp_attach_adapter,
+ .detach_client = smbusarp_detach_client,
+};
+
+#if 0
+/* -- SENSORS SYSCTL START -- */
+#define ARP_SYSCTL1 1000
+#define ARP_SYSCTL2 1001
+#define ARP_SYSCTL3 1002
+#define ARP_SYSCTL4 1003
+#define ARP_SYSCTL5 1004
+#define ARP_SYSCTL6 1005
+#define ARP_SYSCTL7 1006
+#define ARP_SYSCTL8 1007
+
+/* -- SENSORS SYSCTL END -- */
+static ctl_table smbusarp_dir_table_template[] = {
+ {ARP_SYSCTL1, "0", NULL, 0, 0644, NULL, &i2c_proc_real,
+ &i2c_sysctl_real, NULL, &smbusarp_contents},
+ {ARP_SYSCTL2, "1", NULL, 0, 0644, NULL, &i2c_proc_real,
+ &i2c_sysctl_real, NULL, &smbusarp_contents},
+ {ARP_SYSCTL3, "2", NULL, 0, 0644, NULL, &i2c_proc_real,
+ &i2c_sysctl_real, NULL, &smbusarp_contents},
+ {ARP_SYSCTL4, "3", NULL, 0, 0644, NULL, &i2c_proc_real,
+ &i2c_sysctl_real, NULL, &smbusarp_contents},
+ {ARP_SYSCTL5, "4", NULL, 0, 0644, NULL, &i2c_proc_real,
+ &i2c_sysctl_real, NULL, &smbusarp_contents},
+ {ARP_SYSCTL6, "5", NULL, 0, 0644, NULL, &i2c_proc_real,
+ &i2c_sysctl_real, NULL, &smbusarp_contents},
+ {ARP_SYSCTL7, "6", NULL, 0, 0644, NULL, &i2c_proc_real,
+ &i2c_sysctl_real, NULL, &smbusarp_contents},
+ {ARP_SYSCTL8, "7", NULL, 0, 0644, NULL, &i2c_proc_real,
+ &i2c_sysctl_real, NULL, &smbusarp_contents},
+ {0}
+};
+#endif
+
+static int smbusarp_id = 0;
+
+static int smbusarp_attach_adapter(struct i2c_adapter *adapter)
+{
+ return i2c_detect(adapter, &addr_data, smbusarp_detect);
+}
+
+/* This function is called by i2c_detect */
+static int smbusarp_detect(struct i2c_adapter *adapter, int address, int kind)
+{
+ struct i2c_client *new_client;
+ struct arp_data *data;
+ int err = 0;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BLOCK_DATA))
+ return(0);
+
+ if (!(new_client = kmalloc(sizeof(struct i2c_client) +
+ sizeof(struct arp_data),
+ GFP_KERNEL))) {
+ return(-ENOMEM);
+ }
+ memset(new_client, 0x00, sizeof(struct i2c_client) +
+ sizeof(struct arp_data));
+
+ data = (struct arp_data *) (new_client + 1);
+ new_client->addr = address;
+ i2c_set_clientdata(new_client, data);
+ new_client->adapter = adapter;
+ new_client->driver = &smbusarp_driver;
+ new_client->flags = I2C_CLIENT_PEC;
+
+ strcpy(new_client->name, "arp");
+
+ new_client->id = smbusarp_id++;
+ data->valid = 0;
+ init_MUTEX(&data->update_lock);
+
+ if ((err = i2c_attach_client(new_client)))
+ goto error;
+
+ smbusarp_init_client(new_client);
+ return 0;
+
+error:
+ kfree(new_client);
+ return err;
+}
+
+static int smbusarp_detach_client(struct i2c_client *client)
+{
+ int err;
+
+ err = i2c_detach_client(client);
+ if (err) {
+ dev_err(&client->dev, "Client deregistration failed, client not detached.\n");
+ return err;
+ }
+
+ kfree(client);
+
+ return 0;
+}
+
+
+static u8 choose_addr(u8 * pool)
+{
+ int i;
+
+ for (i = 0; i < 0x7f; i++) {
+ if (pool[i] == ARP_FREE)
+ return (u8)i;
+ }
+ return 0xff;
+}
+
+static int smbusarp_init_client(struct i2c_client *client)
+{
+ int ret = -1;
+ struct arp_data *data = i2c_get_clientdata(client);
+ struct list_head *item;
+ u8 blk[I2C_SMBUS_BLOCK_MAX];
+ u8 *r;
+ u8 addr;
+ int i;
+ int found = 0;
+ int newdev = 0;
+
+ for(i = 0; i < ARP_MAX_DEVICES; i++)
+ data->dev[i].status = ARP_FREE;
+
+ for(i = 0; i < SMBUS_ADDRESS_SIZE; i++)
+ data->address_pool[i] = ARP_FREE;
+
+ r = reserved;
+ do {
+ data->address_pool[*r] = ARP_RESERVED;
+ } while(*r++);
+
+ list_for_each(item, &client->adapter->clients) {
+ struct i2c_client *c = list_entry(item, struct i2c_client, list);
+ data->address_pool[c->addr] = ARP_BUSY;
+ }
+
+ ret = i2c_smbus_write_byte(client, ARP_PREPARE);
+ if (ret < 0) {
+ dev_dbg(client->dev, "No ARP response on adapter 0x%X\n", client->adapter->id);
+ return(-1); /* Packet wasn't acked */
+ }
+ while(1) {
+ ret = i2c_smbus_read_block_data(client, ARP_GET_UDID_GEN, blk);
+ if(ret != UDID_LENGTH) {
+ dev_dbg(&client->dev, "No/Bad UDID response %d on adapter 0x%X\n", ret, client->adapter->id);
+ if(found)
+ return found;
+ else
+ return -1; /* Bad response */
+ }
+ dev_dbg(&client->dev, "Good UDID response on adapter 0x%X\n", client->adapter->id);
+ dev_dbg(&client->dev, "Cap. 0x%02x Rev. 0x%02x Vend. 0x%02x%02x Dev. 0x%02x%02x\n", blk[0], blk[1], blk[2], blk[3], blk[4], blk[5]);
+ dev_dbg(&client->dev, "Int. 0x%02x%02x Subvend. 0x%02x%02x Subdev. 0x%02x%02x Spec. 0x%02x%02x%02x%02x\n", blk[6], blk[7], blk[8], blk[9], blk[10], blk[11], blk[12], blk[13], blk[14], blk[15]);
+
+ /* clean up this... */
+ found++;
+ do {
+ if (data->dev[newdev].status == ARP_FREE)
+ break;
+ } while(++newdev < ARP_MAX_DEVICES);
+ if (newdev == ARP_MAX_DEVICES) {
+ printk(KERN_WARNING "smbus-arp.o: No more slots available\n");
+ return -1;
+ }
+
+ /* check device slave addr */
+ addr = blk[16];
+ if(addr != 0xFF) {
+ addr >>= 1;
+ if(blk[0] & 0xC0) {
+ if(data->address_pool[addr] == ARP_FREE) {
+ dev_dbg(&client->dev, "Requested free Non-fixed Address 0x%02x\n", addr);
+ } else {
+ dev_dbg(&client->dev, "Requested busy Non-fixed Address 0x%02x\n", addr);
+ addr = choose_addr(data->address_pool);
+ if (addr == 0xff) {
+ dev_warn(&client->dev, "Address pool exhausted\n");
+ return -1;
+ }
+ }
+ } else {
+ dev_dbg(&client->dev, "Fixed Address 0x%02x\n", addr);
+ }
+ } else {
+ dev_dbg(&client->dev, "No Address\n");
+ addr = choose_addr(data->address_pool);
+ if (addr == 0xff) {
+ dev_warn(&client->dev, "Address pool exhausted\n");
+ return -1;
+ }
+ }
+ /* store things both ways */
+ for (i = 0; i < UDID_LENGTH; i++)
+ data->dev[newdev].udid[i] = blk[i];
+ data->dev[newdev].saddr = addr;
+ data->dev[newdev].status = ARP_BUSY;
+ data->dev[newdev].dev_cap = blk[0];
+ data->dev[newdev].dev_ver = blk[1];
+ data->dev[newdev].dev_vid = (blk[2] << 8) | blk[3];
+ data->dev[newdev].dev_did = (blk[4] << 8) | blk[5];
+ data->dev[newdev].dev_int = (blk[6] << 8) | blk[7];
+ data->dev[newdev].dev_svid = (blk[8] << 8) | blk[9];
+ data->dev[newdev].dev_sdid = (blk[10] << 8) | blk[11];
+ data->dev[newdev].dev_vsid = (blk[12] << 24) | (blk[13] << 16) |
+ (blk[14] << 8) | blk[15] ;
+
+ blk[16] = addr << 1;
+ ret = i2c_smbus_write_block_data(client, ARP_ASSIGN_ADDR, UDID_LENGTH, blk);
+ if (ret) {
+ dev_dbg(&client->dev, "Bad response, address 0x%02x not assigned\n", addr);
+ } else {
+ data->address_pool[addr] = ARP_BUSY;
+ dev_dbg(&client->dev, "Assigned address 0x%02x\n", addr);
+ }
+ /* retry? */
+
+ } /* while 1 */
+
+ return ret;
+}
+
+#define show(value) \
+static ssize_t show_slot_##value(struct device *dev, char *buf, int slot) \
+{ \
+ struct i2c_client *client = to_i2c_client(dev); \
+ struct arp_data *data = i2c_get_clientdata(client); \
+ \
+ return sprintf(buf, "%d\n", data->dev[slot].value); \
+}
+show(saddr);
+show(dev_cap);
+show(dev_ver);
+show(dev_vid);
+
+#define X(num) \
+static ssize_t show_slot_saddr_##num(struct device *dev, char *buf) \
+{ \
+ return show_slot_saddr(dev, buf, num); \
+} \
+static DEVICE_ATTR(slot_##num, S_IWUSR | S_IRUGO, show_slot_saddr_##num, NULL);
+
+X(0);
+
+#if 0
+/* reassign address on writex */
+static void smbusarp_contents(struct i2c_client *client, int operation,
+ int ctl_name, int *nrels_mag, long *results)
+{
+ int nr = ctl_name - ARP_SYSCTL1;
+ struct arp_data *data = i2c_get_clientdata(client);
+ int ret;
+ u8 save;
+ u8 a;
+
+ if (operation == SENSORS_PROC_REAL_INFO)
+ *nrels_mag = 0;
+ else if (operation == SENSORS_PROC_REAL_READ) {
+ if(data->dev[nr].status == ARP_BUSY) {
+ results[0] = data->dev[nr].saddr;
+ results[1] = data->dev[nr].dev_cap;
+ results[2] = data->dev[nr].dev_ver;
+ results[3] = data->dev[nr].dev_vid;
+ results[4] = data->dev[nr].dev_did;
+ results[5] = data->dev[nr].dev_int;
+ results[6] = data->dev[nr].dev_svid;
+ results[7] = data->dev[nr].dev_sdid;
+ results[8] = data->dev[nr].dev_vsid;
+ *nrels_mag = 9;
+ } else {
+ *nrels_mag = 0;
+ }
+ } else if (operation == SENSORS_PROC_REAL_WRITE) {
+ a = results[0];
+ if ((*nrels_mag >= 1) &&
+ (a < SMBUS_ADDRESS_SIZE) &&
+ (data->dev[nr].status == ARP_BUSY) &&
+ (data->address_pool[a] == ARP_FREE)) {
+ save = data->dev[nr].udid[16];
+ data->dev[nr].udid[16] = a << 1;
+ ret = i2c_smbus_write_block_data(client, ARP_ASSIGN_ADDR, UDID_LENGTH, data->dev[nr].udid);
+ if (ret) {
+ data->dev[nr].udid[16] = save;
+ dev_dbg(&client->dev, "smbus-arp Bad response, address 0x%02x not assigned\n", a);
+ } else {
+ data->dev[nr].saddr = a;
+ data->address_pool[a] = ARP_BUSY;
+ dev_dbg(&client->dev, "smbus-arp Assigned address 0x%02x\n", a);
+ }
+ } else {
+ dev_warn(&client->dev, "smbus-arp Bad address 0x%02x\n", a);
+ }
+ }
+}
+#endif
+
+static int __init sm_smbusarp_init(void)
+{
+ printk(KERN_INFO "smbus-arp\n");
+
+ /* magic force invocation */
+ force_arp[0] = -1;
+ force_arp[1] = ARP_ADDRESS;
+ return i2c_add_driver(&smbusarp_driver);
+}
+
+static void __exit sm_smbusarp_exit(void)
+{
+ i2c_del_driver(&smbusarp_driver);
+}
+
+
+MODULE_AUTHOR("Mark D. Studebaker <mdsxyz123@yahoo.com>");
+MODULE_DESCRIPTION("SMBUS ARP Driver");
+MODULE_LICENSE("GPL");
+
+module_init(sm_smbusarp_init);
+module_exit(sm_smbusarp_exit);
via686a_update_client(client);
return sprintf(buf, "%ld\n", TEMP_FROM_REG10(data->temp[nr])*100 );
}
-/* more like overshoot temperature */
-static ssize_t show_temp_max(struct device *dev, char *buf, int nr) {
+static ssize_t show_temp_over(struct device *dev, char *buf, int nr) {
struct i2c_client *client = to_i2c_client(dev);
struct via686a_data *data = i2c_get_clientdata(client);
via686a_update_client(client);
return sprintf(buf, "%ld\n", TEMP_FROM_REG(data->temp_over[nr])*100);
}
-/* more like hysteresis temperature */
-static ssize_t show_temp_min(struct device *dev, char *buf, int nr) {
+static ssize_t show_temp_hyst(struct device *dev, char *buf, int nr) {
struct i2c_client *client = to_i2c_client(dev);
struct via686a_data *data = i2c_get_clientdata(client);
via686a_update_client(client);
return sprintf(buf, "%ld\n", TEMP_FROM_REG(data->temp_hyst[nr])*100);
}
-static ssize_t set_temp_max(struct device *dev, const char *buf,
+static ssize_t set_temp_over(struct device *dev, const char *buf,
size_t count, int nr) {
struct i2c_client *client = to_i2c_client(dev);
struct via686a_data *data = i2c_get_clientdata(client);
via686a_write_value(client, VIA686A_REG_TEMP_OVER(nr), data->temp_over[nr]);
return count;
}
-static ssize_t set_temp_min(struct device *dev, const char *buf,
+static ssize_t set_temp_hyst(struct device *dev, const char *buf,
size_t count, int nr) {
struct i2c_client *client = to_i2c_client(dev);
struct via686a_data *data = i2c_get_clientdata(client);
return show_temp(dev, buf, 0x##offset - 1); \
} \
static ssize_t \
-show_temp_##offset##_max (struct device *dev, char *buf) \
+show_temp_##offset##_over (struct device *dev, char *buf) \
{ \
- return show_temp_max(dev, buf, 0x##offset - 1); \
+ return show_temp_over(dev, buf, 0x##offset - 1); \
} \
static ssize_t \
-show_temp_##offset##_min (struct device *dev, char *buf) \
+show_temp_##offset##_hyst (struct device *dev, char *buf) \
{ \
- return show_temp_min(dev, buf, 0x##offset - 1); \
+ return show_temp_hyst(dev, buf, 0x##offset - 1); \
} \
-static ssize_t set_temp_##offset##_max (struct device *dev, \
+static ssize_t set_temp_##offset##_over (struct device *dev, \
const char *buf, size_t count) \
{ \
- return set_temp_max(dev, buf, count, 0x##offset - 1); \
+ return set_temp_over(dev, buf, count, 0x##offset - 1); \
} \
-static ssize_t set_temp_##offset##_min (struct device *dev, \
+static ssize_t set_temp_##offset##_hyst (struct device *dev, \
const char *buf, size_t count) \
{ \
- return set_temp_min(dev, buf, count, 0x##offset - 1); \
+ return set_temp_hyst(dev, buf, count, 0x##offset - 1); \
} \
static DEVICE_ATTR(temp_input##offset, S_IRUGO, show_temp_##offset, NULL) \
static DEVICE_ATTR(temp_max##offset, S_IRUGO | S_IWUSR, \
- show_temp_##offset##_max, set_temp_##offset##_max) \
-static DEVICE_ATTR(temp_min##offset, S_IRUGO | S_IWUSR, \
- show_temp_##offset##_min, set_temp_##offset##_min)
+ show_temp_##offset##_over, set_temp_##offset##_over) \
+static DEVICE_ATTR(temp_hyst##offset, S_IRUGO | S_IWUSR, \
+ show_temp_##offset##_hyst, set_temp_##offset##_hyst)
show_temp_offset(1);
show_temp_offset(2);
show_fan_offset(1);
show_fan_offset(2);
-/* Alarm */
-static ssize_t show_alarm(struct device *dev, char *buf) {
+/* Alarms */
+static ssize_t show_alarms(struct device *dev, char *buf) {
struct i2c_client *client = to_i2c_client(dev);
struct via686a_data *data = i2c_get_clientdata(client);
via686a_update_client(client);
return sprintf(buf,"%d\n", ALARMS_FROM_REG(data->alarms));
}
-static DEVICE_ATTR(alarm, S_IRUGO | S_IWUSR, show_alarm, NULL);
+static DEVICE_ATTR(alarms, S_IRUGO | S_IWUSR, show_alarms, NULL);
/* The driver. I choose to use type i2c_driver, as at is identical to both
smbus_driver and isa_driver, and clients could be of either kind */
device_create_file(&new_client->dev, &dev_attr_temp_max1);
device_create_file(&new_client->dev, &dev_attr_temp_max2);
device_create_file(&new_client->dev, &dev_attr_temp_max3);
- device_create_file(&new_client->dev, &dev_attr_temp_min1);
- device_create_file(&new_client->dev, &dev_attr_temp_min2);
- device_create_file(&new_client->dev, &dev_attr_temp_min3);
+ device_create_file(&new_client->dev, &dev_attr_temp_hyst1);
+ device_create_file(&new_client->dev, &dev_attr_temp_hyst2);
+ device_create_file(&new_client->dev, &dev_attr_temp_hyst3);
device_create_file(&new_client->dev, &dev_attr_fan_input1);
device_create_file(&new_client->dev, &dev_attr_fan_input2);
device_create_file(&new_client->dev, &dev_attr_fan_min1);
device_create_file(&new_client->dev, &dev_attr_fan_min2);
device_create_file(&new_client->dev, &dev_attr_fan_div1);
device_create_file(&new_client->dev, &dev_attr_fan_div2);
- device_create_file(&new_client->dev, &dev_attr_alarm);
+ device_create_file(&new_client->dev, &dev_attr_alarms);
return 0;
u8 fan[3]; /* Register value */
u8 fan_min[3]; /* Register value */
u8 temp;
- u8 temp_min; /* Register value */
u8 temp_max; /* Register value */
+ u8 temp_hyst; /* Register value */
u16 temp_add[2]; /* Register value */
u16 temp_max_add[2]; /* Register value */
- u16 temp_min_add[2]; /* Register value */
+ u16 temp_hyst_add[2]; /* Register value */
u8 fan_div[3]; /* Register encoding, shifted right */
u8 vid; /* Register encoding, combined */
u32 alarms; /* Register encoding, combined */
} \
}
show_temp_reg(temp);
-show_temp_reg(temp_min);
show_temp_reg(temp_max);
+show_temp_reg(temp_hyst);
#define store_temp_reg(REG, reg) \
static ssize_t store_temp_##reg (struct device *dev, const char *buf, size_t count, int nr) \
\
return count; \
}
-store_temp_reg(OVER, min);
-store_temp_reg(HYST, max);
+store_temp_reg(OVER, max);
+store_temp_reg(HYST, hyst);
#define sysfs_temp_offset(offset) \
static ssize_t \
#define sysfs_temp_offsets(offset) \
sysfs_temp_offset(offset); \
-sysfs_temp_reg_offset(min, offset); \
-sysfs_temp_reg_offset(max, offset);
+sysfs_temp_reg_offset(max, offset); \
+sysfs_temp_reg_offset(hyst, offset);
sysfs_temp_offsets(1);
sysfs_temp_offsets(2);
do { \
device_create_file(&client->dev, &dev_attr_temp_input##offset); \
device_create_file(&client->dev, &dev_attr_temp_max##offset); \
-device_create_file(&client->dev, &dev_attr_temp_min##offset); \
+device_create_file(&client->dev, &dev_attr_temp_hyst##offset); \
} while (0)
static ssize_t
}
data->temp = w83781d_read_value(client, W83781D_REG_TEMP(1));
- data->temp_min =
- w83781d_read_value(client, W83781D_REG_TEMP_OVER(1));
data->temp_max =
+ w83781d_read_value(client, W83781D_REG_TEMP_OVER(1));
+ data->temp_hyst =
w83781d_read_value(client, W83781D_REG_TEMP_HYST(1));
data->temp_add[0] =
w83781d_read_value(client, W83781D_REG_TEMP(2));
data->temp_max_add[0] =
w83781d_read_value(client, W83781D_REG_TEMP_OVER(2));
- data->temp_min_add[0] =
+ data->temp_hyst_add[0] =
w83781d_read_value(client, W83781D_REG_TEMP_HYST(2));
if (data->type != w83783s && data->type != w83697hf) {
data->temp_add[1] =
data->temp_max_add[1] =
w83781d_read_value(client,
W83781D_REG_TEMP_OVER(3));
- data->temp_min_add[1] =
+ data->temp_hyst_add[1] =
w83781d_read_value(client,
W83781D_REG_TEMP_HYST(3));
}
if (oss) {
err = dev->oss.minor_dsp =
register_sound_dsp(&saa7134_dsp_fops,
- dsp_nr[saa7134_devcount]);
+ dsp_nr[saa7134_devcount],
+ &dev->pci->dev);
if (err < 0) {
goto fail4;
}
err = dev->oss.minor_mixer =
register_sound_mixer(&saa7134_mixer_fops,
- mixer_nr[saa7134_devcount]);
+ mixer_nr[saa7134_devcount],
+ &dev->pci->dev);
if (err < 0)
goto fail5;
printk(KERN_INFO "%s: registered device mixer%d\n",
return -1;
/* everything is fine, register */
- if ((minor = register_sound_mixer(&tvmixer_fops,devnr)) < 0) {
+ if ((minor = register_sound_mixer(&tvmixer_fops,devnr,&client->dev)) < 0) {
printk(KERN_ERR "tvmixer: cannot allocate mixer device\n");
return -1;
}
When in doubt, say N.
+config HOTPLUG_PCI_AMD
+ tristate "AMD Standard Hot Plug Controller (SHPC) driver"
+ depends on HOTPLUG_PCI
+
config HOTPLUG_PCI_CPCI
bool "CompactPCI Hotplug driver"
depends on HOTPLUG_PCI
obj-$(CONFIG_HOTPLUG_PCI_COMPAQ) += cpqphp.o
obj-$(CONFIG_HOTPLUG_PCI_IBM) += ibmphp.o
obj-$(CONFIG_HOTPLUG_PCI_ACPI) += acpiphp.o
+obj-$(CONFIG_HOTPLUG_PCI_AMD) += amdshpc.o
obj-$(CONFIG_HOTPLUG_PCI_CPCI_ZT5550) += cpcihp_zt5550.o
obj-$(CONFIG_HOTPLUG_PCI_CPCI_GENERIC) += cpcihp_generic.o
-obj-$(CONFIG_HOTPLUG_PCI_RPA) += rpaphp.o
-obj-$(CONFIG_HOTPLUG_PCI_RPA_DLPAR) += rpadlpar_io.o
+obj-$(CONFIG_HOTPLUG_PCI_FAKE) += fakephp.o
pci_hotplug-objs := pci_hotplug_core.o
acpiphp_pci.o \
acpiphp_res.o
+amdshpc-objs := amdshpc_core.o \
+ amdshpc_ctrl.o \
+ amdshpc_pci.o \
+ amdshpc_ddi.o \
+ amdshpc_int.o \
+ amdshpc_led.o \
+ amdshpc_enb.o \
+ amdshpc_dsb.o
+
rpaphp-objs := rpaphp_core.o \
rpaphp_pci.o
--- /dev/null
+/*
+ * Copyright (C) 2001,2003 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (C) 2001,2003 IBM Corp.
+ * Copyright (C) 2002-2003 Advanced Micro Devices
+ *
+ * YOUR USE OF THIS CODE IS SUBJECT TO THE TERMS
+ * AND CONDITIONS OF THE GNU GENERAL PUBLIC
+ * LICENSE FOUND IN THE "GPL.TXT" FILE THAT IS
+ * INCLUDED WITH THIS FILE AND POSTED AT
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * Send feedback to <greg@kroah.com> <david.keck@amd.com>
+ *
+ */
+
+#ifndef _SHPC_H_
+#define _SHPC_H_
+
+
+#include <linux/types.h>
+#include <linux/wait.h>
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+#include <linux/timer.h>
+#include <linux/sched.h>
+#include <asm/semaphore.h>
+#include "pci_hotplug.h"
+
+//
+// Timeouts
+//
+#define ONE_TENTH_SEC_TIMEOUT 10 // 0.1 sec
+#define ONE_SEC_TIMEOUT HZ * 1 // 1 sec
+#define FIVE_SEC_TIMEOUT HZ * 5 // 5 secs
+#define TEN_SEC_TIMEOUT HZ * 10 // 10 secs
+#define FIFTEEN_SEC_TIMEOUT HZ * 15 // 15 secs
+#define QUIESCE_QUIET_TIMEOUT HZ * 30 // 30 secs
+#define QUIESCE_TIMEOUT HZ * 60 // 60 secs
+#define ONE_SEC_INCREMENT HZ * 1 // 1 sec
+
+#define SLOT_MAGIC 0x67267322
+struct slot {
+ u32 magic;
+ struct slot *next;
+ struct list_head slot_list;
+ u8 bus;
+ u8 device;
+ u8 number;
+ u8 is_a_board;
+ u8 configured;
+ u8 state;
+ u8 switch_save;
+ u8 presence_save;
+ u32 capabilities;
+ u16 reserved2;
+ struct timer_list task_event;
+ u8 hp_slot;
+ struct controller *ctrl;
+ void *p_sm_slot;
+ struct hotplug_slot *hotplug_slot;
+ void* private;
+};
+
+struct controller {
+ struct controller *next;
+ void *shpc_context;
+ u32 ctrl_int_comp;
+ void *hpc_reg; /* cookie for our pci controller location */
+ struct pci_resource *mem_head;
+ struct pci_resource *p_mem_head;
+ struct pci_resource *io_head;
+ struct pci_resource *bus_head;
+ struct pci_dev *pci_dev;
+ struct pci_bus *pci_bus;
+ struct slot *slot;
+ u8 interrupt;
+ u8 bus;
+ u8 device;
+ u8 function;
+ u8 slot_device_offset;
+ u8 first_slot;
+ u8 add_support;
+ u16 vendor_id;
+};
+
+
+static LIST_HEAD(slot_list);
+
+#if !defined(CONFIG_HOTPLUG_PCI_AMD_MODULE)
+ #define MY_NAME "amd_shpc.o"
+#else
+ #define MY_NAME THIS_MODULE->name
+#endif
+
+//
+// Debug Facilities
+//
+#define debug 0
+#define dbg(format, arg...) \
+ do { \
+ if (debug) \
+ printk (KERN_DEBUG "%s: " format "\n", \
+ MY_NAME , ## arg); \
+ } while (0)
+
+#define err(format, arg...) printk(KERN_ERR "%s: " format "\n", MY_NAME , ## arg)
+#define info(format, arg...) printk(KERN_INFO "%s: " format "\n", MY_NAME , ## arg)
+#define warn(format, arg...) printk(KERN_WARNING "%s: " format "\n", MY_NAME , ## arg)
+
+#define msg_initialization_err "Initialization failure, error=%d\n"
+#define msg_HPC_rev_error "Unsupported revision of the PCI hot plug controller found.\n"
+#define msg_HPC_non_amd "Non-AMD PCI hot plug controller is not supported by this driver.\n"
+#define msg_HPC_not_amd_hp "Device is not a hot plug controller.\n"
+#define msg_HPC_not_supported "This system is not supported by this version of amdshpc. Upgrade to a newer version of amdshpc\n"
+#define msg_unable_to_save "Unable to store PCI hot plug add resource information. This system must be rebooted before adding any PCI devices.\n"
+
+struct hrt {
+ char sig0;
+ char sig1;
+ char sig2;
+ char sig3;
+ u16 unused_IRQ;
+ u16 PCIIRQ;
+ u8 number_of_entries;
+ u8 revision;
+ u16 reserved1;
+ u32 reserved2;
+} __attribute__ ((packed));
+
+/* offsets to the hotplug resource table registers based on the above structure layout */
+enum hrt_offsets {
+ SIG0 = offsetof(struct hrt, sig0),
+ SIG1 = offsetof(struct hrt, sig1),
+ SIG2 = offsetof(struct hrt, sig2),
+ SIG3 = offsetof(struct hrt, sig3),
+ UNUSED_IRQ = offsetof(struct hrt, unused_IRQ),
+ PCIIRQ = offsetof(struct hrt, PCIIRQ),
+ NUMBER_OF_ENTRIES = offsetof(struct hrt, number_of_entries),
+ REVISION = offsetof(struct hrt, revision),
+ HRT_RESERVED1 = offsetof(struct hrt, reserved1),
+ HRT_RESERVED2 = offsetof(struct hrt, reserved2),
+};
+
+struct slot_rt {
+ u8 dev_func;
+ u8 primary_bus;
+ u8 secondary_bus;
+ u8 max_bus;
+ u16 io_base;
+ u16 io_length;
+ u16 mem_base;
+ u16 mem_length;
+ u16 pre_mem_base;
+ u16 pre_mem_length;
+} __attribute__ ((packed));
+
+/* offsets to the hotplug slot resource table registers based on the above structure layout */
+enum slot_rt_offsets {
+ DEV_FUNC = offsetof(struct slot_rt, dev_func),
+ PRIMARY_BUS = offsetof(struct slot_rt, primary_bus),
+ SECONDARY_BUS = offsetof(struct slot_rt, secondary_bus),
+ MAX_BUS = offsetof(struct slot_rt, max_bus),
+ IO_BASE = offsetof(struct slot_rt, io_base),
+ IO_LENGTH = offsetof(struct slot_rt, io_length),
+ MEM_BASE = offsetof(struct slot_rt, mem_base),
+ MEM_LENGTH = offsetof(struct slot_rt, mem_length),
+ PRE_MEM_BASE = offsetof(struct slot_rt, pre_mem_base),
+ PRE_MEM_LENGTH = offsetof(struct slot_rt, pre_mem_length),
+};
+
+struct pci_func {
+ struct pci_func *next;
+ u8 bus;
+ u8 device;
+ u8 function;
+ u8 is_a_board;
+ u16 status;
+ u8 configured;
+ u8 switch_save;
+ u8 presence_save;
+ u32 base_length[0x06];
+ u8 base_type[0x06];
+ u16 reserved2;
+ u32 config_space[0x20];
+ struct pci_resource *mem_head;
+ struct pci_resource *p_mem_head;
+ struct pci_resource *io_head;
+ struct pci_resource *bus_head;
+ struct timer_list *p_task_event;
+ struct pci_dev* pci_dev;
+};
+
+
+#ifndef FALSE
+ #define FALSE 0
+ #define TRUE 1
+#endif
+
+#define IN
+#define OUT
+
+enum mutex_action {
+ ACQUIRE,
+ RELEASE,
+};
+
+enum hp_boolean {
+ HP_FALSE = 0,
+ HP_TRUE = 1,
+};
+
+// card power requirements
+enum hp_power_requirements {
+ POWER_LOW, // low power requirements
+ POWER_MEDIUM, // medium power requirements
+ POWER_HIGH, // high power requirements
+};
+
+//
+// slot event masks
+//
+#define ATTN_BUTTON_EVENT 0x00000001
+#define ALERT_EVENT 0x00000002
+#define BUS_REBALANCE_EVENT 0x00000004
+#define QUIESCE_EVENT 0x00000008
+#define ATTN_LED_PROBLEM_EVENT 0x00000010
+#define ATTN_LED_REQUEST_EVENT 0x00000020
+#define SLOT_REQUEST_EVENT 0x00000040
+#define SLOT_TIMER1_EVENT 0x00000080
+#define SLOT_TIMER2_EVENT 0x00000100
+#define SLOT_TIMER3_EVENT 0x00000200
+#define SLOT_TIMER4_EVENT 0x00000400
+#define SLOT_TIMER5_EVENT 0x00000800
+#define SLOT_TIMER6_EVENT 0x00001000
+#define SLOT_TIMER7_EVENT 0x00002000
+#define SLOT_TIMER8_EVENT 0x00004000
+#define SLOT_TIMER9_EVENT 0x00008000
+#define SLOT_TIMER10_EVENT 0x00010000
+#define LED_TIMER1_EVENT 0x00020000
+#define LED_TIMER2_EVENT 0x00040000
+#define LED_TIMER3_EVENT 0x00080000
+#define LED_TIMER4_EVENT 0x00100000
+#define CMD_ACQUIRE_EVENT 0x00200000
+#define CMD_RELEASE_EVENT 0x00400000
+#define LED_CMD_ACQUIRE_EVENT 0x00800000
+#define LED_CMD_RELEASE_EVENT 0x01000000
+#define BUS_RELEASE_EVENT 0x02000000
+#define BUS_ACQUIRE_EVENT 0x04000000
+
+//
+// controller event masks
+//
+#define BUS_COMPLETE_EVENT 0x00000001
+#define SUSPEND_EVENT 0x00000002
+#define RESUME_EVENT 0x00000004
+#define REMOVE_EVENT 0x00000008
+#define EXIT_REQUEST_EVENT 0x00000010
+#define CTRL_TIMER_EVENT 0x00000020
+#define CMD_COMPLETION_EVENT 0x00000040
+#define CMD_AVAILABLE_MUTEX_EVENT 0x00000080
+#define BUS_AVAILABLE_MUTEX_EVENT 0x00000100
+#define LED_CMD_AVAILABLE_MUTEX_EVENT 0x00000200
+
+
+#define PCI_TO_PCI_BRIDGE_CLASS 0x00060400
+#define SLOT_MASK 0x28
+
+
+#define ADD_NOT_SUPPORTED 0x00000003
+#define ADAPTER_NOT_SAME 0x00000006
+#define NO_ADAPTER_PRESENT 0x00000009
+
+#define REMOVE_NOT_SUPPORTED 0x00000003
+
+
+
+// slot states
+enum hp_states {
+ SLOT_DISABLE, // slot disable
+ SLOT_ENABLE, // slot enable
+};
+
+// indicator values
+enum mode_frequency {
+ MODE_PCI_33, // PCI 33Mhz
+ MODE_PCI_66, // PCI 66Mhz
+ MODE_PCIX_66, // PCI-X 66Mhz
+ MODE_PCIX_100, // PCI-X 100Mhz
+ MODE_PCIX_133, // PCI-X 133Mhz
+};
+
+enum hp_indicators {
+ INDICATOR_OFF, // Indicator off state
+ INDICATOR_ON, // Indicator on state
+ INDICATOR_BLINK, // Indicator blink state
+ INDICATOR_NORMAL, // Indicator normal state
+};
+
+struct pci_resource {
+ struct pci_resource * next;
+ u32 base;
+ u32 length;
+};
+
+struct resource_descriptor {
+ u32 base;
+ u32 limit;
+};
+
+struct irq_mapping {
+ u8 barber_pole;
+ u8 valid_INT;
+ u8 interrupt[4];
+};
+
+struct resource_lists {
+ struct pci_resource *mem_head;
+ struct pci_resource *p_mem_head;
+ struct pci_resource *io_head;
+ struct pci_resource *bus_head;
+ struct irq_mapping *irqs;
+};
+
+#define ROM_PHY_ADDR 0x0F0000
+#define ROM_PHY_LEN 0x00ffff
+
+#define NOT_ENOUGH_RESOURCES 0x0000000B
+#define DEVICE_TYPE_NOT_SUPPORTED 0x0000000C
+
+//
+// Prototypes
+//
+extern int amdshpc_resource_sort_and_combine (struct pci_resource **head);
+
+//
+// State-Machine Function
+//
+typedef long ( *SLOT_STATE_FUNCTION )(
+ void* shpc_context,
+ void* slot_context);
+
+//
+// SHPC Constants
+//
+#define SHPC_MAX_NUM_SLOTS 4
+
+
+#define arraysize(p) (sizeof(p)/sizeof((p)[0]))
+
+
+//
+// SHPC Register Offsets
+//
+enum shpc_register_offset {
+ SHPC_SLOTS_AVAILABLE1_REG_OFFSET = 0x04,
+ SHPC_SLOTS_AVAILABLE2_REG_OFFSET = 0x08,
+ SHPC_SLOT_CONFIG_REG_OFFSET = 0x0C,
+ SHPC_SEC_BUS_CONFIG_REG_OFFSET = 0x10,
+ SHPC_COMMAND_REG_OFFSET = 0x14,
+ SHPC_STATUS_REG_OFFSET = 0x16,
+ SHPC_INT_LOCATOR_REG_OFFSET = 0x18,
+ SHPC_SERR_LOCATOR_REG_OFFSET = 0x1C,
+ SHPC_SERR_INT_REG_OFFSET = 0x20,
+ SHPC_LOGICAL_SLOT_REG_OFFSET = 0x24,
+};
+
+
+//
+// SHPC Slots Available Register I
+//
+union SHPC_SLOTS_AVAILABLE1_DWREG {
+ struct {
+ u32 N_33CONV :5; // 4:0
+ u32 reserved1 :3; // 7:5
+ u32 N_66PCIX :5; // 12:8
+ u32 reserved2 :3; // 15:13
+ u32 N_100PCIX :5; // 20:16
+ u32 reserved3 :3; // 23:21
+ u32 N_133PCIX :5; // 28:24
+ u32 reserved4 :3; // 31:29
+ }x;
+ u32 AsDWord;
+};
+
+
+//
+// SHPC Slots Available Register II
+//
+union SHPC_SLOTS_AVAILABLE2_DWREG {
+ struct {
+ u32 N_66CONV :5; // 4:0
+ u32 reserved4 :27; // 31:5
+ }x;
+ u32 AsDWord;
+};
+
+
+//
+// SHPC Slot Configuration Register
+//
+union SHPC_SLOT_CONFIG_DWREG {
+ struct {
+ u32 NSI :5; // 4:0
+ u32 reserved1 :3; // 7:5
+ u32 FDN :5; // 12:8
+ u32 reserved2 :3; // 15:13
+ u32 PSN :11; // 26:16
+ u32 reserved3 :2; // 28:27
+ u32 PSN_UP :1; // 29
+ u32 MRLSI :1; // 30
+ u32 ABI :1; // 31
+ }x;
+ u32 AsDWord;
+};
+
+
+//
+// SHPC Secondary Bus Configuration Register
+//
+union SHPC_SEC_BUS_CONFIG_DWREG {
+ struct {
+ u32 MODE :3; // 2:0
+ u32 reserved :21; // 23:3
+ u32 format :8; // 31:24
+ }x;
+ u32 AsDWord;
+};
+
+
+//
+// SHPC Command Register
+//
+union SHPC_COMMAND_WREG {
+ struct {
+ u16 state : 2; // 1:0
+ u16 power_led : 2; // 3:2
+ u16 attention_led : 2; // 5:4
+ u16 code : 2; // 7:6
+ u16 TGT : 5; // 12:8
+ u16 reserved : 3; // 15:13
+ } Slot;
+ struct {
+ u16 speed_mode : 3; // 2:0
+ u16 code : 5; // 7:3
+ u16 reserved : 8; // 15:8
+ } Bus;
+ struct {
+ u16 code : 8; // 7:0
+ u16 reserved : 8; // 15:8
+ }x;
+ u16 AsWord;
+};
+
+
+//
+// SHPC Status Register
+//
+union SHPC_STATUS_WREG {
+ struct {
+ u16 BSY :1; // 0
+ u16 MRLO_ERR :1; // 1
+ u16 INVCMD_ERR :1; // 2
+ u16 INVSM_ERR :1; // 3
+ u16 reserved :12; // 15:4
+ }x;
+ u16 AsWord;
+};
+
+
+//
+// SHPC Interrupt Locator Register
+//
+union SHPC_INT_LOCATOR_DWREG {
+ struct {
+ u32 CC_IP :1; // 0
+ u32 SLOT_IP :4; // 4:1
+ u32 reserved :27; // 31:5
+ }x;
+ u32 AsDWord;
+};
+
+
+//
+// SHPC SERR Locator Register
+//
+union SHPC_SERR_LOCATOR_DWREG {
+ struct {
+ u32 A_SERRP :1; // 0
+ u32 SLOT_SERRP :4; // 4:1
+ u32 reserved :27; // 31:5
+ }x;
+ u32 AsDWord;
+};
+
+
+//
+// SHPC SERR-INT Register
+//
+union SHPC_SERR_INT_DWREG {
+ struct {
+ u32 GIM :1; // 0
+ u32 GSERRM :1; // 1
+ u32 CC_IM :1; // 2
+ u32 A_SERRM :1; // 3
+ u32 reserved1 :12; // 15:4
+ u32 CC_STS :1; // 16
+ u32 ATOUT_STS :1; // 17
+ u32 reserved2 :14; // 31:18
+ }x;
+ u32 AsDWord;
+};
+
+
+//
+// SHPC Logical Slot Register
+//
+union SHPC_LOGICAL_SLOT_DWREG {
+ struct {
+ u32 S_STATE :2; // 1:0
+ u32 PIS :2; // 3:2
+ u32 AIS :2; // 5:4
+ u32 PF :1; // 6
+ u32 AB :1; // 7
+ u32 MRLS :1; // 8
+ u32 M66_CAP :1; // 9
+ u32 PRSNT1_2 :2; // 11:10
+ u32 PCIX_CAP :2; // 13:12
+ u32 reserved1 :2; // 15:14
+ u32 CPC_STS :1; // 16
+ u32 IPF_STS :1; // 17
+ u32 ABP_STS :1; // 18
+ u32 MRLSC_STS :1; // 19
+ u32 CPF_STS :1; // 20
+ u32 reserved2 :3; // 23:21
+ u32 CP_IM :1; // 24
+ u32 IPF_IM :1; // 25
+ u32 AB_IM :1; // 26
+ u32 MRLS_IM :1; // 27
+ u32 CPF_IM :1; // 28
+ u32 MRLS_SERRM :1; // 29
+ u32 CPF_SERRM :1; // 30
+ u32 reserved3 :1; // 31
+ }x;
+ u32 AsDWord;
+};
+
+
+//
+// Bus Speed/Mode
+//
+enum shpc_speed_mode {
+ SHPC_BUS_CONV_33 = 0,
+ SHPC_BUS_CONV_66 = 1,
+ SHPC_BUS_PCIX_66 = 2,
+ SHPC_BUS_PCIX_100 = 3,
+ SHPC_BUS_PCIX_133 = 4,
+};
+
+
+//
+// Slot PCIX Capability
+//
+enum shpc_slot_pcix_cap {
+ SHPC_SLOT_CONV = 0,
+ SHPC_SLOT_PCIX_66 = 1,
+ SHPC_SLOT_PCIX_133 = 3,
+};
+
+
+//
+// Slot LEDs
+//
+enum shpc_slot_led {
+ SHPC_led_NO_CHANGE = 0,
+ SHPC_LED_ON = 1,
+ SHPC_LED_BLINK = 2,
+ SHPC_LED_OFF = 3,
+};
+
+
+//
+// Slot State
+//
+enum shpc_slot_state {
+ SHPC_SLOT_NO_CHANGE = 0,
+ SHPC_POWER_ONLY = 1,
+ SHPC_ENABLE_SLOT = 2,
+ SHPC_DISABLE_SLOT = 3,
+};
+
+
+//
+// Command Code
+//
+#define SHPC_SLOT_OPERATION 0x00 // 7:6 (00xxxxxxb)
+#define SHPC_SET_BUS_SPEED_MODE 0x08 // 7:3 (01000xxxb)
+#define SHPC_POWER_ONLY_ALL_SLOTS 0x48 // 7:0 (01001000b)
+#define SHPC_ENABLE_ALL_SLOTS 0x49 // 7:0 (01001001b)
+
+
+//
+// SHPC Status
+//
+enum shpc_status {
+ SHPC_STATUS_CLEARED = 0,
+ SHPC_STATUS_SET = 1,
+};
+
+
+//
+// SHPC Mask
+//
+enum shpc_mask {
+ SHPC_UNMASKED = 0,
+ SHPC_MASKED = 1,
+};
+
+
+//
+// Slot MRL Sensor
+//
+enum shpc_slot_mrl {
+ SHPC_MRL_CLOSED = 0,
+ SHPC_MRL_OPEN = 1,
+};
+
+
+//
+// Slot Attn Button
+//
+enum shpc_slot_attn_button {
+ SHPC_ATTN_BUTTON_RELEASED = 0,
+ SHPC_ATTN_BUTTON_PRESSED = 1,
+};
+
+
+//
+// Card Power Requirements
+//
+enum shpc_card_power {
+ SHPC_CARD_PRESENT_7_5W = 0,
+ SHPC_CARD_PRESENT_15W = 1,
+ SHPC_CARD_PRESENT_25W = 2,
+ SHPC_SLOT_EMPTY = 3,
+};
+
+
+// slot config structure
+union SLOT_CONFIG_INFO {
+ struct {
+ u32 lu_slots_implemented : 5; // [ 4:0 ]Number of slots implemented
+ u32 lu_reserved1 : 3; // [ 7:5 ]Reserved
+ u32 lu_base_FDN : 5; // [ 12:8 ]First Device Number
+ u32 lu_reserved2 : 3; // [ 15:13 ]Reserved
+ u32 lu_base_PSN : 11; // [ 26:16 ]Physical Slot Number
+ u32 lu_reserved3 : 2; // [ 28:27 ]Reserved
+ u32 lu_PSN_up : 1; // [ 29 ]PSN Up (1=TRUE, 0=FALSE)
+ u32 lu_reserved4 : 2; // [ 31:30 ]Reserved
+ }x;
+ u32 AsDWord;
+};
+
+
+// logical slot information
+union SLOT_STATUS_INFO {
+ struct {
+ u32 lu_slot_state : 1; // [ 0 ]Slot state (1=Enabled, 0=Disabled)
+ u32 lu_power_fault : 1; // [ 1 ]Power-Fault? (1=TRUE, 0=FALSE)
+ u32 lu_card_present : 1; // [ 2 ]Card Present? (1=TRUE, 0=FALSE)
+ u32 lu_card_power : 2; // [ 4:3 ]Card Power Requirements (low/medium/high)
+ u32 lu_card_mode_freq_cap : 3; // [ 7:5 ]Card Speed/mode capability
+ u32 lu_mrl_implemented : 1; // [ 8 ]MRL Implemented? (1=TRUE, 0=FALSE)
+ u32 lu_mrl_opened : 1; // [ 9 ]MRL State (if implemented: 1=TRUE, 0=FALSE)
+ u32 lu_ai_state : 2; // [ 11:10 ]Attn Indicator State (Blink/On/Off)
+ u32 lu_pi_state : 2; // [ 13:12 ]Power Indicator State (Blink/On/Off)
+ u32 lu_reserved1 : 2; // [ 14 ]Reserved
+ u32 lu_card_pci66_capable : 1; // [ 15 ]Card PCI66 capability (1=TRUE, 0=FALSE)
+ u32 lu_bus_mode_freq : 3; // [ 18:16 ]Current Bus speed/mode
+ u32 lu_max_bus_mode_freq : 3; // [ 21:19 ]Maximum Bus speed/mode
+ u32 lu_reserved2 : 9; // [ 30:22 ]Reserved
+ u32 lu_request_failed : 1; // [ 31 ]Request Failed? (1=TRUE, 0=FALSE)
+ }x;
+ u32 AsDWord;
+};
+
+enum return_status {
+ STATUS_UNSUCCESSFUL,
+ STATUS_SUCCESS
+};
+
+//
+// Async Request
+//
+enum shpc_async_request {
+ SHPC_ASYNC_ENABLE_SLOT,
+ SHPC_ASYNC_DISABLE_SLOT,
+ SHPC_ASYNC_SURPRISE_REMOVE,
+ SHPC_ASYNC_QUIESCE_DEVNODE,
+ SHPC_ASYNC_QUIESCE_DEVNODE_QUIET,
+ SHPC_ASYNC_QUIESCE_DEVNODE_NOTIFY,
+ SHPC_ASYNC_CANCEL_QUIESCE_DEVNODE,
+ SHPC_ASYNC_LED_LOCATE,
+ SHPC_ASYNC_LED_NORMAL
+};
+
+
+//
+// Async Request
+//
+struct async_request {
+ enum shpc_async_request type;
+ wait_queue_head_t event;
+ unsigned long timeout;
+ void *request_context;
+};
+
+
+//
+// Async Completion
+//
+struct async_completion {
+ enum shpc_async_request type;
+ unsigned long timeout;
+ u8 hw_initiated;
+ u8 done;
+ enum hp_boolean failed;
+ void *request_context;
+};
+
+// ****************************************************************************
+//
+// async_callback() @ PASSIVE_LEVEL
+//
+// Parameters
+// driver_context - Pointer provided in hp_AddDevice()
+// slot_id - Zero-based slot number (0..n-1).
+// Request - Async request completed. For example: Slot Enable/Disable, AttnLED Attn/Normal.
+// Status - Slot status at completion
+// request_context - Pointer provided in hp_StartAsyncRequest(), NULL for
+// completions on hardware-initiated requests.
+//
+// Return Value
+// For QUIESCE_DEVNODE request: #DevNodes associated with a particular slot, else 0.
+//
+// ****************************************************************************
+typedef unsigned long ( *SHPC_ASYNC_CALLBACK )( void* driver_context,
+ u8 slot_id,
+ enum shpc_async_request Request,
+ union SLOT_STATUS_INFO Status,
+ void* request_context );
+
+//
+// Slot Context
+//
+struct slot_context {
+
+ spinlock_t slot_spinlock;
+ struct semaphore slot_event_bits_semaphore;
+ struct semaphore cmd_acquire_mutex;
+ struct semaphore bus_acquire_mutex;
+ u32 *logical_slot_addr;
+ u8 slot_number;
+ u8 slot_psn;
+ u32 quiesce_requests;
+ u32 quiesce_replies;
+ u8 slot_enabled;
+ enum shpc_speed_mode card_speed_mode;
+ u8 card_pci66_capable;
+ u8 in_bus_speed_mode_contention;
+ u8 problem_detected;
+ u8 slot_quiesced;
+ u8 slot_occupied;
+ struct tasklet_struct attn_button_dpc;
+ struct tasklet_struct mrl_sensor_dpc;
+ struct tasklet_struct card_presence_dpc;
+ struct tasklet_struct isolated_power_fault_dpc;
+ struct tasklet_struct connected_power_fault_dpc;
+ wait_queue_head_t slot_event;
+ wait_queue_head_t led_cmd_acquire_event;
+ wait_queue_head_t led_cmd_release_event;
+ wait_queue_head_t cmd_acquire_event;
+ wait_queue_head_t cmd_release_event;
+ wait_queue_head_t bus_acquire_event;
+ wait_queue_head_t bus_release_event;
+ u32 slot_event_bits;
+ void *slot_thread;
+ void *attn_led_thread;
+ SLOT_STATE_FUNCTION slot_function;
+ SLOT_STATE_FUNCTION attn_led_function;
+ struct async_request slot_request;
+ struct async_completion slot_completion;
+ struct async_request attn_led_request;
+ struct async_completion attn_led_completion;
+ void *shpc_context;
+ struct timer_list slot_timer1;
+ struct timer_list slot_timer2;
+ struct timer_list slot_timer3;
+ struct timer_list slot_timer4;
+ struct timer_list slot_timer5;
+ struct timer_list slot_timer6;
+ struct timer_list slot_timer7;
+ struct timer_list slot_timer8;
+ struct timer_list slot_timer9;
+ struct timer_list slot_timer10;
+ struct timer_list led_timer1;
+ struct timer_list led_timer2;
+ struct timer_list led_timer3;
+ struct timer_list led_timer4;
+};
+
+//
+// SHPC Context
+//
+struct shpc_context {
+ spinlock_t shpc_spinlock;
+ struct semaphore shpc_event_bits_semaphore;
+ void *mmio_base_addr;
+ struct shpc_context *next;
+ u8 first_slot;
+ u8 number_of_slots;
+ u8 slots_enabled;
+ u8 at_power_device_d0;
+ u8 bus_released;
+ enum shpc_speed_mode max_speed_mode;
+ enum shpc_speed_mode bus_speed_mode;
+ struct semaphore cmd_available_mutex;
+ struct tasklet_struct cmd_completion_dpc;
+ struct semaphore bus_available_mutex;
+ wait_queue_head_t *user_event_pointer;
+ u32 shpc_event_bits;
+ void *driver_context;
+ SHPC_ASYNC_CALLBACK async_callback;
+ u32 shpc_instance;
+ struct slot_context slot_context[ SHPC_MAX_NUM_SLOTS ];
+ void *hpc_reg; // cookie for our pci controller location
+ struct pci_ops *pci_ops;
+ struct pci_resource *mem_head;
+ struct pci_resource *p_mem_head;
+ struct pci_resource *io_head;
+ struct pci_resource *bus_head;
+ struct pci_dev *pci_dev;
+ u8 interrupt;
+ u8 bus;
+ u8 device;
+ u8 function;
+ u16 vendor_id;
+ u32 ctrl_int_comp;
+ u8 add_support;
+};
+
+//
+// Function Prototypes
+//
+int amdshpc_get_bus_dev (struct controller *ctrl, u8 * bus_num, u8 * dev_num, u8 slot);
+int amdshpc_process_SI (struct controller *ctrl, struct pci_func *func);
+int amdshpc_process_SS (struct controller *ctrl, struct pci_func *func);
+int amdshpc_find_available_resources (struct controller *ctrl, void *rom_start);
+int amdshpc_save_config(struct controller *ctrl, int busnumber, union SLOT_CONFIG_INFO * is_hot_plug);
+struct pci_func *amdshpc_slot_create(u8 busnumber);
+
+
+void hp_clear_shpc_event_bit(struct shpc_context * shpc_context, u32 mask);
+void hp_set_shpc_event_bit(struct shpc_context * shpc_context, u32 mask);
+
+void hp_clear_slot_event_bit(struct slot_context * slot_context, u32 mask);
+void hp_set_slot_event_bit(struct slot_context * slot_context, u32 mask);
+
+void hp_send_event_to_all_slots(struct shpc_context *shpc_context, u32 mask);
+void hp_send_slot_event(struct slot_context *slot_context, u32 mask);
+
+int hp_get_led_cmd_available_mutex_thread(void *slot_context);
+int hp_get_cmd_available_mutex_thread (void *slot_context);
+int hp_get_bus_available_mutex_thread(void *slot_context);
+int hp_cmd_available_mutex_thread(void * slot_context);
+int hp_bus_available_mutex_thread(void * slot_context);
+int hp_led_cmd_available_mutex_thread(void * slot_context);
+
+void hp_slot_timer1_func(unsigned long data);
+void hp_slot_timer2_func(unsigned long data);
+void hp_slot_timer3_func(unsigned long data);
+void hp_slot_timer4_func(unsigned long data);
+void hp_slot_timer5_func(unsigned long data);
+void hp_slot_timer6_func(unsigned long data);
+void hp_slot_timer7_func(unsigned long data);
+void hp_slot_timer8_func(unsigned long data);
+void hp_slot_timer9_func(unsigned long data);
+void hp_slot_timer10_func(unsigned long data);
+void hp_led_timer1_func(unsigned long data);
+void hp_led_timer2_func(unsigned long data);
+void hp_led_timer3_func(unsigned long data);
+void hp_led_timer4_func(unsigned long data);
+
+irqreturn_t hp_interrupt_service(int IRQ, void *v, struct pt_regs *regs);
+
+u32 board_replaced(struct pci_func * func, struct controller * ctrl);
+struct pci_func *amdshpc_slot_find(u8 bus, u8 device, u8 index);
+int amdshpc_save_base_addr_length(struct controller *ctrl, struct pci_func * func);
+int amdshpc_save_used_resources (struct controller *ctrl, struct pci_func * func);
+int amdshpc_return_board_resources(struct pci_func * func, struct resource_lists * resources);
+int amdshpc_save_slot_config (struct controller *ctrl, struct pci_func * new_slot);
+int amdshpc_configure_device (struct controller * ctrl, struct pci_func* func);
+int amdshpc_unconfigure_device(struct pci_func* func);
+
+
+
+void
+hp_attn_button_dpc(
+ unsigned long deferred_context
+ );
+
+void
+hp_mrl_sensor_dpc(
+ unsigned long deferred_context
+ );
+
+void
+hp_card_presence_dpc(
+ unsigned long deferred_context
+ );
+
+void
+hp_isolated_power_fault_dpc(
+ unsigned long deferred_context
+ );
+
+void
+hp_connected_power_fault_dpc(
+ unsigned long deferred_context
+ );
+
+void
+hp_cmd_completion_dpc(
+ unsigned long deferred_context
+ );
+
+int
+hp_slot_thread(
+ void* slot_context
+ );
+
+long
+hp_at_slot_disabled_wait_for_slot_request(
+ struct shpc_context* shpc_context,
+ struct slot_context* slot_context
+ );
+
+long
+hp_at_slot_disabled_wait_for_led_cmd_available(
+ struct shpc_context* shpc_context,
+ struct slot_context* slot_context
+ );
+
+long
+hp_at_slot_disabled_wait_for_led_cmd_completion(
+ struct shpc_context* shpc_context,
+ struct slot_context* slot_context
+ );
+
+long
+hp_at_slot_disabled_wait_for_timeout(
+ struct shpc_context* shpc_context,
+ struct slot_context* slot_context
+ );
+
+long
+hp_at_slot_disabled_wait_for_power_cmd_available(
+ struct shpc_context* shpc_context,
+ struct slot_context* slot_context
+ );
+
+long
+hp_at_slot_disabled_wait_for_power_cmd_timeout(
+ struct shpc_context* shpc_context,
+ struct slot_context* slot_context
+ );
+
+long
+hp_at_slot_disabled_wait_for_power_cmd_completion(
+ struct shpc_context* shpc_context,
+ struct slot_context* slot_context
+ );
+
+long
+hp_at_slot_disabled_wait_for_bus_available(
+ struct shpc_context* shpc_context,
+ struct slot_context* slot_context
+ );
+
+long
+hp_at_slot_disabled_wait_for_bus_released(
+ struct shpc_context* shpc_context,
+ struct slot_context* slot_context
+ );
+
+long
+hp_at_slot_disabled_wait_for_speed_mode_cmd_available(
+ struct shpc_context* shpc_context,
+ struct slot_context* slot_context
+ );
+
+long
+hp_at_slot_disabled_wait_for_speed_mode_cmd_completion(
+ struct shpc_context* shpc_context,
+ struct slot_context* slot_context
+ );
+
+long
+hp_at_slot_disabled_wait_for_enable_cmd_available(
+ struct shpc_context* shpc_context,
+ struct slot_context* slot_context
+ );
+
+long
+hp_at_slot_disabled_wait_for_enable_cmd_completion(
+ struct shpc_context* shpc_context,
+ struct slot_context* slot_context
+ );
+
+long
+hp_at_slot_disabled_wait_for_enable_timeout(
+ struct shpc_context* shpc_context,
+ struct slot_context* slot_context
+ );
+
+long
+hp_to_slot_disabled_wait_for_led_cmd_available(
+ struct shpc_context* shpc_context,
+ struct slot_context* slot_context
+ );
+
+long
+hp_to_slot_disabled_wait_for_led_cmd_completion(
+ struct shpc_context* shpc_context,
+ struct slot_context* slot_context
+ );
+
+long
+hp_to_slot_disabled_wait_for_disable_cmd_available(
+ struct shpc_context* shpc_context,
+ struct slot_context* slot_context
+ );
+
+long
+hp_to_slot_disabled_wait_for_disable_cmd_completion(
+ struct shpc_context* shpc_context,
+ struct slot_context* slot_context
+ );
+
+long
+hp_to_slot_disabled_wait_for_disable_timeout(
+ struct shpc_context* shpc_context,
+ struct slot_context* slot_context
+ );
+
+long
+hp_to_slot_disabled_wait_for_bus_available(
+ struct shpc_context* shpc_context,
+ struct slot_context* slot_context
+ );
+
+long
+hp_at_slot_enabled_wait_for_slot_request(
+ struct shpc_context* shpc_context,
+ struct slot_context* slot_context
+ );
+
+long
+hp_at_slot_enabled_wait_for_stop_on_bus_rebalance(
+ struct shpc_context* shpc_context,
+ struct slot_context* slot_context
+ );
+
+long
+hp_at_slot_enabled_wait_for_power_cmd_available(
+ struct shpc_context* shpc_context,
+ struct slot_context* slot_context
+ );
+
+long
+hp_at_slot_enabled_wait_for_power_cmd_completion(
+ struct shpc_context* shpc_context,
+ struct slot_context* slot_context
+ );
+
+long
+hp_at_slot_enabled_wait_for_led_cmd_available(
+ struct shpc_context* shpc_context,
+ struct slot_context* slot_context
+ );
+
+long
+hp_at_slot_enabled_wait_for_led_cmd_completion(
+ struct shpc_context* shpc_context,
+ struct slot_context* slot_context
+ );
+
+long
+hp_at_slot_enabled_wait_for_timeout(
+ struct shpc_context* shpc_context,
+ struct slot_context* slot_context
+ );
+
+long
+hp_at_slot_enabled_wait_for_stop_on_slot_disable(
+ struct shpc_context* shpc_context,
+ struct slot_context* slot_context
+ );
+
+long
+hp_at_slot_enabled_wait_for_stop_on_slot_disable_quiet(
+ struct shpc_context* shpc_context,
+ struct slot_context* slot_context
+ );
+
+long
+hp_to_slot_enabled_wait_for_led_cmd_available(
+ struct shpc_context* shpc_context,
+ struct slot_context* slot_context
+ );
+
+long
+hp_to_slot_enabled_wait_for_led_cmd_completion(
+ struct shpc_context* shpc_context,
+ struct slot_context* slot_context
+ );
+
+void
+hp_get_slot_configuration(
+ struct shpc_context* shpc_context
+ );
+
+void
+hp_enable_slot_interrupts(
+ struct slot_context* slot_context
+ );
+
+void
+hp_disable_slot_interrupts(
+ struct slot_context* slot_context
+ );
+
+void
+hp_enable_global_interrupts(
+ struct shpc_context* shpc_context
+ );
+
+void
+hp_disable_global_interrupts(
+ struct shpc_context* shpc_context
+ );
+
+enum shpc_speed_mode
+hp_get_bus_speed_mode(
+ struct shpc_context* shpc_context
+ );
+
+enum shpc_speed_mode
+hp_get_card_speed_mode(
+ struct slot_context* slot_context
+ );
+
+enum mode_frequency
+hp_translate_speed_mode(
+ enum shpc_speed_mode shpc_speed_mode
+ );
+
+enum hp_power_requirements
+hp_translate_card_power(
+ enum shpc_card_power ShpcCardPower
+ );
+
+enum hp_indicators
+hp_translate_indicator(
+ enum shpc_slot_led ShpcIndicator
+ );
+
+u8
+hp_flag_slot_as_enabled(
+ struct shpc_context* shpc_context,
+ struct slot_context* slot_context
+ );
+
+u8
+hp_flag_slot_as_disabled(
+ struct shpc_context* shpc_context,
+ struct slot_context* slot_context
+ );
+
+u8
+hp_signal_enabled_slots_to_rebalance_bus(
+ struct shpc_context* shpc_context
+ );
+
+enum shpc_speed_mode
+hp_get_max_speed_mode(
+ struct shpc_context* shpc_context,
+ enum shpc_speed_mode From_speed_mode
+ );
+
+void
+hp_signal_user_event(
+ struct shpc_context* shpc_context
+ );
+
+void
+hp_signal_user_event_at_dpc_level(
+ struct shpc_context* shpc_context
+ );
+
+int
+hp_attn_led_thread(
+ void* slot_context
+ );
+
+long
+hp_wait_for_attn_led_request(
+ struct shpc_context* shpc_context,
+ struct slot_context* slot_context
+ );
+
+long
+hp_wait_for_attn_led_blink_cmd_available(
+ struct shpc_context* shpc_context,
+ struct slot_context* slot_context
+ );
+
+long
+hp_wait_for_attn_led_blink_cmd_completion(
+ struct shpc_context* shpc_context,
+ struct slot_context* slot_context
+ );
+
+long
+hp_wait_for_attn_led_blink_timeout(
+ struct shpc_context* shpc_context,
+ struct slot_context* slot_context
+ );
+
+long
+hp_wait_for_attn_led_normal_cmd_available(
+ struct shpc_context* shpc_context,
+ struct slot_context* slot_context
+ );
+
+long
+hp_wait_for_attn_led_normal_cmd_completion(
+ struct shpc_context* shpc_context,
+ struct slot_context* slot_context
+ );
+
+long
+hp_wait_for_attn_led_back_to_normal_cmd_available(
+ struct shpc_context* shpc_context,
+ struct slot_context* slot_context
+ );
+
+long
+hp_wait_for_attn_led_back_to_normal_cmd_completion(
+ struct shpc_context* shpc_context,
+ struct slot_context* slot_context
+ );
+
+
+
+#endif // _SHPC_H_
--- /dev/null
+/*
+ * Copyright (C) 2001,2003 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (C) 2001,2003 IBM Corp.
+ * Copyright (C) 2002-2003 Advanced Micro Devices
+ *
+ * YOUR USE OF THIS CODE IS SUBJECT TO THE TERMS
+ * AND CONDITIONS OF THE GNU GENERAL PUBLIC
+ * LICENSE FOUND IN THE "GPL.TXT" FILE THAT IS
+ * INCLUDED WITH THIS FILE AND POSTED AT
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ *
+ * Send feedback to <greg@kroah.com> <david.keck@amd.com>
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <asm/uaccess.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include "amdshpc.h"
+#include "amdshpc_ddi.h"
+#include "pci_hotplug.h"
+#include "../../../arch/i386/pci/pci.h"
+
+/* Global variables */
+int amdshpc_debug;
+struct shpc_context *amdshpc_ctrl_list; // used for the shpc state machine
+struct controller *ctrl_list; // used only for resource management
+struct pci_func *amdshpc_slot_list[256];
+
+static int num_slots;
+static void *amdshpc_rom_start;
+static unsigned long shpc_instance;
+
+#define DRIVER_VERSION "1.03"
+#define DRIVER_AUTHOR "Dave Keck <david.keck@amd.com>"
+#define DRIVER_DESC "AMD Standard Hot Plug Controller Driver"
+#define PCI_DEVICE_ID_AMD_GOLAM_7450 0x7450
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+//MODULE_PARM(debug, "i");
+//MODULE_PARM_DESC(debug, "Debugging mode enabled or not");
+
+static int enable_slot (struct hotplug_slot *slot);
+static int disable_slot (struct hotplug_slot *slot);
+static int set_attention_status (struct hotplug_slot *slot, u8 value);
+static int hardware_test (struct hotplug_slot *slot, u32 value);
+static int get_power_status (struct hotplug_slot *slot, u8 *value);
+static int get_attention_status (struct hotplug_slot *slot, u8 *value);
+static int get_latch_status (struct hotplug_slot *slot, u8 *value);
+static int get_adapter_status (struct hotplug_slot *slot, u8 *value);
+
+// values to be returned to the PCI Hotplug Core
+#define CORE_SLOT_DISABLED 0
+#define CORE_SLOT_ENABLED 1
+
+#define CORE_INDICATOR_OFF 0
+#define CORE_INDICATOR_ON 1
+#define CORE_INDICATOR_BLINK 2
+
+#define CORE_LATCH_CLOSED 1
+#define CORE_LATCH_OPENED 0
+
+static int init_slots ( struct controller *ctrl, int num_slots );
+static void translate_slot_info (struct hotplug_slot_info *info,
+ union SLOT_STATUS_INFO *query);
+
+static struct hotplug_slot_ops skel_hotplug_slot_ops = {
+ .owner = THIS_MODULE,
+ .enable_slot = enable_slot,
+ .disable_slot = disable_slot,
+ .set_attention_status = set_attention_status,
+ .hardware_test = hardware_test,
+ .get_power_status = get_power_status,
+ .get_attention_status = get_attention_status,
+ .get_latch_status = get_latch_status,
+ .get_adapter_status = get_adapter_status,
+};
+
+/* Inline functions to check the sanity of a pointer that is passed to us */
+static inline int slot_paranoia_check (struct slot *slot, const char *function)
+{
+ if (!slot) {
+ dbg("-->%s - slot == NULL", function);
+ return -1;
+ }
+ if (slot->magic != SLOT_MAGIC) {
+ dbg("-->%s - bad magic number for slot", function);
+ return -1;
+ }
+ if (!slot->hotplug_slot) {
+ dbg("-->%s - slot->hotplug_slot == NULL!", function);
+ return -1;
+ }
+ return 0;
+}
+
+static inline struct slot *get_slot (struct hotplug_slot *hotplug_slot, const char *function)
+{
+ struct slot *slot;
+
+ if (!hotplug_slot) {
+ dbg("-->%s - hotplug_slot == NULL\n", function);
+ return NULL;
+ }
+
+ slot = (struct slot *)hotplug_slot->private;
+ if (slot_paranoia_check (slot, function))
+ return NULL;
+ return slot;
+}
+
+static int enable_slot (struct hotplug_slot *hotplug_slot)
+{
+ struct slot *slot = get_slot (hotplug_slot, __FUNCTION__);
+ struct shpc_context *shpc_context;
+ union SLOT_STATUS_INFO query;
+ long status;
+ int retval = 0;
+
+
+ if (slot == NULL)
+ return -ENODEV;
+
+ dbg ("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
+
+ /*
+ * enable the specified slot
+ */
+ shpc_context = ( struct shpc_context * )slot->private;
+ status = hp_StartAsyncRequest(shpc_context, slot->number,
+ SHPC_ASYNC_ENABLE_SLOT, 0, slot );
+
+ //
+ // pretend async request was completed (we're not queuing slot requests)
+ //
+ hp_QuerySlotStatus( shpc_context, slot->number, &query );
+ if( status == STATUS_SUCCESS ) {
+ query.x.lu_slot_state = SLOT_ENABLE;
+ query.x.lu_pi_state = INDICATOR_BLINK;
+ if( query.x.lu_card_present &&
+ ( query.x.lu_mrl_implemented == HP_FALSE ||
+ query.x.lu_mrl_opened == HP_FALSE ) &&
+ query.x.lu_power_fault == HP_FALSE ) {
+ query.x.lu_request_failed = HP_FALSE;
+ }
+ else {
+ query.x.lu_request_failed = HP_TRUE;
+ }
+ }
+ else {
+ query.x.lu_request_failed = HP_TRUE;
+ }
+
+ //
+ // translate the slot info to PCI HOTPLUG CORE values
+ //
+ translate_slot_info (hotplug_slot->info, &query);
+
+ retval = ( query.x.lu_request_failed == HP_TRUE ) ? 0 : -1;
+ return retval;
+}
+
+
+static int disable_slot (struct hotplug_slot *hotplug_slot)
+{
+ struct slot *slot = get_slot (hotplug_slot, __FUNCTION__);
+ struct shpc_context *shpc_context;
+ union SLOT_STATUS_INFO query;
+ long status;
+ int retval = 0;
+
+ if (slot == NULL)
+ return -ENODEV;
+
+ dbg ("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
+
+ /*
+ * disable the specified slot
+ */
+ shpc_context = ( struct shpc_context * )slot->private;
+ status = hp_StartAsyncRequest(shpc_context, slot->number,
+ SHPC_ASYNC_DISABLE_SLOT, 0, slot );
+
+ //
+ // pretend async request was completed (we're not queuing slot requests)
+ //
+ hp_QuerySlotStatus( shpc_context, slot->number, &query );
+ if( status == STATUS_SUCCESS ) {
+ query.x.lu_slot_state = SLOT_DISABLE;
+ query.x.lu_pi_state = INDICATOR_BLINK;
+ query.x.lu_request_failed = HP_FALSE;
+ }
+ else {
+ query.x.lu_request_failed = HP_TRUE;
+ }
+
+ //
+ // translate the slot info to CORE values
+ //
+ translate_slot_info (hotplug_slot->info, &query);
+
+ retval = ( query.x.lu_request_failed == HP_TRUE ) ? 0 : -1;
+ return retval;
+}
+
+static int set_attention_status (struct hotplug_slot *hotplug_slot, u8 status)
+{
+ struct slot *slot = get_slot (hotplug_slot, __FUNCTION__);
+ struct shpc_context *shpc_context;
+ int retval = 0;
+
+ if (slot == NULL)
+ return -ENODEV;
+
+ dbg (" %s - physical_slot = %s state = %d",__FUNCTION__, hotplug_slot->name, status);
+
+ /*
+ * turn light on/off
+ */
+ shpc_context = (struct shpc_context *)slot->private;
+
+ status = hp_StartAsyncRequest(shpc_context, slot->number,
+ ((status == CORE_INDICATOR_OFF) ? SHPC_ASYNC_LED_NORMAL : SHPC_ASYNC_LED_LOCATE), 10, slot);
+ hotplug_slot->info->attention_status = status;
+
+ return retval;
+}
+
+static int get_power_status (struct hotplug_slot *hotplug_slot, u8 *value)
+{
+ struct slot *slot = get_slot (hotplug_slot, __FUNCTION__);
+ struct shpc_context *shpc_context;
+ union SLOT_STATUS_INFO query;
+ int retval = 0;
+
+ if (slot == NULL)
+ return -ENODEV;
+
+ dbg("%s - physical_slot = %s\n",__FUNCTION__, hotplug_slot->name);
+
+ /*
+ * get the current power status of the specific
+ * slot and store it in the *value location.
+ */
+ shpc_context = (struct shpc_context *)slot->private;
+ hp_QuerySlotStatus(shpc_context, slot->number, &query);
+ translate_slot_info (hotplug_slot->info, &query);
+ *value = hotplug_slot->info->power_status;
+
+ return retval;
+}
+
+static int get_attention_status (struct hotplug_slot *hotplug_slot, u8 *value)
+{
+ struct slot *slot = get_slot (hotplug_slot, __FUNCTION__);
+ struct shpc_context *shpc_context;
+ union SLOT_STATUS_INFO query;
+ int retval = 0;
+
+ if (slot == NULL)
+ return -ENODEV;
+
+ dbg("%s - physical_slot = %s\n",__FUNCTION__, hotplug_slot->name);
+
+ /*
+ * get the current attention status of the specific
+ * slot and store it in the *value location.
+ */
+ shpc_context = (struct shpc_context *)slot->private;
+ hp_QuerySlotStatus(shpc_context, slot->number, &query);
+ translate_slot_info (hotplug_slot->info, &query);
+ *value = hotplug_slot->info->attention_status;
+
+ return retval;
+}
+
+static int get_latch_status (struct hotplug_slot *hotplug_slot, u8 *value)
+{
+ struct slot *slot = get_slot (hotplug_slot, __FUNCTION__);
+ struct shpc_context *shpc_context;
+ union SLOT_STATUS_INFO query;
+ int retval = 0;
+
+ if (slot == NULL)
+ return -ENODEV;
+
+ dbg("%s - physical_slot = %s\n",__FUNCTION__, hotplug_slot->name);
+
+ /*
+ * get the current latch status of the specific
+ * slot and store it in the *value location.
+ */
+ shpc_context = (struct shpc_context *)slot->private;
+ hp_QuerySlotStatus(shpc_context, slot->number, &query);
+ translate_slot_info (hotplug_slot->info, &query);
+ *value = hotplug_slot->info->latch_status;
+
+ return retval;
+}
+
+static int get_adapter_status (struct hotplug_slot *hotplug_slot, u8 *value)
+{
+ struct slot *slot = get_slot (hotplug_slot, __FUNCTION__);
+ struct shpc_context *shpc_context;
+ union SLOT_STATUS_INFO query;
+ int retval = 0;
+
+ if (slot == NULL)
+ return -ENODEV;
+
+ dbg("%s - physical_slot = %s\n",__FUNCTION__, hotplug_slot->name);
+
+ /*
+ * get the current adapter status of the specific
+ * slot and store it in the *value location.
+ */
+ shpc_context = (struct shpc_context *)slot->private;
+ hp_QuerySlotStatus(shpc_context, slot->number, &query);
+ translate_slot_info (hotplug_slot->info, &query);
+ *value = hotplug_slot->info->adapter_status;
+
+ return retval;
+}
+
+static void translate_slot_info (struct hotplug_slot_info *info,
+ union SLOT_STATUS_INFO *query)
+{
+ // power indicator
+ if( query->x.lu_pi_state == INDICATOR_OFF ) {
+ info->power_status = CORE_INDICATOR_OFF;
+ }
+ else if( query->x.lu_pi_state == INDICATOR_ON ) {
+ info->power_status = CORE_INDICATOR_ON;
+ }
+ else {
+ info->power_status = CORE_INDICATOR_BLINK;
+ }
+
+ // attention indicator
+ if( query->x.lu_ai_state == INDICATOR_OFF ) {
+ info->attention_status = CORE_INDICATOR_OFF;
+ }
+ else if( query->x.lu_ai_state == INDICATOR_ON ) {
+ info->attention_status = CORE_INDICATOR_ON;
+ }
+ else {
+ info->attention_status = CORE_INDICATOR_BLINK;
+ }
+
+ // retention latch
+ if( query->x.lu_mrl_implemented == HP_TRUE &&
+ query->x.lu_mrl_opened == HP_TRUE ) {
+ info->latch_status = CORE_LATCH_OPENED;
+ }
+ else {
+ info->latch_status = CORE_LATCH_CLOSED;
+ }
+
+ // adapter status
+ if( query->x.lu_slot_state == SLOT_ENABLE ) {
+ info->adapter_status = CORE_SLOT_ENABLED;
+ }
+ else {
+ info->adapter_status = CORE_SLOT_DISABLED;
+ }
+}
+
+static int hardware_test (struct hotplug_slot *hotplug_slot, u32 value)
+{
+ struct slot *slot = get_slot (hotplug_slot, __FUNCTION__);
+ int retval = 0;
+
+ if (slot == NULL)
+ return -ENODEV;
+
+ dbg ("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
+
+ err ("No hardware tests are defined for this driver");
+ retval = -ENODEV;
+
+ /* Or you can specify a test if you want to */
+ /* AMD driver does not have a test */
+ return retval;
+}
+
+#define SLOT_NAME_SIZE 10
+static void make_slot_name (struct slot *slot)
+{
+ unsigned long slot_psn;
+ struct shpc_context *shpc_context;
+
+ shpc_context = ( struct shpc_context * )slot->private;
+
+ //
+ // Get physical slot number
+ //
+ hp_Queryslot_psn(shpc_context, slot->number, &slot_psn);
+
+ snprintf (slot->hotplug_slot->name, SLOT_NAME_SIZE, "%d", (char)slot_psn);
+}
+
+static void release_slot(struct hotplug_slot *hotplug_slot)
+{
+ struct slot *slot = get_slot (hotplug_slot, __FUNCTION__);
+
+ if (slot == NULL)
+ return;
+
+ dbg ("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
+
+ kfree(slot->hotplug_slot->info);
+ kfree(slot->hotplug_slot->name);
+ kfree(slot->hotplug_slot);
+ kfree(slot);
+}
+
+static int init_slots (struct controller *ctrl, int num_slots)
+{
+ struct slot *slot;
+ struct hotplug_slot *hotplug_slot;
+ struct hotplug_slot_info *info;
+ char *name;
+ int retval = 0;
+ int i;
+ u8 value;
+
+ /*
+ * Create a structure for each slot, and register that slot
+ * with the pci_hotplug subsystem.
+ */
+ for (i = 0; i < num_slots; ++i) {
+ slot = kmalloc (sizeof (struct slot), GFP_KERNEL);
+ if (!slot)
+ return -ENOMEM;
+ memset(slot, 0, sizeof(struct slot));
+
+ hotplug_slot = kmalloc (sizeof (struct hotplug_slot), GFP_KERNEL);
+ if (!hotplug_slot) {
+ kfree (slot);
+ return -ENOMEM;
+ }
+ memset(hotplug_slot, 0, sizeof (struct hotplug_slot));
+ slot->hotplug_slot = hotplug_slot;
+
+ info = kmalloc (sizeof (struct hotplug_slot_info), GFP_KERNEL);
+ if (!info) {
+ kfree (hotplug_slot);
+ kfree (slot);
+ return -ENOMEM;
+ }
+ memset(info, 0, sizeof (struct hotplug_slot_info));
+ hotplug_slot->info = info;
+
+ name = kmalloc (SLOT_NAME_SIZE, GFP_KERNEL);
+ if (!name) {
+ kfree (info);
+ kfree (hotplug_slot);
+ kfree (slot);
+ return -ENOMEM;
+ }
+ hotplug_slot->name = name;
+
+ slot->magic = SLOT_MAGIC;
+ slot->number = i;
+ slot->private = (void*) ctrl->shpc_context;
+
+ hotplug_slot->private = slot;
+ hotplug_slot->release = &release_slot;
+ make_slot_name (slot);
+ hotplug_slot->ops = &skel_hotplug_slot_ops;
+
+ /*
+ * Initilize the slot info structure with some known
+ * good values.
+ */
+ get_power_status(hotplug_slot, &value);
+ info->power_status = value;
+ get_attention_status(hotplug_slot, &value);
+ info->attention_status = value;
+ get_latch_status(hotplug_slot, &value);
+ info->latch_status = value;
+ get_adapter_status(hotplug_slot, &value);
+ info->adapter_status = value;
+
+ dbg ("registering slot %d\n", i);
+ retval = pci_hp_register (slot->hotplug_slot);
+ if (retval) {
+ err ("pci_hp_register failed with error %d\n", retval);
+ kfree (info);
+ kfree (name);
+ kfree (hotplug_slot);
+ kfree (slot);
+ return retval;
+ }
+
+ /* add slot to our internal list */
+ list_add (&slot->slot_list, &slot_list);
+ }
+
+ return retval;
+}
+
+static int amdshpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+ int retval;
+ int loop;
+ u16 vendor_id;
+ u16 device_id;
+ u32 rc;
+ long status = STATUS_SUCCESS;
+ struct controller *ctrl;
+ struct shpc_context *shpc_context;
+ union SLOT_CONFIG_INFO slot_config;
+
+ rc = pci_read_config_word(pdev, PCI_VENDOR_ID, &vendor_id);
+ dbg( "%s-->Vendor ID: %x\n",__FUNCTION__, vendor_id);
+ if (rc || (vendor_id != PCI_VENDOR_ID_AMD)) {
+ err(msg_HPC_non_amd);
+ return -ENODEV;
+ }
+
+ rc = pci_read_config_word(pdev, PCI_DEVICE_ID, &device_id);
+ dbg( "%s-->Device ID: %x\n",__FUNCTION__, device_id);
+ if (rc || (device_id != PCI_DEVICE_ID_AMD_GOLAM_7450)) {
+ err(msg_HPC_not_amd_hp);
+ return -ENODEV;
+ }
+
+ if (vendor_id == PCI_VENDOR_ID_AMD) {
+
+ shpc_context = (struct shpc_context *)kmalloc(sizeof(struct shpc_context), GFP_KERNEL);
+ if (!shpc_context) {
+ err("%s : out of memory\n",__FUNCTION__);
+ return -ENOMEM;
+ }
+ memset(shpc_context, 0, sizeof(struct shpc_context));
+
+ ctrl = (struct controller *)kmalloc(sizeof(struct controller), GFP_KERNEL);
+ if (!ctrl) {
+ err("%s : out of memory\n", __FUNCTION__);
+ rc = -ENOMEM;
+ goto err_free_shpc_context;
+ }
+ memset(ctrl, 0, sizeof(struct controller));
+
+ /* Set Vendor ID, so it can be accessed later from other functions */
+ ctrl->vendor_id = vendor_id;
+
+ } else {
+ err(msg_HPC_not_supported);
+ return -ENODEV;
+ }
+
+ ctrl->shpc_context = shpc_context;
+ ctrl->pci_dev = pdev;
+ ctrl->interrupt = pdev->irq;
+ ctrl->device = PCI_SLOT(pdev->devfn);
+ ctrl->function = PCI_FUNC(pdev->devfn);
+
+ //
+ // the AMD hotplug bus is behind a bridge
+ //
+// ctrl->pci_ops = pdev->subordinate->ops;
+ ctrl->pci_bus = pdev->subordinate;
+ ctrl->bus = pdev->subordinate->number;
+
+ dbg( "%s-->bus = %d device = %d function = %d\n",__FUNCTION__, ctrl->bus, ctrl->device, ctrl->function);
+
+ info("Found PCI hot plug controller on bus %d\n", pdev->bus->number);
+ info("Checking if MMIO region available for this HP controller...\n");
+
+ //
+ // Get memory mapped I/O region
+ //
+ dbg( "%s-->pdev = %p\n",__FUNCTION__, pdev);
+ dbg("%s -->pci resource start %lx\n",__FUNCTION__, pci_resource_start(pdev, 0));
+ dbg("%s -->pci resource len %lx\n",__FUNCTION__, pci_resource_len (pdev, 0));
+ if (!request_mem_region(pci_resource_start(pdev, 0), pci_resource_len(pdev, 0), MY_NAME)) {
+ err("MMIO region not available, skipping\n");
+ rc = -ENOMEM;
+ goto err_free_ctrl;
+ }
+
+ //
+ // Get linear address to put in controller structure
+ //
+ shpc_context->mmio_base_addr = ioremap(pci_resource_start(pdev, 0), pci_resource_len(pdev, 0));
+ if (!shpc_context->mmio_base_addr) {
+ err("cannot remap MMIO region %lx @ %lx\n", pci_resource_len(pdev, 0), pci_resource_start(pdev, 0));
+ rc = -ENODEV;
+ goto err_free_mem_region;
+ }
+
+ dbg("%s -->shpc_context->mmio_base_addr = %p",__FUNCTION__, (unsigned long*)shpc_context->mmio_base_addr);
+
+ hp_AddDevice(shpc_context, ctrl, shpc_context->async_callback, shpc_instance++);
+
+ // Initialize controller
+ shpc_context->interrupt = pdev->irq;
+ dbg("%s -->shpc_context->interrupt = %d", __FUNCTION__,pdev->irq);
+ if (!hp_StartDevice(shpc_context)){
+ rc = -ENODEV;
+ goto err_iounmap;
+ }
+
+ //
+ // initialize this array only once
+ //
+ if (shpc_context->shpc_instance == 0 ) {
+ dbg("%s Initialize slot lists\n",__FUNCTION__);
+ for (loop = 0; loop < 256; loop++) {
+ amdshpc_slot_list[loop] = NULL;
+ }
+ }
+
+ if (!amdshpc_ctrl_list) {
+ amdshpc_ctrl_list = shpc_context;
+ shpc_context->next = NULL;
+ } else {
+ amdshpc_ctrl_list->next = shpc_context;
+ shpc_context->next = NULL;
+ }
+
+ if (!ctrl_list) {
+ ctrl_list = ctrl;
+ ctrl->next = NULL;
+ } else {
+ ctrl_list->next = ctrl;
+ ctrl->next = NULL;
+ }
+
+ // Map rom address so we can get the HPRT table
+ amdshpc_rom_start = ioremap(ROM_PHY_ADDR, ROM_PHY_LEN);
+ if (!amdshpc_rom_start) {
+ err ("Could not ioremap memory region for ROM\n");
+ retval = -EIO;;
+ iounmap(amdshpc_rom_start);
+ return retval;
+ }
+
+ //**************************************************
+ //
+ // Save configuration headers for this and
+ // subordinate PCI buses
+ //
+ //**************************************************
+
+ // find the physical slot number of the first hot plug slot
+ status = hp_QuerySlots(shpc_context, &slot_config);
+ // first slot on a bridged bus is always #1
+ ctrl->first_slot = 1;
+ dbg("%s hp_QuerySlots: first_slot = %d, FDN = %d PSN_UP = %d\n",__FUNCTION__,
+ ctrl->first_slot, slot_config.x.lu_base_FDN, slot_config.x.lu_PSN_up);
+
+ if (rc) {
+ err(msg_initialization_err, rc);
+ goto err_iounmap;
+ }
+
+ if (!status) {
+ err(msg_initialization_err, (int)status);
+ goto err_iounmap;
+ }
+
+ // Store PCI Config Space for all devices on this bus
+ rc = amdshpc_save_config(ctrl, ctrl->bus, &slot_config);
+ if (rc) {
+ err("%s: unable to save PCI configuration data, error %d",__FUNCTION__, rc);
+ goto err_iounmap;
+ }
+
+ //
+ // Get IO, memory, and IRQ resources for new PCI devices
+ //
+ rc = amdshpc_find_available_resources(ctrl, amdshpc_rom_start);
+ if (rc) {
+ dbg("%s -->amdshpc_find_available_resources = 0x%x\n",__FUNCTION__, rc);
+ err("unable to locate PCI configuration resources for hot plug.\n");
+ goto err_iounmap;
+ }
+
+ //
+ // set global variable num_slots
+ //
+ num_slots = shpc_context->number_of_slots;
+
+ dbg("%s about to call init_slots()",__FUNCTION__);
+ rc = init_slots(ctrl, num_slots);
+ if (rc){
+ goto err_iounmap;
+ }
+
+ return 0;
+
+err_iounmap:
+ iounmap((void *)shpc_context->mmio_base_addr);
+err_free_mem_region:
+ release_mem_region(pci_resource_start(pdev, 0), pci_resource_len(pdev, 0));
+err_free_shpc_context:
+ kfree(shpc_context);
+err_free_ctrl:
+ kfree(ctrl);
+ return rc;
+}
+
+static void cleanup_slots (void)
+{
+ struct list_head *tmp;
+ struct list_head *next;
+ struct slot *slot;
+
+ /*
+ * Unregister all of our slots with the pci_hotplug subsystem.
+ * The memory will be freed in the release_slot() callback.
+ */
+ list_for_each_safe(tmp, next, &slot_list) {
+ slot = list_entry(tmp, struct slot, slot_list);
+ list_del(&slot->slot_list);
+ pci_hp_deregister(slot->hotplug_slot);
+ }
+
+ return;
+}
+static void unload_amdshpc(void)
+{
+ struct pci_func *next;
+ struct pci_func *TempSlot;
+ int loop;
+ struct shpc_context *shpc_context;
+ struct shpc_context *tshpc_context;
+ struct controller *ctrl;
+ struct controller *tctrl;
+ struct pci_resource *res;
+ struct pci_resource *tres;
+
+ ctrl = ctrl_list;
+
+ while (ctrl) {
+ //reclaim PCI mem
+ release_mem_region(pci_resource_start(ctrl->pci_dev, 0),
+ pci_resource_len(ctrl->pci_dev, 0));
+
+ res = ctrl->io_head;
+ while (res) {
+ tres = res;
+ res = res->next;
+ kfree(tres);
+ }
+
+ res = ctrl->mem_head;
+ while (res) {
+ tres = res;
+ res = res->next;
+ kfree(tres);
+ }
+
+ res = ctrl->p_mem_head;
+ while (res) {
+ tres = res;
+ res = res->next;
+ kfree(tres);
+ }
+
+ res = ctrl->bus_head;
+ while (res) {
+ tres = res;
+ res = res->next;
+ kfree(tres);
+ }
+
+ tctrl = ctrl;
+ ctrl = ctrl->next;
+ kfree(tctrl);
+ }
+
+ for (loop = 0; loop < 256; loop++) {
+ next = amdshpc_slot_list[loop];
+ while (next != NULL) {
+ res = next->io_head;
+ while (res) {
+ tres = res;
+ res = res->next;
+ kfree(tres);
+ }
+
+ res = next->mem_head;
+ while (res) {
+ tres = res;
+ res = res->next;
+ kfree(tres);
+ }
+
+ res = next->p_mem_head;
+ while (res) {
+ tres = res;
+ res = res->next;
+ kfree(tres);
+ }
+
+ res = next->bus_head;
+ while (res) {
+ tres = res;
+ res = res->next;
+ kfree(tres);
+ }
+
+ TempSlot = next;
+ next = next->next;
+ kfree(TempSlot);
+ }
+ }
+
+ shpc_context = amdshpc_ctrl_list;
+
+ while(shpc_context){
+
+ dbg("%s -->shpc_context = %p",__FUNCTION__ , shpc_context);
+ dbg("%s -->kill_amdshpc() instance = %d", __FUNCTION__ ,shpc_context->shpc_instance);
+ hp_StopDevice(shpc_context);
+
+ //Free IRQ associated with hot plug device
+ free_irq(shpc_context->interrupt, shpc_context);
+
+ //Unmap the memory
+ iounmap(shpc_context->mmio_base_addr);
+
+ // free the controller memory
+ tshpc_context = shpc_context;
+ shpc_context = shpc_context->next;
+ kfree(tshpc_context);
+ }
+
+ //unmap the rom address
+ if (amdshpc_rom_start)
+ iounmap(amdshpc_rom_start);
+}
+
+
+static struct pci_device_id hpcd_pci_tbl[] = {
+ {
+ /* handle AMD Standard Hotplug controller */
+
+// class: ((PCI_CLASS_SYSTEM_PCI_HOTPLUG << 8) | 0x00),
+ class: ((PCI_CLASS_BRIDGE_PCI << 8) | 0x00),
+ class_mask: ~0,
+
+ /* AMD makes it */
+ vendor: PCI_VENDOR_ID_AMD,
+ device: PCI_DEVICE_ID_AMD_GOLAM_7450,
+ subvendor: PCI_ANY_ID,
+ subdevice: PCI_ANY_ID,
+
+ }, { /* end: all zeroes */ }
+};
+
+MODULE_DEVICE_TABLE(pci, hpcd_pci_tbl);
+
+
+
+static struct pci_driver amdshpc_driver = {
+ name: "pci_hotplug",
+ id_table: hpcd_pci_tbl,
+ probe: amdshpc_probe,
+ /* remove: amdshpc_remove_one, */
+};
+
+
+static int __init amdshpc_init(void)
+{
+ int result;
+
+ amdshpc_debug = debug;
+ /*
+ * Do specific initialization stuff for your driver here
+ * Like initilizing your controller hardware (if any) and
+ * determining the number of slots you have in the system
+ * right now.
+ */
+
+ result = pci_module_init(&amdshpc_driver);
+ dbg("%s -->pci_module_init = %d\n",__FUNCTION__ , result);
+ if (result)
+ return result;
+
+
+ info (DRIVER_DESC " version: " DRIVER_VERSION "\n");
+ return 0;
+}
+
+static void __exit amdshpc_exit(void)
+{
+ //
+ // Clean everything up.
+ //
+ dbg("%s -->unload_amdshpc()\n",__FUNCTION__ );
+ unload_amdshpc();
+
+ cleanup_slots();
+
+ dbg("%s -->pci_unregister_driver\n",__FUNCTION__ );
+ pci_unregister_driver(&amdshpc_driver);
+
+}
+
+module_init(amdshpc_init);
+module_exit(amdshpc_exit);
--- /dev/null
+/*
+ * Copyright (C) 2001,2003 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (C) 2001,2003 IBM Corp.
+ * Copyright (C) 2002-2003 Advanced Micro Devices
+ *
+ * YOUR USE OF THIS CODE IS SUBJECT TO THE TERMS
+ * AND CONDITIONS OF THE GNU GENERAL PUBLIC
+ * LICENSE FOUND IN THE "GPL.TXT" FILE THAT IS
+ * INCLUDED WITH THIS FILE AND POSTED AT
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ *
+ * Send feedback to <david.keck@amd.com>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/smp_lock.h>
+#include "amdshpc_ddi.h"
+#include "amdshpc.h"
+
+
+// ****************************************************************************
+//
+// hp_slot_thread() @ PASSIVE_LEVEL
+//
+// ****************************************************************************
+int hp_slot_thread(void* ptr)
+{
+ unsigned long old_irq_flags;
+ long status = STATUS_SUCCESS;
+ struct shpc_context* shpc_context;
+ struct slot_context* slot_context;
+ union SLOT_STATUS_INFO slot_status;
+
+ lock_kernel ();
+ daemonize ("amdshpc_slot");
+ unlock_kernel ();
+
+ slot_context = (struct slot_context*) ptr;
+ shpc_context = (struct shpc_context*) slot_context->shpc_context;
+
+ //
+ // Insertion/Removal State Machine (loops until requested to exit)
+ //
+ do {
+ status = slot_context->slot_function( shpc_context, slot_context );
+ //
+ // Suspend?
+ //
+ if(!status) {
+ spin_lock_irqsave(&shpc_context->shpc_spinlock, old_irq_flags);
+ if(shpc_context->shpc_event_bits & SUSPEND_EVENT ) {
+ status = STATUS_SUCCESS;
+ }
+ spin_unlock_irqrestore( &shpc_context->shpc_spinlock, old_irq_flags );
+
+ if(status) {
+ dbg( "%s-->SUSPEND: slot_id[ %d:%d ]",__FUNCTION__,
+ (int)shpc_context->shpc_instance, slot_context->slot_number-1 );
+
+ do {
+ interruptible_sleep_on(&slot_context->slot_event);
+ }while(!((shpc_context->shpc_event_bits & RESUME_EVENT) ||
+ (shpc_context->shpc_event_bits & REMOVE_EVENT)));
+
+ if(shpc_context->shpc_event_bits & REMOVE_EVENT ) {
+ status = STATUS_UNSUCCESSFUL;
+ }
+ else {
+ dbg("%s-->RESUME: slot_id[ %d:%d ]",__FUNCTION__,
+ shpc_context->shpc_instance, slot_context->slot_number-1 );
+ }
+ }
+ }
+ } while(status);
+
+ //
+ // We're exiting, most likely due to an exit_request_event. So, let's cleanup!
+ //
+ dbg("%s-->Slot Thread Termination: slot_id[ %d:%d ]",__FUNCTION__,
+ shpc_context->shpc_instance, slot_context->slot_number-1 );
+
+ //
+ // Pending SW-initiated slot request?
+ //
+ if(slot_context->slot_event_bits & SLOT_REQUEST_EVENT ) {
+ //
+ // Complete it with failure code
+ //
+ hp_QuerySlotStatus( shpc_context, slot_context->slot_number - 1, &slot_status );
+ slot_status.x.lu_request_failed = HP_TRUE;
+ shpc_context->async_callback(
+ shpc_context->driver_context,
+ slot_context->slot_number - 1,
+ slot_context->slot_request.type,
+ slot_status,
+ slot_context->slot_request.request_context );
+
+ //
+ // Signal registered user EVENT
+ //
+ hp_signal_user_event( shpc_context );
+ }
+ return(status);
+}
+
+
+// ****************************************************************************
+//
+// hp_attn_led_thread() @ PASSIVE_LEVEL
+//
+// ****************************************************************************
+int
+hp_attn_led_thread(
+ void* ptr
+)
+{
+ unsigned long old_irq_flags;
+ long status = STATUS_SUCCESS;
+ struct shpc_context* shpc_context;
+ struct slot_context* slot_context;
+ union SLOT_STATUS_INFO slot_status;
+
+ lock_kernel ();
+ daemonize ("amdshpc_led");
+ unlock_kernel ();
+
+ slot_context = (struct slot_context*) ptr;
+ shpc_context = (struct shpc_context*) slot_context->shpc_context;
+
+ //
+ // Attention LED State Machine (loops until requested to exit)
+ //
+ do {
+ status = slot_context->attn_led_function(shpc_context, slot_context);
+ //
+ // Suspend?
+ //
+ if(!status) {
+ spin_lock_irqsave(&shpc_context->shpc_spinlock, old_irq_flags);
+ if(shpc_context->shpc_event_bits & SUSPEND_EVENT ) {
+ status = STATUS_SUCCESS;
+ }
+ spin_unlock_irqrestore( &shpc_context->shpc_spinlock, old_irq_flags );
+
+ if(status) {
+ dbg("%s-->SUSPEND: slot_id[ %d:%d ]",__FUNCTION__,
+ shpc_context->shpc_instance, slot_context->slot_number-1 );
+
+ do {
+ interruptible_sleep_on(&slot_context->slot_event);
+ }while(!((shpc_context->shpc_event_bits & RESUME_EVENT) ||
+ (shpc_context->shpc_event_bits & REMOVE_EVENT)));
+
+ if(shpc_context->shpc_event_bits & REMOVE_EVENT ) {
+ status = STATUS_UNSUCCESSFUL;
+ }
+ else {
+ dbg("%s-->RESUME: slot_id[ %d:%d ]",__FUNCTION__,
+ shpc_context->shpc_instance, slot_context->slot_number-1 );
+ }
+ }
+ }
+ } while(status);
+
+ //
+ // We're exiting, most likely due to an exit_request_event. So, let's cleanup!
+ //
+ dbg("%s-->LED Thread Termination: slot_id[ %d:%d ]",__FUNCTION__,
+ shpc_context->shpc_instance, slot_context->slot_number-1 );
+
+ //
+ // Pending SW-initiated AttnLED request?
+ //
+ if(slot_context->slot_event_bits & ATTN_LED_REQUEST_EVENT ) {
+ //
+ // Complete it with failure code
+ //
+ hp_QuerySlotStatus( shpc_context, slot_context->slot_number - 1, &slot_status );
+ slot_status.x.lu_request_failed = HP_TRUE;
+ shpc_context->async_callback(
+ shpc_context->driver_context,
+ slot_context->slot_number - 1,
+ slot_context->attn_led_request.type,
+ slot_status,
+ slot_context->attn_led_request.request_context );
+
+ //
+ // Signal registered user EVENT
+ //
+ hp_signal_user_event( shpc_context );
+ }
+ return(status);
+}
+
+
+// ****************************************************************************
+//
+// hp_get_slot_configuration() @ Any IRQL
+//
+// ****************************************************************************
+void
+hp_get_slot_configuration(
+ struct shpc_context* shpc_context
+)
+{
+ struct slot_context* slot_context;
+ union SHPC_SLOTS_AVAILABLE1_DWREG SlotAvail1Reg;
+ union SHPC_SLOTS_AVAILABLE2_DWREG SlotAvail2Reg;
+ union SHPC_SLOT_CONFIG_DWREG SlotConfigReg;
+ union SHPC_LOGICAL_SLOT_DWREG logical_slot_reg;
+ enum shpc_speed_mode max_speed_mode;
+ u8 i;
+
+ //
+ // Get max number of slots available
+ //
+ SlotAvail1Reg.AsDWord = readl(shpc_context->mmio_base_addr + SHPC_SLOTS_AVAILABLE1_REG_OFFSET);
+ SlotAvail2Reg.AsDWord = readl(shpc_context->mmio_base_addr + SHPC_SLOTS_AVAILABLE2_REG_OFFSET);
+ //
+ // Get slot configuration
+ //
+ SlotConfigReg.AsDWord = readl(shpc_context->mmio_base_addr + SHPC_SLOT_CONFIG_REG_OFFSET);
+
+ //
+ // Get number of available slots per speed/mode
+ //
+ shpc_context->slots_enabled = 0;
+ shpc_context->number_of_slots = 0;
+ if( SlotAvail1Reg.x.N_133PCIX ) {
+ shpc_context->number_of_slots = ( u8 )SlotAvail1Reg.x.N_133PCIX;
+ shpc_context->max_speed_mode = SHPC_BUS_PCIX_133;
+ }
+ else if( SlotAvail1Reg.x.N_100PCIX ) {
+ shpc_context->number_of_slots = ( u8 )SlotAvail1Reg.x.N_100PCIX;
+ shpc_context->max_speed_mode = SHPC_BUS_PCIX_100;
+ }
+ else if( SlotAvail1Reg.x.N_66PCIX ) {
+ shpc_context->number_of_slots = ( u8 )SlotAvail1Reg.x.N_66PCIX;
+ shpc_context->max_speed_mode = SHPC_BUS_PCIX_66;
+ }
+ else if( SlotAvail2Reg.x.N_66CONV ) {
+ shpc_context->number_of_slots = ( u8 )SlotAvail2Reg.x.N_66CONV;
+ shpc_context->max_speed_mode = SHPC_BUS_CONV_66;
+ }
+ else if( SlotAvail1Reg.x.N_33CONV ) {
+ shpc_context->number_of_slots = ( u8 )SlotAvail1Reg.x.N_33CONV;
+ shpc_context->max_speed_mode = SHPC_BUS_CONV_33;
+ }
+
+ if( shpc_context->number_of_slots ) {
+ //
+ // Be sure NSI field is not exceeded (this should not happen!)
+ //
+ if( shpc_context->number_of_slots > SlotConfigReg.x.NSI ) {
+ shpc_context->number_of_slots = ( u8 )SlotConfigReg.x.NSI;
+ }
+
+ //
+ // Limit slot count to what we're prepared to support
+ //
+ if( shpc_context->number_of_slots > SHPC_MAX_NUM_SLOTS ) {
+ shpc_context->number_of_slots = SHPC_MAX_NUM_SLOTS;
+ }
+
+ //
+ // Get current Bus speed/mode
+ //
+ shpc_context->bus_speed_mode = hp_get_bus_speed_mode( shpc_context );
+
+ //
+ // Initialize slot state based on HW disposition
+ //
+ for( i=0; i< shpc_context->number_of_slots; ++i ) {
+ slot_context = &shpc_context->slot_context[ i ];
+
+ //
+ // Get Physical Slot Number (PSN-based)
+ //
+ if( SlotConfigReg.x.PSN_UP ) {
+ slot_context->slot_psn = ( u8 )SlotConfigReg.x.PSN + i;
+ }
+ else {
+ slot_context->slot_psn = ( u8 )SlotConfigReg.x.PSN - i;
+ }
+
+ //
+ // Assign Logical Slot Number (1-based)
+ //
+ slot_context->slot_number = ( u8 )i+1;
+
+ //
+ // Get Card's speed/mode capabilities
+ //
+ hp_get_card_speed_mode( slot_context );
+
+ //
+ // Check current HW state
+ //
+ logical_slot_reg.AsDWord = readl( slot_context->logical_slot_addr );
+
+ //
+ // Already enabled: Card Present, MRL closed, Slot Enabled, No Power-Fault?
+ //
+ if( logical_slot_reg.x.PRSNT1_2 != SHPC_SLOT_EMPTY &&
+ ( logical_slot_reg.x.MRLS_IM == SHPC_MASKED ||
+ logical_slot_reg.x.MRLS == SHPC_MRL_CLOSED ) &&
+ logical_slot_reg.x.PF == SHPC_STATUS_CLEARED &&
+ logical_slot_reg.x.S_STATE == SHPC_ENABLE_SLOT ) {
+ //
+ // Treat it as a SUCCESSFUL "Slot Enabled" HW-initiated request
+ //
+ slot_context->slot_completion.hw_initiated = TRUE;
+ slot_context->slot_completion.type = SHPC_ASYNC_ENABLE_SLOT;
+ slot_context->slot_completion.failed = HP_FALSE;
+ slot_context->slot_completion.request_context = NULL;
+ slot_context->slot_completion.done = TRUE;
+
+ //
+ // Flag as "Slot Enabled"
+ //
+ ++shpc_context->slots_enabled;
+ slot_context->slot_enabled = TRUE;
+ slot_context->in_bus_speed_mode_contention = TRUE;
+ if( logical_slot_reg.x.PIS == SHPC_LED_ON ) {
+ slot_context->slot_function = (SLOT_STATE_FUNCTION) hp_at_slot_enabled_wait_for_slot_request;
+ }
+ else {
+ slot_context->slot_function = (SLOT_STATE_FUNCTION) hp_to_slot_enabled_wait_for_led_cmd_available;
+ }
+ }
+ else {
+ //
+ // Treat it as a SUCCESSFUL "Slot Disable" HW-initiated request
+ //
+ slot_context->slot_completion.hw_initiated = TRUE;
+ slot_context->slot_completion.type = SHPC_ASYNC_DISABLE_SLOT;
+ slot_context->slot_completion.failed = HP_FALSE;
+ slot_context->slot_completion.request_context = NULL;
+ slot_context->slot_completion.done = TRUE;
+
+ //
+ // Flag as "Slot Disabled"
+ //
+ slot_context->slot_enabled = FALSE;
+ slot_context->in_bus_speed_mode_contention = FALSE;
+ if( logical_slot_reg.x.S_STATE == SHPC_DISABLE_SLOT ) {
+ slot_context->slot_function = (SLOT_STATE_FUNCTION) hp_at_slot_disabled_wait_for_slot_request;
+ }
+ else {
+ slot_context->slot_function = (SLOT_STATE_FUNCTION) hp_to_slot_disabled_wait_for_disable_cmd_available;
+ }
+ }
+
+ //
+ // Set Attention LED function
+ //
+ if( logical_slot_reg.x.PF == SHPC_STATUS_SET ) {
+ //
+ // Turn it ON
+ //
+ slot_context->problem_detected = TRUE;
+ slot_context->attn_led_function = (SLOT_STATE_FUNCTION) hp_wait_for_attn_led_back_to_normal_cmd_available;
+ }
+ else {
+ //
+ // Make sure it is turned OFF
+ //
+ slot_context->problem_detected = FALSE;
+ if( logical_slot_reg.x.AIS == SHPC_LED_OFF ) {
+ slot_context->attn_led_function = (SLOT_STATE_FUNCTION) hp_wait_for_attn_led_request;
+ }
+ else {
+ slot_context->attn_led_function = (SLOT_STATE_FUNCTION) hp_wait_for_attn_led_back_to_normal_cmd_available;
+ }
+ }
+ }
+
+ //
+ // Enabled slots running at maximum speed/mode?
+ //
+ if( shpc_context->slots_enabled ) {
+ max_speed_mode = hp_get_max_speed_mode( shpc_context, shpc_context->max_speed_mode );
+
+ //
+ // Signal enabled slots to release the bus, then change bus speed/mode
+ //
+ if( shpc_context->bus_speed_mode != max_speed_mode ) {
+ hp_signal_enabled_slots_to_rebalance_bus( shpc_context );
+ }
+ }
+ }
+}
+
+
+// ****************************************************************************
+//
+// hp_enable_slot_interrupts() @ Any IRQL
+//
+// ****************************************************************************
+void
+hp_enable_slot_interrupts(
+ struct slot_context* slot_context
+)
+{
+ struct shpc_context* shpc_context = ( struct shpc_context* )slot_context->shpc_context;
+ union SHPC_SLOT_CONFIG_DWREG SlotConfigReg;
+ union SHPC_LOGICAL_SLOT_DWREG logical_slot_reg;
+
+ //
+ // Get HW implementation: Attention Button, MRL Sensor
+ //
+ SlotConfigReg.AsDWord = readl(shpc_context->mmio_base_addr + SHPC_SLOT_CONFIG_REG_OFFSET);
+
+ //
+ // Attention Button: Enabled only if implemented
+ //
+ logical_slot_reg.x.AB_IM = ( SlotConfigReg.x.ABI == SHPC_STATUS_SET ) ?
+ SHPC_UNMASKED : SHPC_MASKED;
+ logical_slot_reg.x.ABP_STS = SHPC_STATUS_SET;
+
+ //
+ // MRL Sensor: Enabled only if implemented (System Error Disabled)
+ //
+ logical_slot_reg.x.MRLS_IM = ( SlotConfigReg.x.MRLSI == SHPC_STATUS_SET ) ?
+ SHPC_UNMASKED : SHPC_MASKED;
+ logical_slot_reg.x.MRLS_SERRM = SHPC_MASKED;
+ logical_slot_reg.x.MRLSC_STS = SHPC_STATUS_SET;
+
+ //
+ // Card Presence: Enabled
+ //
+ logical_slot_reg.x.CP_IM = SHPC_UNMASKED;
+ logical_slot_reg.x.CPC_STS = SHPC_STATUS_SET;
+
+ //
+ // Isolated Power-Fault: Enabled
+ //
+ logical_slot_reg.x.IPF_IM = SHPC_UNMASKED;
+ logical_slot_reg.x.IPF_STS = SHPC_STATUS_SET;
+
+ //
+ // Connected Power-Fault: Enabled (System Error Disabled)
+ //
+ logical_slot_reg.x.CPF_IM = SHPC_UNMASKED;
+ logical_slot_reg.x.CPF_SERRM = SHPC_MASKED;
+ logical_slot_reg.x.CPF_STS = SHPC_STATUS_SET;
+
+ //
+ // Update Mask and Status bits
+ //
+ writel(logical_slot_reg.AsDWord, slot_context->logical_slot_addr);
+}
+
+
+// ****************************************************************************
+//
+// hp_disable_slot_interrupts() @ Any IRQL
+//
+// ****************************************************************************
+void
+hp_disable_slot_interrupts(
+ struct slot_context* slot_context
+)
+{
+ union SHPC_LOGICAL_SLOT_DWREG logical_slot_reg;
+
+ //
+ // Get HW implementation: Attention Button, MRL Sensor
+ //
+ logical_slot_reg.AsDWord = readl(slot_context->logical_slot_addr);
+
+ //
+ // Attention Button: Disabled
+ //
+ logical_slot_reg.x.AB_IM = SHPC_MASKED;
+ logical_slot_reg.x.ABP_STS = SHPC_STATUS_SET;
+
+ //
+ // MRL Sensor: Disabled
+ //
+ logical_slot_reg.x.MRLS_IM = SHPC_MASKED;
+ logical_slot_reg.x.MRLS_SERRM = SHPC_MASKED;
+ logical_slot_reg.x.MRLSC_STS = SHPC_STATUS_SET;
+
+ //
+ // Card Presence: Disabled
+ //
+ logical_slot_reg.x.CP_IM = SHPC_MASKED;
+ logical_slot_reg.x.CPC_STS = SHPC_STATUS_SET;
+
+ //
+ // Isolated Power-Fault: Disabled
+ //
+ logical_slot_reg.x.IPF_IM = SHPC_MASKED;
+ logical_slot_reg.x.IPF_STS = SHPC_STATUS_SET;
+
+ //
+ // Connected Power-Fault: Enabled (System Error Disabled)
+ //
+ logical_slot_reg.x.CPF_IM = SHPC_MASKED;
+ logical_slot_reg.x.CPF_SERRM = SHPC_MASKED;
+ logical_slot_reg.x.CPF_STS = SHPC_STATUS_SET;
+
+ //
+ // Update Mask and Status bits
+ //
+ writel(logical_slot_reg.AsDWord, slot_context->logical_slot_addr);
+}
+
+
+// ****************************************************************************
+//
+// hp_enable_global_interrupts() @ Any IRQL
+//
+// ****************************************************************************
+void
+hp_enable_global_interrupts(
+ struct shpc_context* shpc_context
+)
+{
+ union SHPC_SERR_INT_DWREG SerrIntReg;
+
+ SerrIntReg.AsDWord = readl(shpc_context->mmio_base_addr + SHPC_SERR_INT_REG_OFFSET);
+
+ //
+ // Arbiter timeout: System Error Disabled
+ //
+ SerrIntReg.x.A_SERRM = SHPC_MASKED;
+ SerrIntReg.x.ATOUT_STS = SHPC_STATUS_SET;
+
+ //
+ // Command Completion: Enabled
+ //
+ SerrIntReg.x.CC_IM = SHPC_UNMASKED;
+ SerrIntReg.x.CC_STS = SHPC_STATUS_SET;
+
+ //
+ // Global: Interrputs Enabled, System Error Disabled
+ //
+ SerrIntReg.x.GIM = SHPC_UNMASKED;
+ SerrIntReg.x.GSERRM = SHPC_MASKED;
+
+ //
+ // Update Mask and Status bits
+ //
+ writel(SerrIntReg.AsDWord, shpc_context->mmio_base_addr + SHPC_SERR_INT_REG_OFFSET);
+}
+
+
+// ****************************************************************************
+//
+// hp_disable_global_interrupts() @ Any IRQL
+//
+// ****************************************************************************
+void
+hp_disable_global_interrupts(
+ struct shpc_context* shpc_context
+)
+{
+ union SHPC_SERR_INT_DWREG SerrIntReg;
+
+ SerrIntReg.AsDWord = readl(shpc_context->mmio_base_addr + SHPC_SERR_INT_REG_OFFSET);
+ //
+ // Arbiter timeout: System Error Disabled
+ //
+ SerrIntReg.x.A_SERRM = SHPC_MASKED;
+ SerrIntReg.x.ATOUT_STS = SHPC_STATUS_SET;
+
+ //
+ // Command Completion: Disabled
+ //
+ SerrIntReg.x.CC_IM = SHPC_MASKED;
+ SerrIntReg.x.CC_STS = SHPC_STATUS_SET;
+
+ //
+ // Global: Interrputs Disabled, System Error Disabled
+ //
+ SerrIntReg.x.GIM = SHPC_MASKED;
+ SerrIntReg.x.GSERRM = SHPC_MASKED;
+
+ //
+ // Update Mask and Status bits
+ //
+ writel(SerrIntReg.AsDWord, shpc_context->mmio_base_addr + SHPC_SERR_INT_REG_OFFSET);
+}
+
+
+// ****************************************************************************
+//
+// hp_get_card_speed_mode() @ Any IRQL
+//
+// ****************************************************************************
+enum shpc_speed_mode
+hp_get_card_speed_mode(
+ struct slot_context* slot_context
+)
+{
+ union SHPC_LOGICAL_SLOT_DWREG logical_slot_reg;
+
+ //
+ // Slot powered-up?
+ //
+ logical_slot_reg.AsDWord = readl( slot_context->logical_slot_addr );
+ if(( logical_slot_reg.x.PRSNT1_2 != SHPC_SLOT_EMPTY ) &&
+ ( logical_slot_reg.x.S_STATE == SHPC_POWER_ONLY ||
+ logical_slot_reg.x.S_STATE == SHPC_ENABLE_SLOT )) {
+ //
+ // Get Card's maximum speed/mode
+ //
+ if( logical_slot_reg.x.PCIX_CAP == SHPC_SLOT_PCIX_133 ) {
+ slot_context->card_speed_mode = SHPC_BUS_PCIX_133;
+ }
+ else if( logical_slot_reg.x.PCIX_CAP == SHPC_SLOT_PCIX_66 ) {
+ slot_context->card_speed_mode = SHPC_BUS_PCIX_66;
+ }
+ else if( logical_slot_reg.x.M66_CAP == SHPC_STATUS_SET ) {
+ slot_context->card_speed_mode = SHPC_BUS_CONV_66;
+ }
+ else {
+ slot_context->card_speed_mode = SHPC_BUS_CONV_33;
+ }
+
+ //
+ // Get Card's PCI-66 capability
+ //
+ if( logical_slot_reg.x.M66_CAP == SHPC_STATUS_SET ) {
+ slot_context->card_pci66_capable = TRUE;
+ }
+ }
+ else {
+ //
+ // Slot is not powered-up, use PCI-33 as default
+ //
+ slot_context->card_speed_mode = SHPC_BUS_CONV_33;
+ slot_context->card_pci66_capable = FALSE;
+ }
+
+ return slot_context->card_speed_mode;
+}
+
+// ****************************************************************************
+//
+// hp_get_bus_speed_mode() @ Any IRQL
+//
+// ****************************************************************************
+enum shpc_speed_mode
+hp_get_bus_speed_mode(
+ struct shpc_context* shpc_context
+)
+{
+ union SHPC_SEC_BUS_CONFIG_DWREG bus_config_reg;
+ enum shpc_speed_mode bus_speed_mode;
+
+ bus_config_reg.AsDWord = readl(shpc_context->mmio_base_addr + SHPC_SEC_BUS_CONFIG_REG_OFFSET);
+
+ bus_speed_mode = ( enum shpc_speed_mode )bus_config_reg.x.MODE;
+ if( bus_speed_mode > SHPC_BUS_PCIX_133 ) {
+ bus_speed_mode = SHPC_BUS_CONV_33;
+ }
+
+ return bus_speed_mode;
+}
+
+
+// ****************************************************************************
+//
+// hp_translate_speed_mode() @ Any IRQL
+//
+// ****************************************************************************
+enum mode_frequency
+hp_translate_speed_mode(
+ enum shpc_speed_mode shpc_speed_mode
+)
+{
+ enum mode_frequency translated_speed_mode;
+
+ switch( shpc_speed_mode ) {
+ case SHPC_BUS_PCIX_133:
+ translated_speed_mode = MODE_PCIX_133;
+ break;
+
+ case SHPC_BUS_PCIX_100:
+ translated_speed_mode = MODE_PCIX_100;
+ break;
+
+ case SHPC_BUS_PCIX_66:
+ translated_speed_mode = MODE_PCIX_66;
+ break;
+
+ case SHPC_BUS_CONV_66:
+ translated_speed_mode = MODE_PCI_66;
+ break;
+
+ case SHPC_BUS_CONV_33:
+ default:
+ translated_speed_mode = MODE_PCI_33;
+ break;
+ }
+
+ return translated_speed_mode;
+}
+
+
+// ****************************************************************************
+//
+// hp_translate_card_power() @ Any IRQL
+//
+// ****************************************************************************
+enum hp_power_requirements
+hp_translate_card_power(
+ enum shpc_card_power ShpcCardPower
+)
+{
+ enum hp_states TranslatedCardPower;
+
+ switch( ShpcCardPower ) {
+ case SHPC_CARD_PRESENT_25W:
+ TranslatedCardPower = POWER_HIGH;
+ break;
+
+ case SHPC_CARD_PRESENT_15W:
+ TranslatedCardPower = POWER_MEDIUM;
+ break;
+
+ case SHPC_CARD_PRESENT_7_5W:
+ default:
+ TranslatedCardPower = POWER_LOW;
+ break;
+ }
+
+ return TranslatedCardPower;
+}
+
+
+// ****************************************************************************
+//
+// hp_translate_indicator() @ Any IRQL
+//
+// ****************************************************************************
+enum hp_indicators
+hp_translate_indicator(
+ enum shpc_slot_led ShpcIndicator
+)
+{
+ enum hp_indicators TranslatedIndicator;
+
+ switch( ShpcIndicator ) {
+ case SHPC_LED_ON:
+ TranslatedIndicator = INDICATOR_ON;
+ break;
+
+ case SHPC_LED_BLINK:
+ TranslatedIndicator = INDICATOR_BLINK;
+ break;
+
+ case SHPC_LED_OFF:
+ default:
+ TranslatedIndicator =INDICATOR_OFF;
+ break;
+ }
+
+ return TranslatedIndicator;
+}
+
+
+// ****************************************************************************
+//
+// hp_flag_slot_as_enabled() @ <= DISPATCH_LEVEL
+//
+// ****************************************************************************
+u8
+hp_flag_slot_as_enabled(
+ struct shpc_context* shpc_context,
+ struct slot_context* slot_context
+)
+{
+ unsigned long old_irq_flags;
+ u8 SlotFlagged = FALSE;
+
+ spin_lock_irqsave( &shpc_context->shpc_spinlock, old_irq_flags );
+ if( !slot_context->slot_enabled ) {
+ //
+ // Slot just coming on-line
+ //
+ SlotFlagged = TRUE;
+ ++shpc_context->slots_enabled;
+ slot_context->slot_enabled = TRUE;
+ hp_clear_shpc_event_bit(shpc_context, BUS_REBALANCE_EVENT);
+ }
+ spin_unlock_irqrestore( &shpc_context->shpc_spinlock, old_irq_flags );
+
+ return SlotFlagged;
+}
+
+
+// ****************************************************************************
+//
+// hp_flag_slot_as_disabled() @ <= DISPATCH_LEVEL
+//
+// ****************************************************************************
+u8
+hp_flag_slot_as_disabled(
+ struct shpc_context* shpc_context,
+ struct slot_context* slot_context
+)
+{
+ unsigned long old_irq_flags;
+ u8 SlotFlagged = FALSE;
+
+ spin_lock_irqsave( &shpc_context->shpc_spinlock, old_irq_flags );
+ if( slot_context->slot_enabled ) {
+ if( --shpc_context->slots_enabled == 0 ) {
+ //
+ // This was the last enabled slot, signal waiting thread that bus is released,
+ //
+ shpc_context->bus_released = TRUE;
+ hp_send_event_to_all_slots(shpc_context, BUS_COMPLETE_EVENT);
+ }
+ SlotFlagged = TRUE;
+ slot_context->slot_enabled = FALSE;
+ }
+ spin_unlock_irqrestore( &shpc_context->shpc_spinlock, old_irq_flags );
+
+ return SlotFlagged;
+}
+
+
+// ****************************************************************************
+//
+// hp_signal_enabled_slots_to_rebalance_bus() @ <= DISPATCH_LEVEL
+//
+// Comments:
+// Assumes shpc_spinlock is already held.
+//
+// ****************************************************************************
+u8
+hp_signal_enabled_slots_to_rebalance_bus(
+ struct shpc_context* shpc_context
+)
+{
+ struct slot_context* SlotArray[ SHPC_MAX_NUM_SLOTS ];
+ struct slot_context* Slot;
+ u8 i, j, n;
+
+ //
+ // Initialize array of slot pointers
+ //
+ n = shpc_context->number_of_slots;
+ for( i=0, j=0; i<n; ++i ) {
+ Slot = &shpc_context->slot_context[ i ];
+ if( Slot->slot_enabled ) {
+ SlotArray[ j++ ] = Slot;
+ }
+ }
+ //
+ // Found slots enabled?
+ //
+ if( j ) {
+ //
+ // Bubble-sort enabled slots in order of increasing card speed/mode
+ //
+ n = j;
+ for( i=0; i<n-1; i++ ) {
+ for( j=0; j<n-1-i; j++ ) {
+ if( SlotArray[ j+1 ]->card_speed_mode < SlotArray[ j ]->card_speed_mode ) {
+ Slot = SlotArray[ j ];
+ SlotArray[ j ] = SlotArray[ j+1 ];
+ SlotArray[ j+1 ] = Slot;
+ }
+ }
+ }
+ //
+ // Signal enabled slots in sorted order as an attempt to re-enable slower cards first
+ //
+ hp_set_shpc_event_bit(shpc_context, BUS_REBALANCE_EVENT);
+ for( i=0; i<n; i++ ) {
+ wake_up_interruptible( &SlotArray[ i ]->slot_event);
+ }
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+// ****************************************************************************
+//
+// hp_get_max_speed_mode() @ <= DISPATCH_LEVEL
+//
+// Comments:
+// Assumes shpc_spinlock is already held.
+//
+// ****************************************************************************
+enum shpc_speed_mode
+hp_get_max_speed_mode(
+ struct shpc_context* shpc_context,
+ enum shpc_speed_mode From_speed_mode
+)
+{
+ struct slot_context* slot_context;
+ enum shpc_speed_mode max_speed_mode;
+ u8 i;
+
+ max_speed_mode = From_speed_mode;
+ for( i=0; i< shpc_context->number_of_slots; ++i ) {
+ slot_context = &shpc_context->slot_context[ i ];
+ if( slot_context->in_bus_speed_mode_contention &&
+ slot_context->card_speed_mode < max_speed_mode ) {
+ //
+ // Can only go as fast as the slowest card
+ //
+ max_speed_mode = slot_context->card_speed_mode;
+ }
+ }
+
+ //
+ // Make sure all cards support conventional PCI-66 speed/mode
+ //
+ if( max_speed_mode == SHPC_BUS_CONV_66 ) {
+ for( i=0; i< shpc_context->number_of_slots; ++i ) {
+ slot_context = &shpc_context->slot_context[ i ];
+ if( slot_context->in_bus_speed_mode_contention &&
+ !slot_context->card_pci66_capable ) {
+ //
+ // Fall back to slower common denominator
+ //
+ max_speed_mode = SHPC_BUS_CONV_33;
+ }
+ }
+ }
+
+ return max_speed_mode;
+}
+
+
+// ****************************************************************************
+//
+// hp_signal_user_event() @ <= DISPATCH_LEVEL
+//
+// ****************************************************************************
+void hp_signal_user_event(struct shpc_context* shpc_context)
+{
+ unsigned long old_irq_flags;
+return;
+ spin_lock_irqsave( &shpc_context->shpc_spinlock, old_irq_flags );
+ if( shpc_context->user_event_pointer ) {
+ wake_up_interruptible( shpc_context->user_event_pointer);
+ }
+ spin_unlock_irqrestore( &shpc_context->shpc_spinlock, old_irq_flags );
+}
+
+
+// ****************************************************************************
+//
+// hp_signal_user_event_at_dpc_level() @ DISPATCH_LEVEL
+//
+// ****************************************************************************
+void hp_signal_user_event_at_dpc_level(struct shpc_context* shpc_context)
+{
+return;
+ spin_lock_bh( &shpc_context->shpc_spinlock );
+ if( shpc_context->user_event_pointer ) {
+ wake_up_interruptible( shpc_context->user_event_pointer);
+ }
+ spin_unlock_bh( &shpc_context->shpc_spinlock );
+}
--- /dev/null
+/*
+ * Copyright (C) 2001,2003 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (C) 2001,2003 IBM Corp.
+ * Copyright (C) 2002-2003 Advanced Micro Devices
+ *
+ * YOUR USE OF THIS CODE IS SUBJECT TO THE TERMS
+ * AND CONDITIONS OF THE GNU GENERAL PUBLIC
+ * LICENSE FOUND IN THE "GPL.TXT" FILE THAT IS
+ * INCLUDED WITH THIS FILE AND POSTED AT
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ *
+ * Send feedback to <david.keck@amd.com>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/smp_lock.h>
+#include "amdshpc_ddi.h"
+#include "amdshpc.h"
+
+static unsigned long async_callback (void* driver_context,
+ u8 slot_number,
+ enum shpc_async_request async_request,
+ union SLOT_STATUS_INFO slot_tatus,
+ void* request_context );
+
+// ****************************************************************************
+//
+// hp_AddDevice()
+//
+// Parameters
+// shpc_context - Caller provided storage for SHPC context data (per hardware-instance).
+// driver_context - Caller provided pointer to be returned upon completion.
+// Callback - Caller provided function to be called upon completion of async requests.
+// shpc_instance - Zero-based hardware instance.
+//
+// Return Value
+// Status returned by any system calls made within hp_AddDevice().
+//
+// ****************************************************************************
+long
+hp_AddDevice(
+ struct shpc_context* shpc_context,
+ void* driver_context,
+ SHPC_ASYNC_CALLBACK Callback,
+ unsigned long shpc_instance
+ )
+
+{
+ struct slot_context* slot_context;
+ u8 i;
+ DECLARE_TASKLET(mrl_sensor_dpc0, hp_mrl_sensor_dpc, (unsigned long) &shpc_context->slot_context[0] );
+ DECLARE_TASKLET(attn_button_dpc0, hp_attn_button_dpc, (unsigned long) &shpc_context->slot_context[0]);
+ DECLARE_TASKLET(card_presence_dpc0, hp_card_presence_dpc, (unsigned long) &shpc_context->slot_context[0]);
+ DECLARE_TASKLET(isolated_power_fault_dpc0, hp_isolated_power_fault_dpc, (unsigned long) &shpc_context->slot_context[0]);
+ DECLARE_TASKLET(connected_power_fault_dpc0, hp_connected_power_fault_dpc, (unsigned long) &shpc_context->slot_context[0]);
+
+ DECLARE_TASKLET(mrl_sensor_dpc1, hp_mrl_sensor_dpc, (unsigned long) &shpc_context->slot_context[1] );
+ DECLARE_TASKLET(attn_button_dpc1, hp_attn_button_dpc, (unsigned long) &shpc_context->slot_context[1]);
+ DECLARE_TASKLET(card_presence_dpc1, hp_card_presence_dpc, (unsigned long) &shpc_context->slot_context[1]);
+ DECLARE_TASKLET(isolated_power_fault_dpc1, hp_isolated_power_fault_dpc, (unsigned long) &shpc_context->slot_context[1]);
+ DECLARE_TASKLET(connected_power_fault_dpc1, hp_connected_power_fault_dpc, (unsigned long) &shpc_context->slot_context[1]);
+
+ DECLARE_TASKLET(mrl_sensor_dpc2, hp_mrl_sensor_dpc, (unsigned long) &shpc_context->slot_context[2] );
+ DECLARE_TASKLET(attn_button_dpc2, hp_attn_button_dpc, (unsigned long) &shpc_context->slot_context[2]);
+ DECLARE_TASKLET(card_presence_dpc2, hp_card_presence_dpc, (unsigned long) &shpc_context->slot_context[2]);
+ DECLARE_TASKLET(isolated_power_fault_dpc2, hp_isolated_power_fault_dpc, (unsigned long) &shpc_context->slot_context[2]);
+ DECLARE_TASKLET(connected_power_fault_dpc2, hp_connected_power_fault_dpc, (unsigned long) &shpc_context->slot_context[2]);
+
+ DECLARE_TASKLET(mrl_sensor_dpc3, hp_mrl_sensor_dpc, (unsigned long) &shpc_context->slot_context[3] );
+ DECLARE_TASKLET(attn_button_dpc3, hp_attn_button_dpc, (unsigned long) &shpc_context->slot_context[3]);
+ DECLARE_TASKLET(card_presence_dpc3, hp_card_presence_dpc, (unsigned long) &shpc_context->slot_context[3]);
+ DECLARE_TASKLET(isolated_power_fault_dpc3, hp_isolated_power_fault_dpc, (unsigned long) &shpc_context->slot_context[3]);
+ DECLARE_TASKLET(connected_power_fault_dpc3, hp_connected_power_fault_dpc, (unsigned long) &shpc_context->slot_context[3]);
+
+
+ DECLARE_TASKLET(cmd_completion_dpc, hp_cmd_completion_dpc, (unsigned long) shpc_context );
+
+ //
+ // Init common resources
+ //
+ shpc_context->cmd_completion_dpc = cmd_completion_dpc;
+ shpc_context->driver_context = driver_context;
+ shpc_context->async_callback = (SHPC_ASYNC_CALLBACK)async_callback;
+ shpc_context->shpc_instance = shpc_instance;
+ shpc_context->slots_enabled = 0;
+ shpc_context->number_of_slots = 0;
+ shpc_context->at_power_device_d0 = FALSE;
+ shpc_context->bus_released = FALSE;
+ shpc_context->user_event_pointer = NULL;
+ spin_lock_init( &shpc_context->shpc_spinlock );
+ sema_init( &shpc_context->cmd_available_mutex, 1);
+ sema_init( &shpc_context->bus_available_mutex, 1);
+ sema_init( &shpc_context->shpc_event_bits_semaphore, 1);
+
+ shpc_context->shpc_event_bits=0; // all shpc events cleared
+
+ dbg("%s -->HwInstance[ %d ]", __FUNCTION__ ,shpc_context->shpc_instance );
+
+ //
+ // Init slot resources
+ //
+ for( i=0; i< SHPC_MAX_NUM_SLOTS; ++i ) {
+ slot_context = &shpc_context->slot_context[ i ];
+ slot_context->shpc_context = ( void* )shpc_context;
+ slot_context->slot_number = ( u8 )i+1;
+ slot_context->slot_enabled = FALSE;
+ slot_context->in_bus_speed_mode_contention = FALSE;
+ slot_context->problem_detected = FALSE;
+ slot_context->slot_quiesced = FALSE;
+ slot_context->slot_thread = NULL;
+ slot_context->slot_function = NULL;
+ slot_context->attn_led_thread = NULL;
+ slot_context->attn_led_function = NULL;
+
+ //
+ // Slot SpinLocks and semaphores
+ //
+ spin_lock_init( &slot_context->slot_spinlock);
+ sema_init(&slot_context->slot_event_bits_semaphore, 1);
+ sema_init(&slot_context->cmd_acquire_mutex, 1);
+ sema_init(&slot_context->bus_acquire_mutex, 1);
+
+ //
+ // Slot timers
+ //
+ init_timer(&slot_context->slot_timer1);
+ init_timer(&slot_context->slot_timer2);
+ init_timer(&slot_context->slot_timer3);
+ init_timer(&slot_context->slot_timer4);
+ init_timer(&slot_context->slot_timer5);
+ init_timer(&slot_context->slot_timer6);
+ init_timer(&slot_context->slot_timer7);
+ init_timer(&slot_context->slot_timer8);
+ init_timer(&slot_context->slot_timer9);
+ init_timer(&slot_context->slot_timer10);
+ init_timer(&slot_context->led_timer1);
+ init_timer(&slot_context->led_timer2);
+ init_timer(&slot_context->led_timer3);
+ init_timer(&slot_context->led_timer4);
+
+ //
+ // Interrupt Service
+ //
+ switch (i) {
+ case 0:
+ slot_context->attn_button_dpc = attn_button_dpc0;
+ slot_context->mrl_sensor_dpc = mrl_sensor_dpc0;
+ slot_context->card_presence_dpc = card_presence_dpc0;
+ slot_context->isolated_power_fault_dpc = isolated_power_fault_dpc0;
+ slot_context->connected_power_fault_dpc = connected_power_fault_dpc0;
+ break;
+ case 1:
+ slot_context->attn_button_dpc = attn_button_dpc1;
+ slot_context->mrl_sensor_dpc = mrl_sensor_dpc1;
+ slot_context->card_presence_dpc = card_presence_dpc1;
+ slot_context->isolated_power_fault_dpc = isolated_power_fault_dpc1;
+ slot_context->connected_power_fault_dpc = connected_power_fault_dpc1;
+ break;
+ case 2:
+ slot_context->attn_button_dpc = attn_button_dpc2;
+ slot_context->mrl_sensor_dpc = mrl_sensor_dpc2;
+ slot_context->card_presence_dpc = card_presence_dpc2;
+ slot_context->isolated_power_fault_dpc = isolated_power_fault_dpc2;
+ slot_context->connected_power_fault_dpc = connected_power_fault_dpc2;
+ break;
+ case 3:
+ slot_context->attn_button_dpc = attn_button_dpc3;
+ slot_context->mrl_sensor_dpc = mrl_sensor_dpc3;
+ slot_context->card_presence_dpc = card_presence_dpc3;
+ slot_context->isolated_power_fault_dpc = isolated_power_fault_dpc3;
+ slot_context->connected_power_fault_dpc = connected_power_fault_dpc3;
+ break;
+ }
+
+
+ //
+ // Slot Events
+ //
+ slot_context->slot_event_bits=0; // all slot events cleared
+
+ dbg("%s -->Init slot wait queues",__FUNCTION__ );
+
+ init_waitqueue_head(&slot_context->slot_event);
+ init_waitqueue_head(&slot_context->led_cmd_acquire_event);
+ init_waitqueue_head(&slot_context->led_cmd_release_event);
+ init_waitqueue_head(&slot_context->cmd_acquire_event);
+ init_waitqueue_head(&slot_context->cmd_release_event);
+ init_waitqueue_head(&slot_context->bus_acquire_event);
+ init_waitqueue_head(&slot_context->bus_release_event);
+ }
+ return STATUS_SUCCESS;
+}
+
+
+// ****************************************************************************
+//
+// hp_StartDevice()
+//
+// Parameters
+// shpc_context - Caller provided storage for SHPC context data.
+//
+// Return Value
+// Status returned by any system calls made within hp_StartDevice().
+//
+//
+// ****************************************************************************
+long
+hp_StartDevice(
+ struct shpc_context* shpc_context
+ )
+{
+ struct slot_context* slot_context;
+ long status = STATUS_SUCCESS;
+ u32 *logical_slot_addr;
+ u8 i;
+ int pid;
+
+ dbg("%s -->From hp_StartDevice: MmioBase[ %p ]",__FUNCTION__ , (unsigned long*)shpc_context->mmio_base_addr);
+
+ //
+ // Disable Global Interrupts
+ //
+ dbg("%s -->hp_disable_global_interrupts( shpc_context=%p );",__FUNCTION__ , shpc_context);
+ hp_disable_global_interrupts( shpc_context );
+
+ //
+ // Reset common resources
+ //
+ shpc_context->at_power_device_d0 = TRUE;
+ shpc_context->bus_released = FALSE;
+
+ //
+ // Reset slot resources
+ //
+ logical_slot_addr = shpc_context->mmio_base_addr + SHPC_LOGICAL_SLOT_REG_OFFSET;
+ for( i=0; i< SHPC_MAX_NUM_SLOTS; ++i ) {
+ slot_context = &shpc_context->slot_context[ i ];
+
+ //
+ // Assign Logical Slot Register Address
+ //
+ slot_context->logical_slot_addr = logical_slot_addr++;
+
+ //
+ // Disable Slot Interrupts
+ //
+ dbg("%s -->hp_disable_slot_interrupts(slot_context)=%p",__FUNCTION__ , slot_context);
+ hp_disable_slot_interrupts(slot_context);
+
+ //
+ // Reset slot flags and pointers
+ //
+ slot_context->slot_enabled = FALSE;
+ slot_context->in_bus_speed_mode_contention = FALSE;
+ slot_context->problem_detected = FALSE;
+ slot_context->slot_quiesced = FALSE;
+ slot_context->slot_thread = NULL;
+ slot_context->slot_function = NULL;
+ slot_context->attn_led_thread = NULL;
+ slot_context->attn_led_function = NULL;
+ slot_context->slot_occupied = 0;
+ }
+
+ //
+ // Get initial slot configuration: number_of_slots, slots_enabled, SlotStateFunction
+ //
+ shpc_context->slots_enabled = 0;
+ shpc_context->number_of_slots = 0;
+ hp_get_slot_configuration( shpc_context );
+ dbg("%s -->from hp_StartDevice() number_of_slots = %d", __FUNCTION__ ,shpc_context->number_of_slots);
+ if( shpc_context->number_of_slots == 0 ) {
+ status = STATUS_UNSUCCESSFUL;
+ }
+
+ //
+ // Hook Interrupt
+ //
+ dbg("%s -->HPC interrupt = %d \n", __FUNCTION__ ,shpc_context->interrupt);
+
+ if (request_irq(shpc_context->interrupt, hp_interrupt_service, SA_SHIRQ, MY_NAME, shpc_context)) {
+ err("Can't get irq %d for the PCI hotplug controller\n", shpc_context->interrupt);
+ status = STATUS_UNSUCCESSFUL;
+ return(status);
+ }
+
+ //
+ // Set slot operation in motion
+ //
+ for( i=0; i<shpc_context->number_of_slots && status; ++i ) {
+
+ slot_context = &shpc_context->slot_context[ i ];
+
+ //
+ // Launch slot command and bus completion mutex threads
+ //
+ // get led cmd available thread
+ pid = kernel_thread(hp_get_led_cmd_available_mutex_thread, slot_context, CLONE_SIGHAND);
+ if (pid < 0) {
+ err ("Can't start up our get_led_cmd_available_mutex thread\n");
+ status = STATUS_UNSUCCESSFUL;
+ }
+ dbg("%s -->Our hp_get_led_cmd_available_mutex thread pid = %d",__FUNCTION__ , pid);
+
+ // get cmd available thread
+ pid = kernel_thread(hp_get_cmd_available_mutex_thread, slot_context, CLONE_SIGHAND);
+ if (pid < 0) {
+ err ("Can't start up our get_cmd_available_mutex thread\n");
+ status = STATUS_UNSUCCESSFUL;
+ }
+ dbg("%s -->Our hp_get_cmd_available_mutex thread pid = %d",__FUNCTION__ , pid);
+
+ // get bus available thread
+ pid = kernel_thread(hp_get_bus_available_mutex_thread, slot_context, CLONE_SIGHAND);
+ if (pid < 0) {
+ err ("Can't start up our get_bus_available_mutex thread\n");
+ status = STATUS_UNSUCCESSFUL;
+ }
+ dbg("%s \n\n\n-->Our get_bus_available_mutex thread pid = %d",__FUNCTION__ , pid);
+
+ //
+ // Launch slot thread
+ //
+ pid = kernel_thread(hp_slot_thread, slot_context, CLONE_SIGHAND);
+ if (pid < 0) {
+ err ("Can't start up our event thread\n");
+ status = STATUS_UNSUCCESSFUL;
+ }
+ dbg("%s -->Our slot event thread pid = %d\n",__FUNCTION__ , pid);
+
+ //
+ // Launch Attention LED Thread
+ //
+ pid = kernel_thread(hp_attn_led_thread, slot_context, CLONE_SIGHAND);
+ if (pid < 0) {
+ err ("Can't start up our event thread\n");
+ status = STATUS_UNSUCCESSFUL;
+ }
+ dbg("%s -->Our LED event thread pid = %d\n",__FUNCTION__ , pid);
+
+ //
+ // Enable Slot Interrupts: Attn Button, MRL Sensor, Card Presence, Power-Fault
+ //
+ if(status) {
+ dbg("%s -->hpStartDevice() Enabling slot interrupts...",__FUNCTION__ );
+ hp_enable_slot_interrupts( slot_context );
+ }
+ }
+
+ //
+ // Enable Global Interrupts: Command Completion
+ //
+ if(status) {
+ dbg("%s -->hpStartDevice() Enabling global interrupts...",__FUNCTION__ );
+ hp_enable_global_interrupts( shpc_context );
+ } else {
+ //
+ // Bail out, we're hosed!
+ //
+ hp_StopDevice( shpc_context );
+ status = STATUS_UNSUCCESSFUL;
+ }
+ dbg("%s -->status = %d\n",__FUNCTION__ , (u32)status);
+
+ return status;
+}
+
+
+// ****************************************************************************
+//
+// hp_StopDevice()
+//
+// Parameters
+// shpc_context - Caller provided storage for SHPC context data.
+//
+// Return Value
+// Status returned by any system calls made within hp_StopDevice().
+//
+// Comments:
+// The caller is responsible for unmapping mmio_base_addr, via MmUnmapIoSpace(),
+// after calling hp_StopDevice() for resource re-balancing or device removal.
+//
+// ****************************************************************************
+long
+hp_StopDevice(
+ struct shpc_context* shpc_context
+ )
+{
+ struct slot_context* slot_context;
+ long status = STATUS_SUCCESS;
+ unsigned long old_irq_flags;
+ u8 i;
+
+ //
+ // Already stopped or never started ?
+ //
+ if( shpc_context->mmio_base_addr == 0 ) {
+ return STATUS_UNSUCCESSFUL;
+ }
+ //
+ // Disable Global Interrupts
+ //
+ hp_disable_global_interrupts( shpc_context );
+
+ //
+ // Signal EXIT request to slot threads
+ //
+
+ spin_lock_irqsave(&shpc_context->shpc_spinlock, old_irq_flags);
+ hp_clear_shpc_event_bit(shpc_context, SUSPEND_EVENT);
+ hp_send_event_to_all_slots(shpc_context,
+ RESUME_EVENT || REMOVE_EVENT || EXIT_REQUEST_EVENT);
+ spin_unlock_irqrestore( &shpc_context->shpc_spinlock, old_irq_flags );
+
+ for( i=0; i<SHPC_MAX_NUM_SLOTS; ++i ) {
+ slot_context = &shpc_context->slot_context[ i ];
+
+ //
+ // Disable Slot Interrupts
+ //
+ hp_disable_slot_interrupts( slot_context );
+
+ //
+ // Remove scheduled slot DPCs
+ //
+ tasklet_kill( &slot_context->attn_button_dpc );
+ tasklet_kill( &slot_context->card_presence_dpc );
+ tasklet_kill( &slot_context->isolated_power_fault_dpc );
+ tasklet_kill( &slot_context->connected_power_fault_dpc );
+
+ //
+ // Send events to kill all threads
+ //
+ //
+ // Set event bits to send to running threads
+ //
+ hp_set_shpc_event_bit(shpc_context,
+ (RESUME_EVENT | REMOVE_EVENT | EXIT_REQUEST_EVENT));
+
+
+
+ wake_up_interruptible(&slot_context->led_cmd_acquire_event);
+ wake_up_interruptible(&slot_context->cmd_acquire_event);
+ wake_up_interruptible(&slot_context->bus_acquire_event);
+ wake_up_interruptible(&slot_context->led_cmd_release_event);
+ wake_up_interruptible(&slot_context->cmd_release_event);
+ wake_up_interruptible(&slot_context->bus_release_event);
+ //
+ // Reset slot pointers and flags
+ //
+ slot_context->slot_enabled = FALSE;
+ slot_context->slot_thread = NULL;
+ slot_context->slot_function = NULL;
+ slot_context->attn_led_thread = NULL;
+ slot_context->attn_led_function = NULL;
+ }
+
+ //
+ // Remove scheduled common DPC
+ //
+ tasklet_kill(&shpc_context->cmd_completion_dpc );
+
+ //
+ // Reset common resources
+ //
+ shpc_context->number_of_slots = 0;
+ shpc_context->slots_enabled = 0;
+ shpc_context->at_power_device_d0 = FALSE;
+
+ return status;
+}
+
+
+// ****************************************************************************
+//
+// hp_SuspendDevice()
+//
+// Parameters
+// shpc_context - Caller provided storage for SHPC context data.
+//
+// Return Value
+// Status returned by any system calls made within hp_SuspendDevice().
+//
+// Comments:
+// hp_SuspendDevice() must be called before transitioning away from PowerDeviceD0.
+//
+// ****************************************************************************
+long
+hp_SuspendDevice(
+ struct shpc_context* shpc_context
+ )
+{
+ long status = STATUS_SUCCESS;
+ unsigned long old_irq_flags;
+
+ dbg("%s -->HwInstance[ %d ]", __FUNCTION__ ,shpc_context->shpc_instance );
+
+ spin_lock_irqsave( &shpc_context->shpc_spinlock, old_irq_flags );
+
+ if(shpc_context->mmio_base_addr &&
+ (!shpc_context->shpc_event_bits & SUSPEND_EVENT) &&
+ (!shpc_context->shpc_event_bits & REMOVE_EVENT)) {
+ hp_clear_shpc_event_bit(shpc_context, RESUME_EVENT);
+
+ hp_send_event_to_all_slots(shpc_context, SUSPEND_EVENT);
+ hp_send_event_to_all_slots(shpc_context, EXIT_REQUEST_EVENT);
+
+ shpc_context->at_power_device_d0 = FALSE;
+ }
+ spin_unlock_irqrestore( &shpc_context->shpc_spinlock, old_irq_flags );
+
+ return status;
+}
+
+
+// ****************************************************************************
+//
+// hp_ResumeDevice()
+//
+// Parameters
+// shpc_context - Caller provided storage for SHPC context data.
+//
+// Return Value
+// Status returned by any system calls made within hp_ResumeDevice().
+//
+// Comments:
+// hp_SuspendDevice() must be called after transitioning back to PowerDeviceD0.
+//
+// ****************************************************************************
+long
+hp_ResumeDevice(
+ struct shpc_context* shpc_context
+ )
+{
+ long status = STATUS_SUCCESS;
+ unsigned long old_irq_flags;
+
+ dbg("%s -->HwInstance[ %d ]", __FUNCTION__ ,shpc_context->shpc_instance );
+
+ spin_lock_irqsave( &shpc_context->shpc_spinlock, old_irq_flags );
+ if(shpc_context->mmio_base_addr &&
+ (shpc_context->shpc_event_bits & SUSPEND_EVENT) &&
+ (!shpc_context->shpc_event_bits & REMOVE_EVENT)) {
+ hp_clear_shpc_event_bit(shpc_context, SUSPEND_EVENT);
+ hp_clear_shpc_event_bit(shpc_context, EXIT_REQUEST_EVENT);
+ hp_send_event_to_all_slots(shpc_context, RESUME_EVENT);
+ shpc_context->at_power_device_d0 = TRUE;
+ }
+
+ spin_unlock_irqrestore( &shpc_context->shpc_spinlock, old_irq_flags );
+
+ return status;
+}
+
+
+// ****************************************************************************
+//
+// hp_QuerySlots()
+//
+// Parameters
+// shpc_context - Caller provided storage for SHPC context data.
+// slot_config - Caller provided storage for slots configuration info.
+//
+// Return Value
+// Status returned by any system calls made within hp_QuerySlots().
+//
+// ****************************************************************************
+long
+hp_QuerySlots(
+ struct shpc_context* shpc_context,
+ union SLOT_CONFIG_INFO* slot_config
+ )
+{
+ long status = STATUS_SUCCESS;
+ union SHPC_SLOT_CONFIG_DWREG slot_config_reg;
+
+ dbg("%s -->HwInstance[ %d ] Slots[ %d ]",__FUNCTION__ ,
+ shpc_context->shpc_instance, shpc_context->number_of_slots );
+
+ //
+ // Get slot configuration
+ //
+ slot_config_reg.AsDWord = readl(shpc_context->mmio_base_addr + SHPC_SLOT_CONFIG_REG_OFFSET);
+
+ slot_config->AsDWord = 0;
+ slot_config->x.lu_slots_implemented = slot_config_reg.x.NSI;
+ slot_config->x.lu_base_PSN = slot_config_reg.x.PSN;
+ slot_config->x.lu_PSN_up = slot_config_reg.x.PSN_UP;
+ slot_config->x.lu_base_FDN = slot_config_reg.x.FDN;
+
+ return status;
+}
+
+
+// ****************************************************************************
+//
+// hp_QuerySlotStatus()
+//
+// Parameters
+// shpc_context - Caller provided storage for SHPC context data.
+// slot_id - Zero-based slot number (0..n-1).
+// Query - Pointer to Slot Status Structure
+//
+// Return Value
+// Status returned by any system calls made within hp_QuerySlotStatus().
+//
+// ****************************************************************************
+long
+hp_QuerySlotStatus(
+ struct shpc_context* shpc_context,
+ u8 slot_id,
+ union SLOT_STATUS_INFO* Query
+ )
+{
+ struct slot_context* slot_context;
+ long status = STATUS_SUCCESS;
+ union SHPC_LOGICAL_SLOT_DWREG logical_slot_reg;
+
+ dbg("%s -->slot_id[ %d:%d ]",__FUNCTION__ , shpc_context->shpc_instance, slot_id );
+
+ //
+ // Valid slot_id?
+ //
+ if( slot_id >= shpc_context->number_of_slots ) {
+ status = STATUS_UNSUCCESSFUL;
+ }
+ else {
+ //
+ // Which slot?
+ //
+ slot_context = &shpc_context->slot_context[ slot_id ];
+
+ //
+ // Get Max Speed/Mode from common context
+ //
+ Query->x.lu_max_bus_mode_freq = hp_translate_speed_mode( shpc_context->max_speed_mode );
+
+ //
+ // Get Bus Speed/Mode from HW
+ //
+ Query->x.lu_bus_mode_freq = hp_translate_speed_mode( hp_get_bus_speed_mode( shpc_context ));
+
+ //
+ // Get Card Speed/Mode from HW
+ //
+ Query->x.lu_card_mode_freq_cap = hp_translate_speed_mode( hp_get_card_speed_mode( slot_context ));
+
+ //
+ // Get current slot info from HW
+ //
+ logical_slot_reg.AsDWord = readl( slot_context->logical_slot_addr );
+
+ //
+ // Card Present?
+ //
+ Query->x.lu_card_present = ( logical_slot_reg.x.PRSNT1_2 != SHPC_SLOT_EMPTY ) ?
+ HP_TRUE : HP_FALSE;
+
+ //
+ // Get Card PCI-66 capability
+ //
+ Query->x.lu_card_pci66_capable = (( logical_slot_reg.x.PRSNT1_2 != SHPC_SLOT_EMPTY ) &&
+ ( logical_slot_reg.x.S_STATE == SHPC_POWER_ONLY || logical_slot_reg.x.S_STATE == SHPC_ENABLE_SLOT ) &&
+ ( logical_slot_reg.x.M66_CAP == SHPC_STATUS_SET )) ?
+ HP_TRUE : HP_FALSE;
+
+ //
+ // Power-Fault?
+ //
+ Query->x.lu_power_fault = ( logical_slot_reg.x.PF == SHPC_STATUS_SET ) ?
+ HP_TRUE : HP_FALSE;
+
+ //
+ // Card Power Requirements
+ //
+ Query->x.lu_card_power = hp_translate_card_power( logical_slot_reg.x.PRSNT1_2 );
+
+ //
+ // Attention Indicator
+ //
+ Query->x.lu_ai_state = hp_translate_indicator( logical_slot_reg.x.AIS );
+
+ //
+ // Power Indicator
+ //
+ Query->x.lu_pi_state = hp_translate_indicator( logical_slot_reg.x.PIS );
+
+ //
+ // MRL Implemented?
+ //
+ Query->x.lu_mrl_implemented = ( logical_slot_reg.x.MRLS_IM == SHPC_UNMASKED ) ?
+ HP_TRUE : HP_FALSE;
+
+ //
+ // MRL Opened?
+ //
+ Query->x.lu_mrl_opened = (( logical_slot_reg.x.MRLS == SHPC_MRL_OPEN ) &&
+ ( logical_slot_reg.x.MRLS_IM == SHPC_UNMASKED )) ? HP_TRUE : HP_FALSE;
+
+ //
+ // Slot State: Card Present, MRL closed, No Power-Fault, Enabled?
+ //
+ if( logical_slot_reg.x.PRSNT1_2 != SHPC_SLOT_EMPTY &&
+ ( logical_slot_reg.x.MRLS_IM == SHPC_MASKED ||
+ logical_slot_reg.x.MRLS == SHPC_MRL_CLOSED ) &&
+ logical_slot_reg.x.PF == SHPC_STATUS_CLEARED &&
+ logical_slot_reg.x.S_STATE == SHPC_ENABLE_SLOT ) {
+ Query->x.lu_slot_state = SLOT_ENABLE;
+ }
+ else {
+ Query->x.lu_slot_state = SLOT_DISABLE;
+ }
+
+ //
+ // OK, it's all there!
+ //
+ Query->x.lu_reserved1 = 0;
+ Query->x.lu_reserved2 = 0;
+ Query->x.lu_request_failed = HP_FALSE;
+ }
+
+ return status;
+}
+
+
+// ****************************************************************************
+//
+// hp_StartAsyncRequest()
+//
+// Parameters
+// shpc_context - Caller provided storage for SHPC context data.
+// slot_id - Zero-based slot number (0..n-1).
+// Request - Async request: Slot "Enable/Disable", AttnLED "Attn/Normal").
+// timeout - For AttnLED "Attn" requests (in seconds)
+// request_context - Caller provided pointer to be returned upon completion.
+//
+// Return Value
+// STATUS_SUCCESS if the request is accepted. The Callback() is later invoked with a completion status.
+// STATUS_UNSUCCESSFUL if the request is rejected (invalid parameters, or similar request in progress),
+//
+// Comment:
+// For AttnLED "Attn" requests, the completion Callback() function is invoked as soon as the hardware
+// completes (Blink) execution. When the timeout period expires, the AttnLED is brought back to
+// its "Normal" (On/Off) state, and the Callback() is invoked once again.
+//
+// ****************************************************************************
+long
+hp_StartAsyncRequest(
+ struct shpc_context* shpc_context,
+ u8 slot_id,
+ enum shpc_async_request request,
+ u32 timeout,
+ void* request_context
+ )
+{
+ unsigned long old_irq_flags;
+ struct slot_context* slot_context;
+ long status = STATUS_SUCCESS;
+
+ dbg("%s -->slot_id[ %d:%d ] Request[ %d ]",__FUNCTION__ ,
+ shpc_context->shpc_instance, slot_id, request );
+
+ //
+ // Valid slot_id?
+ //
+ if( slot_id >= shpc_context->number_of_slots ) {
+ status = STATUS_UNSUCCESSFUL;
+ }
+ else {
+ slot_context = &shpc_context->slot_context[ slot_id ];
+
+ switch( request ) {
+ case SHPC_ASYNC_ENABLE_SLOT:
+ dbg("%s SHPC_ASYNC_ENABLE_SLOT",__FUNCTION__);
+ case SHPC_ASYNC_DISABLE_SLOT:
+ dbg("%s SHPC_ASYNC_DISABLE_SLOT",__FUNCTION__);
+ //
+ // Slot Request Pending?
+ //
+ spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags );
+ down_interruptible(&slot_context->slot_event_bits_semaphore);
+ down_interruptible(&shpc_context->shpc_event_bits_semaphore);
+ if((slot_context->slot_event_bits & SLOT_REQUEST_EVENT) ||
+ (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT)) {
+ status = STATUS_UNSUCCESSFUL;
+ up(&slot_context->slot_event_bits_semaphore);
+ up(&shpc_context->shpc_event_bits_semaphore);
+ }
+ else {
+ up(&slot_context->slot_event_bits_semaphore);
+ up(&shpc_context->shpc_event_bits_semaphore);
+ slot_context->slot_request.type = request;
+ slot_context->slot_request.request_context = request_context;
+ hp_send_slot_event(slot_context, SLOT_REQUEST_EVENT);
+ }
+ spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags );
+ break;
+
+ case SHPC_ASYNC_LED_LOCATE:
+ dbg("%s SHPC_ASYNC_LED_LOCATE",__FUNCTION__);
+ case SHPC_ASYNC_LED_NORMAL:
+ dbg("%s SHPC_ASYNC_LED_NORMAL",__FUNCTION__);
+ //
+ // AttnLED Request Pending?
+ //
+ spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags );
+ down_interruptible(&slot_context->slot_event_bits_semaphore);
+ down_interruptible(&shpc_context->shpc_event_bits_semaphore);
+ if((slot_context->slot_event_bits & ATTN_LED_REQUEST_EVENT) ||
+ (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT)) {
+ dbg("%s LED--STATUS_UNSUCCESSFUL slot_event_bits = %08X", __FUNCTION__ ,slot_context->slot_event_bits);
+ status = STATUS_UNSUCCESSFUL;
+ up(&slot_context->slot_event_bits_semaphore);
+ up(&shpc_context->shpc_event_bits_semaphore);
+ }
+ else {
+ up(&slot_context->slot_event_bits_semaphore);
+ up(&shpc_context->shpc_event_bits_semaphore);
+ slot_context->attn_led_request.type = request;
+ slot_context->attn_led_request.timeout = timeout;
+ slot_context->attn_led_request.request_context = request_context;
+ hp_send_slot_event(slot_context, ATTN_LED_REQUEST_EVENT);
+ }
+ spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags );
+ break;
+
+ case SHPC_ASYNC_QUIESCE_DEVNODE_NOTIFY:
+ dbg("%s SHPC_ASYNC_QUIESCE_DEVNODE_NOTIFY",__FUNCTION__);
+ //
+ // HP library notification: DevNode is quiesced
+ //
+ spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags );
+ ++slot_context->quiesce_replies;
+ if( slot_context->quiesce_requests &&
+ slot_context->quiesce_replies >= slot_context->quiesce_requests ) {
+ slot_context->slot_quiesced = TRUE;
+ hp_send_slot_event(slot_context, QUIESCE_EVENT);
+ }
+ spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags );
+ break;
+
+ case SHPC_ASYNC_CANCEL_QUIESCE_DEVNODE:
+ dbg("%s SHPC_ASYNC_CANCEL_QUIESCE_DEVNODE",__FUNCTION__);
+ //
+ // HP library notification: could not quiesce DevNode
+ //
+ spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags );
+ slot_context->slot_quiesced = FALSE;
+ hp_send_slot_event(slot_context, QUIESCE_EVENT);
+ spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags );
+
+ //
+ // Abort bus-rebalancing
+ //
+ spin_lock_irqsave( &shpc_context->shpc_spinlock, old_irq_flags );
+ shpc_context->bus_released = FALSE;
+ hp_send_event_to_all_slots(shpc_context, BUS_COMPLETE_EVENT);
+ spin_unlock_irqrestore( &shpc_context->shpc_spinlock, old_irq_flags );
+ break;
+
+ default:
+ status = STATUS_UNSUCCESSFUL;
+ break;
+ }
+ }
+
+ return status;
+}
+
+// ****************************************************************************
+//
+// hp_Queryslot_psn()
+//
+// Parameters
+// shpc_context - Caller provided storage for SHPC context data.
+// SlotID - Zero-based slot number (0..n-1).
+// slot_psn - Pointer to Physical Slot Number
+//
+// Return Value
+// STATUS_SUCCESS, or STATUS_UNSUCCESSFUL for invalid SlotID.
+//
+// ****************************************************************************
+long hp_Queryslot_psn(struct shpc_context *shpc_context, unsigned char slot_ID, unsigned long *slot_psn)
+{
+ struct slot_context *slot_context;
+ long status = STATUS_SUCCESS;
+ dbg("%s slot_ID[ %d:%d ]",__FUNCTION__ , shpc_context->shpc_instance, slot_ID);
+ //
+ // Valid SlotID?
+ //
+ if( slot_ID >= shpc_context->number_of_slots || slot_psn == NULL ) {
+ status = STATUS_UNSUCCESSFUL;
+ }
+ else {
+ //
+ // Which slot?
+ //
+ slot_context = &shpc_context->slot_context[ slot_ID ];
+ //
+ // Get slot PSN
+ //
+ *slot_psn = slot_context->slot_psn;
+ }
+ return status;
+}
+
+// ****************************************************************************
+//
+// hp_slot_timers1-10func(): Function passed to timer to send event
+//
+// Parameters
+// slot_context - Caller provided storage for SHPC context data.
+//
+// Return Value
+// void
+//
+// ****************************************************************************
+void hp_slot_timer1_func(unsigned long data){
+
+ struct slot_context *slot_context;
+ slot_context = (struct slot_context*) data;
+
+ dbg("%s", __FUNCTION__);
+ hp_set_slot_event_bit(slot_context, SLOT_TIMER1_EVENT);
+
+ wake_up_interruptible(&slot_context->slot_event);
+}
+
+void hp_slot_timer2_func(unsigned long data){
+
+ struct slot_context *slot_context;
+ slot_context = (struct slot_context*) data;
+
+ dbg("%s", __FUNCTION__);
+ hp_set_slot_event_bit(slot_context, SLOT_TIMER2_EVENT);
+
+ wake_up_interruptible(&slot_context->slot_event);
+}
+
+void hp_slot_timer3_func(unsigned long data){
+
+ struct slot_context *slot_context;
+ slot_context = (struct slot_context*) data;
+
+ dbg("%s", __FUNCTION__);
+ hp_set_slot_event_bit(slot_context, SLOT_TIMER3_EVENT);
+
+ wake_up_interruptible(&slot_context->slot_event);
+}
+
+void hp_slot_timer4_func(unsigned long data){
+
+ struct slot_context *slot_context;
+ slot_context = (struct slot_context*) data;
+
+ dbg("%s", __FUNCTION__);
+ hp_set_slot_event_bit(slot_context, SLOT_TIMER4_EVENT);
+
+ wake_up_interruptible(&slot_context->slot_event);
+}
+
+void hp_slot_timer5_func(unsigned long data){
+
+ struct slot_context *slot_context;
+ slot_context = (struct slot_context*) data;
+
+ dbg("%s", __FUNCTION__);
+ hp_set_slot_event_bit(slot_context, SLOT_TIMER5_EVENT);
+
+ wake_up_interruptible(&slot_context->slot_event);
+}
+
+void hp_slot_timer6_func(unsigned long data){
+
+ struct slot_context *slot_context;
+ slot_context = (struct slot_context*) data;
+
+ dbg("%s", __FUNCTION__);
+ hp_set_slot_event_bit(slot_context, SLOT_TIMER6_EVENT);
+
+ wake_up_interruptible(&slot_context->slot_event);
+}
+
+void hp_slot_timer7_func(unsigned long data){
+
+ struct slot_context *slot_context;
+ slot_context = (struct slot_context*) data;
+
+ dbg("%s", __FUNCTION__);
+ hp_set_slot_event_bit(slot_context, SLOT_TIMER7_EVENT);
+
+ wake_up_interruptible(&slot_context->slot_event);
+}
+
+void hp_slot_timer8_func(unsigned long data){
+
+ struct slot_context *slot_context;
+ slot_context = (struct slot_context*) data;
+
+ dbg("%s", __FUNCTION__);
+ hp_set_slot_event_bit(slot_context, SLOT_TIMER8_EVENT);
+
+ wake_up_interruptible(&slot_context->slot_event);
+}
+
+void hp_slot_timer9_func(unsigned long data){
+
+ struct slot_context *slot_context;
+ slot_context = (struct slot_context*) data;
+
+ dbg("%s", __FUNCTION__);
+ hp_set_slot_event_bit(slot_context, SLOT_TIMER9_EVENT);
+
+ wake_up_interruptible(&slot_context->slot_event);
+}
+
+void hp_slot_timer10_func(unsigned long data){
+
+ struct slot_context *slot_context;
+ slot_context = (struct slot_context*) data;
+
+ dbg("%s", __FUNCTION__);
+ hp_set_slot_event_bit(slot_context, SLOT_TIMER10_EVENT);
+
+ wake_up_interruptible(&slot_context->slot_event);
+}
+
+// ****************************************************************************
+//
+// hp_led_timers1-4_func(): Function passed to timer to send event
+//
+// Parameters
+// slot_context - Caller provided storage for SHPC context data.
+//
+// Return Value
+// void
+//
+// ****************************************************************************
+void hp_led_timer1_func(unsigned long data){
+
+ struct slot_context *slot_context;
+ slot_context = (struct slot_context*) data;
+
+ dbg("%s", __FUNCTION__);
+ hp_set_slot_event_bit(slot_context, LED_TIMER1_EVENT);
+
+ wake_up_interruptible(&slot_context->slot_event);
+}
+
+void hp_led_timer2_func(unsigned long data){
+
+ struct slot_context *slot_context;
+ slot_context = (struct slot_context*) data;
+
+ dbg("%s", __FUNCTION__);
+ hp_set_slot_event_bit(slot_context, LED_TIMER2_EVENT);
+
+ wake_up_interruptible(&slot_context->slot_event);
+}
+
+void hp_led_timer3_func(unsigned long data){
+
+ struct slot_context *slot_context;
+ slot_context = (struct slot_context*) data;
+
+ dbg("%s", __FUNCTION__);
+ hp_set_slot_event_bit(slot_context, LED_TIMER3_EVENT);
+
+ wake_up_interruptible(&slot_context->slot_event);
+}
+
+void hp_led_timer4_func(unsigned long data){
+
+ struct slot_context *slot_context;
+ slot_context = (struct slot_context*) data;
+
+ dbg("%s", __FUNCTION__);
+ hp_set_slot_event_bit(slot_context, LED_TIMER4_EVENT);
+
+ wake_up_interruptible(&slot_context->slot_event);
+}
+
+// ****************************************************************************
+//
+// hp_clear_slot_event_bit():
+//
+// Parameters
+// slot_context - Caller provided storage for SHPC context data.
+//
+// Return Value
+// void
+//
+// ****************************************************************************
+void hp_clear_slot_event_bit(struct slot_context * slot_context, u32 mask)
+{
+// dbg("%s -->slot bits %08X MASK=%08X",__FUNCTION__ ,slot_context->slot_event_bits, mask);
+
+ down_interruptible(&slot_context->slot_event_bits_semaphore);
+ // cleareventbit
+ slot_context->slot_event_bits &= ~mask;
+ up(&slot_context->slot_event_bits_semaphore);
+}
+
+// ****************************************************************************
+//
+// hp_set_slot_event_bit():
+//
+// Parameters
+// slot_context - Caller provided storage for SHPC context data.
+//
+// Return Value
+// void
+//
+// ****************************************************************************
+void hp_set_slot_event_bit(struct slot_context * slot_context, u32 mask)
+{
+// dbg("%s -->slot bits %08X MASK=%08X",__FUNCTION__ ,slot_context->slot_event_bits, mask);
+
+ down_interruptible(&slot_context->slot_event_bits_semaphore);
+ // cleareventbit
+ slot_context->slot_event_bits |= mask;
+ up(&slot_context->slot_event_bits_semaphore);
+}
+
+// ****************************************************************************
+//
+// hp_clear_shpc_event_bit():
+//
+// Parameters
+// slot_context - Caller provided storage for SHPC context data.
+//
+// Return Value
+// void
+//
+// ****************************************************************************
+void hp_clear_shpc_event_bit(struct shpc_context * shpc_context, u32 mask)
+{
+ down_interruptible(&shpc_context->shpc_event_bits_semaphore);
+ // cleareventbit
+ shpc_context->shpc_event_bits &= ~mask;
+ up(&shpc_context->shpc_event_bits_semaphore);
+}
+
+// ****************************************************************************
+//
+// hp_set_shpc_event_bit():
+//
+// Parameters
+// slot_context - Caller provided storage for SHPC context data.
+//
+// Return Value
+// void
+//
+// ****************************************************************************
+void hp_set_shpc_event_bit(struct shpc_context * shpc_context, u32 mask)
+{
+ down_interruptible(&shpc_context->shpc_event_bits_semaphore);
+ // set event bit
+ shpc_context->shpc_event_bits |= mask;
+ up(&shpc_context->shpc_event_bits_semaphore);
+}
+
+// ****************************************************************************
+//
+// hp_send_event_to_all_slots():
+//
+// Parameters
+// slot_context - Caller provided storage for SHPC context data.
+//
+// Return Value
+// void
+//
+// ****************************************************************************
+void hp_send_event_to_all_slots(struct shpc_context *shpc_context, u32 mask)
+{
+ u8 i;
+ struct slot_context * slot_context;
+
+ down_interruptible(&shpc_context->shpc_event_bits_semaphore);
+ // set event bit
+ shpc_context->shpc_event_bits |= mask;
+ // send event to each slot thread
+ for( i=0; i<shpc_context->number_of_slots; ++i ) {
+ slot_context = &shpc_context->slot_context[ i ];
+ wake_up_interruptible(&slot_context->slot_event);
+ }
+ up(&shpc_context->shpc_event_bits_semaphore);
+}
+
+// ****************************************************************************
+//
+// hp_send_slot_event():
+//
+// Parameters
+// slot_context - Caller provided storage for SHPC context data.
+//
+// Return Value
+// void
+//
+// ****************************************************************************
+void hp_send_slot_event(struct slot_context * slot_context, u32 mask)
+{
+ // set event bit
+ hp_set_slot_event_bit(slot_context, mask);
+ wake_up_interruptible( &slot_context->slot_event);
+}
+
+
+// ****************************************************************************
+//
+// hp_get_led_cmd_available_mutex_thread(): run as a thread per each slot
+//
+// Parameters
+// slot_context - Caller provided storage for slot context data.
+//
+// Return Value
+// void
+//
+// ****************************************************************************
+int hp_get_led_cmd_available_mutex_thread(void *ptr)
+{
+ long status = STATUS_SUCCESS;
+ struct shpc_context* shpc_context;
+ struct slot_context* slot_context;
+ int pid;
+
+ lock_kernel ();
+ daemonize ("amdshpc_getledcmd_av_mutex");
+ unlock_kernel ();
+
+ slot_context = (struct slot_context* ) ptr;
+ shpc_context = (struct shpc_context* ) slot_context->shpc_context;
+ do {
+ interruptible_sleep_on(&slot_context->led_cmd_acquire_event);
+ if (slot_context->slot_event_bits & LED_CMD_ACQUIRE_EVENT){
+ hp_clear_slot_event_bit(slot_context, LED_CMD_ACQUIRE_EVENT);
+ pid = kernel_thread(hp_led_cmd_available_mutex_thread, slot_context, CLONE_SIGHAND);
+ if (pid < 0) {
+ err ("Can't start up our hp_led_cmd_available_mutex_thread\n");
+ status = STATUS_UNSUCCESSFUL;
+ break;
+ }
+ } else {
+ dbg("%s terminating return 0 slot_id[ %d:%d ]",__FUNCTION__,
+ shpc_context->shpc_instance, slot_context->slot_number-1);
+ return 0;
+ }
+ } while (1);
+ return(status);
+}
+
+// ****************************************************************************
+//
+// hp_led_cmd_available_mutex_thread(): run as a thread per each request for cmd
+//
+// Parameters
+// slot_context - Caller provided storage for SHPC context data.
+//
+// Return Value
+// void
+//
+// ****************************************************************************
+int hp_led_cmd_available_mutex_thread(void *ptr)
+{
+ struct shpc_context* shpc_context;
+ struct slot_context* slot_context;
+
+ lock_kernel ();
+ daemonize ("amdshpc_ledcmd_av_mutex");
+ unlock_kernel ();
+
+ slot_context = (struct slot_context* ) ptr;
+ shpc_context = (struct shpc_context* ) slot_context->shpc_context;
+
+ //
+ // acquire the main mutex for all slots exclusion
+ //
+ down_interruptible(&shpc_context->cmd_available_mutex);
+ if ((shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT)||
+ (shpc_context->shpc_event_bits & RESUME_EVENT)||
+ (shpc_context->shpc_event_bits & REMOVE_EVENT)){
+ dbg("%s return 0 slot_id[ %d:%d ]",__FUNCTION__,
+ shpc_context->shpc_instance, slot_context->slot_number-1);
+ up(&shpc_context->cmd_available_mutex);
+ return 0;
+ }
+
+ //
+ // now tell our slot thread that it has the mutex
+ //
+ hp_set_shpc_event_bit(shpc_context, LED_CMD_AVAILABLE_MUTEX_EVENT);
+ wake_up_interruptible(&slot_context->slot_event);
+
+ //
+ // wait for our slot thread to release the mutex
+ //
+ interruptible_sleep_on(&slot_context->led_cmd_release_event);
+ if ((shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT)||
+ (shpc_context->shpc_event_bits & RESUME_EVENT)||
+ (shpc_context->shpc_event_bits & REMOVE_EVENT)){
+ dbg("%s return 0 slot_id[ %d:%d ]",__FUNCTION__,
+ shpc_context->shpc_instance, slot_context->slot_number-1);
+ up(&shpc_context->cmd_available_mutex);
+ return 0;
+ }
+ hp_clear_slot_event_bit(slot_context, LED_CMD_RELEASE_EVENT);
+
+ hp_clear_shpc_event_bit(shpc_context, LED_CMD_AVAILABLE_MUTEX_EVENT);
+ up(&shpc_context->cmd_available_mutex);
+ dbg("%s cmd_available_mutex RELEASED",__FUNCTION__);
+ return(0);
+}
+
+// ****************************************************************************
+//
+// hp_get_cmd_available_mutex_thread(): run as a thread per each slot
+//
+// Parameters
+// slot_context - Caller provided storage for slot context data.
+//
+// Return Value
+// void
+//
+// ****************************************************************************
+int hp_get_cmd_available_mutex_thread(void *ptr)
+{
+ long status = STATUS_SUCCESS;
+ struct shpc_context* shpc_context;
+ struct slot_context* slot_context;
+ int pid;
+
+ lock_kernel ();
+ daemonize ("amdshpc_getcmd_av_mutex");
+ unlock_kernel ();
+
+ slot_context = (struct slot_context* ) ptr;
+ shpc_context = (struct shpc_context* ) slot_context->shpc_context;
+
+ do {
+ interruptible_sleep_on(&slot_context->cmd_acquire_event);
+ if ((slot_context->slot_event_bits & CMD_ACQUIRE_EVENT) ||
+ (slot_context->slot_event_bits & CMD_RELEASE_EVENT)){
+ hp_clear_slot_event_bit(slot_context,CMD_ACQUIRE_EVENT);
+ pid = kernel_thread(hp_cmd_available_mutex_thread, slot_context, CLONE_SIGHAND);
+ if (pid < 0) {
+ err ("Can't start up our hp_get_cmd_available_mutex_thread\n");
+ status = STATUS_UNSUCCESSFUL;
+ break;
+ }
+ } else {
+ dbg("%s terminating return 0 slot_id[ %d:%d ]",__FUNCTION__,
+ shpc_context->shpc_instance, slot_context->slot_number-1);
+ return 0;
+ }
+ } while (1);
+ return(status);
+}
+
+// ****************************************************************************
+//
+// hp_cmd_available_mutex_thread(): run as a thread per each request for cmd
+//
+// Parameters
+// slot_context - Caller provided storage for SHPC context data.
+//
+// Return Value
+// void
+//
+// ****************************************************************************
+int hp_cmd_available_mutex_thread(void *ptr)
+{
+ struct shpc_context* shpc_context;
+ struct slot_context* slot_context;
+
+ lock_kernel ();
+ daemonize ("amdshpc_cmd_av_mutex");
+ unlock_kernel ();
+
+ slot_context = (struct slot_context* ) ptr;
+ shpc_context = (struct shpc_context* ) slot_context->shpc_context;
+
+ //
+ // acquire the main mutex for all slots exclusion
+ //
+ down_interruptible(&shpc_context->cmd_available_mutex);
+ if ((shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT)||
+ (shpc_context->shpc_event_bits & RESUME_EVENT)||
+ (shpc_context->shpc_event_bits & REMOVE_EVENT)){
+ dbg("%s return 0 slot_id[ %d:%d ]",__FUNCTION__,
+ shpc_context->shpc_instance, slot_context->slot_number-1);
+ up(&shpc_context->cmd_available_mutex);
+ return 0;
+ }
+
+ //
+ // now tell our slot thread that it has the mutex
+ //
+ hp_set_shpc_event_bit(shpc_context, CMD_AVAILABLE_MUTEX_EVENT);
+ wake_up_interruptible(&slot_context->slot_event);
+
+ //
+ // wait for our slot thread to release the mutex
+ //
+ interruptible_sleep_on(&slot_context->cmd_release_event);
+ if ((shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT)||
+ (shpc_context->shpc_event_bits & RESUME_EVENT)||
+ (shpc_context->shpc_event_bits & REMOVE_EVENT)){
+ dbg("%s return 0 slot_id[ %d:%d ]",__FUNCTION__,
+ shpc_context->shpc_instance, slot_context->slot_number-1);
+ up(&shpc_context->cmd_available_mutex);
+ return 0;
+ }
+ hp_clear_slot_event_bit(slot_context,CMD_RELEASE_EVENT);
+ hp_clear_shpc_event_bit(shpc_context, CMD_AVAILABLE_MUTEX_EVENT);
+ up(&shpc_context->cmd_available_mutex);
+ return(0);
+}
+
+// ****************************************************************************
+//
+// hp_get_bus_available_mutex_thread(): run as a thread per each slot
+//
+// Parameters
+// slot_context - Caller provided storage for slot context data.
+//
+// Return Value
+// void
+//
+// ****************************************************************************
+int hp_get_bus_available_mutex_thread(void *ptr)
+{
+ long status = STATUS_SUCCESS;
+ struct shpc_context* shpc_context;
+ struct slot_context* slot_context;
+ int pid;
+
+ lock_kernel ();
+ daemonize ("amdshpc_getbus_av_mutex");
+ unlock_kernel ();
+
+ slot_context = (struct slot_context* ) ptr;
+ shpc_context = (struct shpc_context* ) slot_context->shpc_context;
+
+ do {
+ interruptible_sleep_on(&slot_context->bus_acquire_event);
+ if (slot_context->slot_event_bits & BUS_ACQUIRE_EVENT) {
+ hp_clear_slot_event_bit(slot_context, BUS_ACQUIRE_EVENT);
+ pid = kernel_thread(hp_bus_available_mutex_thread, slot_context, CLONE_SIGHAND);
+ if (pid < 0) {
+ err ("Can't start up our hp_get_bus_available_mutex_thread\n");
+ status = STATUS_UNSUCCESSFUL;
+ break;
+ }
+ } else {
+ dbg("%s terminating return 0 slot_id[ %d:%d ]",__FUNCTION__,
+ shpc_context->shpc_instance, slot_context->slot_number-1);
+ return 0;
+ }
+ } while (1);
+ return(status);
+}
+
+// ****************************************************************************
+//
+// hp_bus_available_mutex_thread():
+//
+// Parameters
+// slot_context - Caller provided storage for SHPC context data.
+//
+// Return Value
+// void
+//
+// ****************************************************************************
+int hp_bus_available_mutex_thread(void *ptr)
+{
+ struct shpc_context* shpc_context;
+ struct slot_context* slot_context;
+
+ lock_kernel ();
+ daemonize ("amdshpc_bus_av_mutex");
+ // New name
+ unlock_kernel ();
+
+ slot_context = (struct slot_context* ) ptr;
+ shpc_context = (struct shpc_context* ) slot_context->shpc_context;
+
+ //
+ // acquire the main mutex for all slots exclusion
+ //
+ down_interruptible(&shpc_context->bus_available_mutex);
+ if ((shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT)||
+ (shpc_context->shpc_event_bits & RESUME_EVENT)||
+ (shpc_context->shpc_event_bits & REMOVE_EVENT)){
+ dbg("%s return 0 slot_id[ %d:%d ]",__FUNCTION__,
+ shpc_context->shpc_instance, slot_context->slot_number-1);
+ up(&shpc_context->bus_available_mutex);
+ return 0;
+ }
+
+ //
+ // now tell our slot thread that it has the mutex
+ //
+ hp_set_shpc_event_bit(shpc_context, BUS_AVAILABLE_MUTEX_EVENT);
+ wake_up_interruptible(&slot_context->slot_event);
+
+ //
+ // wait for our slot thread to release the mutex
+ //
+ interruptible_sleep_on(&slot_context->bus_release_event);
+ if ((shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT)||
+ (shpc_context->shpc_event_bits & RESUME_EVENT)||
+ (shpc_context->shpc_event_bits & REMOVE_EVENT)){
+ dbg("%s return 0 slot_id[ %d:%d ]",__FUNCTION__,
+ shpc_context->shpc_instance, slot_context->slot_number-1);
+ up(&shpc_context->bus_available_mutex);
+ return 0;
+ }
+ hp_clear_slot_event_bit(slot_context, BUS_RELEASE_EVENT);
+
+ hp_clear_shpc_event_bit(shpc_context, BUS_AVAILABLE_MUTEX_EVENT);
+ up(&shpc_context->bus_available_mutex);
+ return(0);
+}
+
+// ****************************************************************************
+//
+// call_back_routine():
+//
+// Parameters
+// slot_context - Caller provided storage for SHPC context data.
+//
+// Return Value
+// void
+//
+// ****************************************************************************
+static unsigned long async_callback (void* driver_context,
+ u8 slot_id,
+ enum shpc_async_request async_request,
+ union SLOT_STATUS_INFO slot_status,
+ void* request_context )
+{
+ u8 phys_slot_num;
+ long rc=0;
+ struct pci_func *slot_func;
+ struct controller *ctrl;
+ struct shpc_context *shpc_context;
+ u8 bus=0;
+ u8 device=0;
+ u8 function=0;
+ unsigned long devices_still_quiescing = 0;
+
+ dbg("%s slot_id = %d",__FUNCTION__, slot_id);
+
+ ctrl = ((struct controller*) driver_context);
+ if (ctrl == NULL){
+ return -ENODEV;
+ }
+
+ shpc_context = (struct shpc_context* ) ctrl->shpc_context;
+ phys_slot_num = shpc_context->slot_context[slot_id].slot_psn;
+
+ bus = ctrl->bus;
+ device = slot_id + 1;
+
+ dbg("%s - physical_slot = %d instance = %d",__FUNCTION__, phys_slot_num, shpc_context->shpc_instance);
+
+ switch( async_request ) {
+ case SHPC_ASYNC_ENABLE_SLOT:
+ dbg("%s SHPC_ASYNC_ENABLE_SLOT",__FUNCTION__);
+ dbg("%s slot occupied = %d",__FUNCTION__,shpc_context->slot_context[slot_id].slot_occupied);
+ if (shpc_context->slot_context[slot_id].slot_occupied == 1) {
+ return 0;
+ }
+ //
+ // Force pci-bus re-enumeration (probe), to load drivers on behalf on enabled device(s) on this slot.
+ //
+ dbg("%s In callback routine processing enable slot",__FUNCTION__ );
+
+ dbg("%s CALLING amdshpc_slot_find bus, dev, fn = %d, %d, %d\n",__FUNCTION__ ,
+ bus, device, function);
+ slot_func = amdshpc_slot_find(bus, device, function);
+ dbg("%s slot_func = %p ",__FUNCTION__ , slot_func);
+ if (!slot_func) {
+ dbg("%s --> slot_func not found",__FUNCTION__ );
+ return -ENODEV;
+ }
+
+ slot_func->bus = bus;
+ slot_func->device = device;
+ slot_func->function = function;
+ slot_func->configured = 0;
+ dbg("%s CALLING amdshpc_process_SI(ctrl=%p slot_func=%p)\n",__FUNCTION__ , ctrl, slot_func);
+ rc = amdshpc_process_SI(ctrl, slot_func);
+ if (!rc ) {
+ shpc_context->slot_context[slot_id].slot_occupied = 1;
+ }
+ dbg("%s amdshpc_process_SI returned rc=%d",__FUNCTION__ , (int)rc);
+ break;
+
+ case SHPC_ASYNC_SURPRISE_REMOVE:
+ dbg("%s SHPC_ASYNC_SURPRISE_REMOVE",__FUNCTION__);
+ //
+ // Something went wrong with the slot (eg, power-fault), and loaded drivers must be removed.
+ //
+ case SHPC_ASYNC_QUIESCE_DEVNODE:
+ dbg("%s SHPC_ASYNC_QUIESCE_DEVNODE",__FUNCTION__);
+ //
+ // Friendly opportunity to quiesce (remove) drivers, prior to disabling the slot.
+ // After device drivers are removed, it's OK to show messages to that effect.
+ //
+ // If device quiecing will complete at a later time (from a separate thread),
+ // then set "devices_still_quiescing" accordingly, and upon quiecing-completion,
+ // call hp_StartAsyncRequest() with a "SHPC_ASYNC_QUIESCE_DEVNODE_NOTIFY" request.
+ //
+ case SHPC_ASYNC_QUIESCE_DEVNODE_QUIET:
+ dbg("%s SHPC_ASYNC_QUIESCE_DEVNODE_QUIET",__FUNCTION__);
+ //
+ // Friendly opportunity to quiesce (remove) drivers, prior to disabling the slot.
+ // After device drivers are removed, don't show messages to that effect.
+ //
+ // If device quiecing will complete at a later time (from a separate thread),
+ // then set "devices_still_quiescing" accordingly, and upon quiecing-completion,
+ // call hp_StartAsyncRequest() with a "SHPC_ASYNC_QUIESCE_DEVNODE_NOTIFY" request.
+ //
+ dbg("%s Processing disable slot",__FUNCTION__ );
+
+ dbg("%s CALLING amdshpc_slot_find bus, dev, fn = %d, %d, %d\n",__FUNCTION__ ,
+ bus, device, function);
+
+ slot_func = amdshpc_slot_find(bus, device, function);
+ dbg("%s slot_func = %p ",__FUNCTION__ , slot_func);
+ if (!slot_func) {
+ dbg("%s --> slot_func not found",__FUNCTION__ );
+ return -ENODEV;
+ }
+
+ dbg("%s CALLING amdshpc_process_SS(ctrl=%p slot_func=%p)\n",__FUNCTION__ , ctrl, slot_func);
+ rc = amdshpc_process_SS(ctrl, slot_func);
+ if (!rc ) {
+ shpc_context->slot_context[slot_id].slot_occupied = 0;
+ }
+ dbg("%s amdshpc_process_SS returned rc=%d",__FUNCTION__ , (int)rc);
+
+ break;
+
+ case SHPC_ASYNC_DISABLE_SLOT:
+ dbg("%s SHPC_ASYNC_DISABLE_SLOT",__FUNCTION__);
+ //
+ // Just a notification, may be used to update some interested GUI application.
+ //
+ break;
+
+ default:
+ break;
+ }
+ return devices_still_quiescing;
+}
+
--- /dev/null
+/*
+* Copyright (C) 2002,2003 Advanced Micro Devices, Inc.
+* YOUR USE OF THIS CODE IS SUBJECT TO THE TERMS
+* AND CONDITIONS OF THE GNU GENERAL PUBLIC
+* LICENSE FOUND IN THE "GPL.TXT" FILE THAT IS
+* INCLUDED WITH THIS FILE AND POSTED AT
+* http://www.gnu.org/licenses/gpl.html
+*
+*
+* This driver is to be used as a skeleton driver to be show how to interface
+* with the pci hotplug core easily.
+*
+* Send feedback to <david.keck@amd.com>
+*
+*/
+
+
+#ifndef _SHPC_DDI_H_
+#define _SHPC_DDI_H_
+
+#include "amdshpc.h"
+//
+// SHPC Constants
+//
+#define SHPC_MAX_NUM_SLOTS 4
+
+
+// ****************************************************************************
+//
+// hp_AddDevice()
+//
+// Parameters
+// shpc_context - Caller provided storage for SHPC context data (per hardware-instance).
+// driver_context - Caller provided pointer to be returned upon completion.
+// Callback - Caller provided function to be called upon completion of async requests.
+// shpc_instance - Zero-based hardware instance.
+//
+// Return Value
+// Status returned by any system calls made within hp_AddDevice().
+//
+// ****************************************************************************
+long
+ hp_AddDevice(
+ struct shpc_context *shpc_context,
+ void* driver_context,
+ SHPC_ASYNC_CALLBACK Callback,
+ unsigned long shpc_instance
+ );
+
+
+// ****************************************************************************
+//
+// hp_StartDevice()
+//
+// Parameters
+// shpc_context - Caller provided storage for SHPC context data.
+// mmio_base_addr - from u.Memory member of CmResourceTypeMemory
+// IntVector - from u.Interrupt.Vector member of CmResourceTypeInterrupt
+// IntMode - from Flags member of CmResourceTypeInterrupt
+// IntShared - from ShareDisposition member of CmResourceTypeInterrupt
+// IntAffinity - from u.Interrupt.Affinity member of CmResourceTypeInterrupt
+//
+// Return Value
+// Status returned by any system calls made within hp_StartDevice().
+//
+// Comments:
+// The caller is responsible for mapping mmio_base_addr, via MmMapIoSpace(),
+// before calling hp_StartDevice().
+//
+// ****************************************************************************
+long
+ hp_StartDevice(
+ struct shpc_context* shpc_context
+ );
+
+
+// ****************************************************************************
+//
+// hp_StopDevice()
+//
+// Parameters
+// shpc_context - Caller provided storage for SHPC context data.
+//
+// Return Value
+// Status returned by any system calls made within hp_StopDevice().
+//
+// Comments:
+// The caller is responsible for unmapping mmio_base_addr, via MmUnmapIoSpace(),
+// after calling hp_StopDevice() for resource re-balancing or device removal.
+//
+// ****************************************************************************
+long hp_StopDevice(struct shpc_context *shpc_context);
+
+// ****************************************************************************
+//
+// hp_SuspendDevice()
+//
+// Parameters
+// shpc_context - Caller provided storage for SHPC context data.
+//
+// Return Value
+// Status returned by any system calls made within hp_SuspendDevice().
+//
+// Comments:
+// hp_SuspendDevice() must be called before transitioning away from PowerDeviceD0.
+//
+// ****************************************************************************
+long hp_SuspendDevice(struct shpc_context *shpc_context);
+
+// ****************************************************************************
+//
+// hp_ResumeDevice()
+//
+// Parameters
+// shpc_context - Caller provided storage for SHPC context data.
+//
+// Return Value
+// Status returned by any system calls made within hp_ResumeDevice().
+//
+// Comments:
+// hp_SuspendDevice() must be called after transitioning back to PowerDeviceD0.
+//
+// ****************************************************************************
+long hp_ResumeDevice(struct shpc_context *shpc_context);
+
+// ****************************************************************************
+//
+// hp_QuerySlots()
+//
+// Parameters
+// shpc_context - Caller provided storage for SHPC context data.
+// SlotConfig - Caller provided storage for slots configuration info.
+//
+// Return Value
+// Status returned by any system calls made within hp_QuerySlots().
+//
+// ****************************************************************************
+long hp_QuerySlots(struct shpc_context *shpc_context, union SLOT_CONFIG_INFO* SlotConfig);
+
+
+// ****************************************************************************
+//
+// hp_QuerySlotStatus()
+//
+// Parameters
+// shpc_context - Caller provided storage for SHPC context data.
+// slot_id - Zero-based slot number (0..n-1).
+// Query - Pointer to Slot Status Structure
+//
+// Return Value
+// Status returned by any system calls made within hp_QuerySlotStatus().
+//
+// ****************************************************************************
+long hp_QuerySlotStatus(struct shpc_context *shpc_context, u8 slot_id, union SLOT_STATUS_INFO* Query);
+
+// ****************************************************************************
+//
+// hp_Queryslot_psn()
+//
+// Parameters
+// shpc_context - Caller provided storage for SHPC context data.
+// SlotID - Zero-based slot number (0..n-1).
+// slot_psn - Pointer to Physical Slot Number
+//
+// Return Value
+// STATUS_SUCCESS, or STATUS_UNSUCCESSFUL for invalid SlotID.
+//
+// ****************************************************************************
+long hp_Queryslot_psn(struct shpc_context *shpc_context, unsigned char slot_ID, unsigned long *slot_psn);
+
+// ****************************************************************************
+//
+// hp_StartAsyncRequest()
+//
+// Parameters
+// shpc_context - Caller provided storage for SHPC context data.
+// slot_id - Zero-based slot number (0..n-1).
+// Request - Async request: Slot "Enable/Disable", AttnLED "Attn/Normal").
+// timeout - For AttnLED "Attn" requests (in seconds)
+// request_context - Caller provided pointer to be returned upon completion.
+//
+// Return Value
+// STATUS_SUCCESS if the request is accepted. The Callback() is later invoked with a completion status.
+// STATUS_UNSUCCESSFUL if the request is rejected (invalid parameters, or similar request in progress),
+//
+// Comment:
+// For AttnLED "Attn" requests, the completion Callback() function is invoked as soon as the hardware
+// completes (Blink) execution. When the timeout period expires, the AttnLED is brought back to
+// its "Normal" (On/Off) state, and the Callback() is invoked once again.
+//
+// ****************************************************************************
+long hp_StartAsyncRequest(
+ struct shpc_context *shpc_context,
+ u8 slot_id,
+ enum shpc_async_request Request,
+ u32 timeout,
+ void* request_context
+ );
+
+
+// ****************************************************************************
+//
+// hp_RegisterUserEvent()
+//
+// Parameters
+// shpc_context - Caller provided storage for SHPC context data.
+// user_event_pointer - Pointer to caller's provided EVENT object.
+//
+// Return Value
+// STATUS_SUCCESS if the request is accepted.
+// STATUS_UNSUCCESSFUL if the request is rejected (EVENT already registered).
+//
+// ****************************************************************************
+long hp_RegisterUserEvent(
+ struct shpc_context *shpc_context,
+ wait_queue_head_t *user_event_pointer
+ );
+
+
+// ****************************************************************************
+//
+// hp_UnRegisterUserEvent()
+//
+// Parameters
+// shpc_context - Caller provided storage for SHPC context data.
+//
+// Return Value
+// STATUS_SUCCESS if the request is accepted.
+// STATUS_UNSUCCESSFUL if the request is rejected (EVENT not previously registered).
+//
+// ****************************************************************************
+long hp_UnRegisterUserEvent(struct shpc_context *shpc_context);
+
+#endif // _SHPC_DDI_H_
+
--- /dev/null
+/*
+ * Copyright (C) 2001,2003 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (C) 2001,2003 IBM Corp.
+ * Copyright (C) 2002-2003 Advanced Micro Devices
+ *
+ * YOUR USE OF THIS CODE IS SUBJECT TO THE TERMS
+ * AND CONDITIONS OF THE GNU GENERAL PUBLIC
+ * LICENSE FOUND IN THE "GPL.TXT" FILE THAT IS
+ * INCLUDED WITH THIS FILE AND POSTED AT
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * Send feedback to <david.keck@amd.com>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/sched.h>
+#include "amdshpc_ddi.h"
+#include "amdshpc.h"
+
+
+// ****************************************************************************
+//
+// hp_at_slot_enabled_wait_for_slot_request()
+//
+// ****************************************************************************
+long
+hp_at_slot_enabled_wait_for_slot_request(
+ struct shpc_context* shpc_context,
+ struct slot_context* slot_context
+)
+{
+ unsigned long old_irq_flags;
+ long status = STATUS_SUCCESS;
+ union SLOT_STATUS_INFO slot_status;
+ unsigned long DevNodes;
+
+ dbg("%s -->slot_id[ %d:%d ]",__FUNCTION__ , shpc_context->shpc_instance, slot_context->slot_number-1 );
+
+ //
+ // Slot Enabled: complete pending slot request
+ //
+ if( slot_context->slot_completion.done ) {
+ dbg("%s -->ENABLE_DONE: slot_id[ %d:%d ] card_speed_mode[ %d+%d ] bus_speed_mode[ %d ]",__FUNCTION__ ,
+ shpc_context->shpc_instance, slot_context->slot_number-1,
+ slot_context->card_speed_mode, slot_context->card_pci66_capable,
+ shpc_context->bus_speed_mode );
+ //
+ // Call Completion Callback()
+ //
+ hp_QuerySlotStatus( shpc_context, slot_context->slot_number - 1, &slot_status );
+ slot_status.x.lu_request_failed = slot_context->slot_completion.failed;
+ shpc_context->async_callback(
+ shpc_context->driver_context,
+ slot_context->slot_number - 1,
+ slot_context->slot_completion.type,
+ slot_status,
+ slot_context->slot_completion.request_context );
+
+ //
+ // Signal registered user EVENT
+ //
+ hp_signal_user_event( shpc_context );
+
+ //
+ // Clear completion flag
+ //
+ slot_context->slot_completion.done = FALSE;
+ }
+
+ //
+ // Clear Button EVENT before waiting
+ //
+ spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags );
+ hp_clear_slot_event_bit(slot_context, ATTN_BUTTON_EVENT);
+ spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags );
+
+ //
+ // Wait for slot request
+ //
+ shpc_context->shpc_event_bits = 0;slot_context->slot_event_bits = 0;
+
+ wait_event_interruptible(slot_context->slot_event,
+ ((slot_context->slot_event_bits & ATTN_BUTTON_EVENT) ||
+ (slot_context->slot_event_bits & SLOT_REQUEST_EVENT) ||
+ (slot_context->slot_event_bits & BUS_REBALANCE_EVENT) ||
+ (slot_context->slot_event_bits & ALERT_EVENT) ||
+ (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT)));
+
+ //
+ // Alert: MRL Opened, Card Removed, Power-Fault?
+ //
+ if(slot_context->slot_event_bits & ALERT_EVENT) {
+ //
+ // Update attn_led_problem_event LED
+ //
+ spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags );
+ slot_context->problem_detected = TRUE;
+ hp_send_slot_event(slot_context, ATTN_LED_PROBLEM_EVENT);
+ spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags );
+
+ //
+ // Notify unrequested removal
+ //
+ slot_context->slot_completion.hw_initiated = TRUE;
+ slot_context->slot_completion.type = SHPC_ASYNC_SURPRISE_REMOVE;
+ slot_context->slot_completion.failed = HP_FALSE;
+ slot_context->slot_completion.request_context = NULL;
+ slot_context->slot_completion.done = TRUE;
+
+ //
+ // Grab Command MUTEX to disable slot
+ //
+ dbg("%s -->ALERT: slot_id[ %d:%d ] LSR_13:0[ %X ]",__FUNCTION__ ,
+ shpc_context->shpc_instance, slot_context->slot_number-1,
+ readl( slot_context->logical_slot_addr ) & 0x3F );
+ slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_disabled_wait_for_disable_cmd_available;
+ }
+ //
+ // bus_rebalance_event
+ //
+ else if(slot_context->slot_event_bits & BUS_REBALANCE_EVENT) {
+ //
+ // Clear Quiesced EVENT before invoking Callback()
+ //
+ spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags );
+ slot_context->quiesce_requests = 0;
+ slot_context->quiesce_replies = 0;
+ slot_context->slot_quiesced = FALSE;
+ hp_clear_slot_event_bit(slot_context, QUIESCE_EVENT);
+ spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags );
+
+ //
+ // Call Completion Callback() to quiesce DevNode(s)
+ //
+ slot_status.AsDWord = 0;
+ DevNodes = shpc_context->async_callback(
+ shpc_context->driver_context,
+ slot_context->slot_number - 1,
+ SHPC_ASYNC_QUIESCE_DEVNODE_QUIET,
+ slot_status,
+ ( void* )(unsigned long)slot_context->slot_psn );
+
+ //
+ // Update request count
+ //
+ spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags );
+ slot_context->quiesce_requests = DevNodes;
+ if( slot_context->quiesce_requests == 0 ||
+ slot_context->quiesce_replies >= slot_context->quiesce_requests ) {
+ slot_context->slot_quiesced = TRUE;
+ hp_send_slot_event(slot_context, QUIESCE_EVENT);
+ }
+ spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags );
+
+ //
+ // Wait for DevNode quiescing
+ //
+ dbg("%s -->BUS_REBALANCE: slot_id[ %d:%d ]",__FUNCTION__ ,
+ shpc_context->shpc_instance, slot_context->slot_number-1 );
+ slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_at_slot_enabled_wait_for_stop_on_bus_rebalance;
+ }
+ //
+ // attn_button_event
+ //
+ else if(slot_context->slot_event_bits & ATTN_BUTTON_EVENT) {
+ //
+ // Set completion info for HW-initiated request
+ //
+ slot_context->slot_completion.hw_initiated = TRUE;
+ slot_context->slot_completion.type = SHPC_ASYNC_DISABLE_SLOT;
+ slot_context->slot_completion.request_context = NULL;
+
+ //
+ // Grab Command MUTEX to blink Power LED
+ //
+ dbg("%s -->DISABLE_REQ: slot_id[ %d:%d ]",__FUNCTION__ ,
+ shpc_context->shpc_instance, slot_context->slot_number-1 );
+ slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_at_slot_enabled_wait_for_led_cmd_available;
+ }
+ //
+ // SlotRequestEvent
+ //
+ else if(slot_context->slot_event_bits & SLOT_REQUEST_EVENT) {
+ //
+ // Set completion info for SW-initiated request
+ //
+ slot_context->slot_completion.hw_initiated = FALSE;
+ slot_context->slot_completion.type = slot_context->slot_request.type;
+ slot_context->slot_completion.request_context = slot_context->slot_request.request_context;
+
+ //
+ // Request to disable slot?
+ //
+ if( slot_context->slot_request.type == SHPC_ASYNC_DISABLE_SLOT ) {
+ //
+ // Grab Command MUTEX to blink Power LED
+ //
+ dbg("%s -->DISABLE_REQ: slot_id[ %d:%d ]",__FUNCTION__ ,
+ shpc_context->shpc_instance, slot_context->slot_number-1 );
+ slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_at_slot_enabled_wait_for_led_cmd_available;
+ }
+ else {
+ //
+ // Slot already enabled, just complete the request
+ //
+ dbg("%s -->ENABLE_REQ: slot_id[ %d:%d ]",__FUNCTION__ ,
+ shpc_context->shpc_instance, slot_context->slot_number-1 );
+ slot_context->slot_completion.failed = HP_FALSE;
+ slot_context->slot_completion.done = TRUE;
+ }
+
+ //
+ // Allow next SW-initiated slot request while processing this one
+ //
+ spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags );
+ hp_clear_slot_event_bit(slot_context, SLOT_REQUEST_EVENT);
+ spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags );
+ }
+ //
+ // exit_request_event
+ //
+ else {
+ status = STATUS_UNSUCCESSFUL;
+ }
+
+ return status;
+}
+
+
+// ****************************************************************************
+//
+// hp_at_slot_enabled_wait_for_stop_on_bus_rebalance()
+//
+// ****************************************************************************
+long
+hp_at_slot_enabled_wait_for_stop_on_bus_rebalance(
+ struct shpc_context* shpc_context,
+ struct slot_context* slot_context
+)
+{
+ unsigned long old_irq_flags;
+ long status = STATUS_SUCCESS;
+
+ dbg("%s -->slot_id[ %d:%d ]",__FUNCTION__ , shpc_context->shpc_instance, slot_context->slot_number-1 );
+
+ //
+ // Setup our timer
+ //
+ hp_clear_slot_event_bit(slot_context, SLOT_TIMER1_EVENT);
+ slot_context->slot_timer1.data = (unsigned long)slot_context;
+ slot_context->slot_timer1.function = hp_slot_timer1_func;
+ slot_context->slot_timer1.expires = jiffies + QUIESCE_QUIET_TIMEOUT;
+ add_timer(&slot_context->slot_timer1);
+
+ //
+ // Wait for Quiescing EVENT
+ //
+ wait_event_interruptible(slot_context->slot_event,
+ ((slot_context->slot_event_bits & QUIESCE_EVENT) ||
+ (slot_context->slot_event_bits & ALERT_EVENT) ||
+ (slot_context->slot_event_bits & SLOT_TIMER1_EVENT) ||
+ (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT)));
+
+ if (!(slot_context->slot_event_bits & SLOT_TIMER1_EVENT)) {
+ //
+ // delete the timer because we got an event other than the timer
+ //
+ del_timer_sync(&slot_context->slot_timer1);
+ }
+
+ //
+ // Alert: MRL Opened, Card Removed, Power-Fault?
+ //
+ if(slot_context->slot_event_bits & ALERT_EVENT) {
+ //
+ // Update attn_led_problem_event LED
+ //
+ spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags );
+ slot_context->problem_detected = TRUE;
+ hp_send_slot_event(slot_context, ATTN_LED_PROBLEM_EVENT);
+ spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags );
+
+ //
+ // Notify unrequested removal
+ //
+ slot_context->slot_completion.hw_initiated = TRUE;
+ slot_context->slot_completion.type = SHPC_ASYNC_SURPRISE_REMOVE;
+ slot_context->slot_completion.failed = HP_FALSE;
+ slot_context->slot_completion.request_context = NULL;
+ slot_context->slot_completion.done = TRUE;
+
+ //
+ // Grab Command MUTEX to disable slot
+ //
+ dbg("%s -->ALERT: slot_id[ %d:%d ] LSR_13:0[ %X ]",__FUNCTION__ ,
+ shpc_context->shpc_instance, slot_context->slot_number-1,
+ readl( slot_context->logical_slot_addr ) & 0x3F );
+ slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_disabled_wait_for_disable_cmd_available;
+ }
+ //
+ // quiesce_event, timeout
+ //
+ else if((slot_context->slot_event_bits & QUIESCE_EVENT) || (slot_context->slot_event_bits & SLOT_TIMER1_EVENT)) {
+ spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags );
+ if((slot_context->slot_event_bits & SLOT_TIMER1_EVENT) || slot_context->slot_quiesced ) {
+ spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags );
+ //
+ // Grab Command MUTEX to set slot at power-only
+ //
+ if((slot_context->slot_event_bits & SLOT_TIMER1_EVENT)) {
+ dbg("%s -->BUS_REBALANCE: slot_id[ %d:%d ] Quiesce timeout",__FUNCTION__ ,
+ shpc_context->shpc_instance, slot_context->slot_number-1 );
+ }
+ else {
+ dbg("%s -->BUS_REBALANCE: slot_id[ %d:%d ] Slot Quiesced",__FUNCTION__ ,
+ shpc_context->shpc_instance, slot_context->slot_number-1 );
+ }
+ slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_at_slot_enabled_wait_for_power_cmd_available;
+ }
+ else {
+ spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags );
+ //
+ // Cancel bus re-balancing and treat it as a "Slot Enabled" request
+ //
+ slot_context->slot_completion.hw_initiated = TRUE;
+ slot_context->slot_completion.type = SHPC_ASYNC_ENABLE_SLOT;
+ slot_context->slot_completion.request_context = NULL;
+ slot_context->slot_completion.failed = HP_FALSE;
+ slot_context->slot_completion.done = TRUE;
+
+ dbg("%s -->BUS_REBALANCE: slot_id[ %d:%d ] Cancelled: BUSY DevNode",__FUNCTION__ ,
+ shpc_context->shpc_instance, slot_context->slot_number-1 );
+ slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_at_slot_enabled_wait_for_slot_request;
+ }
+ }
+ //
+ // exit_request_event
+ //
+ else {
+ status = STATUS_UNSUCCESSFUL;
+ }
+ return status;
+}
+
+
+// ****************************************************************************
+//
+// hp_at_slot_enabled_wait_for_power_cmd_available()
+//
+// ****************************************************************************
+long
+hp_at_slot_enabled_wait_for_power_cmd_available(
+ struct shpc_context* shpc_context,
+ struct slot_context* slot_context
+)
+{
+ struct task_struct;
+ union SHPC_COMMAND_WREG command_reg;
+ unsigned long old_irq_flags;
+ long status = STATUS_SUCCESS;
+
+ dbg("%s -->slot_id[ %d:%d ]",__FUNCTION__ , shpc_context->shpc_instance, slot_context->slot_number-1 );
+
+ //
+ // Wait for Command Available MUTEX
+ //
+ hp_set_slot_event_bit(slot_context, CMD_ACQUIRE_EVENT);
+ wake_up_interruptible(&slot_context->cmd_acquire_event);
+
+ wait_event_interruptible(slot_context->slot_event,
+ ((shpc_context->shpc_event_bits & CMD_AVAILABLE_MUTEX_EVENT) ||
+ (slot_context->slot_event_bits & ALERT_EVENT) ||
+ (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT)));
+
+ //
+ // Alert: MRL Opened, Card Removed, Power-Fault?
+ //
+ if(slot_context->slot_event_bits & ALERT_EVENT) {
+ //
+ // Update attn_led_problem_event LED
+ //
+ spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags );
+ slot_context->problem_detected = TRUE;
+ hp_send_slot_event(slot_context, ATTN_LED_PROBLEM_EVENT);
+ spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags );
+
+ //
+ // Release Command MUTEX
+ //
+ hp_set_slot_event_bit(slot_context, CMD_RELEASE_EVENT);
+ wake_up_interruptible(&slot_context->cmd_release_event);
+
+ //
+ // Notify unrequested removal
+ //
+ slot_context->slot_completion.hw_initiated = TRUE;
+ slot_context->slot_completion.type = SHPC_ASYNC_SURPRISE_REMOVE;
+ slot_context->slot_completion.failed = HP_FALSE;
+ slot_context->slot_completion.request_context = NULL;
+ slot_context->slot_completion.done = TRUE;
+
+ //
+ // Grab Command MUTEX to disable slot
+ //
+ dbg("%s -->ALERT: slot_id[ %d:%d ] LSR_13:0[ %X ]",__FUNCTION__ ,
+ shpc_context->shpc_instance, slot_context->slot_number-1,
+ readl( slot_context->logical_slot_addr ) & 0x3F );
+ slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_disabled_wait_for_disable_cmd_available;
+ }
+ //
+ // cmd_available_mutex
+ //
+ else if(shpc_context->shpc_event_bits & CMD_AVAILABLE_MUTEX_EVENT) {
+ //
+ // Clear Completion EVENT before issuing next command
+ //
+ spin_lock_irqsave( &shpc_context->shpc_spinlock, old_irq_flags );
+ hp_clear_shpc_event_bit(shpc_context, CMD_COMPLETION_EVENT);
+ spin_unlock_irqrestore( &shpc_context->shpc_spinlock, old_irq_flags );
+
+ //
+ // Set slot to "Disable" and blink Power LED
+ //
+ command_reg.Slot.code = SHPC_SLOT_OPERATION;
+ command_reg.Slot.power_led = SHPC_LED_BLINK;
+ command_reg.Slot.attention_led = SHPC_led_NO_CHANGE;
+ command_reg.Slot.state = SHPC_DISABLE_SLOT;
+ command_reg.Slot.TGT = slot_context->slot_number;
+ writew(command_reg.AsWord, shpc_context->mmio_base_addr + SHPC_COMMAND_REG_OFFSET);
+
+ //
+ // Wait for command to complete (while holding MUTEX)
+ //
+ slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_at_slot_enabled_wait_for_power_cmd_completion;
+ }
+ //
+ // exit_request_event
+ //
+ else {
+ //
+ // Release Command MUTEX
+ //
+ hp_set_slot_event_bit(slot_context, CMD_RELEASE_EVENT);
+ wake_up_interruptible(&slot_context->cmd_release_event);
+ status = STATUS_UNSUCCESSFUL;
+ }
+ return status;
+}
+
+
+// ****************************************************************************
+//
+// hp_at_slot_enabled_wait_for_power_cmd_completion()
+//
+// ****************************************************************************
+long
+hp_at_slot_enabled_wait_for_power_cmd_completion(
+ struct shpc_context* shpc_context,
+ struct slot_context* slot_context
+)
+{
+ unsigned long old_irq_flags;
+ long status = STATUS_SUCCESS;
+ union SLOT_STATUS_INFO slot_status;
+ union SHPC_STATUS_WREG status_reg;
+
+ dbg("%s -->slot_id[ %d:%d ]",__FUNCTION__ , shpc_context->shpc_instance, slot_context->slot_number-1 );
+
+ //
+ // Setup our timer
+ //
+ hp_clear_slot_event_bit(slot_context, SLOT_TIMER2_EVENT);
+ slot_context->slot_timer2.data = (unsigned long)slot_context;
+ slot_context->slot_timer2.function = hp_slot_timer2_func;
+ slot_context->slot_timer2.expires = jiffies + FIFTEEN_SEC_TIMEOUT;
+ add_timer(&slot_context->slot_timer2);
+
+ //
+ // Wait for Command Completion EVENT while holding MUTEX
+ //
+ wait_event_interruptible(slot_context->slot_event,
+ ((shpc_context->shpc_event_bits & CMD_COMPLETION_EVENT) ||
+ (slot_context->slot_event_bits & ALERT_EVENT) ||
+ (slot_context->slot_event_bits & SLOT_TIMER2_EVENT) ||
+ (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT)));
+
+ if (!(slot_context->slot_event_bits & SLOT_TIMER2_EVENT)) {
+ //
+ // delete the timer because we got an event other than the timer
+ //
+ del_timer_sync(&slot_context->slot_timer2);
+ }
+
+ //
+ // Alert: MRL Opened, Card Removed, Power-Fault?
+ //
+ if(slot_context->slot_event_bits & ALERT_EVENT) {
+ //
+ // Update attn_led_problem_event LED
+ //
+ spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags );
+ slot_context->problem_detected = TRUE;
+ hp_send_slot_event(slot_context, ATTN_LED_PROBLEM_EVENT);
+ spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags );
+
+ //
+ // Notify unrequested removal
+ //
+ slot_context->slot_completion.hw_initiated = TRUE;
+ slot_context->slot_completion.type = SHPC_ASYNC_SURPRISE_REMOVE;
+ slot_context->slot_completion.failed = HP_FALSE;
+ slot_context->slot_completion.request_context = NULL;
+ slot_context->slot_completion.done = TRUE;
+
+ //
+ // Grab Command MUTEX to disable slot
+ //
+ dbg("%s -->ALERT: slot_id[ %d:%d ] LSR_13:0[ %X ]",__FUNCTION__ ,
+ shpc_context->shpc_instance, slot_context->slot_number-1,
+ readl( slot_context->logical_slot_addr ) & 0x3F );
+ slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_disabled_wait_for_disable_cmd_available;
+ }
+ //
+ // cmd_completion_event, timeout
+ //
+ else if((shpc_context->shpc_event_bits & CMD_COMPLETION_EVENT) ||
+ (slot_context->slot_event_bits & SLOT_TIMER2_EVENT)) {
+ //
+ // Command completed OK?
+ //
+ status_reg.AsWord = readw(shpc_context->mmio_base_addr + SHPC_STATUS_REG_OFFSET);
+
+ if( status_reg.x.BSY == SHPC_STATUS_CLEARED &&
+ status_reg.x.MRLO_ERR == SHPC_STATUS_CLEARED &&
+ status_reg.x.INVCMD_ERR == SHPC_STATUS_CLEARED ) {
+ //
+ // Flag this slot as DISABLED
+ //
+ hp_flag_slot_as_disabled( shpc_context, slot_context );
+
+ //
+ // Call Completion Callback(): slot disabled
+ //
+ hp_QuerySlotStatus( shpc_context, slot_context->slot_number - 1, &slot_status );
+ slot_status.x.lu_request_failed = HP_FALSE;
+ shpc_context->async_callback(
+ shpc_context->driver_context,
+ slot_context->slot_number - 1,
+ SHPC_ASYNC_DISABLE_SLOT,
+ slot_status,
+ NULL );
+
+ //
+ // Signal registered user EVENT
+ //
+ hp_signal_user_event( shpc_context );
+
+ //
+ // Treat it as an on-going ENABLE request
+ //
+ slot_context->slot_completion.hw_initiated = TRUE;
+ slot_context->slot_completion.type = SHPC_ASYNC_ENABLE_SLOT;
+ slot_context->slot_completion.request_context = NULL;
+
+ //
+ // Grab Command MUTEX to power-on the slot
+ //
+ slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_at_slot_disabled_wait_for_power_cmd_available;
+ }
+ else {
+ //
+ // Treat it as a HW-initiated DISABLE request
+ //
+ slot_context->slot_completion.hw_initiated = TRUE;
+ slot_context->slot_completion.type = SHPC_ASYNC_DISABLE_SLOT;
+ slot_context->slot_completion.failed = HP_FALSE;
+ slot_context->slot_completion.request_context = NULL;
+ slot_context->slot_completion.done = TRUE;
+
+ //
+ // Grab Command MUTEX to disable slot
+ //
+ dbg("%s -->CMD_ERROR: slot_id[ %d:%d ] Cmd[ %X ]",__FUNCTION__ ,
+ shpc_context->shpc_instance, slot_context->slot_number-1, status_reg.AsWord );
+ slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_disabled_wait_for_disable_cmd_available;
+ }
+ }
+ //
+ // exit_request_event
+ //
+ else {
+ status = STATUS_UNSUCCESSFUL;
+ }
+ //
+ // Release Command MUTEX
+ //
+ hp_set_slot_event_bit(slot_context, CMD_RELEASE_EVENT);
+ wake_up_interruptible(&slot_context->cmd_release_event);
+
+ return status;
+}
+
+
+// ****************************************************************************
+//
+// hp_at_slot_enabled_wait_for_led_cmd_available()
+//
+// ****************************************************************************
+long
+hp_at_slot_enabled_wait_for_led_cmd_available(
+ struct shpc_context* shpc_context,
+ struct slot_context* slot_context
+)
+{
+ unsigned long old_irq_flags;
+ long status = STATUS_SUCCESS;
+ union SHPC_COMMAND_WREG command_reg;
+
+ dbg("%s -->slot_id[ %d:%d ]",__FUNCTION__ , shpc_context->shpc_instance, slot_context->slot_number-1 );
+
+ //
+ // Wait for Command Available MUTEX
+ //
+ hp_set_slot_event_bit(slot_context, CMD_RELEASE_EVENT);
+ wake_up_interruptible(&slot_context->cmd_acquire_event);
+
+ wait_event_interruptible(slot_context->slot_event,
+ ((shpc_context->shpc_event_bits & CMD_AVAILABLE_MUTEX_EVENT) ||
+ (slot_context->slot_event_bits & ALERT_EVENT) ||
+ (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT)));
+
+ //
+ // Alert: MRL Opened, Card Removed, Power-Fault?
+ //
+ if(slot_context->slot_event_bits & ALERT_EVENT) {
+ //
+ // Update attn_led_problem_event LED
+ //
+ spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags );
+ slot_context->problem_detected = TRUE;
+ hp_send_slot_event(slot_context, ATTN_LED_PROBLEM_EVENT);
+ spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags );
+
+ //
+ // Release Command MUTEX
+ //
+ hp_set_slot_event_bit(slot_context, CMD_RELEASE_EVENT);
+ wake_up_interruptible(&slot_context->cmd_release_event);
+
+ //
+ // Fail on-going request
+ //
+ slot_context->slot_completion.failed = HP_TRUE;
+ slot_context->slot_completion.done = TRUE;
+
+ //
+ // Grab Command MUTEX to disable slot
+ //
+ dbg("%s -->ALERT: slot_id[ %d:%d ] LSR_13:0[ %X ]",__FUNCTION__ ,
+ shpc_context->shpc_instance, slot_context->slot_number-1,
+ readl( slot_context->logical_slot_addr ) & 0x3F );
+ slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_disabled_wait_for_disable_cmd_available;
+ }
+ //
+ // cmd_available_mutex
+ //
+ else if(shpc_context->shpc_event_bits & CMD_AVAILABLE_MUTEX_EVENT) {
+ //
+ // Clear Completion EVENT before issuing next command
+ //
+ spin_lock_irqsave( &shpc_context->shpc_spinlock, old_irq_flags );
+ hp_clear_shpc_event_bit(shpc_context, CMD_COMPLETION_EVENT);
+ spin_unlock_irqrestore( &shpc_context->shpc_spinlock, old_irq_flags );
+
+ //
+ // Blink Power LED
+ //
+ command_reg.Slot.code = SHPC_SLOT_OPERATION;
+ command_reg.Slot.power_led = SHPC_LED_BLINK;
+ command_reg.Slot.attention_led = SHPC_led_NO_CHANGE;
+ command_reg.Slot.state = SHPC_SLOT_NO_CHANGE;
+ command_reg.Slot.TGT = slot_context->slot_number;
+ writew(command_reg.AsWord, shpc_context->mmio_base_addr + SHPC_COMMAND_REG_OFFSET);
+
+ //
+ // Wait for command to complete (while holding MUTEX)
+ //
+ slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_at_slot_enabled_wait_for_led_cmd_completion;
+ }
+ //
+ // exit_request_event
+ //
+ else {
+ //
+ // Release Command MUTEX
+ //
+ hp_set_slot_event_bit(slot_context, CMD_RELEASE_EVENT);
+ wake_up_interruptible(&slot_context->cmd_release_event);
+ status =STATUS_UNSUCCESSFUL;
+ }
+ return status;
+}
+
+
+// ****************************************************************************
+//
+// hp_at_slot_enabled_wait_for_led_cmd_completion()
+//
+// ****************************************************************************
+long
+hp_at_slot_enabled_wait_for_led_cmd_completion(
+ struct shpc_context* shpc_context,
+ struct slot_context* slot_context
+)
+{
+ unsigned long old_irq_flags;
+ long status = STATUS_SUCCESS;
+ union SLOT_STATUS_INFO slot_status;
+ union SHPC_STATUS_WREG status_reg;
+ unsigned long DevNodes;
+
+ dbg("%s -->slot_id[ %d:%d ]",__FUNCTION__ , shpc_context->shpc_instance, slot_context->slot_number-1 );
+
+ //
+ // Setup our timer
+ //
+ hp_clear_slot_event_bit(slot_context, SLOT_TIMER3_EVENT);
+ slot_context->slot_timer3.data = (unsigned long)slot_context;
+ slot_context->slot_timer3.function = hp_slot_timer3_func;
+ slot_context->slot_timer3.expires = jiffies + ONE_SEC_TIMEOUT;
+ add_timer(&slot_context->slot_timer3);
+
+ //
+ // Wait for Command Completion EVENT while holding MUTEX
+ //
+ wait_event_interruptible(slot_context->slot_event,
+ ((shpc_context->shpc_event_bits & CMD_COMPLETION_EVENT) ||
+ (slot_context->slot_event_bits & ALERT_EVENT) ||
+ (slot_context->slot_event_bits & SLOT_TIMER3_EVENT) ||
+ (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT)));
+
+ if (!(slot_context->slot_event_bits & SLOT_TIMER3_EVENT)) {
+ //
+ // delete the timer because we got an event other than the timer
+ //
+ del_timer_sync(&slot_context->slot_timer3);
+ }
+ dbg("%s -->slot bits %08X shpc bits %08X",__FUNCTION__ ,
+ slot_context->slot_event_bits,shpc_context->shpc_event_bits);
+
+ //
+ // Alert: MRL Opened, Card Removed, Power-Fault?
+ //
+ if(slot_context->slot_event_bits & ALERT_EVENT) {
+ //
+ // Update attn_led_problem_event LED
+ //
+ spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags );
+ slot_context->problem_detected = TRUE;
+ hp_send_slot_event(slot_context, ATTN_LED_PROBLEM_EVENT);
+ spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags );
+
+ //
+ // Fail on-going request
+ //
+ slot_context->slot_completion.failed = HP_TRUE;
+ slot_context->slot_completion.done = TRUE;
+
+ //
+ // Grab Command MUTEX to disable slot
+ //
+ dbg("%s -->ALERT: slot_id[ %d:%d ] LSR_13:0[ %X ]",__FUNCTION__ ,
+ shpc_context->shpc_instance, slot_context->slot_number-1,
+ readl( slot_context->logical_slot_addr ) & 0x3F );
+ slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_disabled_wait_for_disable_cmd_available;
+ }
+ //
+ // cmd_completion_event, timeout
+ //
+ else if((shpc_context->shpc_event_bits & CMD_COMPLETION_EVENT) ||
+ (slot_context->slot_event_bits & SLOT_TIMER3_EVENT)) {
+ //
+ // Command completed OK?
+ //
+ status_reg.AsWord = readw(shpc_context->mmio_base_addr + SHPC_STATUS_REG_OFFSET);
+
+ if( status_reg.x.BSY == SHPC_STATUS_CLEARED &&
+ status_reg.x.MRLO_ERR == SHPC_STATUS_CLEARED &&
+ status_reg.x.INVCMD_ERR == SHPC_STATUS_CLEARED ) {
+ //
+ // Allow cancellation of operation?
+ //
+ if( slot_context->slot_completion.hw_initiated ) {
+ //
+ // Wait for 5 sec timeout
+ //
+ slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_at_slot_enabled_wait_for_timeout;
+ }
+ else {
+ //
+ // Clear Quiesced EVENT before invoking Callback()
+ //
+ spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags );
+ slot_context->quiesce_requests = 0;
+ slot_context->quiesce_replies = 0;
+ slot_context->slot_quiesced = FALSE;
+ hp_clear_slot_event_bit(slot_context, QUIESCE_EVENT);
+ spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags );
+
+ //
+ // Call Completion Callback() to quiesce DevNode(s)
+ //
+ slot_status.AsDWord = 0;
+ DevNodes = shpc_context->async_callback(
+ shpc_context->driver_context,
+ slot_context->slot_number - 1,
+ SHPC_ASYNC_QUIESCE_DEVNODE,
+ slot_status,
+ ( void* )(unsigned long)slot_context->slot_psn );
+
+ //
+ // Update request count
+ //
+ spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags );
+ slot_context->quiesce_requests = DevNodes;
+ if( slot_context->quiesce_requests == 0 ||
+ slot_context->quiesce_replies >= slot_context->quiesce_requests ) {
+ slot_context->slot_quiesced = TRUE;
+ hp_send_slot_event(slot_context, QUIESCE_EVENT);
+ }
+ spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags );
+
+ //
+ // Wait for DevNode quiescing
+ //
+ slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_at_slot_enabled_wait_for_stop_on_slot_disable;
+ }
+ }
+ else {
+ //
+ // Fail on-going request
+ //
+ slot_context->slot_completion.failed = HP_TRUE;
+ slot_context->slot_completion.done = TRUE;
+
+ //
+ // Grab Command MUTEX to disable slot
+ //
+ dbg("%s -->CMD_ERROR: slot_id[ %d:%d ] Cmd[ %X ]",__FUNCTION__ ,
+ shpc_context->shpc_instance, slot_context->slot_number-1, status_reg.AsWord );
+ slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_disabled_wait_for_disable_cmd_available;
+ }
+ }
+ //
+ // exit_request_event
+ //
+ else {
+ status = STATUS_UNSUCCESSFUL;
+ }
+ //
+ // Release Command MUTEX
+ //
+ hp_set_slot_event_bit(slot_context, CMD_RELEASE_EVENT);
+ wake_up_interruptible(&slot_context->cmd_release_event);
+
+ return status;
+}
+
+
+// ****************************************************************************
+//
+// hp_at_slot_enabled_wait_for_timeout()
+//
+// ****************************************************************************
+long
+hp_at_slot_enabled_wait_for_timeout(
+ struct shpc_context* shpc_context,
+ struct slot_context* slot_context
+)
+{
+ unsigned long old_irq_flags;
+ long status = STATUS_SUCCESS;
+ union SLOT_STATUS_INFO slot_status;
+ unsigned long DevNodes;
+
+ dbg("%s -->slot_id[ %d:%d ]",__FUNCTION__ , shpc_context->shpc_instance, slot_context->slot_number-1 );
+
+ //
+ // Clear Button EVENT before waiting
+ //
+ spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags );
+ down_interruptible(&slot_context->slot_event_bits_semaphore);
+ slot_context->slot_event_bits &= ~ATTN_BUTTON_EVENT;
+ up(&slot_context->slot_event_bits_semaphore);
+ spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags );
+
+ //
+ // Setup our timer
+ //
+ hp_clear_slot_event_bit(slot_context, SLOT_TIMER7_EVENT);
+ slot_context->slot_timer7.data = (unsigned long)slot_context;
+ slot_context->slot_timer7.function = hp_slot_timer7_func;
+ slot_context->slot_timer7.expires = jiffies + FIVE_SEC_TIMEOUT;
+ add_timer(&slot_context->slot_timer7);
+
+ //
+ // Wait for 5 sec timeout
+ //
+ wait_event_interruptible(slot_context->slot_event,
+ ((slot_context->slot_event_bits & ATTN_BUTTON_EVENT) ||
+ (slot_context->slot_event_bits & ALERT_EVENT) ||
+ (slot_context->slot_event_bits & SLOT_TIMER7_EVENT) ||
+ (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT)));
+
+ if (!(slot_context->slot_event_bits & SLOT_TIMER7_EVENT)) {
+ //
+ // delete the timer because we got an event other than the timer
+ //
+ del_timer_sync(&slot_context->slot_timer7);
+ }
+
+ //
+ // Alert: MRL Opened, Card Removed, Power-Fault?
+ //
+ if(slot_context->slot_event_bits & ALERT_EVENT) {
+ //
+ // Update attn_led_problem_event LED
+ //
+ spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags );
+ slot_context->problem_detected = TRUE;
+ hp_send_slot_event(slot_context, ATTN_LED_PROBLEM_EVENT);
+ spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags );
+
+ //
+ // Fail on-going request
+ //
+ slot_context->slot_completion.failed = HP_TRUE;
+ slot_context->slot_completion.done = TRUE;
+
+ //
+ // Grab Command MUTEX to disable slot
+ //
+ dbg("%s -->ALERT: slot_id[ %d:%d ] LSR_13:0[ %X ]",__FUNCTION__ ,
+ shpc_context->shpc_instance, slot_context->slot_number-1,
+ readl( slot_context->logical_slot_addr ) & 0x3F );
+ slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_disabled_wait_for_disable_cmd_available;
+ }
+ //
+ // attn_button_event
+ //
+ else if(slot_context->slot_event_bits & ATTN_BUTTON_EVENT) {
+ //
+ // Cancel request, grab Command MUTEX to Power LED back ON
+ //
+ dbg("%s -->DISABLE_REQ: slot_id[ %d:%d ] Cancelled: Attn Button",__FUNCTION__ ,
+ shpc_context->shpc_instance, slot_context->slot_number-1 );
+ slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_enabled_wait_for_led_cmd_available;
+ }
+ //
+ // timeout
+ //
+ else if(slot_context->slot_event_bits & SLOT_TIMER7_EVENT) {
+ //
+ // Clear Quiesced EVENT before invoking Callback()
+ //
+ spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags );
+ slot_context->quiesce_requests = 0;
+ slot_context->quiesce_replies = 0;
+ slot_context->slot_quiesced = FALSE;
+ hp_clear_slot_event_bit(slot_context, QUIESCE_EVENT);
+ spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags );
+
+ //
+ // Call Completion Callback() to quiesce DevNode(s)
+ //
+ slot_status.AsDWord = 0;
+ DevNodes = shpc_context->async_callback(
+ shpc_context->driver_context,
+ slot_context->slot_number - 1,
+ SHPC_ASYNC_QUIESCE_DEVNODE,
+ slot_status,
+ ( void* )(unsigned long)slot_context->slot_psn );
+
+ //
+ // Update request count
+ //
+ spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags );
+ slot_context->quiesce_requests = DevNodes;
+ if( slot_context->quiesce_requests == 0 ||
+ slot_context->quiesce_replies == slot_context->quiesce_requests ) {
+ slot_context->slot_quiesced = TRUE;
+ hp_send_slot_event(slot_context, QUIESCE_EVENT);
+ }
+ spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags );
+
+ //
+ // Wait for DevNode quiescing
+ //
+ slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_at_slot_enabled_wait_for_stop_on_slot_disable;
+ }
+ //
+ // exit_request_event
+ //
+ else {
+ status = STATUS_UNSUCCESSFUL;
+ }
+
+ return status;
+}
+
+
+// ****************************************************************************
+//
+// hp_at_slot_enabled_wait_for_stop_on_slot_disable()
+//
+// ****************************************************************************
+long
+hp_at_slot_enabled_wait_for_stop_on_slot_disable(
+ struct shpc_context* shpc_context,
+ struct slot_context* slot_context
+)
+{
+ unsigned long old_irq_flags;
+ long status = STATUS_SUCCESS;
+ union SLOT_STATUS_INFO slot_status;
+ unsigned long DevNodes;
+
+ dbg("%s -->slot_id[ %d:%d ]", __FUNCTION__ ,shpc_context->shpc_instance, slot_context->slot_number-1 );
+
+ //
+ // Setup our timer
+ //
+ hp_clear_slot_event_bit(slot_context, SLOT_TIMER4_EVENT);
+ slot_context->slot_timer4.data = (unsigned long)slot_context;
+ slot_context->slot_timer4.function = hp_slot_timer4_func;
+ slot_context->slot_timer4.expires = jiffies + QUIESCE_QUIET_TIMEOUT;
+ add_timer(&slot_context->slot_timer4);
+
+ //
+ // Wait for Quiescing EVENT
+ //
+ wait_event_interruptible(slot_context->slot_event,
+ ((slot_context->slot_event_bits & QUIESCE_EVENT) ||
+ (slot_context->slot_event_bits & ALERT_EVENT) ||
+ (slot_context->slot_event_bits & SLOT_TIMER4_EVENT) ||
+ (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT)));
+
+ if (!(slot_context->slot_event_bits & SLOT_TIMER4_EVENT)) {
+ //
+ // delete the timer because we got an event other than the timer
+ //
+ del_timer_sync(&slot_context->slot_timer4);
+ }
+
+ //
+ // Alert: MRL Opened, Card Removed, Power-Fault?
+ //
+ if(slot_context->slot_event_bits & ALERT_EVENT) {
+ //
+ // Update attn_led_problem_event LED
+ //
+ spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags );
+ slot_context->problem_detected = TRUE;
+ hp_send_slot_event(slot_context, ATTN_LED_PROBLEM_EVENT);
+ spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags );
+
+ //
+ // Fail on-going request
+ //
+ slot_context->slot_completion.failed = HP_TRUE;
+ slot_context->slot_completion.done = TRUE;
+
+ //
+ // Grab Command MUTEX to disable slot
+ //
+ dbg("%s -->ALERT: slot_id[ %d:%d ] LSR_13:0[ %X ]",__FUNCTION__ ,
+ shpc_context->shpc_instance, slot_context->slot_number-1,
+ readl( slot_context->logical_slot_addr ) & 0x3F );
+ slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_disabled_wait_for_disable_cmd_available;
+ }
+ //
+ // quiesce_event
+ //
+ else if(slot_context->slot_event_bits & QUIESCE_EVENT) {
+ spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags );
+ if( slot_context->slot_quiesced ) {
+ spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags );
+ //
+ // Complete succesful DISABLE request
+ //
+ slot_context->slot_completion.failed = HP_FALSE;
+ slot_context->slot_completion.done = TRUE;
+
+ //
+ // Grab Command MUTEX to disable slot
+ //
+ //
+ dbg("%s -->DISABLE_REQ: slot_id[ %d:%d ] Slot Quiesced",__FUNCTION__ ,
+ shpc_context->shpc_instance, slot_context->slot_number-1 );
+ slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_disabled_wait_for_disable_cmd_available;
+ }
+ else {
+ spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags );
+ //
+ // Cancel request, grab Command MUTEX to turn Power LED back ON
+ //
+ slot_context->slot_completion.hw_initiated = TRUE;
+ slot_context->slot_completion.type = SHPC_ASYNC_ENABLE_SLOT;
+ slot_context->slot_completion.request_context = NULL;
+ slot_context->slot_completion.failed = HP_FALSE;
+ slot_context->slot_completion.done = TRUE;
+
+ dbg("%s -->DISABLE_REQ: slot_id[ %d:%d ] Cancelled: BUSY DevNode",__FUNCTION__ ,
+ shpc_context->shpc_instance, slot_context->slot_number-1 );
+ slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_enabled_wait_for_led_cmd_available;
+ }
+ }
+ //
+ // timeout
+ //
+ else if(slot_context->slot_event_bits & SLOT_TIMER4_EVENT) {
+ //
+ // Clear Quiesced EVENT before invoking Callback()
+ //
+ spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags );
+ slot_context->quiesce_requests = 0;
+ slot_context->quiesce_replies = 0;
+ slot_context->slot_quiesced = FALSE;
+ hp_clear_slot_event_bit(slot_context, QUIESCE_EVENT);
+ spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags );
+
+ //
+ // Call Completion Callback() to quiesce DevNode(s)
+ //
+ slot_status.AsDWord = 0;
+ DevNodes = shpc_context->async_callback(
+ shpc_context->driver_context,
+ slot_context->slot_number - 1,
+ SHPC_ASYNC_QUIESCE_DEVNODE_QUIET,
+ slot_status,
+ ( void* )(unsigned long)slot_context->slot_psn );
+
+ //
+ // Update request count
+ //
+ spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags );
+ slot_context->quiesce_requests = DevNodes;
+ if( slot_context->quiesce_requests == 0 ||
+ slot_context->quiesce_replies == slot_context->quiesce_requests ) {
+ slot_context->slot_quiesced = TRUE;
+ hp_send_slot_event(slot_context, QUIESCE_EVENT);
+ }
+ spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags );
+
+ //
+ // Wait for DevNode quiescing
+ //
+ slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_at_slot_enabled_wait_for_stop_on_slot_disable_quiet;
+ }
+ //
+ // exit_request_event
+ //
+ else {
+ status = STATUS_UNSUCCESSFUL;
+ }
+ return status;
+}
+
+
+// ****************************************************************************
+//
+// hp_at_slot_enabled_wait_for_stop_on_slot_disable_quiet()
+//
+// ****************************************************************************
+long
+hp_at_slot_enabled_wait_for_stop_on_slot_disable_quiet(
+ struct shpc_context* shpc_context,
+ struct slot_context* slot_context
+)
+{
+ unsigned long old_irq_flags;
+ long status = STATUS_SUCCESS;
+
+ dbg("%s -->slot_id[ %d:%d ]", __FUNCTION__ ,shpc_context->shpc_instance, slot_context->slot_number-1 );
+
+ //
+ // Setup our timer
+ //
+ hp_clear_slot_event_bit(slot_context, SLOT_TIMER5_EVENT);
+ slot_context->slot_timer5.data = (unsigned long)slot_context;
+ slot_context->slot_timer5.function = hp_slot_timer5_func;
+ slot_context->slot_timer5.expires = jiffies + QUIESCE_QUIET_TIMEOUT;
+ add_timer(&slot_context->slot_timer5);
+
+ //
+ // Wait for Quiescing EVENT
+ //
+ wait_event_interruptible(slot_context->slot_event,
+ ((slot_context->slot_event_bits & QUIESCE_EVENT) ||
+ (slot_context->slot_event_bits & ALERT_EVENT) ||
+ (slot_context->slot_event_bits & SLOT_TIMER5_EVENT) ||
+ (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT)));
+
+ if (!(slot_context->slot_event_bits & SLOT_TIMER5_EVENT)) {
+ //
+ // delete the timer because we got an event other than the timer
+ //
+ del_timer_sync(&slot_context->slot_timer5);
+ }
+
+ //
+ // Alert: MRL Opened, Card Removed, Power-Fault?
+ //
+ if(slot_context->slot_event_bits & ALERT_EVENT) {
+ //
+ // Update attn_led_problem_event LED
+ //
+ spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags );
+ slot_context->problem_detected = TRUE;
+ hp_send_slot_event(slot_context, ATTN_LED_PROBLEM_EVENT);
+ spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags );
+
+ //
+ // Fail on-going request
+ //
+ slot_context->slot_completion.failed = HP_TRUE;
+ slot_context->slot_completion.done = TRUE;
+
+ //
+ // Grab Command MUTEX to disable slot
+ //
+ dbg("%s -->ALERT: slot_id[ %d:%d ] LSR_13:0[ %X ]",__FUNCTION__ ,
+ shpc_context->shpc_instance, slot_context->slot_number-1,
+ readl( slot_context->logical_slot_addr ) & 0x3F );
+ slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_disabled_wait_for_disable_cmd_available;
+ }
+ //
+ // quiesce_event, timeout
+ //
+ else if((slot_context->slot_event_bits & QUIESCE_EVENT) ||
+ (slot_context->slot_event_bits & SLOT_TIMER5_EVENT)) {
+ spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags );
+ if((slot_context->slot_event_bits & SLOT_TIMER5_EVENT) || slot_context->slot_quiesced ) {
+ spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags );
+ //
+ // Complete succesful DISABLE request
+ //
+ slot_context->slot_completion.failed = HP_FALSE;
+ slot_context->slot_completion.done = TRUE;
+
+ //
+ // Grab Command MUTEX to disable slot
+ //
+ //
+ if(slot_context->slot_event_bits & SLOT_TIMER5_EVENT) {
+ dbg("%s -->DISABLE_REQ: slot_id[ %d:%d ] Quiesce timeout",__FUNCTION__ ,
+ shpc_context->shpc_instance, slot_context->slot_number-1 );
+ }
+ else {
+ dbg("%s -->DISABLE_REQ: slot_id[ %d:%d ] Slot Quiesced",__FUNCTION__ ,
+ shpc_context->shpc_instance, slot_context->slot_number-1 );
+ }
+ slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_disabled_wait_for_disable_cmd_available;
+ }
+ else {
+ spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags );
+ //
+ // Cancel request, grab Command MUTEX to turn Power LED back ON
+ //
+ dbg("%s -->DISABLE_REQ: slot_id[ %d:%d ] Cancelled: BUSY DevNode",__FUNCTION__ ,
+ shpc_context->shpc_instance, slot_context->slot_number-1 );
+ slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_enabled_wait_for_led_cmd_available;
+ }
+ }
+ //
+ // exit_request_event
+ //
+ else {
+ status = STATUS_UNSUCCESSFUL;
+ }
+ return status;
+}
+
+
+// ****************************************************************************
+//
+// hp_to_slot_enabled_wait_for_led_cmd_available()
+//
+// ****************************************************************************
+long
+hp_to_slot_enabled_wait_for_led_cmd_available(
+ struct shpc_context* shpc_context,
+ struct slot_context* slot_context
+)
+{
+ unsigned long old_irq_flags;
+ long status = STATUS_SUCCESS;
+ union SHPC_COMMAND_WREG command_reg;
+
+ dbg("%s -->slot_id[ %d:%d ]", __FUNCTION__ ,shpc_context->shpc_instance, slot_context->slot_number-1 );
+
+ //
+ // Wait for Command Available MUTEX
+ //
+ hp_set_slot_event_bit(slot_context, CMD_ACQUIRE_EVENT);
+ wake_up_interruptible(&slot_context->cmd_acquire_event);
+
+ wait_event_interruptible(slot_context->slot_event,
+ ((shpc_context->shpc_event_bits & CMD_AVAILABLE_MUTEX_EVENT) ||
+ (slot_context->slot_event_bits & ALERT_EVENT) ||
+ (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT)));
+
+ //
+ // Alert: MRL Opened, Card Removed, Power-Fault?
+ //
+ if(slot_context->slot_event_bits & ALERT_EVENT) {
+ //
+ // Update attn_led_problem_event LED
+ //
+ spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags );
+ slot_context->problem_detected = TRUE;
+ hp_send_slot_event(slot_context, ATTN_LED_PROBLEM_EVENT);
+ spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags );
+
+ //
+ // Release Command MUTEX
+ //
+ hp_set_slot_event_bit(slot_context, CMD_RELEASE_EVENT);
+ wake_up_interruptible(&slot_context->cmd_release_event);
+
+ //
+ // Fail on-going request
+ //
+ slot_context->slot_completion.failed = HP_TRUE;
+ slot_context->slot_completion.done = TRUE;
+
+ //
+ // Grab Command MUTEX to disable slot
+ //
+ dbg("%s -->ALERT: slot_id[ %d:%d ] LSR_13:0[ %X ]",__FUNCTION__ ,
+ shpc_context->shpc_instance, slot_context->slot_number-1,
+ readl( slot_context->logical_slot_addr ) & 0x3F );
+ slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_disabled_wait_for_disable_cmd_available;
+ }
+ //
+ // cmd_available_mutex
+ //
+ else if(shpc_context->shpc_event_bits & CMD_AVAILABLE_MUTEX_EVENT) {
+ //
+ // Clear Completion EVENT before issuing next command
+ //
+ spin_lock_irqsave( &shpc_context->shpc_spinlock, old_irq_flags );
+ hp_clear_shpc_event_bit(shpc_context, CMD_COMPLETION_EVENT);
+ spin_unlock_irqrestore( &shpc_context->shpc_spinlock, old_irq_flags );
+
+ //
+ // Turn Power LED back ON
+ //
+ command_reg.Slot.code = SHPC_SLOT_OPERATION;
+ command_reg.Slot.power_led = SHPC_LED_ON;
+ command_reg.Slot.attention_led = SHPC_led_NO_CHANGE;
+ command_reg.Slot.state = SHPC_SLOT_NO_CHANGE;
+ command_reg.Slot.TGT = slot_context->slot_number;
+ writew(command_reg.AsWord, shpc_context->mmio_base_addr + SHPC_COMMAND_REG_OFFSET);
+
+ //
+ // Wait for command to complete (while holding MUTEX)
+ //
+ slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_enabled_wait_for_led_cmd_completion;
+ }
+ //
+ // exit_request_event
+ //
+ else {
+ //
+ // Release Command MUTEX
+ //
+ hp_set_slot_event_bit(slot_context, CMD_RELEASE_EVENT);
+ wake_up_interruptible(&slot_context->cmd_release_event);
+ status = STATUS_UNSUCCESSFUL;
+ }
+ return status;
+}
+
+
+// ****************************************************************************
+//
+// hp_to_slot_enabled_wait_for_led_cmd_completion()
+//
+// ****************************************************************************
+long
+hp_to_slot_enabled_wait_for_led_cmd_completion(
+ struct shpc_context* shpc_context,
+ struct slot_context* slot_context
+)
+{
+ unsigned long old_irq_flags;
+ long status = STATUS_SUCCESS;
+ union SHPC_STATUS_WREG status_reg;
+
+ dbg("%s -->slot_id[ %d:%d ]",__FUNCTION__ , (int)shpc_context->shpc_instance, slot_context->slot_number-1 );
+
+ //
+ // Setup our timer
+ //
+ hp_clear_slot_event_bit(slot_context, SLOT_TIMER6_EVENT);
+ slot_context->slot_timer6.data = (unsigned long)slot_context;
+ slot_context->slot_timer6.function = hp_slot_timer6_func;
+ slot_context->slot_timer6.expires = jiffies + ONE_SEC_TIMEOUT;
+ add_timer(&slot_context->slot_timer6);
+
+ //
+ // Wait for Command Completion EVENT while holding MUTEX
+ //
+ wait_event_interruptible(slot_context->slot_event,
+ ((shpc_context->shpc_event_bits & CMD_COMPLETION_EVENT) ||
+ (slot_context->slot_event_bits & ALERT_EVENT) ||
+ (slot_context->slot_event_bits & SLOT_TIMER6_EVENT) ||
+ (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT)));
+
+ if (!(slot_context->slot_event_bits & SLOT_TIMER6_EVENT)) {
+ //
+ // delete the timer because we got an event other than the timer
+ //
+ del_timer_sync(&slot_context->slot_timer6);
+ }
+
+ //
+ // Alert: MRL Opened, Card Removed, Power-Fault?
+ //
+ if(slot_context->slot_event_bits & ALERT_EVENT) {
+ //
+ // Update attn_led_problem_event LED
+ //
+ spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags );
+ slot_context->problem_detected = TRUE;
+ hp_send_slot_event(slot_context, ATTN_LED_PROBLEM_EVENT);
+ spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags );
+
+ //
+ // Fail on-going request
+ //
+ slot_context->slot_completion.failed = HP_TRUE;
+ slot_context->slot_completion.done = TRUE;
+
+ //
+ // Grab Command MUTEX to disable slot
+ //
+ dbg("%s -->ALERT: slot_id[ %d:%d ] LSR_13:0[ %X ]",__FUNCTION__ ,
+ shpc_context->shpc_instance, slot_context->slot_number-1,
+ readl( slot_context->logical_slot_addr ) & 0x3F );
+ slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_disabled_wait_for_disable_cmd_available;
+ }
+ //
+ // cmd_completion_event, timeout
+ //
+ else if((shpc_context->shpc_event_bits & CMD_COMPLETION_EVENT) ||
+ (slot_context->slot_event_bits & SLOT_TIMER6_EVENT)) {
+ //
+ // Command completed OK?
+ //
+ status_reg.AsWord = readw(shpc_context->mmio_base_addr + SHPC_STATUS_REG_OFFSET);
+
+ if( status_reg.x.BSY == SHPC_STATUS_CLEARED &&
+ status_reg.x.MRLO_ERR == SHPC_STATUS_CLEARED &&
+ status_reg.x.INVCMD_ERR == SHPC_STATUS_CLEARED ) {
+ //
+ // Wait for next request
+ //
+ slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_at_slot_enabled_wait_for_slot_request;
+ }
+ else {
+ //
+ // Fail on-going request
+ //
+ slot_context->slot_completion.failed = HP_TRUE;
+ slot_context->slot_completion.done = TRUE;
+
+ //
+ // Grab Command MUTEX to disable slot
+ //
+ dbg("%s -->CMD_ERROR: slot_id[ %d:%d ] Cmd[ %X ]",__FUNCTION__ ,
+ shpc_context->shpc_instance, slot_context->slot_number-1, status_reg.AsWord );
+ slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_disabled_wait_for_disable_cmd_available;
+ }
+ }
+ //
+ // exit_request_event
+ //
+ else {
+ status = STATUS_UNSUCCESSFUL;
+ }
+ //
+ // Release Command MUTEX
+ //
+ hp_set_slot_event_bit(slot_context, CMD_RELEASE_EVENT);
+ wake_up_interruptible(&slot_context->cmd_release_event);
+
+ return status;
+}
+
--- /dev/null
+/*
+ * Copyright (C) 2001,2003 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (C) 2001,2003 IBM Corp.
+ * Copyright (C) 2002-2003 Advanced Micro Devices
+ *
+ * YOUR USE OF THIS CODE IS SUBJECT TO THE TERMS
+ * AND CONDITIONS OF THE GNU GENERAL PUBLIC
+ * LICENSE FOUND IN THE "GPL.TXT" FILE THAT IS
+ * INCLUDED WITH THIS FILE AND POSTED AT
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * Send feedback to <david.keck@amd.com>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/sched.h>
+#include "amdshpc_ddi.h"
+#include "amdshpc.h"
+
+
+// ****************************************************************************
+//
+// hp_at_slot_disabled_wait_for_slot_request()
+//
+// ****************************************************************************
+long
+hp_at_slot_disabled_wait_for_slot_request(
+ struct shpc_context* shpc_context,
+ struct slot_context* slot_context
+ )
+{
+ unsigned long old_irq_flags;
+ long status = STATUS_SUCCESS;
+ union SLOT_STATUS_INFO slot_status;
+ union SHPC_LOGICAL_SLOT_DWREG logical_slot_reg;
+
+ dbg("%s -->slot_id[ %d:%d ]", __FUNCTION__ ,shpc_context->shpc_instance, slot_context->slot_number-1 );
+
+ //
+ // Slot Disabled: complete pending slot request
+ //
+ if( slot_context->slot_completion.done ) {
+ dbg("%s -->DISABLE_DONE: slot_id[ %d:%d ]",__FUNCTION__ ,
+ shpc_context->shpc_instance, slot_context->slot_number-1 );
+ //
+ // Call Completion Callback()
+ //
+ hp_QuerySlotStatus( shpc_context, slot_context->slot_number - 1, &slot_status );
+ slot_status.x.lu_request_failed = slot_context->slot_completion.failed;
+ shpc_context->async_callback(
+ shpc_context->driver_context,
+ slot_context->slot_number - 1,
+ slot_context->slot_completion.type,
+ slot_status,
+ slot_context->slot_completion.request_context );
+
+ //
+ // Signal registered user EVENT
+ //
+ hp_signal_user_event( shpc_context );
+
+ //
+ // Clear completion flag
+ //
+ slot_context->slot_completion.done = FALSE;
+ }
+
+ //
+ // Clear Button EVENT before waiting
+ //
+ spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags );
+ hp_clear_slot_event_bit(slot_context, ATTN_BUTTON_EVENT);
+ spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags );
+
+ //
+ // Wait for slot request
+ //
+ wait_event_interruptible(slot_context->slot_event,
+ ((slot_context->slot_event_bits & ATTN_BUTTON_EVENT) ||
+ (slot_context->slot_event_bits & SLOT_REQUEST_EVENT) ||
+ (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT)));
+
+ //
+ // attn_button_event
+ //
+ if(slot_context->slot_event_bits & ATTN_BUTTON_EVENT) {
+ //
+ // Set completion info for HW-initiated request
+ //
+ slot_context->slot_completion.hw_initiated = TRUE;
+ slot_context->slot_completion.type = SHPC_ASYNC_ENABLE_SLOT;
+ slot_context->slot_completion.request_context = NULL;
+
+ //
+ // Get current HW disposition
+ //
+ logical_slot_reg.AsDWord = readl( slot_context->logical_slot_addr );
+ //
+ // Card present, MRL closed, and no Power-Fault?
+ //
+ if( logical_slot_reg.x.PRSNT1_2 != SHPC_SLOT_EMPTY &&
+ ( logical_slot_reg.x.MRLS_IM == SHPC_MASKED ||
+ logical_slot_reg.x.MRLS == SHPC_MRL_CLOSED ) &&
+ logical_slot_reg.x.PF == SHPC_STATUS_CLEARED ) {
+ //
+ // Clear Alert EVENT and Attention LED
+ //
+ spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags );
+ hp_clear_slot_event_bit(slot_context, ALERT_EVENT);
+ slot_context->problem_detected = FALSE;
+ hp_send_slot_event(slot_context, ATTN_LED_PROBLEM_EVENT);
+ spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags );
+
+ //
+ // Grab Command MUTEX to blink Power LED
+ //
+ dbg("%s -->ENABLE_REQ: slot_id[ %d:%d ]",__FUNCTION__ ,
+ shpc_context->shpc_instance, slot_context->slot_number-1 );
+ slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_at_slot_disabled_wait_for_led_cmd_available;
+ }
+ //
+ // Alert: MRL Opened, Power-Fault?
+ //
+ else if( logical_slot_reg.x.PRSNT1_2 != SHPC_SLOT_EMPTY ) {
+ //
+ // Update Attention LED
+ //
+ spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags );
+ slot_context->problem_detected = TRUE;
+ hp_send_slot_event(slot_context, ATTN_LED_PROBLEM_EVENT);
+ spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags );
+
+ //
+ // Fail on-going request
+ //
+ slot_context->slot_completion.failed = HP_TRUE;
+ slot_context->slot_completion.done = TRUE;
+ dbg("%s -->ALERT: slot_id[ %d:%d ] LSR_13:0[ %X ]",__FUNCTION__ ,
+ shpc_context->shpc_instance, slot_context->slot_number-1,
+ logical_slot_reg.AsDWord & 0x3F );
+ }
+ }
+ //
+ // SlotRequestEvent
+ //
+ else if(slot_context->slot_event_bits & SLOT_REQUEST_EVENT) {
+ //
+ // Set completion info for SW-initiated request
+ //
+ slot_context->slot_completion.hw_initiated = FALSE;
+ slot_context->slot_completion.type = slot_context->slot_request.type;
+ slot_context->slot_completion.request_context = slot_context->slot_request.request_context;
+
+ //
+ // Request to enable slot?
+ //
+ if( slot_context->slot_request.type == SHPC_ASYNC_ENABLE_SLOT ) {
+ //
+ // Update alert events based on current HW disposition
+ //
+ logical_slot_reg.AsDWord = readl( slot_context->logical_slot_addr );
+ //
+ // Card present, MRL closed, and no Power-Fault?
+ //
+ if( logical_slot_reg.x.PRSNT1_2 != SHPC_SLOT_EMPTY &&
+ (logical_slot_reg.x.MRLS_IM == SHPC_MASKED ||
+ logical_slot_reg.x.MRLS == SHPC_MRL_CLOSED ) &&
+ logical_slot_reg.x.PF == SHPC_STATUS_CLEARED ) {
+ //
+ // Clear Alert EVENT and Attention LED
+ //
+ spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags );
+ slot_context->problem_detected = FALSE;
+ hp_clear_slot_event_bit(slot_context, ALERT_EVENT);
+ hp_send_slot_event(slot_context, ATTN_LED_PROBLEM_EVENT);
+ spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags );
+
+ //
+ // Grab Command MUTEX to blink Power LED
+ //
+ dbg("%s -->ENABLE_REQ: slot_id[ %d:%d ]",__FUNCTION__ ,
+ shpc_context->shpc_instance, slot_context->slot_number-1 );
+ slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_at_slot_disabled_wait_for_led_cmd_available;
+ }
+ //
+ // Alert: MRL Opened, Power-Fault?
+ //
+ else if( logical_slot_reg.x.PRSNT1_2 != SHPC_SLOT_EMPTY ) {
+ //
+ // Update Attention LED
+ //
+ spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags );
+ slot_context->problem_detected = TRUE;
+ hp_send_slot_event(slot_context, ATTN_LED_PROBLEM_EVENT);
+ spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags );
+
+ //
+ // Fail on-going request
+ //
+ slot_context->slot_completion.failed = HP_TRUE;
+ slot_context->slot_completion.done = TRUE;
+ dbg("%s -->ALERT: slot_id[ %d:%d ] LSR_13:0[ %X ]",__FUNCTION__ ,
+ shpc_context->shpc_instance, slot_context->slot_number-1,
+ logical_slot_reg.AsDWord & 0x3F );
+ }
+ }
+ else {
+ //
+ // Slot already disabled, just complete the request
+ //
+ dbg("%s -->DISABLE_REQ: slot_id[ %d:%d ]",__FUNCTION__ ,
+ shpc_context->shpc_instance, slot_context->slot_number-1 );
+ slot_context->slot_completion.failed = HP_FALSE;
+ slot_context->slot_completion.done = TRUE;
+ }
+
+ //
+ // Allow next SW-initiated slot request while processing this one
+ //
+ spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags );
+ hp_clear_slot_event_bit(slot_context, SLOT_REQUEST_EVENT);
+ spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags );
+ }
+ //
+ // exit_request_event
+ //
+ else {
+ status = STATUS_UNSUCCESSFUL;
+ }
+ return status;
+}
+
+
+// ****************************************************************************
+//
+// hp_at_slot_disabled_wait_for_led_cmd_available()
+//
+// ****************************************************************************
+long
+hp_at_slot_disabled_wait_for_led_cmd_available(
+ struct shpc_context* shpc_context,
+ struct slot_context* slot_context
+ )
+{
+ unsigned long old_irq_flags;
+ long status = STATUS_SUCCESS;
+ union SHPC_COMMAND_WREG command_reg;
+
+ dbg("%s -->slot_id[ %d:%d ]",__FUNCTION__ , shpc_context->shpc_instance, slot_context->slot_number-1 );
+ //
+ // Wait for Command Available MUTEX
+ //
+ hp_set_slot_event_bit(slot_context, CMD_ACQUIRE_EVENT);
+ wake_up_interruptible(&slot_context->cmd_acquire_event);
+
+ wait_event_interruptible(slot_context->slot_event,
+ ((shpc_context->shpc_event_bits & CMD_AVAILABLE_MUTEX_EVENT) ||
+ (slot_context->slot_event_bits & ALERT_EVENT) ||
+ (slot_context->slot_event_bits & SLOT_REQUEST_EVENT) ||
+ (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT)));
+
+ //
+ // Alert: MRL Opened, Card Removed, Power-Fault?
+ //
+ if(slot_context->slot_event_bits & ALERT_EVENT) {
+
+ //
+ // Update attn_led_problem_event LED
+ //
+ spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags );
+ slot_context->problem_detected = TRUE;
+ hp_send_slot_event(slot_context, ATTN_LED_PROBLEM_EVENT);
+ spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags );
+
+ //
+ // Release Command MUTEX
+ //
+ hp_set_slot_event_bit(slot_context, CMD_RELEASE_EVENT);
+ wake_up_interruptible(&slot_context->cmd_release_event);
+
+ //
+ // Fail on-going request
+ //
+ slot_context->slot_completion.failed = HP_TRUE;
+ slot_context->slot_completion.done = TRUE;
+
+ //
+ // Wait for next request
+ //
+ dbg("%s -->ALERT: slot_id[ %d:%d ] LSR_13:0[ %X ]",__FUNCTION__ ,
+ shpc_context->shpc_instance, slot_context->slot_number-1,
+ (readl( slot_context->logical_slot_addr ) & 0x3F ));
+ slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_at_slot_disabled_wait_for_slot_request;
+ }
+ //
+ // cmd_available_mutex
+ //
+ else if(shpc_context->shpc_event_bits & CMD_AVAILABLE_MUTEX_EVENT) {
+ //
+ // Clear Completion EVENT before issuing next command
+ //
+ spin_lock_irqsave( &shpc_context->shpc_spinlock, old_irq_flags );
+ hp_clear_shpc_event_bit(shpc_context, CMD_COMPLETION_EVENT);
+ spin_unlock_irqrestore( &shpc_context->shpc_spinlock, old_irq_flags );
+
+ //
+ // Blink Power LED
+ //
+ command_reg.Slot.code = SHPC_SLOT_OPERATION;
+ command_reg.Slot.power_led = SHPC_LED_BLINK;
+ command_reg.Slot.attention_led = SHPC_led_NO_CHANGE;
+ command_reg.Slot.state = SHPC_SLOT_NO_CHANGE;
+ command_reg.Slot.TGT = slot_context->slot_number;
+ writew(command_reg.AsWord ,shpc_context->mmio_base_addr + SHPC_COMMAND_REG_OFFSET);
+
+ //
+ // Wait for command to complete (while holding MUTEX)
+ //
+ slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_at_slot_disabled_wait_for_led_cmd_completion;
+ }
+ //
+ // exit_request_event
+ //
+ else {
+ //
+ // Release Command MUTEX
+ //
+ hp_set_slot_event_bit(slot_context, CMD_RELEASE_EVENT);
+ wake_up_interruptible(&slot_context->cmd_release_event);
+ status = STATUS_UNSUCCESSFUL;
+ }
+ return status;
+}
+
+
+// ****************************************************************************
+//
+// hp_at_slot_disabled_wait_for_led_cmd_completion()
+//
+// ****************************************************************************
+long
+hp_at_slot_disabled_wait_for_led_cmd_completion(
+ struct shpc_context* shpc_context,
+ struct slot_context* slot_context
+ )
+{
+ unsigned long old_irq_flags;
+ long status = STATUS_SUCCESS;
+ union SHPC_STATUS_WREG status_reg;
+
+ dbg( "%s -->slot_id[ %d:%d ]",__FUNCTION__ , shpc_context->shpc_instance, slot_context->slot_number-1 );
+ //
+ // Setup our timer
+ //
+ hp_clear_slot_event_bit(slot_context, SLOT_TIMER1_EVENT);
+ slot_context->slot_timer1.data = (unsigned long)slot_context;
+ slot_context->slot_timer1.function = hp_slot_timer1_func;
+ slot_context->slot_timer1.expires = jiffies + ONE_SEC_TIMEOUT;
+ add_timer(&slot_context->slot_timer1);
+
+ //
+ // Wait for Command Completion EVENT while holding MUTEX
+ //
+ wait_event_interruptible(slot_context->slot_event,
+ ((shpc_context->shpc_event_bits & CMD_COMPLETION_EVENT) ||
+ (slot_context->slot_event_bits & ALERT_EVENT) ||
+ (slot_context->slot_event_bits & SLOT_TIMER1_EVENT) ||
+ (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT)));
+
+ if (!(slot_context->slot_event_bits & SLOT_TIMER1_EVENT)) {
+ //
+ // delete the timer because we got an event other than the timer
+ //
+ del_timer_sync(&slot_context->slot_timer1);
+ }
+ //
+ // Alert: MRL Opened, Card Removed, Power-Fault?
+ //
+ if(slot_context->slot_event_bits & ALERT_EVENT) {
+
+ //
+ // Update attn_led_problem_event LED
+ //
+ spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags );
+ slot_context->problem_detected = TRUE;
+ hp_send_slot_event(slot_context, ATTN_LED_PROBLEM_EVENT);
+ spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags );
+
+ //
+ // Fail on-going request
+ //
+ slot_context->slot_completion.failed = HP_TRUE;
+ slot_context->slot_completion.done = TRUE;
+
+ //
+ // Grab Command MUTEX to make sure Power LED is OFF
+ //
+ dbg("%s -->ALERT: slot_id[ %d:%d ] LSR_13:0[ %X ]",__FUNCTION__ ,
+ shpc_context->shpc_instance, slot_context->slot_number-1,
+ readl( slot_context->logical_slot_addr ) & 0x3F );
+ slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_disabled_wait_for_led_cmd_available;
+ }
+ //
+ // cmd_completion_event, timeout
+ //
+ else if((shpc_context->shpc_event_bits & CMD_COMPLETION_EVENT) ||
+ (slot_context->slot_event_bits & SLOT_TIMER1_EVENT)) {
+ //
+ // Command completed OK?
+ //
+ status_reg.AsWord = readw(shpc_context->mmio_base_addr + SHPC_STATUS_REG_OFFSET);
+
+ if( status_reg.x.BSY == SHPC_STATUS_CLEARED &&
+ status_reg.x.INVCMD_ERR == SHPC_STATUS_CLEARED ) {
+ //
+ // Allow cancellation of operation?
+ //
+ if( slot_context->slot_completion.hw_initiated ) {
+ //
+ // Wait for 5 sec timeout
+ //
+ slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_at_slot_disabled_wait_for_timeout;
+ }
+ else {
+ //
+ // Grab Command MUTEX to power-on the slot
+ //
+ slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_at_slot_disabled_wait_for_power_cmd_available;
+ }
+ }
+ else {
+ //
+ // Fail on-going request
+ //
+ slot_context->slot_completion.failed = HP_TRUE;
+ slot_context->slot_completion.done = TRUE;
+
+ //
+ // Grab Command MUTEX to make sure Power LED is OFF
+ //
+ dbg("%s -->CMD_ERROR: slot_id[ %d:%d ] Cmd[ %X ]",__FUNCTION__ ,
+ shpc_context->shpc_instance, slot_context->slot_number-1, status_reg.AsWord );
+ slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_disabled_wait_for_led_cmd_available;
+ }
+ }
+ //
+ // exit_request_event
+ //
+ else {
+ status = STATUS_UNSUCCESSFUL;
+ }
+ //
+ // Release Command MUTEX
+ //
+ hp_set_slot_event_bit(slot_context, CMD_RELEASE_EVENT);
+ wake_up_interruptible(&slot_context->cmd_release_event);
+
+ return status;
+}
+
+
+// ****************************************************************************
+//
+// hp_at_slot_disabled_wait_for_timeout()
+//
+// ****************************************************************************
+long
+hp_at_slot_disabled_wait_for_timeout(
+ struct shpc_context* shpc_context,
+ struct slot_context* slot_context
+ )
+{
+ unsigned long old_irq_flags;
+ long status = STATUS_SUCCESS;
+
+ dbg("%s -->slot_id[ %d:%d ]", __FUNCTION__ ,shpc_context->shpc_instance, slot_context->slot_number-1 );
+
+ //
+ // Clear Button EVENT before waiting
+ //
+ spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags );
+ hp_clear_slot_event_bit(slot_context, ATTN_BUTTON_EVENT);
+ spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags );
+ //
+ // Setup our timer
+ //
+ hp_clear_slot_event_bit(slot_context, SLOT_TIMER2_EVENT);
+ slot_context->slot_timer2.data = (unsigned long)slot_context;
+ slot_context->slot_timer2.function = hp_slot_timer2_func;
+ slot_context->slot_timer2.expires = jiffies + FIVE_SEC_TIMEOUT;
+ add_timer(&slot_context->slot_timer2);
+
+ //
+ // Wait for 5 sec timeout
+ //
+ wait_event_interruptible(slot_context->slot_event,
+ ((slot_context->slot_event_bits & ATTN_BUTTON_EVENT) ||
+ (slot_context->slot_event_bits & ALERT_EVENT) ||
+ (slot_context->slot_event_bits & SLOT_TIMER2_EVENT) ||
+ (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT)));
+
+ if (!(slot_context->slot_event_bits & SLOT_TIMER2_EVENT)) {
+ //
+ // delete the timer because we got an event other than the timer
+ //
+ del_timer_sync(&slot_context->slot_timer2);
+ }
+
+ //
+ // Alert: MRL Opened, Card Removed, Power-Fault?
+ //
+ if(slot_context->slot_event_bits & ALERT_EVENT) {
+
+ //
+ // Update attn_led_problem_event LED
+ //
+ spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags );
+ slot_context->problem_detected = TRUE;
+ hp_send_slot_event(slot_context, ATTN_LED_PROBLEM_EVENT);
+ spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags );
+
+ //
+ // Fail on-going request
+ //
+ slot_context->slot_completion.failed = HP_TRUE;
+ slot_context->slot_completion.done = TRUE;
+
+ //
+ // Grab Command MUTEX to turn OFF Power LED
+ //
+ dbg("%s -->ALERT: slot_id[ %d:%d ] LSR_13:0[ %X ]",__FUNCTION__ ,
+ shpc_context->shpc_instance, slot_context->slot_number-1,
+ readl( slot_context->logical_slot_addr ) & 0x3F );
+ slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_disabled_wait_for_led_cmd_available;
+ }
+ //
+ // attn_button_event
+ //
+ else if(slot_context->slot_event_bits & ATTN_BUTTON_EVENT) {
+ //
+ // Cancel request, grab Command MUTEX to turn OFF Power LED
+ //
+ dbg("%s -->ENABLE_REQ: slot_id[ %d:%d ] Cancelled: Attn Button",__FUNCTION__ ,
+ shpc_context->shpc_instance, slot_context->slot_number-1 );
+ slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_disabled_wait_for_led_cmd_available;
+ }
+ //
+ // timeout
+ //
+ else if(slot_context->slot_event_bits & SLOT_TIMER2_EVENT) {
+ //
+ // Grab Command MUTEX to set slot at Power-Only state
+ //
+ slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_at_slot_disabled_wait_for_power_cmd_available;
+ }
+ //
+ // exit_request_event
+ //
+ else {
+ status = STATUS_UNSUCCESSFUL;
+ }
+ return status;
+}
+
+
+// ****************************************************************************
+//
+// hp_at_slot_disabled_wait_for_power_cmd_available()
+//
+// ****************************************************************************
+long
+hp_at_slot_disabled_wait_for_power_cmd_available(
+ struct shpc_context* shpc_context,
+ struct slot_context* slot_context
+ )
+{
+ unsigned long old_irq_flags;
+ long status = STATUS_SUCCESS;
+ union SHPC_COMMAND_WREG command_reg;
+
+ dbg("%s -->slot_id[ %d:%d ]",__FUNCTION__ , shpc_context->shpc_instance, slot_context->slot_number-1 );
+
+ //
+ // Wait for Command Available MUTEX
+ //
+ hp_set_slot_event_bit(slot_context, CMD_ACQUIRE_EVENT);
+ wake_up_interruptible(&slot_context->cmd_acquire_event);
+
+ wait_event_interruptible(slot_context->slot_event,
+ ((shpc_context->shpc_event_bits & CMD_AVAILABLE_MUTEX_EVENT) ||
+ (slot_context->slot_event_bits & ALERT_EVENT) ||
+ (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT)));
+
+ //
+ // Alert: MRL Opened, Card Removed, Power-Fault?
+ //
+ if(slot_context->slot_event_bits & ALERT_EVENT) {
+
+ //
+ // Update attn_led_problem_event LED
+ //
+ spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags );
+ slot_context->problem_detected = TRUE;
+ hp_send_slot_event(slot_context, ATTN_LED_PROBLEM_EVENT);
+ spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags );
+
+ //
+ // Release Command MUTEX
+ //
+ hp_set_slot_event_bit(slot_context, CMD_RELEASE_EVENT);
+ wake_up_interruptible(&slot_context->cmd_release_event);
+
+ //
+ // Fail on-going request
+ //
+ slot_context->slot_completion.failed = HP_TRUE;
+ slot_context->slot_completion.done = TRUE;
+
+ //
+ // Grab Command MUTEX to turn OFF Power LED
+ //
+ dbg("%s -->ALERT: slot_id[ %d:%d ] LSR_13:0[ %X ]",__FUNCTION__ ,
+ shpc_context->shpc_instance, slot_context->slot_number-1,
+ readl( slot_context->logical_slot_addr ) & 0x3F );
+ slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_disabled_wait_for_led_cmd_available;
+ }
+ //
+ // cmd_available_mutex
+ //
+ else if(shpc_context->shpc_event_bits & CMD_AVAILABLE_MUTEX_EVENT) {
+ //
+ // Clear Completion EVENT before issuing next command
+ //
+ spin_lock_irqsave( &shpc_context->shpc_spinlock, old_irq_flags );
+ hp_clear_shpc_event_bit(shpc_context, CMD_COMPLETION_EVENT);
+ spin_unlock_irqrestore( &shpc_context->shpc_spinlock, old_irq_flags );
+
+ //
+ // Power-on the slot
+ //
+ command_reg.Slot.code = SHPC_SLOT_OPERATION;
+ command_reg.Slot.power_led = SHPC_led_NO_CHANGE;
+ command_reg.Slot.attention_led = SHPC_led_NO_CHANGE;
+ command_reg.Slot.state = SHPC_POWER_ONLY;
+ command_reg.Slot.TGT = slot_context->slot_number;
+ writew(command_reg.AsWord, shpc_context->mmio_base_addr + SHPC_COMMAND_REG_OFFSET);
+
+ //
+ // Wait for 100ms completion pre-amble on RevB-Errata (while holding MUTEX)
+ //
+ slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_at_slot_disabled_wait_for_power_cmd_timeout;
+ }
+ //
+ // exit_request_event
+ //
+ else {
+ //
+ // Release Command MUTEX
+ //
+ hp_set_slot_event_bit(slot_context, CMD_RELEASE_EVENT);
+ wake_up_interruptible(&slot_context->cmd_release_event);
+ status = STATUS_UNSUCCESSFUL;
+ }
+ return status;
+}
+
+
+// ****************************************************************************
+//
+// hp_at_slot_disabled_wait_for_power_cmd_timeout()
+//
+// ****************************************************************************
+long
+hp_at_slot_disabled_wait_for_power_cmd_timeout(
+ struct shpc_context* shpc_context,
+ struct slot_context* slot_context
+ )
+{
+ unsigned long old_irq_flags;
+ long status = STATUS_SUCCESS;
+
+ dbg("%s -->slot_id[ %d:%d ]",__FUNCTION__ , shpc_context->shpc_instance, slot_context->slot_number-1 );
+ //
+ // Setup our timer
+ //
+ hp_clear_slot_event_bit(slot_context, SLOT_TIMER3_EVENT);
+ slot_context->slot_timer3.data = (unsigned long)slot_context;
+ slot_context->slot_timer3.function = hp_slot_timer3_func;
+ slot_context->slot_timer3.expires = jiffies + ONE_TENTH_SEC_TIMEOUT;
+ add_timer(&slot_context->slot_timer3);
+
+ //
+ // Wait for 100ms completion pre-amble on RevB-Errata (while holding MUTEX)
+ //
+ wait_event_interruptible(slot_context->slot_event,
+ ((slot_context->slot_event_bits & ALERT_EVENT) ||
+ (slot_context->slot_event_bits & SLOT_TIMER3_EVENT) ||
+ (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT)));
+
+ if (!(slot_context->slot_event_bits & SLOT_TIMER3_EVENT)) {
+ //
+ // delete the timer because we got an event other than the timer
+ //
+ del_timer_sync(&slot_context->slot_timer3);
+ }
+
+ //
+ // Alert: MRL Opened, Card Removed, Power-Fault?
+ //
+ if(slot_context->slot_event_bits & ALERT_EVENT) {
+
+ //
+ // Update attn_led_problem_event LED
+ //
+ spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags );
+ slot_context->problem_detected = TRUE;
+ hp_send_slot_event(slot_context, ATTN_LED_PROBLEM_EVENT);
+ spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags );
+
+ //
+ // Release Command MUTEX
+ //
+ hp_set_slot_event_bit(slot_context, CMD_RELEASE_EVENT);
+ wake_up_interruptible(&slot_context->cmd_release_event);
+
+ //
+ // Fail on-going request
+ //
+ slot_context->slot_completion.failed = HP_TRUE;
+ slot_context->slot_completion.done = TRUE;
+
+ //
+ // Grab Command MUTEX to disable slot
+ //
+ dbg("%s -->ALERT: slot_id[ %d:%d ] LSR_13:0[ %X ]",__FUNCTION__ ,
+ shpc_context->shpc_instance, slot_context->slot_number-1,
+ readl( slot_context->logical_slot_addr ) & 0x3F );
+ slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_disabled_wait_for_disable_cmd_available;
+ }
+ //
+ // timeout
+ //
+ else if(slot_context->slot_event_bits & SLOT_TIMER3_EVENT) {
+ //
+ // Wait for command to complete (while holding MUTEX)
+ //
+ slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_at_slot_disabled_wait_for_power_cmd_completion;
+ }
+ //
+ // exit_request_event
+ //
+ else {
+ //
+ // Release Command MUTEX
+ //
+ hp_set_slot_event_bit(slot_context, CMD_RELEASE_EVENT);
+ wake_up_interruptible(&slot_context->cmd_release_event);
+ status = STATUS_UNSUCCESSFUL;
+ }
+ return status;
+}
+
+
+// ****************************************************************************
+//
+// hp_at_slot_disabled_wait_for_power_cmd_completion()
+//
+// ****************************************************************************
+long
+hp_at_slot_disabled_wait_for_power_cmd_completion(
+ struct shpc_context* shpc_context,
+ struct slot_context* slot_context
+ )
+{
+ unsigned long old_irq_flags;
+ long status = STATUS_SUCCESS;
+ union SHPC_STATUS_WREG status_reg;
+
+ dbg("%s -->slot_id[ %d:%d ]",__FUNCTION__ , shpc_context->shpc_instance, slot_context->slot_number-1 );
+
+ //
+ // Setup our timer
+ //
+ hp_clear_slot_event_bit(slot_context, SLOT_TIMER4_EVENT);
+ slot_context->slot_timer4.data = (unsigned long)slot_context;
+ slot_context->slot_timer4.function = hp_slot_timer4_func;
+ slot_context->slot_timer4.expires = jiffies + FIFTEEN_SEC_TIMEOUT;
+ add_timer(&slot_context->slot_timer4);
+
+ //
+ // Wait for Command Completion EVENT while holding MUTEX
+ //
+ wait_event_interruptible(slot_context->slot_event,
+ ((shpc_context->shpc_event_bits & CMD_COMPLETION_EVENT) ||
+ (slot_context->slot_event_bits & ALERT_EVENT) ||
+ (slot_context->slot_event_bits & SLOT_TIMER4_EVENT) ||
+ (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT)));
+
+ if (!(slot_context->slot_event_bits & SLOT_TIMER4_EVENT)) {
+ //
+ // delete the timer because we got an event other than the timer
+ //
+ del_timer_sync(&slot_context->slot_timer4);
+ }
+
+ //
+ // Alert: MRL Opened, Card Removed, Power-Fault?
+ //
+ if(slot_context->slot_event_bits & ALERT_EVENT) {
+
+ //
+ // Update attn_led_problem_event LED
+ //
+ spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags );
+ slot_context->problem_detected = TRUE;
+ hp_send_slot_event(slot_context, ATTN_LED_PROBLEM_EVENT);
+ spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags );
+
+ //
+ // Fail on-going request
+ //
+ slot_context->slot_completion.failed = HP_TRUE;
+ slot_context->slot_completion.done = TRUE;
+
+ //
+ // Grab Command MUTEX to disable slot
+ //
+ dbg("%s -->ALERT: slot_id[ %d:%d ] LSR_13:0[ %X ]",__FUNCTION__ ,
+ shpc_context->shpc_instance, slot_context->slot_number-1,
+ readl( slot_context->logical_slot_addr ) & 0x3F );
+ slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_disabled_wait_for_disable_cmd_available;
+ }
+ //
+ // cmd_completion_event, timeout
+ //
+ else if((shpc_context->shpc_event_bits & CMD_COMPLETION_EVENT) ||
+ (slot_context->slot_event_bits & SLOT_TIMER4_EVENT)) {
+ //
+ // Command completed OK?
+ //
+ status_reg.AsWord = readw(shpc_context->mmio_base_addr + SHPC_STATUS_REG_OFFSET);
+
+ if( status_reg.x.BSY == SHPC_STATUS_CLEARED &&
+ status_reg.x.MRLO_ERR == SHPC_STATUS_CLEARED &&
+ status_reg.x.INVCMD_ERR == SHPC_STATUS_CLEARED ) {
+ //
+ // Grab Bus MUTEX to validate speed/mode
+ //
+ slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_at_slot_disabled_wait_for_bus_available;
+ }
+ else {
+ //
+ // Fail on-going request
+ //
+ slot_context->slot_completion.failed = HP_TRUE;
+ slot_context->slot_completion.done = TRUE;
+
+ //
+ // Grab Command MUTEX to disable slot
+ //
+ dbg("%s -->CMD_ERROR: slot_id[ %d:%d ] Cmd[ %X ]",__FUNCTION__ ,
+ shpc_context->shpc_instance, slot_context->slot_number-1, status_reg.AsWord );
+ slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_disabled_wait_for_disable_cmd_available;
+ }
+ }
+ //
+ // exit_request_event
+ //
+ else {
+ status = STATUS_UNSUCCESSFUL;
+ }
+
+ //
+ // Release Command MUTEX
+ //
+ hp_set_slot_event_bit(slot_context, CMD_RELEASE_EVENT);
+ wake_up_interruptible(&slot_context->cmd_release_event);
+
+ return status;
+}
+
+
+// ****************************************************************************
+//
+// hp_at_slot_disabled_wait_for_bus_available()
+//
+// ****************************************************************************
+long
+hp_at_slot_disabled_wait_for_bus_available(
+ struct shpc_context* shpc_context,
+ struct slot_context* slot_context
+ )
+{
+ unsigned long old_irq_flags;
+ long status = STATUS_SUCCESS;
+ enum shpc_speed_mode max_speed_mode, bus_speed_mode;
+
+ dbg("%s -->slot_id[ %d:%d ]",__FUNCTION__ , shpc_context->shpc_instance, slot_context->slot_number-1 );
+
+ //
+ // Wait for Bus Available MUTEX
+ //
+ hp_set_slot_event_bit(slot_context, BUS_ACQUIRE_EVENT);
+ wake_up_interruptible(&slot_context->bus_acquire_event);
+
+ wait_event_interruptible(slot_context->slot_event,
+ ((shpc_context->shpc_event_bits & BUS_AVAILABLE_MUTEX_EVENT) ||
+ (slot_context->slot_event_bits & ALERT_EVENT) ||
+ (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT)));
+
+ //
+ // Alert: MRL Opened, Card Removed, Power-Fault?
+ //
+ if(slot_context->slot_event_bits & ALERT_EVENT) {
+
+ //
+ // Update attn_led_problem_event LED
+ //
+ spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags );
+ slot_context->problem_detected = TRUE;
+ hp_send_slot_event(slot_context, ATTN_LED_PROBLEM_EVENT);
+ spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags );
+
+ //
+ // Release Bus MUTEX
+ //
+ hp_set_slot_event_bit(slot_context, BUS_RELEASE_EVENT);
+ wake_up_interruptible(&slot_context->bus_release_event);
+
+ //
+ // Fail on-going request
+ //
+ slot_context->slot_completion.failed = HP_TRUE;
+ slot_context->slot_completion.done = TRUE;
+
+ //
+ // Grab Command MUTEX to disable slot
+ //
+ dbg("%s -->ALERT: slot_id[ %d:%d ] LSR_13:0[ %X ]",__FUNCTION__ ,
+ shpc_context->shpc_instance, slot_context->slot_number-1,
+ readl( slot_context->logical_slot_addr ) & 0x3F );
+ slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_disabled_wait_for_disable_cmd_available;
+ }
+ //
+ // bus_available_mutex
+ //
+ else if(shpc_context->shpc_event_bits & BUS_AVAILABLE_MUTEX_EVENT) {
+ //
+ // Get current HW speed/mode
+ //
+ bus_speed_mode = hp_get_bus_speed_mode( shpc_context );
+ max_speed_mode = hp_get_card_speed_mode( slot_context );
+ if( max_speed_mode > shpc_context->max_speed_mode ) {
+ //
+ // Can only go as fast as the controller allows
+ //
+ max_speed_mode = shpc_context->max_speed_mode;
+ }
+
+ //
+ // Grab global spinlock to check current speed/mode settings
+ //
+ spin_lock_irqsave( &shpc_context->shpc_spinlock, old_irq_flags );
+
+ //
+ // Other slots in contetion for bus speed/mode changes?
+ //
+ slot_context->in_bus_speed_mode_contention = FALSE;
+ max_speed_mode = hp_get_max_speed_mode( shpc_context, max_speed_mode );
+
+ //
+ // Make this card can handle PCI-66 speed/mode
+ //
+ if( max_speed_mode == SHPC_BUS_CONV_66 && !slot_context->card_pci66_capable ) {
+ //
+ // Fall back to slower common denominator
+ //
+ max_speed_mode = SHPC_BUS_CONV_33;
+ }
+
+ //
+ // Bus running at incompatible speed/mode?
+ //
+ if( bus_speed_mode != max_speed_mode ) {
+ //
+ // Other slots already enabled?
+ //
+ if( hp_signal_enabled_slots_to_rebalance_bus( shpc_context )) {
+ //
+ // Wait for enabled slots to release the bus, then change bus speed/mode
+ //
+ shpc_context->bus_speed_mode = max_speed_mode;
+ shpc_context->bus_released = FALSE;
+ hp_clear_shpc_event_bit(shpc_context, BUS_COMPLETE_EVENT);
+ slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_at_slot_disabled_wait_for_bus_released;
+ }
+ else {
+ //
+ // Change bus speed/mode to enable this slot
+ //
+ shpc_context->bus_speed_mode = max_speed_mode;
+ slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_at_slot_disabled_wait_for_speed_mode_cmd_available;
+ }
+ }
+ else {
+ //
+ // Enable slot at current bus speed/mode
+ //
+ shpc_context->bus_speed_mode = bus_speed_mode;
+ slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_at_slot_disabled_wait_for_enable_cmd_available;
+ }
+
+ //
+ // Flag this slot in contention for bus speed/mode validation
+ //
+ slot_context->in_bus_speed_mode_contention = TRUE;
+
+ //
+ // Release global spinlock since we're done checking speed/mode
+ //
+ spin_unlock_irqrestore( &shpc_context->shpc_spinlock, old_irq_flags );
+
+ dbg("%s -->ENABLE_IN_PROGRESS: slot_id[ %d:%d ] card_speed_mode[ %d+%d ] bus_speed_mode[ %d=>%d ]",__FUNCTION__ ,
+ shpc_context->shpc_instance, slot_context->slot_number-1,
+ slot_context->card_speed_mode, slot_context->card_pci66_capable,
+ bus_speed_mode, shpc_context->bus_speed_mode );
+ }
+ //
+ // exit_request_event
+ //
+ else {
+ //
+ // Release Bus MUTEX
+ //
+ hp_set_slot_event_bit(slot_context, BUS_RELEASE_EVENT);
+ wake_up_interruptible(&slot_context->bus_release_event);
+ status = STATUS_UNSUCCESSFUL;
+ }
+ return status;
+}
+
+
+// ****************************************************************************
+//
+// hp_at_slot_disabled_wait_for_bus_released()
+//
+// ****************************************************************************
+long
+hp_at_slot_disabled_wait_for_bus_released(
+ struct shpc_context* shpc_context,
+ struct slot_context* slot_context
+ )
+{
+ unsigned long old_irq_flags;
+ long status = STATUS_SUCCESS;
+
+ dbg("%s -->slot_id[ %d:%d ]",__FUNCTION__ , shpc_context->shpc_instance, slot_context->slot_number-1 );
+
+ //
+ // Wait for Bus Release EVENT while holding MUTEX
+ //
+ shpc_context->shpc_event_bits = 0;slot_context->slot_event_bits = 0;
+
+ wait_event_interruptible(slot_context->slot_event,
+ ((shpc_context->shpc_event_bits & BUS_RELEASE_EVENT) ||
+ (slot_context->slot_event_bits & ALERT_EVENT) ||
+ (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT)));
+
+ //
+ // Alert: MRL Opened, Card Removed, Power-Fault?
+ //
+ if(slot_context->slot_event_bits & ALERT_EVENT) {
+
+ //
+ // Update attn_led_problem_event LED
+ //
+ spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags );
+ slot_context->problem_detected = TRUE;
+ hp_send_slot_event(slot_context, ATTN_LED_PROBLEM_EVENT);
+ spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags );
+
+ //
+ // Release Bus MUTEX
+ //
+ hp_set_slot_event_bit(slot_context, BUS_RELEASE_EVENT);
+ wake_up_interruptible(&slot_context->bus_release_event);
+
+ //
+ // Fail on-going request
+ //
+ slot_context->slot_completion.failed = HP_TRUE;
+ slot_context->slot_completion.done = TRUE;
+
+ //
+ // Grab Command MUTEX to disable slot
+ //
+ dbg("%s -->ALERT: slot_id[ %d:%d ] LSR_13:0[ %X ]",__FUNCTION__ ,
+ shpc_context->shpc_instance, slot_context->slot_number-1,
+ readl( slot_context->logical_slot_addr ) & 0x3F );
+ slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_disabled_wait_for_disable_cmd_available;
+ }
+ //
+ // bus_release_event
+ //
+ else if(shpc_context->shpc_event_bits & BUS_RELEASE_EVENT) {
+ spin_lock_irqsave( &shpc_context->shpc_spinlock, old_irq_flags );
+ if( shpc_context->bus_released ) {
+ spin_unlock_irqrestore( &shpc_context->shpc_spinlock, old_irq_flags );
+ //
+ // Grab Command MUTEX to set Bus speed/mode
+ //
+ slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_at_slot_disabled_wait_for_speed_mode_cmd_available;
+ }
+ else {
+ spin_unlock_irqrestore( &shpc_context->shpc_spinlock, old_irq_flags );
+ //
+ // Release Bus MUTEX
+ //
+ hp_set_slot_event_bit(slot_context, BUS_RELEASE_EVENT);
+ wake_up_interruptible(&slot_context->bus_release_event);
+
+ //
+ // Fail on-going request
+ //
+ slot_context->slot_completion.failed = HP_TRUE;
+ slot_context->slot_completion.done = TRUE;
+
+ //
+ // Grab Command MUTEX to disable slot
+ //
+ dbg("%s -->ENABLE_REQ: slot_id[ %d:%d ] Cancelled: BUSY DevNode",__FUNCTION__ ,
+ shpc_context->shpc_instance, slot_context->slot_number-1 );
+ slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_disabled_wait_for_disable_cmd_available;
+ }
+ }
+ //
+ // exit_request_event
+ //
+ else {
+ //
+ // Release Bus MUTEX
+ //
+ hp_set_slot_event_bit(slot_context, BUS_RELEASE_EVENT);
+ wake_up_interruptible(&slot_context->bus_release_event);
+ status =STATUS_UNSUCCESSFUL;
+ }
+ return status;
+}
+
+
+// ****************************************************************************
+//
+// hp_at_slot_disabled_wait_for_speed_mode_cmd_available()
+//
+// ****************************************************************************
+long
+hp_at_slot_disabled_wait_for_speed_mode_cmd_available(
+ struct shpc_context* shpc_context,
+ struct slot_context* slot_context
+ )
+{
+ unsigned long old_irq_flags;
+ long status = STATUS_SUCCESS;
+ union SHPC_COMMAND_WREG command_reg;
+
+ dbg("%s -->slot_id[ %d:%d ]",__FUNCTION__ , shpc_context->shpc_instance, slot_context->slot_number-1 );
+
+ //
+ // Wait for Command Available MUTEX
+ //
+ hp_set_slot_event_bit(slot_context, CMD_ACQUIRE_EVENT);
+ wake_up_interruptible(&slot_context->cmd_acquire_event);
+
+ wait_event_interruptible(slot_context->slot_event,
+ ((shpc_context->shpc_event_bits & CMD_AVAILABLE_MUTEX_EVENT) ||
+ (slot_context->slot_event_bits & ALERT_EVENT) ||
+ (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT)));
+
+ //
+ // Alert: MRL Opened, Card Removed, Power-Fault?
+ //
+ if(slot_context->slot_event_bits & ALERT_EVENT) {
+
+ //
+ // Update attn_led_problem_event LED
+ //
+ spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags );
+ slot_context->problem_detected = TRUE;
+ hp_send_slot_event(slot_context, ATTN_LED_PROBLEM_EVENT);
+ spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags );
+
+ //
+ // Release Bus, Command MUTEX
+ //
+ hp_set_slot_event_bit(slot_context, CMD_RELEASE_EVENT);
+ wake_up_interruptible(&slot_context->cmd_release_event);
+ hp_set_slot_event_bit(slot_context, BUS_RELEASE_EVENT);
+ wake_up_interruptible(&slot_context->bus_release_event);
+
+ //
+ // Fail on-going request
+ //
+ slot_context->slot_completion.failed = HP_TRUE;
+ slot_context->slot_completion.done = TRUE;
+
+ //
+ // Grab Command MUTEX to disable slot
+ //
+ dbg("%s -->ALERT: slot_id[ %d:%d ] LSR_13:0[ %X ]",__FUNCTION__ ,
+ shpc_context->shpc_instance, slot_context->slot_number-1,
+ readl( slot_context->logical_slot_addr ) & 0x3F );
+ slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_disabled_wait_for_disable_cmd_available;
+ }
+ //
+ // cmd_available_mutex
+ //
+ else if(shpc_context->shpc_event_bits & CMD_AVAILABLE_MUTEX_EVENT) {
+ //
+ // Clear Completion EVENT before issuing next command
+ //
+ spin_lock_irqsave( &shpc_context->shpc_spinlock, old_irq_flags );
+ hp_clear_shpc_event_bit(shpc_context, CMD_COMPLETION_EVENT);
+ spin_unlock_irqrestore( &shpc_context->shpc_spinlock, old_irq_flags );
+
+ //
+ // Set Bus speed/mode
+ //
+ command_reg.Bus.code = SHPC_SET_BUS_SPEED_MODE;
+ command_reg.Bus.speed_mode = shpc_context->bus_speed_mode;
+ writew(command_reg.AsWord, shpc_context->mmio_base_addr + SHPC_COMMAND_REG_OFFSET);
+
+ //
+ // Wait for command to complete (while holding MUTEX)
+ //
+ slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_at_slot_disabled_wait_for_speed_mode_cmd_completion;
+ }
+ //
+ // exit_request_event
+ //
+ else {
+ //
+ // Release Bus, Command MUTEX
+ //
+ hp_set_slot_event_bit(slot_context, CMD_RELEASE_EVENT);
+ wake_up_interruptible(&slot_context->cmd_release_event);
+ hp_set_slot_event_bit(slot_context, BUS_RELEASE_EVENT);
+ wake_up_interruptible(&slot_context->bus_release_event);
+ status =STATUS_UNSUCCESSFUL;
+ }
+ return status;
+}
+
+
+// ****************************************************************************
+//
+// hp_at_slot_disabled_wait_for_speed_mode_cmd_completion()
+//
+// ****************************************************************************
+long
+hp_at_slot_disabled_wait_for_speed_mode_cmd_completion(
+ struct shpc_context* shpc_context,
+ struct slot_context* slot_context
+ )
+{
+ unsigned long old_irq_flags;
+ long status = STATUS_SUCCESS;
+ union SHPC_STATUS_WREG status_reg;
+
+ dbg("%s -->slot_id[ %d:%d ]",__FUNCTION__ , shpc_context->shpc_instance, slot_context->slot_number-1 );
+
+ //
+ // Setup our timer
+ //
+ hp_clear_slot_event_bit(slot_context, SLOT_TIMER5_EVENT);
+ slot_context->slot_timer5.data = (unsigned long)slot_context;
+ slot_context->slot_timer5.function = hp_slot_timer5_func;
+ slot_context->slot_timer5.expires = jiffies + FIFTEEN_SEC_TIMEOUT;
+ add_timer(&slot_context->slot_timer5);
+
+ //
+ // Wait for Command Completion EVENT while holding MUTEX
+ //
+ wait_event_interruptible(slot_context->slot_event,
+ ((shpc_context->shpc_event_bits & CMD_COMPLETION_EVENT) ||
+ (slot_context->slot_event_bits & ALERT_EVENT) ||
+ (slot_context->slot_event_bits & SLOT_TIMER5_EVENT) ||
+ (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT)));
+
+ if (!(slot_context->slot_event_bits & SLOT_TIMER5_EVENT)) {
+ //
+ // delete the timer because we got an event other than the timer
+ //
+ del_timer_sync(&slot_context->slot_timer5);
+ }
+
+ //
+ // Alert: MRL Opened, Card Removed, Power-Fault?
+ //
+ if(slot_context->slot_event_bits & ALERT_EVENT) {
+
+ //
+ // Update attn_led_problem_event LED
+ //
+ spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags );
+ slot_context->problem_detected = TRUE;
+ hp_send_slot_event(slot_context, ATTN_LED_PROBLEM_EVENT);
+ spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags );
+
+ //
+ // Release Bus MUTEX
+ //
+ hp_set_slot_event_bit(slot_context, BUS_RELEASE_EVENT);
+ wake_up_interruptible(&slot_context->bus_release_event);
+
+ //
+ // Fail on-going request
+ //
+ slot_context->slot_completion.failed = HP_TRUE;
+ slot_context->slot_completion.done = TRUE;
+
+ //
+ // Grab Command MUTEX to disable slot
+ //
+ dbg("%s -->ALERT: slot_id[ %d:%d ] LSR_13:0[ %X ]",__FUNCTION__ ,
+ shpc_context->shpc_instance, slot_context->slot_number-1,
+ readl( slot_context->logical_slot_addr ) & 0x3F );
+ slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_disabled_wait_for_disable_cmd_available;
+ }
+ //
+ // cmd_completion_event, timeout
+ //
+ else if((shpc_context->shpc_event_bits & CMD_COMPLETION_EVENT) ||
+ (slot_context->slot_event_bits & SLOT_TIMER5_EVENT)) {
+
+ //
+ // Command completed OK?
+ //
+ status_reg.AsWord = readw(shpc_context->mmio_base_addr + SHPC_STATUS_REG_OFFSET);
+
+ if( status_reg.x.BSY == SHPC_STATUS_CLEARED &&
+ status_reg.x.MRLO_ERR == SHPC_STATUS_CLEARED &&
+ status_reg.x.INVCMD_ERR == SHPC_STATUS_CLEARED &&
+ status_reg.x.INVSM_ERR == SHPC_STATUS_CLEARED ) {
+ //
+ // Grab Command MUTEX to enable slot
+ //
+ slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_at_slot_disabled_wait_for_enable_cmd_available;
+ }
+ else {
+ //
+ // Release Bus MUTEX
+ //
+ hp_set_slot_event_bit(slot_context, BUS_RELEASE_EVENT);
+ wake_up_interruptible(&slot_context->bus_release_event);
+
+ //
+ // Fail on-going request
+ //
+ slot_context->slot_completion.failed = HP_TRUE;
+ slot_context->slot_completion.done = TRUE;
+
+ //
+ // Grab Command MUTEX to disable slot
+ //
+ dbg("%s -->CMD_ERROR: slot_id[ %d:%d ] Cmd[ %X ]",__FUNCTION__ ,
+ shpc_context->shpc_instance, slot_context->slot_number-1, status_reg.AsWord );
+ slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_disabled_wait_for_disable_cmd_available;
+ }
+ }
+ //
+ // exit_request_event
+ //
+ else {
+ //
+ // Release Bus MUTEX
+ //
+ hp_set_slot_event_bit(slot_context, BUS_RELEASE_EVENT);
+ wake_up_interruptible(&slot_context->bus_release_event);
+ status =STATUS_UNSUCCESSFUL;
+ }
+ //
+ // Release Command MUTEX
+ //
+ hp_set_slot_event_bit(slot_context, CMD_RELEASE_EVENT);
+ wake_up_interruptible(&slot_context->cmd_release_event);
+
+ return status;
+}
+
+
+// ****************************************************************************
+//
+// hp_at_slot_disabled_wait_for_enable_cmd_available()
+//
+// ****************************************************************************
+long
+hp_at_slot_disabled_wait_for_enable_cmd_available(
+ struct shpc_context* shpc_context,
+ struct slot_context* slot_context
+ )
+{
+ unsigned long old_irq_flags;
+ long status = STATUS_SUCCESS;
+ union SHPC_COMMAND_WREG command_reg;
+
+ dbg("%s -->slot_id[ %d:%d ]",__FUNCTION__ , shpc_context->shpc_instance, slot_context->slot_number-1 );
+
+ //
+ // Wait for Command Available MUTEX
+ //
+ hp_set_slot_event_bit(slot_context, CMD_ACQUIRE_EVENT);
+ wake_up_interruptible(&slot_context->cmd_acquire_event);
+
+ wait_event_interruptible(slot_context->slot_event,
+ ((shpc_context->shpc_event_bits & CMD_AVAILABLE_MUTEX_EVENT) ||
+ (slot_context->slot_event_bits & ALERT_EVENT) ||
+ (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT)));
+
+ //
+ // Alert: MRL Opened, Card Removed, Power-Fault?
+ //
+ if(slot_context->slot_event_bits & ALERT_EVENT) {
+
+ //
+ // Update attn_led_problem_event LED
+ //
+ spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags );
+ slot_context->problem_detected = TRUE;
+ hp_send_slot_event(slot_context, ATTN_LED_PROBLEM_EVENT);
+ spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags );
+
+ //
+ // Release Bus, Command MUTEX
+ //
+ hp_set_slot_event_bit(slot_context, BUS_RELEASE_EVENT);
+ wake_up_interruptible(&slot_context->bus_release_event);
+ hp_set_slot_event_bit(slot_context, CMD_RELEASE_EVENT);
+ wake_up_interruptible(&slot_context->cmd_release_event);
+
+ //
+ // Fail on-going request
+ //
+ slot_context->slot_completion.failed = HP_TRUE;
+ slot_context->slot_completion.done = TRUE;
+
+ //
+ // Grab Command MUTEX to disable slot
+ //
+ dbg("%s -->ALERT: slot_id[ %d:%d ] LSR_13:0[ %X ]",__FUNCTION__ ,
+ shpc_context->shpc_instance, slot_context->slot_number-1,
+ readl( slot_context->logical_slot_addr ) & 0x3F );
+ slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_disabled_wait_for_disable_cmd_available;
+ }
+ //
+ // cmd_available_mutex
+ //
+ else if(shpc_context->shpc_event_bits & CMD_AVAILABLE_MUTEX_EVENT) {
+ //
+ // Clear Completion EVENT before issuing next command
+ //
+ spin_lock_irqsave( &shpc_context->shpc_spinlock, old_irq_flags );
+ hp_clear_shpc_event_bit(shpc_context, CMD_COMPLETION_EVENT);
+ spin_unlock_irqrestore( &shpc_context->shpc_spinlock, old_irq_flags );
+
+ //
+ // Enable the slot
+ //
+ dbg("%s ENABLING SLOT...",__FUNCTION__ );
+ command_reg.Slot.code = SHPC_SLOT_OPERATION;
+ command_reg.Slot.power_led = SHPC_LED_ON;
+ command_reg.Slot.attention_led = SHPC_led_NO_CHANGE;
+ command_reg.Slot.state = SHPC_ENABLE_SLOT;
+ command_reg.Slot.TGT = slot_context->slot_number;
+ writew(command_reg.AsWord, shpc_context->mmio_base_addr + SHPC_COMMAND_REG_OFFSET);
+
+ //
+ // Wait for command to complete (while holding Bus,Command MUTEX)
+ //
+ slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_at_slot_disabled_wait_for_enable_cmd_completion;
+ }
+ //
+ // exit_request_event
+ //
+ else {
+ //
+ // Release Bus, Command MUTEX
+ //
+ hp_set_slot_event_bit(slot_context, BUS_RELEASE_EVENT);
+ wake_up_interruptible(&slot_context->bus_release_event);
+ hp_set_slot_event_bit(slot_context, CMD_RELEASE_EVENT);
+ wake_up_interruptible(&slot_context->cmd_release_event);
+ status = STATUS_UNSUCCESSFUL;
+ }
+ return status;
+}
+
+
+// ****************************************************************************
+//
+// hp_at_slot_disabled_wait_for_enable_cmd_completion()
+//
+// ****************************************************************************
+long
+hp_at_slot_disabled_wait_for_enable_cmd_completion(
+ struct shpc_context* shpc_context,
+ struct slot_context* slot_context
+ )
+{
+ unsigned long old_irq_flags;
+ long status = STATUS_SUCCESS;
+ union SHPC_STATUS_WREG status_reg;
+
+ dbg("%s -->slot_id[ %d:%d ]",__FUNCTION__ , shpc_context->shpc_instance, slot_context->slot_number-1 );
+
+ //
+ // Setup our timer
+ //
+ hp_clear_slot_event_bit(slot_context, SLOT_TIMER6_EVENT);
+ slot_context->slot_timer6.data = (unsigned long)slot_context;
+ slot_context->slot_timer6.function = hp_slot_timer6_func;
+ slot_context->slot_timer6.expires = jiffies + FIFTEEN_SEC_TIMEOUT;
+ add_timer(&slot_context->slot_timer6);
+
+ //
+ // Wait for Command Completion EVENT while holding MUTEX
+ //
+ wait_event_interruptible(slot_context->slot_event,
+ ((shpc_context->shpc_event_bits & CMD_COMPLETION_EVENT) ||
+ (slot_context->slot_event_bits & ALERT_EVENT) ||
+ (slot_context->slot_event_bits & SLOT_TIMER6_EVENT) ||
+ (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT)));
+
+ if (!(slot_context->slot_event_bits & SLOT_TIMER6_EVENT)) {
+ //
+ // delete the timer because we got an event other than the timer
+ //
+ del_timer_sync(&slot_context->slot_timer6);
+ }
+
+ //
+ // Alert: MRL Opened, Card Removed, Power-Fault?
+ //
+ if(slot_context->slot_event_bits & ALERT_EVENT) {
+
+ //
+ // Update attn_led_problem_event LED
+ //
+ spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags );
+ slot_context->problem_detected = TRUE;
+ hp_send_slot_event(slot_context, ATTN_LED_PROBLEM_EVENT);
+ spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags );
+
+ //
+ // Release Bus MUTEX
+ //
+ hp_set_slot_event_bit(slot_context, BUS_RELEASE_EVENT);
+ wake_up_interruptible(&slot_context->bus_release_event);
+
+ //
+ // Fail on-going request
+ //
+ slot_context->slot_completion.failed = HP_TRUE;
+ slot_context->slot_completion.done = TRUE;
+
+ //
+ // Grab Command MUTEX to disable slot
+ //
+ dbg("%s -->ALERT: slot_id[ %d:%d ] LSR_13:0[ %X ]", __FUNCTION__,
+ shpc_context->shpc_instance, slot_context->slot_number-1,
+ readl( slot_context->logical_slot_addr ) & 0x3F );
+ slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_disabled_wait_for_disable_cmd_available;
+ }
+ //
+ // cmd_completion_event, timeout
+ //
+ else if((shpc_context->shpc_event_bits & CMD_COMPLETION_EVENT) ||
+ (slot_context->slot_event_bits & SLOT_TIMER6_EVENT)) {
+ //
+ // Command completed OK?
+ //
+ status_reg.AsWord = readw(shpc_context->mmio_base_addr + SHPC_STATUS_REG_OFFSET);
+
+ if( status_reg.x.BSY == SHPC_STATUS_CLEARED &&
+ status_reg.x.MRLO_ERR == SHPC_STATUS_CLEARED &&
+ status_reg.x.INVCMD_ERR == SHPC_STATUS_CLEARED &&
+ status_reg.x.INVSM_ERR == SHPC_STATUS_CLEARED ) {
+ //
+ // Wait for settling time
+ //
+ slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_at_slot_disabled_wait_for_enable_timeout;
+ }
+ else {
+ //
+ // Release Bus MUTEX
+ //
+ hp_set_slot_event_bit(slot_context, BUS_RELEASE_EVENT);
+ wake_up_interruptible(&slot_context->bus_release_event);
+
+ //
+ // Fail on-going request
+ //
+ slot_context->slot_completion.failed = HP_TRUE;
+ slot_context->slot_completion.done = TRUE;
+
+ //
+ // Grab Command MUTEX to disable slot
+ //
+ dbg("%s -->CMD_ERROR: slot_id[ %d:%d ] Cmd[ %X ]", __FUNCTION__,
+ shpc_context->shpc_instance, slot_context->slot_number-1, status_reg.AsWord );
+ slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_disabled_wait_for_disable_cmd_available;
+ }
+ }
+ //
+ // exit_request_event
+ //
+ else {
+ //
+ // Release Bus MUTEX
+ //
+ hp_set_slot_event_bit(slot_context, BUS_RELEASE_EVENT);
+ wake_up_interruptible(&slot_context->bus_release_event);
+ status = STATUS_UNSUCCESSFUL;
+ }
+ //
+ // Release Command MUTEX
+ //
+ up( &shpc_context->cmd_available_mutex);
+
+ return status;
+}
+
+
+// ****************************************************************************
+//
+// hp_at_slot_disabled_wait_for_enable_timeout()
+//
+// ****************************************************************************
+long
+hp_at_slot_disabled_wait_for_enable_timeout(
+ struct shpc_context* shpc_context,
+ struct slot_context* slot_context
+ )
+{
+ unsigned long old_irq_flags;
+ long status = STATUS_SUCCESS;
+
+ dbg("%s -->slot_id[ %d:%d ]", __FUNCTION__, shpc_context->shpc_instance, slot_context->slot_number-1 );
+
+ //
+ // Setup our timer
+ //
+ hp_clear_slot_event_bit(slot_context, SLOT_TIMER7_EVENT);
+ slot_context->slot_timer7.data = (unsigned long)slot_context;
+ slot_context->slot_timer7.function = hp_slot_timer7_func;
+ slot_context->slot_timer7.expires = jiffies + ONE_SEC_TIMEOUT;
+ add_timer(&slot_context->slot_timer7);
+
+ //
+ // Wait for timeout
+ //
+ wait_event_interruptible(slot_context->slot_event,
+ ((slot_context->slot_event_bits & ALERT_EVENT) ||
+ (slot_context->slot_event_bits & SLOT_TIMER7_EVENT) ||
+ (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT)));
+
+ if (!(slot_context->slot_event_bits & SLOT_TIMER7_EVENT)) {
+ //
+ // delete the timer because we got an event other than the timer
+ //
+ del_timer_sync(&slot_context->slot_timer7);
+ }
+ //
+ // Alert: MRL Opened, Card Removed, Power-Fault?
+ //
+ if(slot_context->slot_event_bits & ALERT_EVENT) {
+
+ //
+ // Update attn_led_problem_event LED
+ //
+ spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags );
+ slot_context->problem_detected = TRUE;
+ hp_send_slot_event(slot_context, ATTN_LED_PROBLEM_EVENT);
+ spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags );
+
+ //
+ // Fail on-going request
+ //
+ slot_context->slot_completion.failed = HP_TRUE;
+ slot_context->slot_completion.done = TRUE;
+
+ //
+ // Grab Command MUTEX to disable slot
+ //
+ dbg("%s -->ALERT: slot_id[ %d:%d ] LSR_13:0[ %X ]", __FUNCTION__,
+ shpc_context->shpc_instance, slot_context->slot_number-1,
+ readl( slot_context->logical_slot_addr ) & 0x3F );
+ slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_disabled_wait_for_disable_cmd_available;
+ }
+ //
+ // timeout
+ //
+ else if(slot_context->slot_event_bits & SLOT_TIMER7_EVENT) {
+ //
+ // Flag this slot as ENABLED
+ //
+ hp_flag_slot_as_enabled( shpc_context, slot_context );
+
+ //
+ // Complete succesful ENABLE request
+ //
+ slot_context->slot_completion.failed = HP_FALSE;
+ slot_context->slot_completion.done = TRUE;
+ slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_at_slot_enabled_wait_for_slot_request;
+ }
+ //
+ // exit_request_event
+ //
+ else {
+ status =STATUS_UNSUCCESSFUL;
+ }
+ //
+ // Release Bus MUTEX
+ //
+ hp_set_slot_event_bit(slot_context, BUS_RELEASE_EVENT);
+ wake_up_interruptible(&slot_context->bus_release_event);
+
+ return status;
+}
+
+
+// ****************************************************************************
+//
+// hp_to_slot_disabled_wait_for_led_cmd_available()
+//
+// ****************************************************************************
+long
+hp_to_slot_disabled_wait_for_led_cmd_available(
+ struct shpc_context* shpc_context,
+ struct slot_context* slot_context
+ )
+{
+ unsigned long old_irq_flags;
+ long status = STATUS_SUCCESS;
+ union SHPC_COMMAND_WREG command_reg;
+
+ dbg("%s -->slot_id[ %d:%d ]", __FUNCTION__, shpc_context->shpc_instance, slot_context->slot_number-1 );
+
+ //
+ // Wait for Command Available MUTEX
+ //
+ hp_set_slot_event_bit(slot_context, CMD_ACQUIRE_EVENT);
+ wake_up_interruptible(&slot_context->cmd_acquire_event);
+
+ wait_event_interruptible(slot_context->slot_event,
+ ((shpc_context->shpc_event_bits & CMD_AVAILABLE_MUTEX_EVENT) ||
+ (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT)));
+
+ if (shpc_context->shpc_event_bits & CMD_AVAILABLE_MUTEX_EVENT) {
+ //
+ // Clear Completion EVENT before issuing next command
+ //
+ spin_lock_irqsave( &shpc_context->shpc_spinlock, old_irq_flags );
+ hp_clear_shpc_event_bit(shpc_context, CMD_COMPLETION_EVENT);
+ spin_unlock_irqrestore( &shpc_context->shpc_spinlock, old_irq_flags );
+
+ //
+ // Turn OFF Power LED
+ //
+ command_reg.Slot.code = SHPC_SLOT_OPERATION;
+ command_reg.Slot.power_led = SHPC_LED_OFF;
+ command_reg.Slot.attention_led = SHPC_led_NO_CHANGE;
+ command_reg.Slot.state = SHPC_SLOT_NO_CHANGE;
+ command_reg.Slot.TGT = slot_context->slot_number;
+ writew(command_reg.AsWord, shpc_context->mmio_base_addr + SHPC_COMMAND_REG_OFFSET);
+
+ //
+ // Wait for Power LED command to complete
+ //
+ slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_disabled_wait_for_led_cmd_completion;
+ }
+ else { // exit_request_event
+ //
+ // Release Command MUTEX
+ //
+ hp_set_slot_event_bit(slot_context, CMD_RELEASE_EVENT);
+ wake_up_interruptible(&slot_context->cmd_release_event);
+ status = STATUS_UNSUCCESSFUL;
+ }
+ return status;
+}
+
+// ****************************************************************************
+//
+// hp_to_slot_disabled_wait_for_led_cmd_completion()
+//
+// ****************************************************************************
+long
+hp_to_slot_disabled_wait_for_led_cmd_completion(
+ struct shpc_context* shpc_context,
+ struct slot_context* slot_context
+ )
+{
+ long status = STATUS_SUCCESS;
+
+ dbg("%s -->slot_id[ %d:%d ]", __FUNCTION__, shpc_context->shpc_instance, slot_context->slot_number-1 );
+
+ //
+ // Setup our timer
+ //
+ hp_clear_slot_event_bit(slot_context, SLOT_TIMER8_EVENT);
+ slot_context->slot_timer8.data = (unsigned long)slot_context;
+ slot_context->slot_timer8.function = hp_slot_timer8_func;
+ slot_context->slot_timer8.expires = jiffies + ONE_SEC_TIMEOUT;
+ add_timer(&slot_context->slot_timer8);
+
+ //
+ // Wait for Command Completion EVENT while holding MUTEX
+ //
+ wait_event_interruptible(slot_context->slot_event,
+ ((shpc_context->shpc_event_bits & CMD_COMPLETION_EVENT) ||
+ (slot_context->slot_event_bits & SLOT_TIMER8_EVENT) ||
+ (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT)));
+
+ if (!(slot_context->slot_event_bits & SLOT_TIMER8_EVENT)) {
+ //
+ // delete the timer because we got an event other than the timer
+ //
+ del_timer_sync(&slot_context->slot_timer8);
+ }
+
+ if((shpc_context->shpc_event_bits & CMD_COMPLETION_EVENT) ||
+ (slot_context->slot_event_bits & SLOT_TIMER8_EVENT)) {
+ //
+ // Wait for next request
+ //
+ slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_at_slot_disabled_wait_for_slot_request;
+
+ }else{ // exit_request_event
+ status = STATUS_UNSUCCESSFUL;
+ }
+
+ //
+ // Release command MUTEX
+ //
+ hp_set_slot_event_bit(slot_context, CMD_RELEASE_EVENT);
+ wake_up_interruptible(&slot_context->cmd_release_event);
+
+ return status;
+}
+
+
+// ****************************************************************************
+//
+// hp_to_slot_disabled_wait_for_disable_cmd_available()
+//
+// ****************************************************************************
+long
+hp_to_slot_disabled_wait_for_disable_cmd_available(
+ struct shpc_context* shpc_context,
+ struct slot_context* slot_context
+ )
+{
+ unsigned long old_irq_flags;
+ long status = STATUS_SUCCESS;
+ union SHPC_COMMAND_WREG command_reg;
+
+ dbg("%s -->slot_id[ %d:%d ]", __FUNCTION__, shpc_context->shpc_instance, slot_context->slot_number-1 );
+
+ //
+ // Wait for Command Available MUTEX
+ //
+ hp_set_slot_event_bit(slot_context, CMD_ACQUIRE_EVENT);
+ wake_up_interruptible(&slot_context->cmd_acquire_event);
+
+ wait_event_interruptible(slot_context->slot_event,
+ ((shpc_context->shpc_event_bits & CMD_AVAILABLE_MUTEX_EVENT) ||
+ (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT)));
+
+ if(shpc_context->shpc_event_bits & CMD_AVAILABLE_MUTEX_EVENT) {
+ //
+ // Clear Completion EVENT before issuing next command
+ //
+ spin_lock_irqsave( &shpc_context->shpc_spinlock, old_irq_flags );
+ hp_clear_shpc_event_bit(shpc_context, CMD_COMPLETION_EVENT);
+ spin_unlock_irqrestore( &shpc_context->shpc_spinlock, old_irq_flags );
+
+ //
+ // Disable slot and turn OFF Power LED
+ //
+ command_reg.Slot.code = SHPC_SLOT_OPERATION;
+ command_reg.Slot.power_led = SHPC_LED_OFF;
+ command_reg.Slot.attention_led = SHPC_led_NO_CHANGE;
+ command_reg.Slot.state = SHPC_DISABLE_SLOT;
+ command_reg.Slot.TGT = slot_context->slot_number;
+ writew(command_reg.AsWord, shpc_context->mmio_base_addr + SHPC_COMMAND_REG_OFFSET);
+
+ //
+ // Wait for command to complete
+ //
+ slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_disabled_wait_for_disable_cmd_completion;
+ }
+ else {
+ //
+ // Release Command MUTEX
+ //
+ hp_set_slot_event_bit(slot_context, CMD_RELEASE_EVENT);
+ wake_up_interruptible(&slot_context->cmd_release_event);
+ status = STATUS_UNSUCCESSFUL;
+ }
+ return status;
+}
+
+
+// ****************************************************************************
+//
+// hp_to_slot_disabled_wait_for_disable_cmd_completion()
+//
+// ****************************************************************************
+long
+hp_to_slot_disabled_wait_for_disable_cmd_completion(
+ struct shpc_context* shpc_context,
+ struct slot_context* slot_context
+ )
+{
+ long status = STATUS_SUCCESS;
+
+ dbg("%s -->slot_id[ %d:%d ]", __FUNCTION__, shpc_context->shpc_instance, slot_context->slot_number-1 );
+
+ //
+ // Setup our timer
+ //
+ hp_clear_slot_event_bit(slot_context, SLOT_TIMER9_EVENT);
+ slot_context->slot_timer9.data = (unsigned long)slot_context;
+ slot_context->slot_timer9.function = hp_slot_timer9_func;
+ slot_context->slot_timer9.expires = jiffies + FIFTEEN_SEC_TIMEOUT;
+ add_timer(&slot_context->slot_timer9);
+
+ //
+ // Wait for Command Completion EVENT while holding MUTEX
+ //
+ wait_event_interruptible(slot_context->slot_event,
+ ((shpc_context->shpc_event_bits & CMD_COMPLETION_EVENT) ||
+ (slot_context->slot_event_bits & SLOT_TIMER9_EVENT) ||
+ (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT)));
+
+ if (!(slot_context->slot_event_bits & SLOT_TIMER9_EVENT)) {
+ //
+ // delete the timer because we got an event other than the timer
+ //
+ del_timer_sync(&slot_context->slot_timer9);
+ }
+
+ if((shpc_context->shpc_event_bits & CMD_COMPLETION_EVENT) ||
+ (slot_context->slot_event_bits & SLOT_TIMER9_EVENT)) {
+ //
+ // Flag this slot as DISABLED (if enabled)
+ //
+ if( hp_flag_slot_as_disabled( shpc_context, slot_context )) {
+ //
+ // Wait for settling time
+ //
+ slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_disabled_wait_for_disable_timeout;
+ }
+ else {
+ //
+ // Wait for next request
+ //
+ slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_at_slot_disabled_wait_for_slot_request;
+ }
+ }
+ else { // exit_request_event
+ status = STATUS_UNSUCCESSFUL;
+ }
+ //
+ // Release command MUTEX
+ //
+ hp_set_slot_event_bit(slot_context, CMD_RELEASE_EVENT);
+ wake_up_interruptible(&slot_context->cmd_release_event);
+
+ return status;
+}
+
+// ****************************************************************************
+//
+// hp_to_slot_disabled_wait_for_DisableTimeout()
+//
+// ****************************************************************************
+long
+hp_to_slot_disabled_wait_for_disable_timeout(
+ struct shpc_context* shpc_context,
+ struct slot_context* slot_context
+ )
+{
+ long status = STATUS_SUCCESS;
+
+ dbg("%s -->slot_id[ %d:%d ]", __FUNCTION__, shpc_context->shpc_instance, slot_context->slot_number-1 );
+
+ //
+ // Setup our timer
+ //
+ hp_clear_slot_event_bit(slot_context, SLOT_TIMER10_EVENT);
+ slot_context->slot_timer10.data = (unsigned long)slot_context;
+ slot_context->slot_timer10.function = hp_slot_timer10_func;
+ slot_context->slot_timer10.expires = jiffies + ONE_SEC_TIMEOUT;
+ add_timer(&slot_context->slot_timer10);
+
+ //
+ // Wait for timeout
+ //
+ wait_event_interruptible(slot_context->slot_event,
+ ((slot_context->slot_event_bits & SLOT_TIMER10_EVENT) ||
+ (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT)));
+
+ if (!(slot_context->slot_event_bits & SLOT_TIMER10_EVENT)) {
+ //
+ // delete the timer because we got an event other than the timer
+ //
+ del_timer_sync(&slot_context->slot_timer10);
+ }
+
+ //
+ // timeout
+ //
+ if(slot_context->slot_event_bits & SLOT_TIMER10_EVENT) {
+ slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_disabled_wait_for_bus_available;
+ }
+ //
+ // exit_request_event
+ //
+ else {
+ status = STATUS_UNSUCCESSFUL;
+ }
+ return status;
+}
+
+
+// ****************************************************************************
+//
+// hp_to_slot_disabled_wait_for_bus_available()
+//
+// ****************************************************************************
+long
+hp_to_slot_disabled_wait_for_bus_available(
+ struct shpc_context* shpc_context,
+ struct slot_context* slot_context
+ )
+{
+ unsigned long old_irq_flags;
+ long status = STATUS_SUCCESS;
+ enum shpc_speed_mode max_speed_mode;
+
+ dbg("%s -->slot_id[ %d:%d ]", __FUNCTION__, shpc_context->shpc_instance, slot_context->slot_number-1 );
+
+ //
+ // Wait for Bus Available MUTEX
+ //
+ hp_set_slot_event_bit(slot_context, BUS_ACQUIRE_EVENT);
+ wake_up_interruptible(&slot_context->bus_acquire_event);
+
+ wait_event_interruptible(slot_context->slot_event,
+ ((shpc_context->shpc_event_bits & BUS_AVAILABLE_MUTEX_EVENT) ||
+ (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT)));
+
+ if(shpc_context->shpc_event_bits & BUS_AVAILABLE_MUTEX_EVENT) {
+ //
+ // Grab global spinlock to check current speed/mode settings
+ //
+ spin_lock_irqsave( &shpc_context->shpc_spinlock, old_irq_flags );
+
+ //
+ // Flag this slot out of contetion for bus speed/mode changes
+ //
+ slot_context->in_bus_speed_mode_contention = FALSE;
+
+ //
+ // Enabled slots running at maximum speed/mode?
+ //
+ if( shpc_context->slots_enabled ) {
+ max_speed_mode = hp_get_max_speed_mode( shpc_context, shpc_context->max_speed_mode );
+
+ //
+ // Signal enabled slots to release the bus, then change bus speed/mode
+ //
+ if( shpc_context->bus_speed_mode != max_speed_mode ) {
+ hp_signal_enabled_slots_to_rebalance_bus( shpc_context );
+ }
+ }
+
+ //
+ // Release global spinlock since we're done checking speed/mode
+ //
+ spin_unlock_irqrestore( &shpc_context->shpc_spinlock, old_irq_flags );
+
+ //
+ // Wait for next request on this slot
+ //
+ slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_at_slot_disabled_wait_for_slot_request;
+ }
+ else { // exit_request_event
+ status = STATUS_UNSUCCESSFUL;
+ }
+
+ //
+ // Release Bus MUTEX
+ //
+ hp_set_slot_event_bit(slot_context, BUS_RELEASE_EVENT);
+ wake_up_interruptible(&slot_context->bus_release_event);
+
+ return status;
+}
+
--- /dev/null
+/*
+ * Copyright (C) 2003 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (C) 2003 IBM Corp.
+ * Copyright (C) 2002-2003 Advanced Micro Devices
+ *
+ * YOUR USE OF THIS CODE IS SUBJECT TO THE TERMS
+ * AND CONDITIONS OF THE GNU GENERAL PUBLIC
+ * LICENSE FOUND IN THE "GPL.TXT" FILE THAT IS
+ * INCLUDED WITH THIS FILE AND POSTED AT
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * Send feedback to <david.keck@amd.com>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/sched.h>
+#include "amdshpc_ddi.h"
+#include "amdshpc.h"
+
+
+// ****************************************************************************
+//
+// hp_interrupt_service()
+//
+// ****************************************************************************
+irqreturn_t hp_interrupt_service(int IRQ, void *v, struct pt_regs *regs)
+{
+ struct shpc_context *shpc_context = v;
+ struct slot_context *slot_context;
+ union SHPC_SERR_INT_DWREG SerrIntReg;
+ union SHPC_INT_LOCATOR_DWREG IntLocatorReg, SlotIndex;
+ union SHPC_LOGICAL_SLOT_DWREG logical_slot_reg;
+ u8 IsShpcInterrupt = FALSE;
+ u8 i;
+
+ //
+ // Device at PowerDeviceD0?
+ //
+ if( !shpc_context->at_power_device_d0 ) {
+ return IRQ_HANDLED;
+ }
+
+ //
+ // Read Interrupt Locator Register ( Pending Interrupts )
+ //
+ IntLocatorReg.AsDWord = readl(shpc_context->mmio_base_addr + SHPC_INT_LOCATOR_REG_OFFSET);
+
+ //
+ // Read SERR-INT Register ( Global Mask, Command Completion )
+ //
+ SerrIntReg.AsDWord = readl(shpc_context->mmio_base_addr + SHPC_SERR_INT_REG_OFFSET);
+
+ //
+ // Global Interrupts Disabled?
+ //
+// if( SerrIntReg.x.GIM == SHPC_MASKED ) {
+// return FALSE;
+// }
+
+ //
+ // Command Completion?
+ //
+ if( IntLocatorReg.x.CC_IP ) {
+ if(( SerrIntReg.x.CC_STS == SHPC_STATUS_SET ) &&
+ ( SerrIntReg.x.CC_IM == SHPC_UNMASKED )) {
+ //
+ // Schedule Dpc
+ //
+ IsShpcInterrupt = TRUE;
+ tasklet_schedule(&shpc_context->cmd_completion_dpc);
+
+ //
+ // Clear Interrput (Write-back 1 to STS bits)
+ //
+ writel(SerrIntReg.AsDWord, shpc_context->mmio_base_addr + SHPC_SERR_INT_REG_OFFSET);
+ }
+ }
+
+ //
+ // Slot Interrupts?
+ //
+ if( IntLocatorReg.x.SLOT_IP ) {
+ //
+ // Walk a "1" thru each bit position (one bit per slot)
+ //
+ for( i=0, SlotIndex.x.SLOT_IP = 1; i< SHPC_MAX_NUM_SLOTS; ++i, SlotIndex.x.SLOT_IP <<= 1 ) {
+ slot_context = &shpc_context->slot_context[ i ];
+
+ //
+ // Interrupt from this slot?
+ //
+ if( IntLocatorReg.x.SLOT_IP & SlotIndex.x.SLOT_IP ) {
+ //
+ // Read Logical Slot Register
+ //
+ logical_slot_reg.AsDWord = readl( slot_context->logical_slot_addr );
+
+ //
+ // Attention Button?
+ //
+ if(( logical_slot_reg.x.ABP_STS == SHPC_STATUS_SET ) &&
+ ( logical_slot_reg.x.AB_IM == SHPC_UNMASKED )) {
+ //
+ // Schedule Dpc
+ //
+ IsShpcInterrupt = TRUE;
+ tasklet_schedule(&slot_context->attn_button_dpc);
+ }
+
+ //
+ // MRL Sensor?
+ //
+ if(( logical_slot_reg.x.MRLSC_STS == SHPC_STATUS_SET ) &&
+ ( logical_slot_reg.x.MRLS_IM == SHPC_UNMASKED )) {
+ //
+ // Schedule Dpc
+ //
+ IsShpcInterrupt = TRUE;
+ tasklet_schedule(&slot_context->mrl_sensor_dpc);
+ }
+
+ //
+ // Card Presence Change?
+ //
+ if(( logical_slot_reg.x.CPC_STS == SHPC_STATUS_SET ) &&
+ ( logical_slot_reg.x.CP_IM == SHPC_UNMASKED )) {
+ //
+ // Schedule Dpc
+ //
+ IsShpcInterrupt = TRUE;
+ tasklet_schedule(&slot_context->card_presence_dpc);
+ }
+
+ //
+ // Isolated Power Fault?
+ //
+ if(( logical_slot_reg.x.IPF_STS == SHPC_STATUS_SET ) &&
+ ( logical_slot_reg.x.IPF_IM == SHPC_UNMASKED )) {
+ //
+ // Schedule Dpc
+ //
+ IsShpcInterrupt = TRUE;
+ tasklet_schedule(&slot_context->isolated_power_fault_dpc);
+ }
+
+ //
+ // Connected Power Fault?
+ //
+ if(( logical_slot_reg.x.CPF_STS == SHPC_STATUS_SET ) &&
+ ( logical_slot_reg.x.CPF_IM == SHPC_UNMASKED )) {
+ //
+ // Schedule Dpc
+ //
+ IsShpcInterrupt = TRUE;
+ tasklet_schedule(&slot_context->connected_power_fault_dpc);
+ }
+
+ //
+ // Clear Interrputs for this slot (Write-back 1 to STS bits)
+ //
+ writel(logical_slot_reg.AsDWord, slot_context->logical_slot_addr);
+ }
+ }
+ }
+ return IRQ_HANDLED;
+}
+
+
+// ****************************************************************************
+//
+// hp_attn_button_dpc() @ DISPATCH_LEVEL
+//
+// ****************************************************************************
+void
+hp_attn_button_dpc(
+ unsigned long deferred_context // struct slot_context*
+ )
+{
+ struct slot_context* slot_context = ( struct slot_context* )deferred_context;
+ struct shpc_context* shpc_context = slot_context->shpc_context;
+
+ dbg("%s ->slot_id[ %d:%d ]", __FUNCTION__, shpc_context->shpc_instance, (slot_context->slot_number-1) );
+ //
+ // Notification Event: Attention Button pressed
+ //
+ spin_lock( &slot_context->slot_spinlock );
+ hp_send_slot_event(slot_context, ATTN_BUTTON_EVENT);
+ spin_unlock( &slot_context->slot_spinlock );
+}
+
+
+// ****************************************************************************
+//
+// hp_card_presence_dpc() @ DISPATCH_LEVEL
+//
+// ****************************************************************************
+void
+hp_card_presence_dpc(
+ unsigned long deferred_context // struct slot_context*
+ )
+{
+ struct slot_context* slot_context = ( struct slot_context* )deferred_context;
+ struct shpc_context* shpc_context = slot_context->shpc_context;
+ union SHPC_LOGICAL_SLOT_DWREG logical_slot_reg;
+
+ dbg("%s -->slot_id[ %d:%d ]", __FUNCTION__, shpc_context->shpc_instance, slot_context->slot_number-1 );
+
+ //
+ // Signal registered user EVENT
+ //
+ hp_signal_user_event_at_dpc_level( shpc_context );
+
+ //
+ // Card Removed?
+ //
+ logical_slot_reg.AsDWord = readl( slot_context->logical_slot_addr );
+ if( logical_slot_reg.x.PRSNT1_2 == SHPC_SLOT_EMPTY ) {
+ //
+ // Signal Alert EVENT
+ //
+ spin_lock( &slot_context->slot_spinlock );
+ hp_send_slot_event(slot_context, ALERT_EVENT);
+ spin_unlock( &slot_context->slot_spinlock );
+ }
+}
+
+
+// ****************************************************************************
+//
+// hp_mrl_sensor_dpc() @ DISPATCH_LEVEL
+//
+// ****************************************************************************
+void
+hp_mrl_sensor_dpc(
+ unsigned long deferred_context // struct slot_context*
+ )
+{
+ struct slot_context* slot_context = ( struct slot_context* )deferred_context;
+ struct shpc_context* shpc_context = slot_context->shpc_context;
+ union SHPC_LOGICAL_SLOT_DWREG logical_slot_reg;
+
+ dbg("%s -->slot_id[ %d:%d ]", __FUNCTION__, shpc_context->shpc_instance, slot_context->slot_number-1 );
+
+ //
+ // Signal registered user EVENT
+ //
+ hp_signal_user_event_at_dpc_level( shpc_context );
+
+ //
+ // MRL Sensor opened?
+ //
+ logical_slot_reg.AsDWord = readl( slot_context->logical_slot_addr );
+ if( logical_slot_reg.x.MRLS == SHPC_MRL_OPEN ) {
+ //
+ // Card Present?
+ //
+ if( logical_slot_reg.x.PRSNT1_2 != SHPC_SLOT_EMPTY ) {
+ //
+ // Signal Alert EVENT
+ //
+ spin_lock( &slot_context->slot_spinlock );
+ hp_send_slot_event(slot_context, ALERT_EVENT);
+ spin_unlock( &slot_context->slot_spinlock );
+ }
+ }
+ else {
+ //
+ // Power Fault detected whith MRL closed?
+ // Note: Golem A0 may not generate power-fault interrupt
+ if( logical_slot_reg.x.PF == SHPC_STATUS_SET ) {
+ //
+ // Signal Alert EVENT
+ //
+ spin_lock( &slot_context->slot_spinlock );
+ hp_send_slot_event(slot_context, ALERT_EVENT);
+ spin_unlock( &slot_context->slot_spinlock );
+ }
+ }
+}
+
+// ****************************************************************************
+//
+// isolated_power_fault_dpc() @ DISPATCH_LEVEL
+//
+// ****************************************************************************
+void
+hp_isolated_power_fault_dpc(
+ unsigned long deferred_context // struct slot_context*
+ )
+{
+ struct slot_context* slot_context = ( struct slot_context* )deferred_context;
+ struct shpc_context* shpc_context = slot_context->shpc_context;
+ union SHPC_LOGICAL_SLOT_DWREG logical_slot_reg;
+
+ dbg("%s -->slot_id[ %d:%d ]", __FUNCTION__, shpc_context->shpc_instance, slot_context->slot_number-1 );
+
+ //
+ // Signal registered user EVENT
+ //
+ hp_signal_user_event_at_dpc_level( shpc_context );
+
+ //
+ // Power Fault detected?
+ //
+ logical_slot_reg.AsDWord = readl( slot_context->logical_slot_addr );
+ if( logical_slot_reg.x.PF == SHPC_STATUS_SET ) {
+ //
+ // Signal Alert EVENT
+ //
+ spin_lock( &slot_context->slot_spinlock );
+ hp_send_slot_event(slot_context, ALERT_EVENT);
+ spin_unlock( &slot_context->slot_spinlock );
+ }
+}
+
+
+// ****************************************************************************
+//
+// connected_power_fault_dpc() @ DISPATCH_LEVEL
+//
+// ****************************************************************************
+void
+hp_connected_power_fault_dpc(
+ unsigned long deferred_context // struct slot_context*
+ )
+{
+ struct slot_context* slot_context = ( struct slot_context* )deferred_context;
+ struct shpc_context* shpc_context = slot_context->shpc_context;
+ union SHPC_LOGICAL_SLOT_DWREG logical_slot_reg;
+
+ dbg("%s -->slot_id[ %d:%d ]", __FUNCTION__, shpc_context->shpc_instance, slot_context->slot_number-1 );
+
+ //
+ // Signal registered user EVENT
+ //
+ hp_signal_user_event_at_dpc_level( shpc_context );
+
+ //
+ // Power Fault detected?
+ //
+ logical_slot_reg.AsDWord = readl( slot_context->logical_slot_addr );
+ if( logical_slot_reg.x.PF == SHPC_STATUS_SET ) {
+ //
+ // Signal Alert EVENT
+ //
+ spin_lock( &slot_context->slot_spinlock );
+ hp_send_slot_event(slot_context, ALERT_EVENT);
+ spin_unlock( &slot_context->slot_spinlock );
+ }
+}
+
+
+// ****************************************************************************
+//
+// hp_cmd_completion_dpc() @ DISPATCH_LEVEL
+//
+// ****************************************************************************
+void
+hp_cmd_completion_dpc(
+ unsigned long deferred_context // struct shpc_context*
+ )
+{
+ struct shpc_context* shpc_context = ( struct shpc_context* )deferred_context;
+
+ dbg("%s -->HwInstance[ %d ]", __FUNCTION__, shpc_context->shpc_instance );
+
+ //
+ // Notification Event: Command Completion
+ //
+ spin_lock( &shpc_context->shpc_spinlock );
+ hp_send_event_to_all_slots(shpc_context, CMD_COMPLETION_EVENT);
+ spin_unlock( &shpc_context->shpc_spinlock );
+}
--- /dev/null
+/*
+ * Copyright (C) 2001,2003 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (C) 2001,2003 IBM Corp.
+ * Copyright (C) 2002-2003 Advanced Micro Devices
+ *
+ * YOUR USE OF THIS CODE IS SUBJECT TO THE TERMS
+ * AND CONDITIONS OF THE GNU GENERAL PUBLIC
+ * LICENSE FOUND IN THE "GPL.TXT" FILE THAT IS
+ * INCLUDED WITH THIS FILE AND POSTED AT
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * Send feedback to <david.keck@amd.com>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/sched.h>
+#include "amdshpc_ddi.h"
+#include "amdshpc.h"
+
+
+// ****************************************************************************
+//
+// hp_wait_for_attn_led_request() @ PASSIVE_LEVEL
+//
+// ****************************************************************************
+long
+hp_wait_for_attn_led_request(
+ struct shpc_context* shpc_context,
+ struct slot_context* slot_context
+ )
+{
+ unsigned long old_irq_flags;
+ long status = STATUS_SUCCESS;
+ union SLOT_STATUS_INFO slot_status;
+ union SHPC_LOGICAL_SLOT_DWREG logical_slot_reg;
+
+ dbg("%s -->slot_id[ %d:%d ]", __FUNCTION__, shpc_context->shpc_instance, slot_context->slot_number-1 );
+
+ //
+ // LED "Normal": complete pending request
+ //
+ if( slot_context->attn_led_completion.done ) {
+ //
+ // Call Completion Callback()
+ //
+ hp_QuerySlotStatus( shpc_context, slot_context->slot_number - 1, &slot_status );
+ slot_status.x.lu_request_failed = slot_context->slot_completion.failed;
+ shpc_context->async_callback(
+ shpc_context->driver_context,
+ slot_context->slot_number - 1,
+ slot_context->attn_led_completion.type,
+ slot_status,
+ slot_context->attn_led_completion.request_context );
+
+ //
+ // Signal registered user EVENT
+ //
+ hp_signal_user_event( shpc_context );
+
+ //
+ // Clear completion flag
+ //
+ slot_context->attn_led_completion.done = FALSE;
+ }
+
+ //
+ // Wait for slot request
+ //
+ wait_event_interruptible(slot_context->slot_event,
+ ((slot_context->slot_event_bits & ATTN_LED_REQUEST_EVENT) ||
+ (slot_context->slot_event_bits & ATTN_LED_PROBLEM_EVENT) ||
+ (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT)));
+
+ // AttnLEDRequestEvent
+ if(slot_context->slot_event_bits & ATTN_LED_REQUEST_EVENT) {
+ //
+ // Set completion info for SW-initiated request
+ //
+ slot_context->attn_led_completion.hw_initiated = FALSE;
+ slot_context->attn_led_completion.type = slot_context->attn_led_request.type;
+ slot_context->attn_led_completion.timeout = slot_context->attn_led_request.timeout;
+ slot_context->attn_led_completion.request_context = slot_context->attn_led_request.request_context;
+
+ //
+ // Request to locate slot?
+ //
+ if( slot_context->attn_led_request.type == SHPC_ASYNC_LED_LOCATE ) {
+ dbg("%s -->LED_LOCATE_REQ: slot_id[ %d:%d ]", __FUNCTION__,
+ shpc_context->shpc_instance, slot_context->slot_number-1 );
+
+ //
+ // Grab Command MUTEX to blink Attn LED
+ //
+ slot_context->attn_led_function = (SLOT_STATE_FUNCTION)hp_wait_for_attn_led_blink_cmd_available;
+ }
+ else {
+ dbg("%s -->LED_NORMAL_REQ: slot_id[ %d:%d ]", __FUNCTION__,
+ shpc_context->shpc_instance, slot_context->slot_number-1 );
+
+ logical_slot_reg.AsDWord = readl( slot_context->logical_slot_addr );
+ if( logical_slot_reg.x.AIS == SHPC_LED_ON || logical_slot_reg.x.AIS == SHPC_LED_OFF ) {
+ //
+ // Already "Normal", just complete the request
+ //
+ slot_context->attn_led_completion.failed = HP_FALSE;
+ slot_context->attn_led_completion.done = TRUE;
+ }
+ //
+ // While waitimg on a request here, the Attn LED should already be On/Off, but...
+ //
+ else {
+ //
+ // Grab Command MUTEX to set Attn LED to "Normal" (On/Off) state
+ //
+ slot_context->attn_led_function = (SLOT_STATE_FUNCTION)hp_wait_for_attn_led_normal_cmd_available;
+ }
+ }
+
+ //
+ // Allow next SW-initiated request while processing this one
+ //
+ spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags );
+ hp_clear_slot_event_bit(slot_context, ATTN_LED_REQUEST_EVENT);
+ spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags );
+ }
+ // attn_led_problem_event: Detected, Resolved
+ else if (slot_context->slot_event_bits & ATTN_LED_PROBLEM_EVENT){
+ //
+ // Set completion info for HW-initiated request
+ //
+ slot_context->attn_led_completion.hw_initiated = TRUE;
+ slot_context->attn_led_completion.type = SHPC_ASYNC_LED_NORMAL;
+ slot_context->attn_led_completion.timeout = 0;
+ slot_context->attn_led_completion.request_context = NULL;
+
+ //
+ // Grab Command MUTEX to update Attention LED (On/Off)
+ //
+ slot_context->attn_led_function = (SLOT_STATE_FUNCTION)hp_wait_for_attn_led_normal_cmd_available;
+ }
+ else { // exit_request_event
+ status = STATUS_UNSUCCESSFUL;
+ dbg("%s -->EXIT_REQUEST: slot_id[ %d:%d ]", __FUNCTION__,
+ shpc_context->shpc_instance, slot_context->slot_number-1 );
+ }
+ return status;
+}
+
+
+// ****************************************************************************
+//
+// hp_wait_for_attn_led_blink_cmd_available() @ PASSIVE_LEVEL
+//
+// ****************************************************************************
+long
+hp_wait_for_attn_led_blink_cmd_available(
+ struct shpc_context* shpc_context,
+ struct slot_context* slot_context
+ )
+{
+ unsigned long old_irq_flags;
+ long status = STATUS_SUCCESS;
+ union SHPC_COMMAND_WREG command_reg;
+
+ dbg("%s -->slot_id[ %d:%d ]", __FUNCTION__, shpc_context->shpc_instance, slot_context->slot_number-1 );
+
+ //
+ // Wait for Command Available MUTEX
+ //
+ //down_interruptible(&slot_context->cmd_acquire_mutex);
+ hp_set_slot_event_bit(slot_context, LED_CMD_ACQUIRE_EVENT);
+ wake_up_interruptible(&slot_context->led_cmd_acquire_event);
+
+ wait_event_interruptible(slot_context->slot_event,
+ ((shpc_context->shpc_event_bits & LED_CMD_AVAILABLE_MUTEX_EVENT) ||
+ (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT)));
+
+ // cmd_available_mutex
+ if(shpc_context->shpc_event_bits & LED_CMD_AVAILABLE_MUTEX_EVENT) {
+ //
+ // Clear Completion EVENT before issuing next command
+ //
+ spin_lock_irqsave( &shpc_context->shpc_spinlock, old_irq_flags );
+ hp_clear_shpc_event_bit(shpc_context, CMD_COMPLETION_EVENT);
+ spin_unlock_irqrestore( &shpc_context->shpc_spinlock, old_irq_flags );
+
+ //
+ // Blink Attention LED
+ //
+ command_reg.Slot.code = SHPC_SLOT_OPERATION;
+ command_reg.Slot.power_led = SHPC_led_NO_CHANGE;
+ command_reg.Slot.attention_led = SHPC_LED_BLINK;
+ command_reg.Slot.state = SHPC_SLOT_NO_CHANGE;
+ command_reg.Slot.TGT = slot_context->slot_number;
+ writew(command_reg.AsWord, shpc_context->mmio_base_addr + SHPC_COMMAND_REG_OFFSET);
+
+ //
+ // Wait for command to complete (while holding MUTEX)
+ //
+ slot_context->attn_led_function = (SLOT_STATE_FUNCTION)hp_wait_for_attn_led_blink_cmd_completion;
+ }
+ // exit_request_event
+ else {
+ //
+ // Release Command MUTEX
+ //
+ hp_set_slot_event_bit(slot_context, LED_CMD_RELEASE_EVENT);
+ wake_up_interruptible(&slot_context->led_cmd_release_event);
+ status = STATUS_UNSUCCESSFUL;
+ }
+ return status;
+}
+
+
+// ****************************************************************************
+//
+// hp_wait_for_attn_led_blink_cmd_completion() @ PASSIVE_LEVEL
+//
+// ****************************************************************************
+long
+hp_wait_for_attn_led_blink_cmd_completion(
+ struct shpc_context* shpc_context,
+ struct slot_context* slot_context
+ )
+{
+ long status = STATUS_SUCCESS;
+ union SLOT_STATUS_INFO slot_status;
+ union SHPC_STATUS_WREG status_reg;
+
+ dbg("%s -->slot_id[ %d:%d ]", __FUNCTION__, shpc_context->shpc_instance, slot_context->slot_number-1 );
+
+ //
+ // Setup our timer
+ //
+ hp_clear_slot_event_bit(slot_context, LED_TIMER1_EVENT);
+ slot_context->led_timer1.data = (unsigned long)slot_context;
+ slot_context->led_timer1.function = hp_led_timer1_func;
+ slot_context->led_timer1.expires = jiffies + ONE_SEC_TIMEOUT;
+ add_timer(&slot_context->led_timer1);
+
+ //
+ // Wait for Command Completion EVENT while holding MUTEX
+ //
+ wait_event_interruptible(slot_context->slot_event,
+ ((shpc_context->shpc_event_bits & CMD_COMPLETION_EVENT) ||
+ (slot_context->slot_event_bits & LED_TIMER1_EVENT) ||
+ (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT)));
+
+ if (!(slot_context->slot_event_bits & LED_TIMER1_EVENT)) {
+ //
+ // delete the timer because we got an event other than the timer
+ //
+ del_timer_sync(&slot_context->led_timer1);
+ }
+
+ if(shpc_context->shpc_event_bits & CMD_COMPLETION_EVENT) {
+ // cmd_completion_event, timeout
+ if ((shpc_context->shpc_event_bits & CMD_COMPLETION_EVENT) || (slot_context->slot_event_bits & LED_TIMER1_EVENT)) {
+ //
+ // Command completed OK?
+ //
+ status_reg.AsWord = readw(shpc_context->mmio_base_addr + SHPC_STATUS_REG_OFFSET);
+
+ if( status_reg.x.BSY == SHPC_STATUS_CLEARED &&
+ status_reg.x.INVCMD_ERR == SHPC_STATUS_CLEARED ) {
+ //
+ // Call Completion Callback()
+ //
+ hp_QuerySlotStatus( shpc_context, slot_context->slot_number - 1, &slot_status );
+ slot_status.x.lu_request_failed = HP_FALSE;
+ shpc_context->async_callback(
+ shpc_context->driver_context,
+ slot_context->slot_number - 1,
+ SHPC_ASYNC_LED_LOCATE,
+ slot_status,
+ slot_context->attn_led_completion.request_context );
+
+ //
+ // Signal registered user EVENT
+ //
+ hp_signal_user_event( shpc_context );
+
+ //
+ // Wait for specified timeout (in seconds)
+ //
+ slot_context->attn_led_function = (SLOT_STATE_FUNCTION)hp_wait_for_attn_led_blink_timeout;
+ }
+ else {
+ //
+ // Fail on-going request
+ //
+ slot_context->attn_led_completion.failed = HP_TRUE;
+ slot_context->attn_led_completion.done = TRUE;
+
+ //
+ // Grab Command MUTEX to make sure Attn LED gets back to "Normal" (On/Off)
+ //
+ dbg("%s -->CMD_ERROR: slot_id[ %d:%d ] Cmd[ %X ]", __FUNCTION__,
+ shpc_context->shpc_instance, slot_context->slot_number-1, status_reg.AsWord );
+ slot_context->attn_led_function = (SLOT_STATE_FUNCTION)hp_wait_for_attn_led_back_to_normal_cmd_available;
+ }
+
+ // exit_request_event
+ }
+ else {
+ status = STATUS_UNSUCCESSFUL;
+ }
+ }
+ //
+ // Release Command MUTEX
+ //
+ hp_set_slot_event_bit(slot_context, LED_CMD_RELEASE_EVENT);
+ wake_up_interruptible(&slot_context->led_cmd_release_event);
+
+ return status;
+}
+
+// ****************************************************************************
+//
+// hp_wait_for_attn_led_blink_timeout() @ PASSIVE_LEVEL
+//
+// ****************************************************************************
+long
+hp_wait_for_attn_led_blink_timeout(
+ struct shpc_context* shpc_context,
+ struct slot_context* slot_context
+ )
+{
+ unsigned long old_irq_flags;
+ long status = STATUS_SUCCESS;
+ union SLOT_STATUS_INFO slot_status;
+
+ dbg("%s -->slot_id[ %d:%d ]", __FUNCTION__, shpc_context->shpc_instance, slot_context->slot_number-1 );
+
+ //
+ // Setup our timer
+ //
+ hp_clear_slot_event_bit(slot_context, LED_TIMER2_EVENT);
+ slot_context->led_timer2.data = (unsigned long)slot_context;
+ slot_context->led_timer2.function = hp_led_timer2_func;
+ slot_context->led_timer2.expires = jiffies + (ONE_SEC_INCREMENT * slot_context->attn_led_completion.timeout);
+ add_timer(&slot_context->led_timer2);
+
+ //
+ // Wait for specified timeout ( in seconds )
+ //
+ wait_event_interruptible(slot_context->slot_event,
+ ((slot_context->slot_event_bits & ATTN_LED_REQUEST_EVENT) ||
+ (slot_context->slot_event_bits & LED_TIMER2_EVENT) ||
+ (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT)));
+
+ if (!(slot_context->slot_event_bits & LED_TIMER2_EVENT)) {
+ //
+ // delete the timer because we got an event other than the timer
+ //
+ hp_clear_slot_event_bit(slot_context, LED_TIMER2_EVENT);
+ del_timer_sync(&slot_context->led_timer2);
+ }
+
+ // AttnLEDRequestEvent
+ if(slot_context->slot_event_bits & ATTN_LED_REQUEST_EVENT) {
+ //
+ // Set completion info for SW-initiated request
+ //
+ slot_context->attn_led_completion.hw_initiated = FALSE;
+ slot_context->attn_led_completion.type = slot_context->attn_led_request.type;
+ slot_context->attn_led_completion.timeout = slot_context->attn_led_request.timeout;
+ slot_context->attn_led_completion.request_context = slot_context->attn_led_request.request_context;
+
+ //
+ // Request to cancel locate?
+ //
+ if( slot_context->attn_led_request.type == SHPC_ASYNC_LED_NORMAL ) {
+ dbg("%s -->LED_NORMAL_REQ: slot_id[ %d:%d ]", __FUNCTION__,
+ shpc_context->shpc_instance, slot_context->slot_number-1 );
+
+ //
+ // Grab Command MUTEX to set Attn LED at "Normal" (On/Off) state
+ //
+ slot_context->attn_led_function = (SLOT_STATE_FUNCTION)&hp_wait_for_attn_led_normal_cmd_available;
+
+ //
+ // Allow next SW-initiated request while processing this one
+ //
+ spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags );
+ hp_clear_slot_event_bit(slot_context, ATTN_LED_REQUEST_EVENT);
+ spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags );
+ }
+ //
+ // Already located (Attn LED blinking), just re-start timeout
+ //
+ else {
+ dbg("%s -->LED_LOCATE_REQ: slot_id[ %d:%d ]", __FUNCTION__,
+ shpc_context->shpc_instance, slot_context->slot_number-1 );
+
+ //
+ // Allow next SW-initiated request before invoking callback, since next
+ // request may be sent in the context of this thread.
+ //
+ spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags );
+ hp_clear_slot_event_bit(slot_context, ATTN_LED_REQUEST_EVENT);
+ spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags );
+
+ //
+ // Call Completion Callback()
+ //
+ hp_QuerySlotStatus( shpc_context, slot_context->slot_number - 1, &slot_status );
+ slot_status.x.lu_request_failed = HP_FALSE;
+ shpc_context->async_callback(
+ shpc_context->driver_context,
+ slot_context->slot_number - 1,
+ SHPC_ASYNC_LED_LOCATE,
+ slot_status,
+ slot_context->attn_led_completion.request_context );
+
+ //
+ // Signal registered user EVENT
+ //
+ hp_signal_user_event( shpc_context );
+ }
+ }
+ // timeout
+ else if (slot_context->slot_event_bits & LED_TIMER2_EVENT) {
+ //
+ // Set completion info for HW-initiated request
+ //
+ slot_context->attn_led_completion.hw_initiated = TRUE;
+ slot_context->attn_led_completion.type = SHPC_ASYNC_LED_NORMAL;
+ slot_context->attn_led_completion.timeout = 0;
+ slot_context->attn_led_completion.request_context = NULL;
+
+ //
+ // Grab Command MUTEX to set Attn LED at "Normal" (On/Off) state
+ //
+ slot_context->attn_led_function = (SLOT_STATE_FUNCTION)hp_wait_for_attn_led_normal_cmd_available;
+
+ }
+ // exit_request_event
+ else {
+ status = STATUS_UNSUCCESSFUL;
+ }
+ return status;
+}
+
+
+// ****************************************************************************
+//
+// hp_wait_for_attn_led_normal_cmd_available() @ PASSIVE_LEVEL
+//
+// ****************************************************************************
+long
+hp_wait_for_attn_led_normal_cmd_available(
+ struct shpc_context* shpc_context,
+ struct slot_context* slot_context
+ )
+{
+ unsigned long old_irq_flags;
+ long status = STATUS_SUCCESS;
+ union SHPC_COMMAND_WREG command_reg;
+
+ dbg("%s -->slot_id[ %d:%d ]", __FUNCTION__, shpc_context->shpc_instance, slot_context->slot_number-1 );
+
+ //
+ // Wait for Command Available MUTEX
+ //
+ //down_interruptible(&slot_context->cmd_acquire_mutex);
+ hp_set_slot_event_bit(slot_context, LED_CMD_ACQUIRE_EVENT);
+ wake_up_interruptible(&slot_context->led_cmd_acquire_event);
+
+ wait_event_interruptible(slot_context->slot_event,
+ ((shpc_context->shpc_event_bits & LED_CMD_AVAILABLE_MUTEX_EVENT) ||
+ (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT)));
+
+ //
+ // cmd_available_mutex
+ //
+ if(shpc_context->shpc_event_bits & LED_CMD_AVAILABLE_MUTEX_EVENT) {
+ //
+ // Clear Completion EVENT before issuing next command
+ //
+ spin_lock_irqsave( &shpc_context->shpc_spinlock, old_irq_flags );
+ hp_clear_shpc_event_bit(shpc_context, CMD_COMPLETION_EVENT);
+ spin_unlock_irqrestore( &shpc_context->shpc_spinlock, old_irq_flags );
+
+ //
+ // Update Attention LED
+ //
+ spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags );
+ command_reg.Slot.attention_led = slot_context->problem_detected ?
+ SHPC_LED_ON : SHPC_LED_OFF;
+ hp_clear_slot_event_bit(slot_context, ATTN_LED_PROBLEM_EVENT);
+ spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags );
+ command_reg.Slot.code = SHPC_SLOT_OPERATION;
+ command_reg.Slot.power_led = SHPC_led_NO_CHANGE;
+ command_reg.Slot.state = SHPC_SLOT_NO_CHANGE;
+ command_reg.Slot.TGT = slot_context->slot_number;
+ writew(command_reg.AsWord, shpc_context->mmio_base_addr + SHPC_COMMAND_REG_OFFSET);
+
+ //
+ // Wait for command to complete (while holding MUTEX)
+ //
+ slot_context->attn_led_function = (SLOT_STATE_FUNCTION)hp_wait_for_attn_led_normal_cmd_completion;
+ }
+ //
+ // exit_request_event
+ //
+ else {
+ //
+ // Release Command MUTEX
+ //
+ hp_set_slot_event_bit(slot_context, LED_CMD_RELEASE_EVENT);
+ wake_up_interruptible(&slot_context->led_cmd_release_event);
+ status = STATUS_UNSUCCESSFUL;
+ }
+ return status;
+}
+
+
+// ****************************************************************************
+//
+// hp_wait_for_attn_led_normal_cmd_completion() @ PASSIVE_LEVEL
+//
+// ****************************************************************************
+long
+hp_wait_for_attn_led_normal_cmd_completion(
+ struct shpc_context* shpc_context,
+ struct slot_context* slot_context
+ )
+{
+ long status = STATUS_SUCCESS;
+ union SHPC_STATUS_WREG status_reg;
+
+ dbg("%s -->slot_id[ %d:%d ]", __FUNCTION__, shpc_context->shpc_instance, slot_context->slot_number-1 );
+
+ //
+ // Setup our timer
+ //
+ hp_clear_slot_event_bit(slot_context, LED_TIMER3_EVENT);
+ slot_context->led_timer3.data = (unsigned long)slot_context;
+ slot_context->led_timer3.function = hp_led_timer3_func;
+ slot_context->led_timer3.expires = jiffies + ONE_SEC_TIMEOUT;
+ add_timer(&slot_context->led_timer3);
+
+ //
+ // Wait for Command Completion EVENT while holding MUTEX
+ //
+ wait_event_interruptible(slot_context->slot_event,
+ ((shpc_context->shpc_event_bits & CMD_COMPLETION_EVENT) ||
+ (slot_context->slot_event_bits & LED_TIMER3_EVENT) ||
+ (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT)));
+
+ if (!(slot_context->slot_event_bits & LED_TIMER3_EVENT)) {
+ //
+ // delete the timer because we got an event other than the timer
+ //
+ del_timer_sync(&slot_context->led_timer3);
+ }
+
+ //
+ // cmd_completion_event, timeout
+ //
+ if((shpc_context->shpc_event_bits & CMD_COMPLETION_EVENT) ||
+ (slot_context->slot_event_bits & LED_TIMER3_EVENT)) {
+ //
+ // Command completed OK?
+ //
+ status_reg.AsWord = readw(shpc_context->mmio_base_addr + SHPC_STATUS_REG_OFFSET);
+
+ if( status_reg.x.BSY == SHPC_STATUS_CLEARED &&
+ status_reg.x.INVCMD_ERR == SHPC_STATUS_CLEARED ) {
+ //
+ // Complete succesful ENABLE request
+ //
+ slot_context->attn_led_completion.failed = HP_FALSE;
+ slot_context->attn_led_completion.done = TRUE;
+ slot_context->attn_led_function = (SLOT_STATE_FUNCTION)hp_wait_for_attn_led_request;
+ }
+ else {
+ //
+ // Fail on-going request
+ //
+ slot_context->attn_led_completion.failed = HP_TRUE;
+ slot_context->attn_led_completion.done = TRUE;
+
+ //
+ // Grab Command MUTEX to make sure Attn LED gets back to "Normal" (On/Off)
+ //
+ dbg("%s -->CMD_ERROR: slot_id[ %d:%d ] Cmd[ %X ]", __FUNCTION__,
+ shpc_context->shpc_instance, slot_context->slot_number-1, status_reg.AsWord );
+ slot_context->attn_led_function = (SLOT_STATE_FUNCTION)hp_wait_for_attn_led_back_to_normal_cmd_available;
+ }
+ }
+ //
+ // exit_request_event
+ //
+ else {
+ status = STATUS_UNSUCCESSFUL;
+ }
+ //
+ // Release Command MUTEX
+ //
+ hp_set_slot_event_bit(slot_context, LED_CMD_RELEASE_EVENT);
+ wake_up_interruptible(&slot_context->led_cmd_release_event);
+
+ return status;
+}
+
+
+// ****************************************************************************
+//
+// hp_wait_for_attn_led_back_to_normal_cmd_available() @ PASSIVE_LEVEL
+//
+// ****************************************************************************
+long
+hp_wait_for_attn_led_back_to_normal_cmd_available(
+ struct shpc_context* shpc_context,
+ struct slot_context* slot_context
+ )
+{
+ unsigned long old_irq_flags;
+ long status = STATUS_SUCCESS;
+ union SHPC_COMMAND_WREG command_reg;
+
+ dbg("%s -->slot_id[ %d:%d ]", __FUNCTION__, shpc_context->shpc_instance, slot_context->slot_number-1 );
+
+ //
+ // Wait for Command Available MUTEX
+ //
+ //down_interruptible(&slot_context->cmd_acquire_mutex);
+ hp_set_slot_event_bit(slot_context, LED_CMD_ACQUIRE_EVENT);
+ wake_up_interruptible(&slot_context->led_cmd_acquire_event);
+
+ wait_event_interruptible(slot_context->slot_event,
+ ((shpc_context->shpc_event_bits & LED_CMD_AVAILABLE_MUTEX_EVENT) ||
+ (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT)));
+
+ //
+ // cmd_available_mutex
+ //
+ if(shpc_context->shpc_event_bits & LED_CMD_AVAILABLE_MUTEX_EVENT) {
+ //
+ // Clear Completion EVENT before issuing next command
+ //
+ spin_lock_irqsave( &shpc_context->shpc_spinlock, old_irq_flags );
+ hp_clear_shpc_event_bit(shpc_context, CMD_COMPLETION_EVENT);
+ spin_unlock_irqrestore( &shpc_context->shpc_spinlock, old_irq_flags );
+
+ //
+ // Update Attention LED
+ //
+ spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags );
+ command_reg.Slot.attention_led = slot_context->problem_detected ?
+ SHPC_LED_ON : SHPC_LED_OFF;
+ hp_clear_slot_event_bit(slot_context, ATTN_LED_PROBLEM_EVENT);
+ spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags );
+ command_reg.Slot.code = SHPC_SLOT_OPERATION;
+ command_reg.Slot.power_led = SHPC_led_NO_CHANGE;
+ command_reg.Slot.state = SHPC_SLOT_NO_CHANGE;
+ command_reg.Slot.TGT = slot_context->slot_number;
+ writew(command_reg.AsWord, shpc_context->mmio_base_addr + SHPC_COMMAND_REG_OFFSET);
+
+ //
+ // Wait for command to complete (while holding MUTEX)
+ //
+ slot_context->attn_led_function = (SLOT_STATE_FUNCTION)hp_wait_for_attn_led_back_to_normal_cmd_completion;
+ }
+ //
+ // exit_request_event
+ //
+ else {
+ //
+ // Release Command MUTEX
+ //
+ hp_set_slot_event_bit(slot_context, LED_CMD_RELEASE_EVENT);
+ wake_up_interruptible(&slot_context->led_cmd_release_event);
+ status = STATUS_UNSUCCESSFUL;
+ }
+ return status;
+}
+
+
+// ****************************************************************************
+//
+// hp_wait_for_attn_led_back_to_normal_cmd_completion() @ PASSIVE_LEVEL
+//
+// ****************************************************************************
+long
+hp_wait_for_attn_led_back_to_normal_cmd_completion(
+ struct shpc_context* shpc_context,
+ struct slot_context* slot_context
+ )
+{
+ long status = STATUS_SUCCESS;
+
+ dbg("%s -->slot_id[ %d:%d ]", __FUNCTION__, shpc_context->shpc_instance, slot_context->slot_number-1 );
+
+ //
+ // Setup our timer
+ //
+ hp_clear_slot_event_bit(slot_context, LED_TIMER4_EVENT);
+ slot_context->led_timer4.data = (unsigned long)slot_context;
+ slot_context->led_timer4.function = hp_led_timer4_func;
+ slot_context->led_timer4.expires = jiffies + ONE_SEC_TIMEOUT;
+ add_timer(&slot_context->led_timer4);
+
+ //
+ // Wait for Command Completion EVENT while holding MUTEX
+ //
+ wait_event_interruptible(slot_context->slot_event,
+ ((shpc_context->shpc_event_bits & CMD_COMPLETION_EVENT) ||
+ (slot_context->slot_event_bits & LED_TIMER4_EVENT) ||
+ (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT)));
+
+ if (!(slot_context->slot_event_bits & LED_TIMER4_EVENT)) {
+ //
+ // delete the timer because we got an event other than the timer
+ //
+ del_timer_sync(&slot_context->led_timer4);
+ }
+
+ //
+ // cmd_completion_event, timeout
+ //
+ if((shpc_context->shpc_event_bits & CMD_COMPLETION_EVENT) ||
+ (slot_context->slot_event_bits & LED_TIMER4_EVENT)) {
+ slot_context->attn_led_function = (SLOT_STATE_FUNCTION)hp_wait_for_attn_led_request;
+ }
+ //
+ // exit_request_event
+ //
+ else {
+ status = STATUS_UNSUCCESSFUL;
+ }
+ //
+ // Release Command MUTEX
+ //
+ hp_set_slot_event_bit(slot_context, LED_CMD_RELEASE_EVENT);
+ wake_up_interruptible(&slot_context->led_cmd_release_event);
+
+ return status;
+}
--- /dev/null
+/*
+ * Copyright (C) 2001,2003 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (C) 2001,2003 IBM Corp.
+ * Copyright (C) 2002-2003 Advanced Micro Devices
+ *
+ * YOUR USE OF THIS CODE IS SUBJECT TO THE TERMS
+ * AND CONDITIONS OF THE GNU GENERAL PUBLIC
+ * LICENSE FOUND IN THE "GPL.TXT" FILE THAT IS
+ * INCLUDED WITH THIS FILE AND POSTED AT
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * Send feedback to <greg@kroah.com> <david.keck@amd.com>
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/proc_fs.h>
+#include <linux/pci.h>
+#include "amdshpc.h"
+#include "../pci.h"
+#include "../../../arch/i386/pci/pci.h"
+
+u8 amdshpc_nic_irq;
+u8 amdshpc_disk_irq;
+
+static u16 unused_IRQ;
+
+extern struct controller *amdshpc_ctrl_list; /* = NULL */
+extern struct pci_func *amdshpc_slot_list[256];
+
+static int bridge_slot_remove(struct pci_func *bridge);
+static int is_bridge(struct pci_func * func);
+static int update_slot_info (struct controller *ctrl, struct slot *slot);
+static int slot_remove(struct pci_func * old_slot);
+static u32 configure_new_device(struct controller * ctrl, struct pci_func *func,u8 behind_bridge, struct resource_lists *resources);
+static int configure_new_function(struct controller * ctrl, struct pci_func *func,u8 behind_bridge, struct resource_lists *resources);
+int amdshpc_process_SI (struct controller *ctrl, struct pci_func *func);
+
+static u16 unused_IRQ;
+
+/**
+ * board_added - Called after a board has been added to the system.
+ *
+ * Turns power on for the board
+ * Configures board
+ *
+ */
+static u32 board_added(struct pci_func * func, struct controller * ctrl)
+{
+ int index;
+ u32 temp_register = 0xFFFFFFFF;
+ u32 rc = 0;
+ struct pci_func *new_slot = NULL;
+ struct resource_lists res_lists;
+
+ dbg("%s: func->device, slot_offset = %d, %d \n",__FUNCTION__,
+ func->device, ctrl->slot_device_offset);
+
+ // Get vendor/device ID u32
+ rc = pci_bus_read_config_dword(ctrl->pci_bus, PCI_DEVFN(func->device, func->function), PCI_VENDOR_ID, &temp_register);
+ dbg("%s: pci_bus_read_config_dword returns %d\n",__FUNCTION__, rc);
+ dbg("%s: temp_register is %x\n",__FUNCTION__, temp_register);
+
+ if (rc != 0) {
+ // Something's wrong here
+ temp_register = 0xFFFFFFFF;
+ dbg("%s: temp register set to %x by error\n",__FUNCTION__, temp_register);
+ }
+ // Preset return code. It will be changed later if things go okay.
+ rc = NO_ADAPTER_PRESENT;
+
+ // All F's is an empty slot or an invalid board
+ if (temp_register != 0xFFFFFFFF) { // Check for a board in the slot
+ res_lists.io_head = ctrl->io_head;
+ res_lists.mem_head = ctrl->mem_head;
+ res_lists.p_mem_head = ctrl->p_mem_head;
+ res_lists.bus_head = ctrl->bus_head;
+ res_lists.irqs = NULL;
+
+ rc = configure_new_device(ctrl, func, 0, &res_lists);
+
+ dbg("%s: back from configure_new_device\n",__FUNCTION__);
+ ctrl->io_head = res_lists.io_head;
+ ctrl->mem_head = res_lists.mem_head;
+ ctrl->p_mem_head = res_lists.p_mem_head;
+ ctrl->bus_head = res_lists.bus_head;
+
+ amdshpc_resource_sort_and_combine(&(ctrl->mem_head));
+ amdshpc_resource_sort_and_combine(&(ctrl->p_mem_head));
+ amdshpc_resource_sort_and_combine(&(ctrl->io_head));
+ amdshpc_resource_sort_and_combine(&(ctrl->bus_head));
+
+ if (rc) {
+ // Something went wrong; disable slot
+// TO_DO_amd_disable_slot();
+ return(rc);
+ } else {
+ amdshpc_save_slot_config(ctrl, func);
+ }
+
+
+ func->status = 0;
+ func->switch_save = 0x10;
+ func->is_a_board = 0x01;
+
+ //next, we will instantiate the linux pci_dev structures (with appropriate driver notification, if already present)
+ dbg("%s: configure linux pci_dev structure\n",__FUNCTION__);
+ index = 0;
+ do {
+ new_slot = amdshpc_slot_find(ctrl->bus, func->device, index++);
+ if (new_slot && !new_slot->pci_dev) {
+ amdshpc_configure_device(ctrl, new_slot);
+ }
+ } while (new_slot);
+ } else {
+ // Something went wrong; disable slot
+// TO_DO_amd_disable_slot();
+ return(rc);
+ }
+ return 0;
+}
+
+
+/**
+ * remove_board - Returns resources
+ */
+static u32 remove_board(struct pci_func * func, u32 replace_flag, struct controller * ctrl)
+{
+ int index;
+ u8 skip = 0;
+ u8 device;
+ u8 hp_slot;
+ u32 rc;
+ struct resource_lists res_lists;
+ struct pci_func *temp_func;
+
+ if (func == NULL)
+ return(1);
+
+ if (amdshpc_unconfigure_device(func))
+ return(1);
+
+ device = func->device;
+
+ hp_slot = func->device - ctrl->slot_device_offset;
+ dbg("In %s, hp_slot = %d\n",__FUNCTION__, hp_slot);
+
+ // When we get here, it is safe to change base Address Registers.
+ // We will attempt to save the base Address Register Lengths
+ if (replace_flag || !ctrl->add_support)
+ rc = amdshpc_save_base_addr_length(ctrl, func);
+ else if (!func->bus_head && !func->mem_head &&
+ !func->p_mem_head && !func->io_head) {
+ // Here we check to see if we've saved any of the board's
+ // resources already. If so, we'll skip the attempt to
+ // determine what's being used.
+ index = 0;
+ temp_func = amdshpc_slot_find(func->bus, func->device, index++);
+ while (temp_func) {
+ if (temp_func->bus_head || temp_func->mem_head
+ || temp_func->p_mem_head || temp_func->io_head) {
+ skip = 1;
+ break;
+ }
+ temp_func = amdshpc_slot_find(temp_func->bus, temp_func->device, index++);
+ }
+
+ if (!skip)
+ rc = amdshpc_save_used_resources(ctrl, func);
+ }
+ // Change status to shutdown
+ if (func->is_a_board)
+ func->status = 0x01;
+ func->configured = 0;
+
+// TO_DO_amd_disable_slot(ctrl, hp_slot);
+
+ if (!replace_flag && ctrl->add_support) {
+ while (func) {
+ res_lists.io_head = ctrl->io_head;
+ res_lists.mem_head = ctrl->mem_head;
+ res_lists.p_mem_head = ctrl->p_mem_head;
+ res_lists.bus_head = ctrl->bus_head;
+
+ amdshpc_return_board_resources(func, &res_lists);
+
+ ctrl->io_head = res_lists.io_head;
+ ctrl->mem_head = res_lists.mem_head;
+ ctrl->p_mem_head = res_lists.p_mem_head;
+ ctrl->bus_head = res_lists.bus_head;
+
+ amdshpc_resource_sort_and_combine(&(ctrl->mem_head));
+ amdshpc_resource_sort_and_combine(&(ctrl->p_mem_head));
+ amdshpc_resource_sort_and_combine(&(ctrl->io_head));
+ amdshpc_resource_sort_and_combine(&(ctrl->bus_head));
+
+ if (is_bridge(func)) {
+ bridge_slot_remove(func);
+ } else
+ slot_remove(func);
+
+ func = amdshpc_slot_find(ctrl->bus, device, 0);
+ }
+
+ // Setup slot structure with entry for empty slot
+ func = amdshpc_slot_create(ctrl->bus);
+
+ if (func == NULL) {
+ // Out of memory
+ return(1);
+ }
+
+ func->bus = ctrl->bus;
+ func->device = device;
+ func->function = 0;
+ func->configured = 0;
+ func->switch_save = 0x10;
+ func->is_a_board = 0;
+ func->p_task_event = NULL;
+ }
+ return 0;
+}
+
+
+/*
+ * find_slot
+ */
+static inline struct slot* find_slot (struct controller* ctrl, u8 device)
+{
+ struct slot *slot;
+
+ dbg("%s", __FUNCTION__);
+ if (!ctrl)
+ return NULL;
+
+ slot = ctrl->slot;
+
+ while (slot && (slot->device != device)) {
+ slot = slot->next;
+ }
+
+ return slot;
+}
+
+// board insertion
+int amdshpc_process_SI (struct controller *ctrl, struct pci_func *func)
+{
+ u8 device, hp_slot;
+ u16 temp_word;
+ u32 tempdword;
+ int rc;
+ struct slot* p_slot;
+ int physical_slot = 0;
+
+ dbg("%s 0", __FUNCTION__);
+ if (!ctrl)
+ return(1);
+
+ tempdword = 0;
+
+ device = func->device;
+ hp_slot = device - ctrl->slot_device_offset;
+ p_slot = find_slot(ctrl, device);
+ if (p_slot) {
+ physical_slot = p_slot->number;
+ }
+
+ if (tempdword & (0x01 << hp_slot)) {
+ dbg("%s 1", __FUNCTION__);
+ return(1);
+ }
+
+ // add board
+ slot_remove(func);
+
+ func = amdshpc_slot_create(ctrl->bus);
+ dbg("%s 2",__FUNCTION__);
+ if (func == NULL) {
+ dbg("%s 3",__FUNCTION__);
+ return(1);
+ }
+
+ func->bus = ctrl->bus;
+ func->device = device;
+ func->function = 0;
+ func->configured = 0;
+ func->is_a_board = 1;
+
+ // We have to save the presence info for these slots
+ temp_word = ctrl->ctrl_int_comp >> 16;
+ func->presence_save = (temp_word >> hp_slot) & 0x01;
+ func->presence_save |= (temp_word >> (hp_slot + 7)) & 0x02;
+
+ dbg("%s 4",__FUNCTION__);
+ if (ctrl->ctrl_int_comp & (0x1L << hp_slot)) {
+ dbg("%s 5",__FUNCTION__);
+ func->switch_save = 0;
+ } else {
+ dbg("%s 6",__FUNCTION__);
+ func->switch_save = 0x10;
+ }
+
+ rc = board_added(func, ctrl);
+ dbg("%s 7 rc=%d",__FUNCTION__,rc);
+ if (rc) {
+ dbg("%s 8",__FUNCTION__);
+ if (is_bridge(func)) {
+ dbg("%s 9",__FUNCTION__);
+ bridge_slot_remove(func);
+ } else {
+ dbg("%s 10",__FUNCTION__);
+ slot_remove(func);
+ }
+
+ // Setup slot structure with entry for empty slot
+ func = amdshpc_slot_create(ctrl->bus);
+
+ dbg("%s 11",__FUNCTION__);
+ if (func == NULL) {
+ // Out of memory
+ return(1);
+ }
+
+ func->bus = ctrl->bus;
+ func->device = device;
+ func->function = 0;
+ func->configured = 0;
+ func->is_a_board = 0;
+
+ // We have to save the presence info for these slots
+ temp_word = ctrl->ctrl_int_comp >> 16;
+ func->presence_save = (temp_word >> hp_slot) & 0x01;
+ func->presence_save |=
+ (temp_word >> (hp_slot + 7)) & 0x02;
+
+ if (ctrl->ctrl_int_comp & (0x1L << hp_slot)) {
+ dbg("%s 12",__FUNCTION__);
+ func->switch_save = 0;
+ } else {
+ dbg("%s 13",__FUNCTION__);
+ func->switch_save = 0x10;
+ }
+ }
+
+ if (rc) {
+ dbg("%s: rc = %d\n",__FUNCTION__, rc);
+ }
+
+ if (p_slot){
+ dbg("%s 14",__FUNCTION__);
+ update_slot_info(ctrl, p_slot);
+ }
+
+ return rc;
+}
+
+// Disable Slot
+int amdshpc_process_SS (struct controller *ctrl, struct pci_func *func)
+{
+ u8 device, class_code, header_type, BCR;
+ u8 index = 0;
+ u8 replace_flag;
+ u32 rc = 0;
+ struct slot* p_slot;
+ int physical_slot=0;
+
+ dbg("%s 0",__FUNCTION__);
+ device = func->device;
+ func = amdshpc_slot_find(ctrl->bus, device, index++);
+ p_slot = find_slot(ctrl, device);
+ if (p_slot) {
+ physical_slot = p_slot->number;
+ }
+
+ // Make sure there are no video controllers here
+ while (func && !rc) {
+ dbg("%s 1..",__FUNCTION__);
+ // Check the Class Code
+ rc = pci_bus_read_config_byte(ctrl->pci_bus, PCI_DEVFN(func->device, func->function), 0x0B, &class_code);
+ dbg("%s 1.1 rc = %d class_code = %02x",__FUNCTION__, rc, class_code);
+ if (rc){
+ dbg("%s 2",__FUNCTION__);
+ return rc;
+ }
+
+ if (class_code == PCI_BASE_CLASS_DISPLAY) {
+ /* Display/Video adapter (not supported) */
+ dbg("%s 3",__FUNCTION__);
+ rc = REMOVE_NOT_SUPPORTED;
+ } else {
+ dbg("%s 3.5",__FUNCTION__);
+ // See if it's a bridge
+ rc = pci_bus_read_config_byte(ctrl->pci_bus, PCI_DEVFN(func->device, func->function), PCI_HEADER_TYPE, &header_type);
+ if (rc){
+ dbg("%s 4",__FUNCTION__);
+ return rc;
+ }
+
+ // If it's a bridge, check the VGA Enable bit
+ if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) {
+ dbg("%s 4.5",__FUNCTION__);
+ rc = pci_bus_read_config_byte(ctrl->pci_bus, PCI_DEVFN(func->device, func->function), PCI_BRIDGE_CONTROL, &BCR);
+ if (rc){
+ dbg("%s 5",__FUNCTION__);
+ return rc;
+ }
+
+ dbg("%s 5.5",__FUNCTION__);
+ // If the VGA Enable bit is set, remove isn't supported
+ if (BCR & PCI_BRIDGE_CTL_VGA) {
+ dbg("%s 6",__FUNCTION__);
+ rc = REMOVE_NOT_SUPPORTED;
+ }
+ }
+ }
+
+ func = amdshpc_slot_find(ctrl->bus, device, index++);
+ dbg("%s 7",__FUNCTION__);
+ }
+
+ func = amdshpc_slot_find(ctrl->bus, device, 0);
+ dbg("%s 8",__FUNCTION__);
+ if ((func != NULL) && !rc) {
+ dbg("%s 9",__FUNCTION__);
+ //FIXME: Replace flag should be passed into process_SS
+ replace_flag = !(ctrl->add_support);
+ rc = remove_board(func, replace_flag, ctrl);
+ } else if (!rc) {
+ dbg("%s 10",__FUNCTION__);
+ rc = 1;
+ }
+
+ if (p_slot){
+ dbg("%s 11",__FUNCTION__);
+ update_slot_info(ctrl, p_slot);
+ }
+
+ dbg("%s 12",__FUNCTION__);
+ return(rc);
+}
+
+
+/*
+ * detect_HRT_floating_pointer
+ *
+ * find the Hot Plug Resource Table in the specified region of memory.
+ *
+ */
+static void *detect_HRT_floating_pointer(void *begin, void *end)
+{
+ void *fp;
+ void *endp;
+ u8 temp1, temp2, temp3, temp4;
+ int status = 0;
+
+ endp = (end - sizeof(struct hrt) + 1);
+
+ for (fp = begin; fp <= endp; fp += 16) {
+ temp1 = readb(fp + SIG0);
+ temp2 = readb(fp + SIG1);
+ temp3 = readb(fp + SIG2);
+ temp4 = readb(fp + SIG3);
+ if (temp1 == '$' &&
+ temp2 == 'H' &&
+ temp3 == 'R' &&
+ temp4 == 'T') {
+ status = 1;
+ dbg("%s -->temp string----> %c%c%c%c at-----> %p\n", __FUNCTION__, temp1,temp2,temp3,temp4,fp);
+ break;
+ }
+ }
+
+ if (!status) {
+ fp = NULL;
+ dbg("%s -->Did not discover Hotplug Resource Table between start:%p end:%p\n", __FUNCTION__, begin, end);
+ return fp;
+ }
+
+ dbg("%s -->Discovered Hotplug Resource Table at %p\n", __FUNCTION__, fp);
+ return fp;
+}
+
+/**
+ * amdshpc_slot_find - Looks for a node by bus, and device, multiple functions accessed
+ * @bus: bus to find
+ * @device: device to find
+ * @index: is 0 for first function found, 1 for the second...
+ *
+ * Returns pointer to the node if successful, %NULL otherwise.
+ */
+struct pci_func *amdshpc_slot_find(u8 bus, u8 device, u8 index) {
+ int found = -1;
+ struct pci_func *func;
+
+ func = amdshpc_slot_list[bus];
+ dbg("%s amdshpc_slot_list[%02x] = %p", __FUNCTION__, bus, amdshpc_slot_list[bus]);
+ dbg("%s bus, device, index %x %d %d", __FUNCTION__, bus, device, index);
+
+ if ((func == NULL) || ((func->device == device) && (index == 0)))
+ return(func);
+
+ if (func->device == device)
+ found++;
+
+ while (func->next != NULL) {
+ func = func->next;
+
+ if (func->device == device)
+ found++;
+
+ if (found == index)
+ return(func);
+ }
+
+ return(NULL);
+}
+
+
+/*
+ * amdshpc_resource_sort_and_combine
+ *
+ * Sorts all of the nodes in the list in ascending order by
+ * their base addresses. Also does garbage collection by
+ * combining adjacent nodes.
+ *
+ * returns 0 if success
+ */
+int amdshpc_resource_sort_and_combine(struct pci_resource **head)
+{
+ struct pci_resource *node1;
+ struct pci_resource *node2;
+ int out_of_order = 1;
+
+ dbg("%s: head = %p, *head = %p\n",__FUNCTION__, head, *head);
+
+ if (!(*head))
+ return(1);
+
+ dbg("%s -->*head->next = %p\n", __FUNCTION__,(*head)->next);
+
+ if (!(*head)->next)
+ return(0); /* only one item on the list, already sorted! */
+
+ dbg("%s -->*head->base = 0x%x\n", __FUNCTION__,(*head)->base);
+ dbg("%s -->*head->next->base = 0x%x\n", __FUNCTION__,(*head)->next->base);
+ while (out_of_order) {
+ out_of_order = 0;
+
+ // Special case for swapping list head
+ if (((*head)->next) &&
+ ((*head)->base > (*head)->next->base)) {
+ node1 = *head;
+ (*head) = (*head)->next;
+ node1->next = (*head)->next;
+ (*head)->next = node1;
+ out_of_order++;
+ }
+
+ node1 = (*head);
+
+ while (node1->next && node1->next->next) {
+ if (node1->next->base > node1->next->next->base) {
+ out_of_order++;
+ node2 = node1->next;
+ node1->next = node1->next->next;
+ node1 = node1->next;
+ node2->next = node1->next;
+ node1->next = node2;
+ } else
+ node1 = node1->next;
+ }
+ } // End of out_of_order loop
+
+ node1 = *head;
+
+ while (node1 && node1->next) {
+ if ((node1->base + node1->length) == node1->next->base) {
+ // Combine
+ dbg("%s -->8..\n", __FUNCTION__);
+ node1->length += node1->next->length;
+ node2 = node1->next;
+ node1->next = node1->next->next;
+ kfree(node2);
+ } else
+ node1 = node1->next;
+ }
+
+ return(0);
+}
+
+
+/*
+ * amdshpc_find_available_resources
+ *
+ * Finds available memory, IO, and IRQ resources for programming
+ * devices which may be added to the system
+ * this function is for hot plug ADD!
+ *
+ * returns 0 if success
+ */
+int amdshpc_find_available_resources (struct controller *ctrl, void *rom_start)
+{
+ u8 temp;
+ u8 populated_slot=0;
+ u8 bridged_slot;
+ u8 slot_index;
+ void *one_slot;
+ struct pci_func *func = NULL;
+ int i = 10, index;
+ u32 temp_dword, rc;
+ struct pci_resource *mem_node;
+ struct pci_resource *p_mem_node;
+ struct pci_resource *io_node;
+ struct pci_resource *bus_node;
+ void *rom_resource_table;
+ struct shpc_context *shpc_context;
+
+ slot_index=0;
+
+ shpc_context = (struct shpc_context* ) ctrl->shpc_context;
+ rom_resource_table = detect_HRT_floating_pointer(rom_start, rom_start+0xffff);
+ dbg("%s -->rom_resource_table = %p\n", __FUNCTION__, rom_resource_table);
+
+ if (rom_resource_table == NULL) {
+ return -ENODEV;
+ }
+ // Sum all resources and setup resource maps
+ unused_IRQ = readl(rom_resource_table + UNUSED_IRQ);
+ dbg("%s -->unused_IRQ = %x\n", __FUNCTION__, unused_IRQ);
+ dbg("%s -->PCI_IRQ = %x\n", __FUNCTION__, readl(rom_resource_table + PCIIRQ));
+
+ temp = 0;
+
+ while (unused_IRQ) {
+ if (unused_IRQ & 1) {
+ amdshpc_disk_irq = temp;
+ break;
+ }
+ unused_IRQ = unused_IRQ >> 1;
+ temp++;
+ }
+
+ dbg("%s -->amdshpc_disk_irq= %d\n", __FUNCTION__, amdshpc_disk_irq);
+ unused_IRQ = unused_IRQ >> 1;
+ temp++;
+
+ while (unused_IRQ) {
+ if (unused_IRQ & 1) {
+ amdshpc_nic_irq = temp;
+ break;
+ }
+ unused_IRQ = unused_IRQ >> 1;
+ temp++;
+ }
+
+ dbg("%s -->amdshpc_nic_irq= %d\n", __FUNCTION__, amdshpc_nic_irq);
+ unused_IRQ = readl(rom_resource_table + PCIIRQ);
+
+ temp = 0;
+
+ if (!amdshpc_nic_irq) {
+ amdshpc_nic_irq = ctrl->interrupt;
+ }
+
+ if (!amdshpc_disk_irq) {
+ amdshpc_disk_irq = ctrl->interrupt;
+ }
+
+ dbg("%s -->amdshpc_disk_irq, amdshpc_nic_irq= %d, %d\n", __FUNCTION__, amdshpc_disk_irq, amdshpc_nic_irq);
+
+ one_slot = rom_resource_table + sizeof (struct hrt);
+
+ i = readb(rom_resource_table + NUMBER_OF_ENTRIES);
+ dbg("%s -->number_of_entries = %d\n", __FUNCTION__, i);
+
+ if (!readb(one_slot + SECONDARY_BUS)) {
+ return(1);
+ }
+
+ dbg("%s -->dev|IO base|length|Mem base|length|Pre base|length|PB SB MB\n", __FUNCTION__);
+
+ while (i && readb(one_slot + SECONDARY_BUS)) {
+ u8 dev_func = readb(one_slot + DEV_FUNC);
+ u8 primary_bus = readb(one_slot + PRIMARY_BUS);
+ u8 secondary_bus = readb(one_slot + SECONDARY_BUS);
+ u8 max_bus = readb(one_slot + MAX_BUS);
+ u16 io_base = readw(one_slot + IO_BASE);
+ u16 io_length = readw(one_slot + IO_LENGTH);
+ u16 mem_base = readw(one_slot + MEM_BASE);
+ u16 mem_length = readw(one_slot + MEM_LENGTH);
+ u16 pre_mem_base = readw(one_slot + PRE_MEM_BASE);
+ u16 pre_mem_length = readw(one_slot + PRE_MEM_LENGTH);
+
+ dbg("%s -->%2.2x | %4.4x | %4.4x | %4.4x | %4.4x | %4.4x | %4.4x |%2.2x %2.2x %2.2x\n", __FUNCTION__,
+ dev_func, io_base, io_length, mem_base, mem_length, pre_mem_base, pre_mem_length,
+ primary_bus, secondary_bus, max_bus);
+
+ // If this entry isn't for our controller's bus, ignore it
+ if (primary_bus != ctrl->bus) {
+ i--;
+ one_slot += sizeof (struct slot_rt);
+ continue;
+ }
+
+ // find out if this entry is for an occupied slot
+ pci_bus_read_config_dword(ctrl->pci_bus, dev_func, PCI_VENDOR_ID, &temp_dword);
+ dbg("bus %p, pri-bus %08x, slot %d, function %d, vend ID %d, tempDW %p\n",
+ ctrl->pci_bus, primary_bus, PCI_SLOT(dev_func), PCI_FUNC(dev_func), PCI_VENDOR_ID, &temp_dword);
+
+ dbg("%s -->temp_D_word = %08X\n", __FUNCTION__, temp_dword);
+
+ if (temp_dword != 0xFFFFFFFF) {
+ index = 0;
+ func = amdshpc_slot_find(primary_bus, dev_func >> 3, 0);
+ dbg("%s -->func = %p",__FUNCTION__, (unsigned long*)func);
+ while (func && (func->function != PCI_FUNC(dev_func))) {
+ dbg("%s -->func = %p (bus, dev, fun) = (%d, %d, %d)\n",__FUNCTION__, func, primary_bus, dev_func >> 3, index);
+ func = amdshpc_slot_find(primary_bus, PCI_SLOT(dev_func), index++);
+ }
+
+ // If we can't find a match, skip this table entry
+ if (!func) {
+ i--;
+ one_slot += sizeof (struct slot_rt);
+ continue;
+ }
+ // this may not work and shouldn't be used
+ if (secondary_bus != primary_bus){
+ bridged_slot = 1;
+ }
+ else{
+ bridged_slot = 0;
+ }
+ shpc_context->slot_context[slot_index].slot_occupied = 1;
+ } else {
+
+ populated_slot = 0;
+ bridged_slot = 0;
+ }
+ slot_index++;
+
+ // If we've got a valid IO base, use it
+
+ temp_dword = io_base + io_length;
+ dbg("%s -->temp_D_word for io base = %08x",__FUNCTION__, temp_dword);
+
+ if ((io_base) && (temp_dword < 0x10000)) {
+ io_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
+ if (!io_node)
+ return -ENOMEM;
+
+ io_node->base = io_base;
+ io_node->length = io_length;
+
+ dbg("%s -->found io_node(base, length) = %x, %x\n",__FUNCTION__, io_node->base, io_node->length);
+ dbg("%s -->populated slot =%d \n",__FUNCTION__, populated_slot);
+ if (!populated_slot) {
+ io_node->next = ctrl->io_head;
+ ctrl->io_head = io_node;
+ } else {
+ io_node->next = func->io_head;
+ func->io_head = io_node;
+ }
+ }
+
+ // If we've got a valid memory base, use it
+ temp_dword = mem_base + mem_length;
+ dbg("%s -->temp_D_word for mem base = %08x",__FUNCTION__, temp_dword);
+ if ((mem_base) && (temp_dword < 0x10000)) {
+ mem_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
+ if (!mem_node)
+ return -ENOMEM;
+
+ mem_node->base = mem_base << 16;
+
+ mem_node->length = mem_length << 16;
+
+ dbg("%s -->found mem_node(base, length) = %08x, %08x\n",__FUNCTION__, mem_node->base, mem_node->length);
+ dbg("%s -->populated slot =%d \n",__FUNCTION__, populated_slot);
+ if (!populated_slot) {
+ mem_node->next = ctrl->mem_head;
+ ctrl->mem_head = mem_node;
+ } else {
+ mem_node->next = func->mem_head;
+ func->mem_head = mem_node;
+ }
+ }
+
+ // If we've got a valid prefetchable memory base, and
+ // the base + length isn't greater than 0xFFFF
+ temp_dword = pre_mem_base + pre_mem_length;
+ dbg("%s -->temp_D_word for pre mem base = %08x",__FUNCTION__, temp_dword);
+ if ((pre_mem_base) && (temp_dword < 0x10000)) {
+ p_mem_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
+ if (!p_mem_node)
+ return -ENOMEM;
+
+ p_mem_node->base = pre_mem_base << 16;
+
+ p_mem_node->length = pre_mem_length << 16;
+ dbg("%s -->found p_mem_node(base, length) = %08x, %08x\n",__FUNCTION__, p_mem_node->base, p_mem_node->length);
+ dbg("%s -->populated slot =%d \n",__FUNCTION__, populated_slot);
+
+ if (!populated_slot) {
+ p_mem_node->next = ctrl->p_mem_head;
+ ctrl->p_mem_head = p_mem_node;
+ } else {
+ p_mem_node->next = func->p_mem_head;
+ func->p_mem_head = p_mem_node;
+ }
+ }
+
+ // If we've got a valid bus number, use it
+ // The second condition is to ignore bus numbers on
+ // populated slots that don't have PCI-PCI bridges
+ if (secondary_bus && (secondary_bus != primary_bus)) {
+ bus_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
+ if (!bus_node)
+ return -ENOMEM;
+
+ bus_node->base = secondary_bus;
+ bus_node->length = max_bus - secondary_bus + 1;
+ dbg("%s -->found bus_node(base, length) = %08x, %08x\n",__FUNCTION__, bus_node->base, bus_node->length);
+ dbg("%s -->populated slot =%d \n",__FUNCTION__, populated_slot);
+ if (!populated_slot) {
+ bus_node->next = ctrl->bus_head;
+ ctrl->bus_head = bus_node;
+ } else {
+ bus_node->next = func->bus_head;
+ func->bus_head = bus_node;
+ }
+ }
+
+ i--;
+ one_slot += sizeof (struct slot_rt);
+ }
+
+ // If all of the following fail, we don't have any resources for
+ // hot plug add
+ rc = 1;
+ rc &= amdshpc_resource_sort_and_combine(&(ctrl->mem_head));
+ dbg("%s -->rc =%d \n",__FUNCTION__, rc);
+ rc &= amdshpc_resource_sort_and_combine(&(ctrl->p_mem_head));
+ dbg("%s -->rc =%d \n",__FUNCTION__, rc);
+ rc &= amdshpc_resource_sort_and_combine(&(ctrl->io_head));
+ dbg("%s -->rc =%d \n",__FUNCTION__, rc);
+ rc &= amdshpc_resource_sort_and_combine(&(ctrl->bus_head));
+ dbg("%s -->rc =%d \n",__FUNCTION__, rc);
+
+ return(rc);
+}
+
+
+
+/*
+ * amdshpc_save_config
+ *
+ * Reads configuration for all slots in a PCI bus and saves info.
+ *
+ * Note: For non-hot plug busses, the slot # saved is the device #
+ *
+ * returns 0 if success
+ */
+int amdshpc_save_config(struct controller *ctrl, int busnumber, union SLOT_CONFIG_INFO* is_hot_plug)
+ {
+ long rc;
+ u8 class_code;
+ u8 header_type;
+ u32 ID;
+ u8 secondary_bus;
+ struct pci_func *new_slot;
+ int sub_bus;
+ int FirstSupported;
+ int LastSupported;
+ int max_functions;
+ int function;
+ u8 DevError;
+ int device = 0;
+ int cloop = 0;
+ int stop_it;
+ int index;
+
+ // Decide which slots are supported
+ if (is_hot_plug) {
+ FirstSupported = ctrl->first_slot;
+ LastSupported = (FirstSupported + is_hot_plug->x.lu_slots_implemented) - 1;
+ } else {
+ FirstSupported = 0;
+ LastSupported = 0x1F;
+ }
+
+ // Save PCI configuration space for all devices in supported slots
+ for (device = FirstSupported; device <= LastSupported; device++) {
+ int devfn = PCI_DEVFN(device, 0);
+
+ ID = 0xFFFFFFFF;
+ rc = pci_bus_read_config_dword(ctrl->pci_bus, devfn, PCI_VENDOR_ID, &ID);
+ if (rc)
+ return rc;
+
+ if (ID != 0xFFFFFFFF) { // device in slot
+ rc = pci_bus_read_config_byte(ctrl->pci_bus, devfn, 0x0B, &class_code);
+ if (rc)
+ return rc;
+
+ rc = pci_bus_read_config_byte(ctrl->pci_bus, devfn, PCI_HEADER_TYPE, &header_type);
+ if (rc)
+ return rc;
+
+ // If multi-function device, set max_functions to 8
+ if (header_type & 0x80)
+ max_functions = 8;
+ else
+ max_functions = 1;
+
+ function = 0;
+
+ do {
+ DevError = 0;
+
+ if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { // P-P Bridge
+ // Recurse the subordinate bus
+ // get the subordinate bus number
+ rc = pci_bus_read_config_byte(ctrl->pci_bus, PCI_DEVFN(device, function), PCI_SECONDARY_BUS, &secondary_bus);
+ if (rc) {
+ return rc;
+ } else {
+ sub_bus = (int) secondary_bus;
+
+ // Save secondary bus cfg spc
+ // with this recursive call.
+ rc = amdshpc_save_config(ctrl, sub_bus, 0);
+
+ if (rc)
+ return rc;
+ }
+ }
+
+ index = 0;
+ new_slot = amdshpc_slot_find(busnumber, device, index++);
+ while (new_slot &&
+ (new_slot->function != (u8) function))
+ new_slot = amdshpc_slot_find(busnumber, device, index++);
+
+ if (!new_slot) {
+ // Setup slot structure.
+ new_slot = amdshpc_slot_create(busnumber);
+
+ if (new_slot == NULL)
+ return(1);
+ }
+
+ new_slot->bus = (u8) busnumber;
+ new_slot->device = (u8) device;
+ new_slot->function = (u8) function;
+ new_slot->is_a_board = 1;
+ new_slot->switch_save = 0x10;
+ // In case of unsupported board
+ new_slot->status = DevError;
+ new_slot->pci_dev = pci_find_slot(new_slot->bus, (new_slot->device << 3) | new_slot->function);
+ dbg("%s EXISTING SLOT", __FUNCTION__);
+ dbg("%s ns->bus = %d", __FUNCTION__, new_slot->bus);
+ dbg("%s ns->device = %d", __FUNCTION__, new_slot->device);
+ dbg("%s ns->function = %d", __FUNCTION__, new_slot->function);
+ dbg("%s ns->is_a_board = %d", __FUNCTION__, new_slot->is_a_board);
+ dbg("%s ns->switch_save = %02x", __FUNCTION__, new_slot->switch_save);
+ dbg("%s ns->pci_dev = %p", __FUNCTION__, new_slot->pci_dev);
+
+ for (cloop = 0; cloop < 0x20; cloop++) {
+ rc = pci_bus_read_config_dword(ctrl->pci_bus, PCI_DEVFN(device, function), cloop << 2, (u32 *) & (new_slot-> config_space [cloop]));
+ if (rc)
+ return rc;
+ }
+
+ function++;
+ stop_it = 0;
+
+ // this loop skips to the next present function
+ // reading in Class Code and Header type.
+ while ((function < max_functions)&&(!stop_it)) {
+ rc = pci_bus_read_config_dword(ctrl->pci_bus, PCI_DEVFN(device, function), PCI_VENDOR_ID, &ID);
+ if (ID == 0xFFFFFFFF) { // nothing there.
+ function++;
+ } else { // Something there
+ rc = pci_bus_read_config_byte(ctrl->pci_bus, PCI_DEVFN(device, function), 0x0B, &class_code);
+ if (rc)
+ return rc;
+
+ rc = pci_bus_read_config_byte(ctrl->pci_bus, PCI_DEVFN(device, function), PCI_HEADER_TYPE, &header_type);
+ if (rc)
+ return rc;
+
+ stop_it++;
+ }
+ }
+
+ } while (function < max_functions);
+ } // End of IF (device in slot?)
+ else if (is_hot_plug) {
+ // Setup slot structure with entry for empty slot
+ new_slot = amdshpc_slot_create(busnumber);
+
+ if (new_slot == NULL) {
+ return(1);
+ }
+
+ new_slot->bus = (u8) busnumber;
+ new_slot->device = (u8) device;
+ new_slot->function = 0;
+ new_slot->is_a_board = 0;
+ new_slot->presence_save = 0;
+ new_slot->switch_save = 0;
+ dbg("%s NEW SLOT", __FUNCTION__);
+ dbg("%s ns->bus = %d", __FUNCTION__, new_slot->bus);
+ dbg("%s ns->device = %d", __FUNCTION__, new_slot->function);
+ dbg("%s ns->function = %d", __FUNCTION__, new_slot->function);
+ }
+ }// End of FOR loop
+
+ return 0;
+}
+
+
+/*
+ * amdshpc_set_irq
+ *
+ * @bus_num: bus number of PCI device
+ * @dev_num: device number of PCI device
+ */
+/*
+int amdshpc_set_irq (u8 bus_num, u8 dev_num, u8 int_pin, u8 irq_num)
+{
+ int rc;
+ u16 temp_word;
+ struct pci_dev fakedev;
+ struct pci_bus fakebus;
+
+ fakedev.devfn = dev_num << 3;
+ fakedev.bus = &fakebus;
+ fakebus.number = bus_num;
+ dbg("%s : dev %d, bus %d, pin %d, num %d\n",__FUNCTION__,
+ dev_num, bus_num, int_pin, irq_num);
+ rc = pcibios_set_irq_routing(&fakedev, int_pin - 0x0a, irq_num);
+ dbg("%s:rc %d\n",__FUNCTION__, rc);
+ if (rc)
+ return rc;
+
+ // set the Edge Level Control Register (ELCR)
+ temp_word = inb(0x4d0);
+ temp_word |= inb(0x4d1) << 8;
+
+ temp_word |= 0x01 << irq_num;
+
+ // This should only be for x86 as it sets the Edge Level Control Register
+ outb((u8) (temp_word & 0xFF), 0x4d0);
+ outb((u8) ((temp_word & 0xFF00) >> 8), 0x4d1);
+
+ return 0;
+}
+*/
+
+/*
+ * do_pre_bridge_resource_split
+ *
+ * Returns zero or one node of resources that aren't in use
+ *
+ */
+static struct pci_resource *do_pre_bridge_resource_split (struct pci_resource **head, struct pci_resource **orig_head, u32 alignment) {
+ struct pci_resource *prevnode = NULL;
+ struct pci_resource *node;
+ struct pci_resource *split_node;
+ u32 rc;
+ u32 temp_dword;
+ dbg("%s -->do_pre_bridge_resource_split\n",__FUNCTION__);
+
+ if (!(*head) || !(*orig_head))
+ return(NULL);
+
+ rc = amdshpc_resource_sort_and_combine(head);
+
+ if (rc)
+ return(NULL);
+
+ if ((*head)->base != (*orig_head)->base)
+ return(NULL);
+
+ if ((*head)->length == (*orig_head)->length)
+ return(NULL);
+
+
+ // If we got here, there the bridge requires some of the resource, but
+ // we may be able to split some off of the front
+
+ node = *head;
+
+ if (node->length & (alignment -1)) {
+ // this one isn't an aligned length, so we'll make a new entry
+ // and split it up.
+ split_node = (struct pci_resource*) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
+
+ if (!split_node)
+ return(NULL);
+
+ temp_dword = (node->length | (alignment-1)) + 1 - alignment;
+
+ split_node->base = node->base;
+ split_node->length = temp_dword;
+
+ node->length -= temp_dword;
+ node->base += split_node->length;
+
+ // Put it in the list
+ *head = split_node;
+ split_node->next = node;
+ }
+
+ if (node->length < alignment) {
+ return(NULL);
+ }
+
+ // Now unlink it
+ if (*head == node) {
+ *head = node->next;
+ node->next = NULL;
+ } else {
+ prevnode = *head;
+ while (prevnode->next != node)
+ prevnode = prevnode->next;
+
+ prevnode->next = node->next;
+ node->next = NULL;
+ }
+
+ return(node);
+}
+
+
+/*
+ * do_bridge_resource_split
+ *
+ * Returns zero or one node of resources that aren't in use
+ *
+ */
+static struct pci_resource *do_bridge_resource_split (struct pci_resource **head, u32 alignment) {
+ struct pci_resource *prevnode = NULL;
+ struct pci_resource *node;
+ u32 rc;
+ u32 temp_dword;
+
+ if (!(*head))
+ return(NULL);
+
+ rc = amdshpc_resource_sort_and_combine(head);
+
+ if (rc)
+ return(NULL);
+
+ node = *head;
+
+ while (node->next) {
+ prevnode = node;
+ node = node->next;
+ kfree(prevnode);
+ }
+
+ if (node->length < alignment) {
+ kfree(node);
+ return(NULL);
+ }
+
+ if (node->base & (alignment - 1)) {
+ // Short circuit if adjusted size is too small
+ temp_dword = (node->base | (alignment-1)) + 1;
+ if ((node->length - (temp_dword - node->base)) < alignment) {
+ kfree(node);
+ return(NULL);
+ }
+
+ node->length -= (temp_dword - node->base);
+ node->base = temp_dword;
+ }
+
+ if (node->length & (alignment - 1)) {
+ // There's stuff in use after this node
+ kfree(node);
+ return(NULL);
+ }
+
+ return(node);
+}
+
+
+/*
+ * sort_by_size
+ *
+ * Sorts nodes on the list by their length.
+ * Smallest first.
+ *
+ */
+static int sort_by_size(struct pci_resource **head)
+{
+ struct pci_resource *current_res;
+ struct pci_resource *next_res;
+ int out_of_order = 1;
+
+ if (!(*head))
+ return(1);
+
+ if (!((*head)->next))
+ return(0);
+
+ while (out_of_order) {
+ out_of_order = 0;
+
+ // Special case for swapping list head
+ if (((*head)->next) &&
+ ((*head)->length > (*head)->next->length)) {
+ out_of_order++;
+ current_res = *head;
+ *head = (*head)->next;
+ current_res->next = (*head)->next;
+ (*head)->next = current_res;
+ }
+
+ current_res = *head;
+
+ while (current_res->next && current_res->next->next) {
+ if (current_res->next->length > current_res->next->next->length) {
+ out_of_order++;
+ next_res = current_res->next;
+ current_res->next = current_res->next->next;
+ current_res = current_res->next;
+ next_res->next = current_res->next;
+ current_res->next = next_res;
+ } else
+ current_res = current_res->next;
+ }
+ } // End of out_of_order loop
+
+ return(0);
+}
+
+/**
+ * amdshpc_slot_create - Creates a node and adds it to the proper bus.
+ * @busnumber - bus where new node is to be located
+ *
+ * Returns pointer to the new node or NULL if unsuccessful
+ */
+struct pci_func *amdshpc_slot_create(u8 busnumber) {
+ struct pci_func *new_slot;
+ struct pci_func *next;
+
+ dbg("%s busnumber = %02xh",__FUNCTION__, busnumber);
+ new_slot = (struct pci_func *) kmalloc(sizeof(struct pci_func), GFP_KERNEL);
+
+ if (new_slot == NULL) {
+ // I'm not dead yet!
+ // You will be.
+ return(new_slot);
+ }
+
+ memset(new_slot, 0, sizeof(struct pci_func));
+
+ new_slot->next = NULL;
+ new_slot->configured = 1;
+
+ if (amdshpc_slot_list[busnumber] == NULL) {
+ amdshpc_slot_list[busnumber] = new_slot;
+ dbg("%s created new slot in amdshpc_slot_list amdshpc_slot_list[%02X] = %p", __FUNCTION__,
+ busnumber, amdshpc_slot_list[busnumber]);
+ } else {
+ next = amdshpc_slot_list[busnumber];
+ while (next->next != NULL)
+ next = next->next;
+ next->next = new_slot;
+ }
+ return(new_slot);
+}
+
+
+/*
+ * return_resource
+ *
+ * Puts node back in the resource list pointed to by head
+ *
+ */
+static inline void return_resource (struct pci_resource **head, struct pci_resource *node)
+{
+ dbg("%s",__FUNCTION__);
+ if (!node || !head)
+ return;
+ node->next = *head;
+ *head = node;
+}
+
+
+/*
+ * sort_by_max_size
+ *
+ * Sorts nodes on the list by their length.
+ * Largest first.
+ *
+ */
+static int sort_by_max_size(struct pci_resource **head)
+{
+ struct pci_resource *current_res;
+ struct pci_resource *next_res;
+ int out_of_order = 1;
+
+ if (!(*head))
+ return(1);
+
+ if (!((*head)->next))
+ return(0);
+
+ while (out_of_order) {
+ out_of_order = 0;
+
+ // Special case for swapping list head
+ if (((*head)->next) &&
+ ((*head)->length < (*head)->next->length)) {
+ out_of_order++;
+ current_res = *head;
+ *head = (*head)->next;
+ current_res->next = (*head)->next;
+ (*head)->next = current_res;
+ }
+
+ current_res = *head;
+
+ while (current_res->next && current_res->next->next) {
+ if (current_res->next->length < current_res->next->next->length) {
+ out_of_order++;
+ next_res = current_res->next;
+ current_res->next = current_res->next->next;
+ current_res = current_res->next;
+ next_res->next = current_res->next;
+ current_res->next = next_res;
+ } else
+ current_res = current_res->next;
+ }
+ } // End of out_of_order loop
+
+ return(0);
+}
+
+
+/*
+ * get_max_resource
+ *
+ * Gets the largest node that is at least "size" big from the
+ * list pointed to by head. It aligns the node on top and bottom
+ * to "size" alignment before returning it.
+ */
+static struct pci_resource *get_max_resource (struct pci_resource **head, u32 size) {
+ struct pci_resource *max;
+ struct pci_resource *temp;
+ struct pci_resource *split_node;
+ u32 temp_dword;
+
+ if (!(*head))
+ return(NULL);
+
+ if (amdshpc_resource_sort_and_combine(head))
+ return(NULL);
+
+ if (sort_by_max_size(head))
+ return(NULL);
+
+ for (max = *head;max; max = max->next) {
+
+ // If not big enough we could probably just bail,
+ // instead we'll continue to the next.
+ if (max->length < size)
+ continue;
+
+ if (max->base & (size - 1)) {
+ // this one isn't base aligned properly
+ // so we'll make a new entry and split it up
+ temp_dword = (max->base | (size-1)) + 1;
+
+ // Short circuit if adjusted size is too small
+ if ((max->length - (temp_dword - max->base)) < size)
+ continue;
+
+ split_node = (struct pci_resource*) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
+
+ if (!split_node)
+ return(NULL);
+
+ split_node->base = max->base;
+ split_node->length = temp_dword - max->base;
+ max->base = temp_dword;
+ max->length -= split_node->length;
+
+ // Put it next in the list
+ split_node->next = max->next;
+ max->next = split_node;
+ }
+
+ if ((max->base + max->length) & (size - 1)) {
+ // this one isn't end aligned properly at the top
+ // so we'll make a new entry and split it up
+ split_node = (struct pci_resource*) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
+
+ if (!split_node)
+ return(NULL);
+ temp_dword = ((max->base + max->length) & ~(size - 1));
+ split_node->base = temp_dword;
+ split_node->length = max->length + max->base
+ - split_node->base;
+ max->length -= split_node->length;
+
+ // Put it in the list
+ split_node->next = max->next;
+ max->next = split_node;
+ }
+
+ // Make sure it didn't shrink too much when we aligned it
+ if (max->length < size)
+ continue;
+
+ // Now take it out of the list
+ temp = (struct pci_resource*) *head;
+ if (temp == max) {
+ *head = max->next;
+ } else {
+ while (temp && temp->next != max) {
+ temp = temp->next;
+ }
+
+ temp->next = max->next;
+ }
+
+ max->next = NULL;
+ return(max);
+ }
+
+ // If we get here, we couldn't find one
+ return(NULL);
+}
+
+
+/*
+ * get_io_resource
+ *
+ * this function sorts the resource list by size and then
+ * returns the first node of "size" length that is not in the
+ * ISA aliasing window. If it finds a node larger than "size"
+ * it will split it up.
+ *
+ * size must be a power of two.
+ */
+static struct pci_resource *get_io_resource (struct pci_resource **head, u32 size) {
+ struct pci_resource *prevnode;
+ struct pci_resource *node;
+ struct pci_resource *split_node;
+ u32 temp_dword;
+
+ if (!(*head))
+ return(NULL);
+
+ if ( amdshpc_resource_sort_and_combine(head) )
+ return(NULL);
+
+ if ( sort_by_size(head) )
+ return(NULL);
+
+ for (node = *head; node; node = node->next) {
+ if (node->length < size)
+ continue;
+
+ if (node->base & (size - 1)) {
+ // this one isn't base aligned properly
+ // so we'll make a new entry and split it up
+ temp_dword = (node->base | (size-1)) + 1;
+
+ // Short circuit if adjusted size is too small
+ if ((node->length - (temp_dword - node->base)) < size)
+ continue;
+
+ split_node = (struct pci_resource*) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
+
+ if (!split_node)
+ return(NULL);
+
+ split_node->base = node->base;
+ split_node->length = temp_dword - node->base;
+ node->base = temp_dword;
+ node->length -= split_node->length;
+
+ // Put it in the list
+ split_node->next = node->next;
+ node->next = split_node;
+ } // End of non-aligned base
+
+ // Don't need to check if too small since we already did
+ if (node->length > size) {
+ // this one is longer than we need
+ // so we'll make a new entry and split it up
+ split_node = (struct pci_resource*) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
+
+ if (!split_node)
+ return(NULL);
+
+ split_node->base = node->base + size;
+ split_node->length = node->length - size;
+ node->length = size;
+
+ // Put it in the list
+ split_node->next = node->next;
+ node->next = split_node;
+ } // End of too big on top end
+
+ // For IO make sure it's not in the ISA aliasing space
+ if (node->base & 0x300L)
+ continue;
+
+ // If we got here, then it is the right size
+ // Now take it out of the list
+ if (*head == node) {
+ *head = node->next;
+ } else {
+ prevnode = *head;
+ while (prevnode->next != node)
+ prevnode = prevnode->next;
+
+ prevnode->next = node->next;
+ }
+ node->next = NULL;
+ // Stop looping
+ break;
+ }
+
+ return(node);
+}
+
+
+/*
+ * get_resource
+ *
+ * this function sorts the resource list by size and then
+ * returns the first node of "size" length. If it finds a node
+ * larger than "size" it will split it up.
+ *
+ * size must be a power of two.
+ */
+static struct pci_resource *get_resource (struct pci_resource **head, u32 size) {
+ struct pci_resource *prevnode;
+ struct pci_resource *node;
+ struct pci_resource *split_node;
+ u32 temp_dword;
+
+ if (!(*head))
+ return(NULL);
+
+ if ( amdshpc_resource_sort_and_combine(head) )
+ return(NULL);
+
+ if ( sort_by_size(head) )
+ return(NULL);
+
+ for (node = *head; node; node = node->next) {
+ dbg("%s: req_size =%x node=%p, base=%x, length=%x\n",__FUNCTION__,
+ size, node, node->base, node->length);
+ if (node->length < size)
+ continue;
+
+ if (node->base & (size - 1)) {
+ dbg("%s: not aligned\n",__FUNCTION__);
+ // this one isn't base aligned properly
+ // so we'll make a new entry and split it up
+ temp_dword = (node->base | (size-1)) + 1;
+
+ // Short circuit if adjusted size is too small
+ if ((node->length - (temp_dword - node->base)) < size)
+ continue;
+
+ split_node = (struct pci_resource*) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
+
+ if (!split_node)
+ return(NULL);
+
+ split_node->base = node->base;
+ split_node->length = temp_dword - node->base;
+ node->base = temp_dword;
+ node->length -= split_node->length;
+
+ // Put it in the list
+ split_node->next = node->next;
+ node->next = split_node;
+ } // End of non-aligned base
+
+ // Don't need to check if too small since we already did
+ if (node->length > size) {
+ dbg("%s: too big\n",__FUNCTION__);
+ // this one is longer than we need
+ // so we'll make a new entry and split it up
+ split_node = (struct pci_resource*) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
+
+ if (!split_node)
+ return(NULL);
+
+ split_node->base = node->base + size;
+ split_node->length = node->length - size;
+ node->length = size;
+
+ // Put it in the list
+ split_node->next = node->next;
+ node->next = split_node;
+ } // End of too big on top end
+
+ dbg("%s: got one!!!\n",__FUNCTION__);
+ // If we got here, then it is the right size
+ // Now take it out of the list
+ if (*head == node) {
+ *head = node->next;
+ } else {
+ prevnode = *head;
+ while (prevnode->next != node)
+ prevnode = prevnode->next;
+
+ prevnode->next = node->next;
+ }
+ node->next = NULL;
+ // Stop looping
+ break;
+ }
+ return(node);
+}
+
+/*
+ * amdshpc_return_board_resources
+ *
+ * this routine returns all resources allocated to a board to
+ * the available pool.
+ *
+ * returns 0 if success
+ */
+int amdshpc_return_board_resources(struct pci_func * func, struct resource_lists * resources)
+{
+ int rc = 1;
+ struct pci_resource *node;
+ struct pci_resource *t_node;
+ dbg("%s",__FUNCTION__);
+
+ if (!func)
+ return(1);
+
+ node = func->io_head;
+ func->io_head = NULL;
+ while (node) {
+ t_node = node->next;
+ return_resource(&(resources->io_head), node);
+ node = t_node;
+ }
+
+ node = func->mem_head;
+ func->mem_head = NULL;
+ while (node) {
+ t_node = node->next;
+ return_resource(&(resources->mem_head), node);
+ node = t_node;
+ }
+
+ node = func->p_mem_head;
+ func->p_mem_head = NULL;
+ while (node) {
+ t_node = node->next;
+ return_resource(&(resources->p_mem_head), node);
+ node = t_node;
+ }
+
+ node = func->bus_head;
+ func->bus_head = NULL;
+ while (node) {
+ t_node = node->next;
+ return_resource(&(resources->bus_head), node);
+ node = t_node;
+ }
+
+ rc |= amdshpc_resource_sort_and_combine(&(resources->mem_head));
+ rc |= amdshpc_resource_sort_and_combine(&(resources->p_mem_head));
+ rc |= amdshpc_resource_sort_and_combine(&(resources->io_head));
+ rc |= amdshpc_resource_sort_and_combine(&(resources->bus_head));
+
+ return(rc);
+}
+
+
+/*
+ * amdshpc_destroy_resource_list
+ *
+ * Puts node back in the resource list pointed to by head
+ */
+void amdshpc_destroy_resource_list (struct resource_lists * resources)
+{
+ struct pci_resource *res, *tres;
+
+ res = resources->io_head;
+ resources->io_head = NULL;
+
+ while (res) {
+ tres = res;
+ res = res->next;
+ kfree(tres);
+ }
+
+ res = resources->mem_head;
+ resources->mem_head = NULL;
+
+ while (res) {
+ tres = res;
+ res = res->next;
+ kfree(tres);
+ }
+
+ res = resources->p_mem_head;
+ resources->p_mem_head = NULL;
+
+ while (res) {
+ tres = res;
+ res = res->next;
+ kfree(tres);
+ }
+
+ res = resources->bus_head;
+ resources->bus_head = NULL;
+
+ while (res) {
+ tres = res;
+ res = res->next;
+ kfree(tres);
+ }
+}
+
+
+/*
+ * amdshpc_destroy_board_resources
+ *
+ * Puts node back in the resource list pointed to by head
+ */
+void amdshpc_destroy_board_resources (struct pci_func * func)
+{
+ struct pci_resource *res, *tres;
+
+ res = func->io_head;
+ func->io_head = NULL;
+
+ while (res) {
+ tres = res;
+ res = res->next;
+ kfree(tres);
+ }
+
+ res = func->mem_head;
+ func->mem_head = NULL;
+
+ while (res) {
+ tres = res;
+ res = res->next;
+ kfree(tres);
+ }
+
+ res = func->p_mem_head;
+ func->p_mem_head = NULL;
+
+ while (res) {
+ tres = res;
+ res = res->next;
+ kfree(tres);
+ }
+
+ res = func->bus_head;
+ func->bus_head = NULL;
+
+ while (res) {
+ tres = res;
+ res = res->next;
+ kfree(tres);
+ }
+}
+
+/**
+ * configure_new_device - Configures the PCI header information of one board.
+ *
+ * @ctrl: pointer to controller structure
+ * @func: pointer to function structure
+ * @behind_bridge: 1 if this is a recursive call, 0 if not
+ * @resources: pointer to set of resource lists
+ *
+ * Returns 0 if success
+ *
+ */
+static u32 configure_new_device (struct controller * ctrl, struct pci_func * func,
+ u8 behind_bridge, struct resource_lists * resources)
+{
+ u8 temp_byte, function, max_functions, stop_it;
+ int rc;
+ u32 ID;
+ struct pci_func *new_slot;
+ int index;
+
+ new_slot = func;
+
+ dbg("%s",__FUNCTION__);
+ // Check for Multi-function device
+ rc = pci_bus_read_config_byte(ctrl->pci_bus, PCI_DEVFN(func->device, func->function), 0x0E, &temp_byte);
+ if (rc) {
+ dbg("%s: rc = %d\n",__FUNCTION__, rc);
+ return rc;
+ }
+
+ if (temp_byte & 0x80) // Multi-function device
+ max_functions = 8;
+ else
+ max_functions = 1;
+
+ function = 0;
+
+ do {
+ rc = configure_new_function(ctrl, new_slot, behind_bridge, resources);
+
+ if (rc) {
+ dbg("%s -->configure_new_function failed %d\n",__FUNCTION__,rc);
+ index = 0;
+
+ while (new_slot) {
+ new_slot = amdshpc_slot_find(new_slot->bus, new_slot->device, index++);
+
+ if (new_slot)
+ amdshpc_return_board_resources(new_slot, resources);
+ }
+
+ return(rc);
+ }
+
+ function++;
+
+ stop_it = 0;
+
+ // The following loop skips to the next present function
+ // and creates a board structure
+
+ while ((function < max_functions) && (!stop_it)) {
+ pci_bus_read_config_dword(ctrl->pci_bus, PCI_DEVFN(func->device, function), 0x00, &ID);
+
+ if (ID == 0xFFFFFFFF) { // There's nothing there.
+ function++;
+ } else { // There's something there
+ // Setup slot structure.
+ new_slot = amdshpc_slot_create(func->bus);
+
+ if (new_slot == NULL) {
+ // Out of memory
+ return(1);
+ }
+
+ new_slot->bus = func->bus;
+ new_slot->device = func->device;
+ new_slot->function = function;
+ new_slot->is_a_board = 1;
+ new_slot->status = 0;
+
+ stop_it++;
+ }
+ }
+
+ } while (function < max_functions);
+ dbg("%s -->returning from configure_new_device\n",__FUNCTION__);
+
+ return 0;
+}
+
+
+/*
+ Configuration logic that involves the hotplug data structures and
+ their bookkeeping
+ */
+
+
+/**
+ * configure_new_function - Configures the PCI header information of one device
+ *
+ * @ctrl: pointer to controller structure
+ * @func: pointer to function structure
+ * @behind_bridge: 1 if this is a recursive call, 0 if not
+ * @resources: pointer to set of resource lists
+ *
+ * Calls itself recursively for bridged devices.
+ * Returns 0 if success
+ *
+ */
+static int configure_new_function (struct controller * ctrl, struct pci_func * func,
+ u8 behind_bridge, struct resource_lists * resources)
+{
+ int cloop;
+ u8 IRQ;
+ u8 temp_byte;
+ u8 device;
+ u8 class_code;
+ u16 command;
+ u16 temp_word;
+ u32 temp_dword;
+ u32 rc;
+ u32 temp_register;
+ u32 base;
+ u32 ID;
+ struct pci_resource *mem_node;
+ struct pci_resource *p_mem_node;
+ struct pci_resource *io_node;
+ struct pci_resource *bus_node;
+ struct pci_resource *hold_mem_node;
+ struct pci_resource *hold_p_mem_node;
+ struct pci_resource *hold_IO_node;
+ struct pci_resource *hold_bus_node;
+ struct irq_mapping irqs;
+ struct pci_func *new_slot;
+ struct resource_lists temp_resources;
+ int devfn = PCI_DEVFN(func->device, func->function);
+
+ dbg("%s", __FUNCTION__);
+ // Check for Bridge
+ rc = pci_bus_read_config_byte(ctrl->pci_bus, devfn, PCI_HEADER_TYPE, &temp_byte);
+ if (rc)
+ return rc;
+
+ if ((temp_byte & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { // PCI-PCI Bridge
+ // set Primary bus
+ dbg("%s -->set Primary bus = %d\n",__FUNCTION__, func->bus);
+ rc = pci_bus_write_config_byte(ctrl->pci_bus, devfn, PCI_PRIMARY_BUS, func->bus);
+ if (rc)
+ return rc;
+
+ // find range of busses to use
+ dbg("%s -->find ranges of buses to use\n",__FUNCTION__);
+ bus_node = get_max_resource(&resources->bus_head, 1);
+
+ // If we don't have any busses to allocate, we can't continue
+ if (!bus_node)
+ return -ENOMEM;
+
+ // set Secondary bus
+ temp_byte = bus_node->base;
+ dbg("%s -->set Secondary bus = %d\n",__FUNCTION__, bus_node->base);
+ rc = pci_bus_write_config_byte(ctrl->pci_bus, devfn, PCI_SECONDARY_BUS, temp_byte);
+ if (rc)
+ return rc;
+
+ // set subordinate bus
+ temp_byte = bus_node->base + bus_node->length - 1;
+ dbg("%s -->set subordinate bus = %d\n",__FUNCTION__, bus_node->base + bus_node->length - 1);
+ rc = pci_bus_write_config_byte(ctrl->pci_bus, devfn, PCI_SUBORDINATE_BUS, temp_byte);
+ if (rc)
+ return rc;
+
+ // set subordinate Latency Timer and base Latency Timer
+ temp_byte = 0x40;
+ rc = pci_bus_write_config_byte(ctrl->pci_bus, devfn, PCI_SEC_LATENCY_TIMER, temp_byte);
+ if (rc)
+ return rc;
+ rc = pci_bus_write_config_byte(ctrl->pci_bus, devfn, PCI_LATENCY_TIMER, temp_byte);
+ if (rc)
+ return rc;
+
+ // set Cache Line size
+ temp_byte = 0x08;
+ rc = pci_bus_write_config_byte(ctrl->pci_bus, devfn, PCI_CACHE_LINE_SIZE, temp_byte);
+ if (rc)
+ return rc;
+
+ // Setup the IO, memory, and prefetchable windows
+
+ io_node = get_max_resource(&(resources->io_head), 0x1000);
+ mem_node = get_max_resource(&(resources->mem_head), 0x100000);
+ p_mem_node = get_max_resource(&(resources->p_mem_head), 0x100000);
+ dbg("%s -->Setup the IO, memory, and prefetchable windows\n",__FUNCTION__);
+ dbg("%s -->io_node\n",__FUNCTION__);
+ dbg("%s -->(base, len, next) (%x, %x, %p)\n",__FUNCTION__, io_node->base, io_node->length, io_node->next);
+ dbg("%s -->mem_node\n",__FUNCTION__);
+ dbg("%s -->(base, len, next) (%x, %x, %p)\n",__FUNCTION__, mem_node->base, mem_node->length, mem_node->next);
+ dbg("%s -->p_mem_node\n",__FUNCTION__);
+ dbg("%s -->(base, len, next) (%x, %x, %p)\n",__FUNCTION__, p_mem_node->base, p_mem_node->length, p_mem_node->next);
+
+ // set up the IRQ info
+ if (!resources->irqs) {
+ irqs.barber_pole = 0;
+ irqs.interrupt[0] = 0;
+ irqs.interrupt[1] = 0;
+ irqs.interrupt[2] = 0;
+ irqs.interrupt[3] = 0;
+ irqs.valid_INT = 0;
+ } else {
+ irqs.barber_pole = resources->irqs->barber_pole;
+ irqs.interrupt[0] = resources->irqs->interrupt[0];
+ irqs.interrupt[1] = resources->irqs->interrupt[1];
+ irqs.interrupt[2] = resources->irqs->interrupt[2];
+ irqs.interrupt[3] = resources->irqs->interrupt[3];
+ irqs.valid_INT = resources->irqs->valid_INT;
+ }
+
+ // set up resource lists that are now aligned on top and bottom
+ // for anything behind the bridge.
+ temp_resources.bus_head = bus_node;
+ temp_resources.io_head = io_node;
+ temp_resources.mem_head = mem_node;
+ temp_resources.p_mem_head = p_mem_node;
+ temp_resources.irqs = &irqs;
+
+ // Make copies of the nodes we are going to pass down so that
+ // if there is a problem,we can just use these to free resources
+ hold_bus_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
+ hold_IO_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
+ hold_mem_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
+ hold_p_mem_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
+
+ if (!hold_bus_node || !hold_IO_node || !hold_mem_node || !hold_p_mem_node) {
+ if (hold_bus_node)
+ kfree(hold_bus_node);
+ if (hold_IO_node)
+ kfree(hold_IO_node);
+ if (hold_mem_node)
+ kfree(hold_mem_node);
+ if (hold_p_mem_node)
+ kfree(hold_p_mem_node);
+
+ return(1);
+ }
+
+ memcpy(hold_bus_node, bus_node, sizeof(struct pci_resource));
+
+ bus_node->base += 1;
+ bus_node->length -= 1;
+ bus_node->next = NULL;
+
+ // If we have IO resources copy them and fill in the bridge's
+ // IO range registers
+ if (io_node) {
+ memcpy(hold_IO_node, io_node, sizeof(struct pci_resource));
+ io_node->next = NULL;
+
+ // set IO base and Limit registers
+ temp_byte = io_node->base >> 8;
+ rc = pci_bus_write_config_byte(ctrl->pci_bus, devfn, PCI_IO_BASE, temp_byte);
+
+ temp_byte = (io_node->base + io_node->length - 1) >> 8;
+ rc = pci_bus_write_config_byte(ctrl->pci_bus, devfn, PCI_IO_LIMIT, temp_byte);
+ } else {
+ kfree(hold_IO_node);
+ hold_IO_node = NULL;
+ }
+
+ // If we have memory resources copy them and fill in the bridge's
+ // memory range registers. Otherwise, fill in the range
+ // registers with values that disable them.
+ if (mem_node) {
+ memcpy(hold_mem_node, mem_node, sizeof(struct pci_resource));
+ mem_node->next = NULL;
+
+ // set Mem base and Limit registers
+ temp_word = mem_node->base >> 16;
+ pci_bus_write_config_word(ctrl->pci_bus, devfn, PCI_MEMORY_BASE, temp_word);
+
+ temp_word = (mem_node->base + mem_node->length - 1) >> 16;
+ pci_bus_write_config_word(ctrl->pci_bus, devfn, PCI_MEMORY_LIMIT, temp_word);
+ } else {
+ temp_word = 0xFFFF;
+ pci_bus_write_config_word(ctrl->pci_bus, devfn, PCI_MEMORY_BASE, temp_word);
+
+ temp_word = 0x0000;
+ pci_bus_write_config_word(ctrl->pci_bus, devfn, PCI_MEMORY_LIMIT, temp_word);
+
+ kfree(hold_mem_node);
+ hold_mem_node = NULL;
+ }
+
+ // If we have prefetchable memory resources copy them and
+ // fill in the bridge's memory range registers. Otherwise,
+ // fill in the range registers with values that disable them.
+ if (p_mem_node) {
+ memcpy(hold_p_mem_node, p_mem_node, sizeof(struct pci_resource));
+ p_mem_node->next = NULL;
+
+ // set Pre Mem base and Limit registers
+ temp_word = p_mem_node->base >> 16;
+ pci_bus_write_config_word(ctrl->pci_bus, devfn, PCI_PREF_MEMORY_BASE, temp_word);
+
+ temp_word = (p_mem_node->base + p_mem_node->length - 1) >> 16;
+ pci_bus_write_config_word(ctrl->pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, temp_word);
+ } else {
+ temp_word = 0xFFFF;
+ pci_bus_write_config_word(ctrl->pci_bus, devfn, PCI_PREF_MEMORY_BASE, temp_word);
+
+ temp_word = 0x0000;
+ pci_bus_write_config_word(ctrl->pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, temp_word);
+
+ kfree(hold_p_mem_node);
+ hold_p_mem_node = NULL;
+ }
+
+ // Adjust this to compensate for extra adjustment in first loop
+ irqs.barber_pole--;
+
+ rc = 0;
+
+ // Here we actually find the devices and configure them
+ for (device = 0; (device <= 0x1F) && !rc; device++) {
+ irqs.barber_pole = (irqs.barber_pole + 1) & 0x03;
+
+ ID = 0xFFFFFFFF;
+ pci_bus_read_config_dword(ctrl->pci_bus, PCI_DEVFN(device, 0), 0x00, &ID);
+
+ if (ID != 0xFFFFFFFF) { // device Present
+ // Setup slot structure.
+ new_slot = amdshpc_slot_create(hold_bus_node->base);
+
+ if (new_slot == NULL) {
+ // Out of memory
+ rc = -ENOMEM;
+ continue;
+ }
+
+ new_slot->bus = hold_bus_node->base;
+ new_slot->device = device;
+ new_slot->function = 0;
+ new_slot->is_a_board = 1;
+ new_slot->status = 0;
+
+ rc = configure_new_device(ctrl, new_slot, 1, &temp_resources);
+ dbg("%s -->configure_new_device rc=0x%x\n",__FUNCTION__,rc);
+ } // End of IF (device in slot?)
+ } // End of FOR loop
+
+ if (rc) {
+ amdshpc_destroy_resource_list(&temp_resources);
+
+ return_resource(&(resources->bus_head), hold_bus_node);
+ return_resource(&(resources->io_head), hold_IO_node);
+ return_resource(&(resources->mem_head), hold_mem_node);
+ return_resource(&(resources->p_mem_head), hold_p_mem_node);
+ return(rc);
+ }
+ // save the interrupt routing information
+ if (resources->irqs) {
+ resources->irqs->interrupt[0] = irqs.interrupt[0];
+ resources->irqs->interrupt[1] = irqs.interrupt[1];
+ resources->irqs->interrupt[2] = irqs.interrupt[2];
+ resources->irqs->interrupt[3] = irqs.interrupt[3];
+ resources->irqs->valid_INT = irqs.valid_INT;
+ } else if (!behind_bridge) {
+ // We need to hook up the interrupts here
+ for (cloop = 0; cloop < 4; cloop++) {
+ if (irqs.valid_INT & (0x01 << cloop)) {
+rc=0;
+// rc = amdshpc_set_irq(func->bus, func->device,
+// 0x0A + cloop, irqs.interrupt[cloop]);
+ if (rc) {
+ amdshpc_destroy_resource_list (&temp_resources);
+
+ return_resource(&(resources-> bus_head), hold_bus_node);
+ return_resource(&(resources-> io_head), hold_IO_node);
+ return_resource(&(resources-> mem_head), hold_mem_node);
+ return_resource(&(resources-> p_mem_head), hold_p_mem_node);
+ return rc;
+ }
+ }
+ } // end of for loop
+ }
+ // Return unused bus resources
+ // First use the temporary node to store information for the board
+ if (hold_bus_node && bus_node && temp_resources.bus_head) {
+ hold_bus_node->length = bus_node->base - hold_bus_node->base;
+
+ hold_bus_node->next = func->bus_head;
+ func->bus_head = hold_bus_node;
+
+ temp_byte = temp_resources.bus_head->base - 1;
+
+ // set subordinate bus
+ pci_bus_write_config_byte(ctrl->pci_bus, devfn, PCI_SUBORDINATE_BUS, temp_byte);
+
+ if (temp_resources.bus_head->length == 0) {
+ kfree(temp_resources.bus_head);
+ temp_resources.bus_head = NULL;
+ } else {
+ return_resource(&(resources->bus_head), temp_resources.bus_head);
+ }
+ }
+
+ // If we have IO space available and there is some left,
+ // return the unused portion
+ if (hold_IO_node && temp_resources.io_head) {
+ io_node = do_pre_bridge_resource_split(&(temp_resources.io_head),
+ &hold_IO_node, 0x1000);
+
+ // Check if we were able to split something off
+ if (io_node) {
+ hold_IO_node->base = io_node->base + io_node->length;
+
+ temp_byte = (hold_IO_node->base) >> 8;
+ pci_bus_write_config_word(ctrl->pci_bus, devfn, PCI_IO_BASE, temp_byte);
+
+ return_resource(&(resources->io_head), io_node);
+ }
+
+ io_node = do_bridge_resource_split(&(temp_resources.io_head), 0x1000);
+
+ // Check if we were able to split something off
+ if (io_node) {
+ // First use the temporary node to store information for the board
+ hold_IO_node->length = io_node->base - hold_IO_node->base;
+
+ // If we used any, add it to the board's list
+ if (hold_IO_node->length) {
+ hold_IO_node->next = func->io_head;
+ func->io_head = hold_IO_node;
+
+ temp_byte = (io_node->base - 1) >> 8;
+ pci_bus_write_config_byte(ctrl->pci_bus, devfn, PCI_IO_LIMIT, temp_byte);
+
+ return_resource(&(resources->io_head), io_node);
+ } else {
+ // it doesn't need any IO
+ temp_word = 0x0000;
+ pci_bus_write_config_word(ctrl->pci_bus, devfn, PCI_IO_LIMIT, temp_word);
+
+ return_resource(&(resources->io_head), io_node);
+ kfree(hold_IO_node);
+ }
+ } else {
+ // it used most of the range
+ hold_IO_node->next = func->io_head;
+ func->io_head = hold_IO_node;
+ }
+ } else if (hold_IO_node) {
+ // it used the whole range
+ hold_IO_node->next = func->io_head;
+ func->io_head = hold_IO_node;
+ }
+ // If we have memory space available and there is some left,
+ // return the unused portion
+ if (hold_mem_node && temp_resources.mem_head) {
+ mem_node = do_pre_bridge_resource_split(&(temp_resources. mem_head),
+ &hold_mem_node, 0x100000);
+
+ // Check if we were able to split something off
+ if (mem_node) {
+ hold_mem_node->base = mem_node->base + mem_node->length;
+
+ temp_word = (hold_mem_node->base) >> 16;
+ pci_bus_write_config_word(ctrl->pci_bus, devfn, PCI_MEMORY_BASE, temp_word);
+
+ return_resource(&(resources->mem_head), mem_node);
+ }
+
+ mem_node = do_bridge_resource_split(&(temp_resources.mem_head), 0x100000);
+
+ // Check if we were able to split something off
+ if (mem_node) {
+ // First use the temporary node to store information for the board
+ hold_mem_node->length = mem_node->base - hold_mem_node->base;
+
+ if (hold_mem_node->length) {
+ hold_mem_node->next = func->mem_head;
+ func->mem_head = hold_mem_node;
+
+ // configure end address
+ temp_word = (mem_node->base - 1) >> 16;
+ pci_bus_write_config_word(ctrl->pci_bus, devfn, PCI_MEMORY_LIMIT, temp_word);
+
+ // Return unused resources to the pool
+ return_resource(&(resources->mem_head), mem_node);
+ } else {
+ // it doesn't need any Mem
+ temp_word = 0x0000;
+ pci_bus_write_config_word(ctrl->pci_bus, devfn, PCI_MEMORY_LIMIT, temp_word);
+
+ return_resource(&(resources->mem_head), mem_node);
+ kfree(hold_mem_node);
+ }
+ } else {
+ // it used most of the range
+ hold_mem_node->next = func->mem_head;
+ func->mem_head = hold_mem_node;
+ }
+ } else if (hold_mem_node) {
+ // it used the whole range
+ hold_mem_node->next = func->mem_head;
+ func->mem_head = hold_mem_node;
+ }
+ // If we have prefetchable memory space available and there is some
+ // left at the end, return the unused portion
+ if (hold_p_mem_node && temp_resources.p_mem_head) {
+ p_mem_node = do_pre_bridge_resource_split(&(temp_resources.p_mem_head),
+ &hold_p_mem_node, 0x100000);
+
+ // Check if we were able to split something off
+ if (p_mem_node) {
+ hold_p_mem_node->base = p_mem_node->base + p_mem_node->length;
+
+ temp_word = (hold_p_mem_node->base) >> 16;
+ pci_bus_write_config_word(ctrl->pci_bus, devfn, PCI_PREF_MEMORY_BASE, temp_word);
+
+ return_resource(&(resources->p_mem_head), p_mem_node);
+ }
+
+ p_mem_node = do_bridge_resource_split(&(temp_resources.p_mem_head), 0x100000);
+
+ // Check if we were able to split something off
+ if (p_mem_node) {
+ // First use the temporary node to store information for the board
+ hold_p_mem_node->length = p_mem_node->base - hold_p_mem_node->base;
+
+ // If we used any, add it to the board's list
+ if (hold_p_mem_node->length) {
+ hold_p_mem_node->next = func->p_mem_head;
+ func->p_mem_head = hold_p_mem_node;
+
+ temp_word = (p_mem_node->base - 1) >> 16;
+ pci_bus_write_config_word(ctrl->pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, temp_word);
+
+ return_resource(&(resources->p_mem_head), p_mem_node);
+ } else {
+ // it doesn't need any PMem
+ temp_word = 0x0000;
+ pci_bus_write_config_word(ctrl->pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, temp_word);
+
+ return_resource(&(resources->p_mem_head), p_mem_node);
+ kfree(hold_p_mem_node);
+ }
+ } else {
+ // it used the most of the range
+ hold_p_mem_node->next = func->p_mem_head;
+ func->p_mem_head = hold_p_mem_node;
+ }
+ } else if (hold_p_mem_node) {
+ // it used the whole range
+ hold_p_mem_node->next = func->p_mem_head;
+ func->p_mem_head = hold_p_mem_node;
+ }
+ // We should be configuring an IRQ and the bridge's base address
+ // registers if it needs them. Although we have never seen such
+ // a device
+
+ // enable card
+ command = 0x0157; // = PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER | PCI_COMMAND_INVALIDATE | PCI_COMMAND_PARITY | PCI_COMMAND_SERR
+ pci_bus_write_config_word(ctrl->pci_bus, devfn, PCI_COMMAND, command);
+
+ // set Bridge Control Register
+ command = 0x07; // = PCI_BRIDGE_CTL_PARITY | PCI_BRIDGE_CTL_SERR | PCI_BRIDGE_CTL_NO_ISA
+ pci_bus_write_config_word(ctrl->pci_bus, devfn, PCI_BRIDGE_CONTROL, command);
+ } else if ((temp_byte & 0x7F) == PCI_HEADER_TYPE_NORMAL) {
+ // Standard device
+ pci_bus_read_config_byte(ctrl->pci_bus, devfn, 0x0B, &class_code);
+
+ if (class_code == PCI_BASE_CLASS_DISPLAY) {
+ // Display (video) adapter (not supported)
+ return(DEVICE_TYPE_NOT_SUPPORTED);
+ }
+ // Figure out IO and memory needs
+ for (cloop = 0x10; cloop <= 0x24; cloop += 4) {
+ temp_register = 0xFFFFFFFF;
+
+ dbg("%s -->CND: devfn=%x, offset=%d\n",__FUNCTION__, devfn, cloop);
+ pci_bus_write_config_dword(ctrl->pci_bus, devfn, cloop, temp_register);
+ pci_bus_read_config_dword(ctrl->pci_bus, devfn, cloop, &temp_register);
+ dbg("%s -->CND: base = 0x%x\n",__FUNCTION__, temp_register);
+
+ if (temp_register) { // If this register is implemented
+ if ((temp_register & 0x03L) == 0x01) {
+ // Map IO
+
+ // set base = amount of IO space
+ base = temp_register & 0xFFFFFFFC;
+ base = ~base + 1;
+
+ dbg("%s -->CND: length = 0x%x\n",__FUNCTION__, base);
+ io_node = get_io_resource(&(resources->io_head), base);
+ dbg("%s -->Got io_node start = %8.8x, length = %8.8x next (%p)\n",__FUNCTION__,
+ io_node->base, io_node->length, io_node->next);
+ dbg("%s -->func (%p) io_head (%p)\n",__FUNCTION__, func, func->io_head);
+
+ // allocate the resource to the board
+ if (io_node) {
+ base = io_node->base;
+
+ io_node->next = func->io_head;
+ func->io_head = io_node;
+ } else
+ return -ENOMEM;
+ } else if ((temp_register & 0x0BL) == 0x08) {
+ // Map prefetchable memory
+ base = temp_register & 0xFFFFFFF0;
+ base = ~base + 1;
+
+ dbg("%s -->CND: length = 0x%x\n",__FUNCTION__, base);
+ p_mem_node = get_resource(&(resources->p_mem_head), base);
+
+ // allocate the resource to the board
+ if (p_mem_node) {
+ base = p_mem_node->base;
+
+ p_mem_node->next = func->p_mem_head;
+ func->p_mem_head = p_mem_node;
+ } else
+ return -ENOMEM;
+ } else if ((temp_register & 0x0BL) == 0x00) {
+ // Map memory
+ base = temp_register & 0xFFFFFFF0;
+ base = ~base + 1;
+
+ dbg("%s -->CND: length = 0x%x\n",__FUNCTION__, base);
+ mem_node = get_resource(&(resources->mem_head), base);
+
+ // allocate the resource to the board
+ if (mem_node) {
+ base = mem_node->base;
+
+ mem_node->next = func->mem_head;
+ func->mem_head = mem_node;
+ } else
+ return -ENOMEM;
+ } else if ((temp_register & 0x0BL) == 0x04) {
+ // Map memory
+ base = temp_register & 0xFFFFFFF0;
+ base = ~base + 1;
+
+ dbg("%s -->CND: length = 0x%x\n",__FUNCTION__, base);
+ mem_node = get_resource(&(resources->mem_head), base);
+
+ // allocate the resource to the board
+ if (mem_node) {
+ base = mem_node->base;
+
+ mem_node->next = func->mem_head;
+ func->mem_head = mem_node;
+ } else
+ return -ENOMEM;
+ } else if ((temp_register & 0x0BL) == 0x06) {
+ // Those bits are reserved, we can't handle this
+ return(1);
+ } else {
+ // Requesting space below 1M
+ return(NOT_ENOUGH_RESOURCES);
+ }
+
+ pci_bus_write_config_dword(ctrl->pci_bus, devfn, cloop, base);
+
+ // Check for 64-bit base
+ if ((temp_register & 0x07L) == 0x04) {
+ cloop += 4;
+
+ // Upper 32 bits of address always zero on today's systems
+ // FIXME this is probably not true on Alpha and ia64???
+ base = 0;
+ pci_bus_write_config_dword(ctrl->pci_bus, devfn, cloop, base);
+ }
+ }
+ } // End of base register loop
+
+ // Figure out which interrupt pin this function uses
+ pci_bus_read_config_byte(ctrl->pci_bus, devfn, PCI_INTERRUPT_PIN, &temp_byte);
+ dbg("%s temp_byte for interrupt pin = %x", __FUNCTION__, temp_byte);
+ // If this function needs an interrupt and we are behind a bridge
+ // and the pin is tied to something that's already mapped,
+ // set this one the same
+ if (temp_byte && resources->irqs &&
+ (resources->irqs->valid_INT &
+ (0x01 << ((temp_byte + resources->irqs->barber_pole - 1) & 0x03)))) {
+ // We have to share with something already set up
+ IRQ = resources->irqs->interrupt[(temp_byte + resources->irqs->barber_pole - 1) & 0x03];
+ dbg("%s We're sharing the IRQ from some other device = %02x", __FUNCTION__, IRQ);
+ } else {
+ // Program IRQ based on card type
+ pci_bus_read_config_byte(ctrl->pci_bus, devfn, 0x0B, &class_code);
+ if (class_code == PCI_BASE_CLASS_STORAGE) {
+ dbg("%s We're sharing the disk IRQ (maybe)", __FUNCTION__);
+ IRQ = amdshpc_disk_irq;
+ } else {
+ dbg("%s We're sharing the NIC IRQ (maybe)", __FUNCTION__);
+ IRQ = amdshpc_nic_irq;
+ }
+ }
+
+ // IRQ Line
+ pci_bus_write_config_byte(ctrl->pci_bus, devfn, PCI_INTERRUPT_LINE, IRQ);
+ if (!behind_bridge) {
+// rc = amdshpc_set_irq(func->bus, func->device, temp_byte + 0x09, IRQ);
+// rc = amdshpc_set_irq(func->bus, func->device, temp_byte + 20, IRQ);
+ rc = 0;
+ if (rc)
+ return 1;
+ } else {
+ //TBD - this code may also belong in the other clause of this If statement
+ resources->irqs->interrupt[(temp_byte + resources->irqs->barber_pole - 1) & 0x03] = IRQ;
+ resources->irqs->valid_INT |= 0x01 << (temp_byte + resources->irqs->barber_pole - 1) & 0x03;
+ }
+
+ // Latency Timer
+ temp_byte = 0x40;
+ pci_bus_write_config_byte(ctrl->pci_bus, devfn, PCI_LATENCY_TIMER, temp_byte);
+
+ // Cache Line size
+ temp_byte = 0x08;
+ pci_bus_write_config_byte(ctrl->pci_bus, devfn, PCI_CACHE_LINE_SIZE, temp_byte);
+
+ // disable ROM base Address
+ temp_dword = 0x00L;
+ pci_bus_write_config_word(ctrl->pci_bus, devfn, PCI_ROM_ADDRESS, temp_dword);
+
+ // enable card
+ temp_word = 0x0157; // = PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER | PCI_COMMAND_INVALIDATE | PCI_COMMAND_PARITY | PCI_COMMAND_SERR
+ pci_bus_write_config_word(ctrl->pci_bus, devfn, PCI_COMMAND, temp_word);
+ } // End of Not-A-Bridge else
+ else {
+ // It's some strange type of PCI adapter (Cardbus?)
+ return DEVICE_TYPE_NOT_SUPPORTED;
+ }
+
+ func->configured = 1;
+
+ return 0;
+}
+
+int amdshpc_configure_device (struct controller * ctrl, struct pci_func* func)
+{
+ unsigned char bus;
+ struct pci_dev dev0;
+ struct pci_bus *child;
+ int num;
+
+ memset(&dev0, 0, sizeof(struct pci_dev));
+ dbg("%s", __FUNCTION__);
+
+ if (func->pci_dev == NULL)
+ func->pci_dev = pci_find_slot(func->bus, PCI_DEVFN(func->device, func->function));
+
+ //Still NULL ? Well then scan for it !
+ if (func->pci_dev == NULL) {
+ num = pci_scan_slot(ctrl->pci_dev->bus, PCI_DEVFN(func->device, func->function));
+ if (num)
+ pci_bus_add_devices(ctrl->pci_dev->bus);
+
+ func->pci_dev = pci_find_slot(func->bus, PCI_DEVFN(func->device, func->function));
+ if (func->pci_dev == NULL) {
+ dbg("ERROR: pci_dev still null\n");
+ return 0;
+ }
+ }
+
+ if (func->pci_dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) {
+ pci_read_config_byte(func->pci_dev, PCI_SECONDARY_BUS, &bus);
+ child = (struct pci_bus*) pci_add_new_bus(func->pci_dev->bus, (func->pci_dev), bus);
+ pci_do_scan_bus(child);
+
+ }
+
+ return 0;
+}
+
+
+int amdshpc_unconfigure_device(struct pci_func* func)
+{
+ int j;
+
+ dbg("%s: bus/dev/func = %x/%x/%x\n",__FUNCTION__,func->bus, func->device, func->function);
+
+ for (j=0; j<8 ; j++) {
+ struct pci_dev* temp = pci_find_slot(func->bus, (func->device << 3) | j);
+ if (temp)
+ pci_remove_bus_device(temp);
+ }
+ return 0;
+}
+
+/*
+static int PCI_RefinedAccessConfig(struct pci_ops *ops, u8 bus, u8 device, u8 function, u8 offset, u32 *value)
+{
+ u32 vendID = 0;
+
+ dbg("%s", __FUNCTION__);
+ if (pci_read_config_dword_nodev (ops, bus, device, function, PCI_VENDOR_ID, &vendID) == -1)
+ return -1;
+ if (vendID == 0xffffffff)
+ return -1;
+ return pci_read_config_dword_nodev (ops, bus, device, function, offset, value);
+}
+
+
+//
+// WTF??? This function isn't in the code, yet a function calls it, but the
+// compiler optimizes it away? strange. Here as a placeholder to keep the
+// compiler happy.
+//
+static int PCI_ScanBusNonBridge (u8 bus, u8 device)
+{
+ return 0;
+}
+
+static int PCI_ScanBusForNonBridge(struct controller *ctrl, u8 bus_num, u8 * dev_num)
+{
+ u8 tdevice;
+ u32 work;
+ u8 tbus;
+
+ dbg("%s", __FUNCTION__);
+ for (tdevice = 0; tdevice < 0x100; tdevice++) {
+ //Scan for access first
+ if (PCI_RefinedAccessConfig(ctrl->pci_ops, bus_num, tdevice >> 3, tdevice & 0x7, 0x08, &work) == -1)
+ continue;
+ dbg("Looking for nonbridge bus_num %d dev_num %d\n", bus_num, tdevice);
+ //Yep we got one. Not a bridge ?
+ if ((work >> 8) != PCI_TO_PCI_BRIDGE_CLASS) {
+ *dev_num = tdevice;
+ dbg("found it !\n");
+ return 0;
+ }
+ }
+ for (tdevice = 0; tdevice < 0x100; tdevice++) {
+ //Scan for access first
+ if (PCI_RefinedAccessConfig(ctrl->pci_ops, bus_num, tdevice >> 3, tdevice & 0x7, 0x08, &work) == -1)
+ continue;
+ dbg("Looking for bridge bus_num %d dev_num %d\n", bus_num, tdevice);
+ //Yep we got one. bridge ?
+ if ((work >> 8) == PCI_TO_PCI_BRIDGE_CLASS) {
+ pci_read_config_byte_nodev (ctrl->pci_ops, tbus, tdevice, 0, PCI_SECONDARY_BUS, &tbus);
+ dbg("Recurse on bus_num %d tdevice %d\n", tbus, tdevice);
+ if (PCI_ScanBusNonBridge(tbus, tdevice) == 0)
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+static int PCI_GetBusDevHelper(struct controller *ctrl, u8 *bus_num, u8 *dev_num, u8 slot, u8 nobridge)
+{
+ struct irq_routing_table *PCIIRQRoutingInfoLength;
+ long len;
+ long loop;
+ u32 work;
+
+ u8 tbus, tdevice, tslot;
+
+ PCIIRQRoutingInfoLength = pcibios_get_irq_routing_table();
+
+ len = (PCIIRQRoutingInfoLength->size -
+ sizeof(struct irq_routing_table)) / sizeof(struct irq_info);
+ dbg("%s len = %d",__FUNCTION__, (int)len);
+ // Make sure I got at least one entry
+ if (len == 0) {
+ if (PCIIRQRoutingInfoLength != NULL)
+ kfree(PCIIRQRoutingInfoLength );
+ return -1;
+ }
+
+ for (loop = 0; loop < len; ++loop) {
+ tbus = PCIIRQRoutingInfoLength->slots[loop].bus;
+ tdevice = PCIIRQRoutingInfoLength->slots[loop].devfn;
+ tslot = PCIIRQRoutingInfoLength->slots[loop].slot;
+ dbg("%s tbus = %02Xh tdevice = %02Xh device = %02Xh function = %d tslot = %d",__FUNCTION__,
+ tbus, tdevice, tdevice >>3, tdevice & 0x7, tslot);
+ if (tslot == slot) {
+ *bus_num = tbus;
+ *dev_num = tdevice;
+ pci_read_config_dword_nodev (ctrl->pci_ops, *bus_num, *dev_num >> 3, *dev_num & 0x7, PCI_VENDOR_ID, &work);
+ if (!nobridge || (work == 0xffffffff)) {
+ if (PCIIRQRoutingInfoLength != NULL)
+ dbg("%s PCIIRQRoutingInfoLength != NULL returning 0",__FUNCTION__);
+ kfree(PCIIRQRoutingInfoLength );
+ return 0;
+ }
+
+ dbg("bus_num %d dev_num %d func_num %d\n", *bus_num, *dev_num >> 3, *dev_num & 0x7);
+ pci_read_config_dword_nodev (ctrl->pci_ops, *bus_num, *dev_num >> 3, *dev_num & 0x7, PCI_CLASS_REVISION, &work);
+ dbg("work >> 8 (%x) = BRIDGE (%x)\n", work >> 8, PCI_TO_PCI_BRIDGE_CLASS);
+
+ if ((work >> 8) == PCI_TO_PCI_BRIDGE_CLASS) {
+ pci_read_config_byte_nodev (ctrl->pci_ops, *bus_num, *dev_num >> 3, *dev_num & 0x7, PCI_SECONDARY_BUS, &tbus);
+ dbg("Scan bus for Non Bridge: bus %d\n", tbus);
+ if (PCI_ScanBusForNonBridge(ctrl, tbus, dev_num) == 0) {
+ *bus_num = tbus;
+ if (PCIIRQRoutingInfoLength != NULL)
+ kfree(PCIIRQRoutingInfoLength );
+ return 0;
+ }
+ } else {
+ if (PCIIRQRoutingInfoLength != NULL)
+ kfree(PCIIRQRoutingInfoLength );
+ return 0;
+ }
+
+ }
+ }
+ if (PCIIRQRoutingInfoLength != NULL)
+ kfree(PCIIRQRoutingInfoLength );
+ return -1;
+}
+
+
+int amdshpc_get_bus_dev (struct controller *ctrl, u8 * bus_num, u8 * dev_num, u8 slot)
+{
+ dbg("%s", __FUNCTION__);
+ return PCI_GetBusDevHelper(ctrl, bus_num, dev_num, slot, 0); //plain (bridges allowed)
+}
+*/
+
+/* More PCI configuration routines; this time centered around hotplug controller */
+
+
+/*
+ * amdshpc_save_slot_config
+ *
+ * Saves configuration info for all PCI devices in a given slot
+ * including subordinate busses.
+ *
+ * returns 0 if success
+ */
+int amdshpc_save_slot_config (struct controller *ctrl, struct pci_func * new_slot)
+{
+ long rc;
+ u8 class_code;
+ u8 header_type;
+ u32 ID;
+ u8 secondary_bus;
+ int sub_bus;
+ int max_functions;
+ int function;
+ int cloop = 0;
+ int stop_it;
+
+ ID = 0xFFFFFFFF;
+
+ dbg("%s", __FUNCTION__);
+ pci_bus_read_config_dword(ctrl->pci_bus, PCI_DEVFN(new_slot->device, 0), PCI_VENDOR_ID, &ID);
+
+ if (ID != 0xFFFFFFFF) { // device in slot
+ pci_bus_read_config_byte(ctrl->pci_bus, PCI_DEVFN(new_slot->device, 0), 0x0B, &class_code);
+ pci_bus_read_config_byte(ctrl->pci_bus, PCI_DEVFN(new_slot->device, 0), PCI_HEADER_TYPE, &header_type);
+
+ if (header_type & 0x80) // Multi-function device
+ max_functions = 8;
+ else
+ max_functions = 1;
+
+ function = 0;
+
+ do {
+ if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { // PCI-PCI Bridge
+ // Recurse the subordinate bus
+ pci_bus_read_config_byte(ctrl->pci_bus, PCI_DEVFN(new_slot->device, function), PCI_SECONDARY_BUS, &secondary_bus);
+
+ sub_bus = (int) secondary_bus;
+
+ // Save the config headers for the secondary bus.
+ rc = amdshpc_save_config(ctrl, sub_bus, 0);
+
+ if (rc)
+ return(rc);
+
+ } // End of IF
+
+ new_slot->status = 0;
+
+ for (cloop = 0; cloop < 0x20; cloop++) {
+ pci_bus_read_config_dword(ctrl->pci_bus, PCI_DEVFN(new_slot->device, function), cloop << 2, (u32 *) & (new_slot-> config_space [cloop]));
+ }
+
+ function++;
+
+ stop_it = 0;
+
+ // this loop skips to the next present function
+ // reading in the Class Code and the Header type.
+
+ while ((function < max_functions) && (!stop_it)) {
+ pci_bus_read_config_dword(ctrl->pci_bus, PCI_DEVFN(new_slot->device, function), PCI_VENDOR_ID, &ID);
+
+ if (ID == 0xFFFFFFFF) { // nothing there.
+ function++;
+ } else { // Something there
+ pci_bus_read_config_byte(ctrl->pci_bus, PCI_DEVFN(new_slot->device, function), 0x0B, &class_code);
+ pci_bus_read_config_byte(ctrl->pci_bus, PCI_DEVFN(new_slot->device, function), PCI_HEADER_TYPE, &header_type);
+
+ stop_it++;
+ }
+ }
+
+ } while (function < max_functions);
+ } // End of IF (device in slot?)
+ else {
+ return(2);
+ }
+
+ return(0);
+}
+
+
+/*
+ * amdshpc_save_base_addr_length
+ *
+ * Saves the length of all base address registers for the
+ * specified slot. this is for hot plug REPLACE
+ *
+ * returns 0 if success
+ */
+int amdshpc_save_base_addr_length(struct controller *ctrl, struct pci_func * func)
+{
+ u8 cloop;
+ u8 header_type;
+ u8 secondary_bus;
+ u8 type;
+ int sub_bus;
+ u32 temp_register;
+ u32 base;
+ u32 rc;
+ struct pci_func *next;
+ int index = 0;
+
+ dbg("%s", __FUNCTION__);
+ func = amdshpc_slot_find(func->bus, func->device, index++);
+
+ while (func != NULL) {
+ int devfn = PCI_DEVFN(func->device, func->function);
+
+ // Check for Bridge
+ pci_bus_read_config_byte(ctrl->pci_bus, devfn, PCI_HEADER_TYPE, &header_type);
+
+ if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) {
+ // PCI-PCI Bridge
+ pci_bus_read_config_byte(ctrl->pci_bus, devfn, PCI_SECONDARY_BUS, &secondary_bus);
+
+ sub_bus = (int) secondary_bus;
+
+ next = amdshpc_slot_list[sub_bus];
+
+ while (next != NULL) {
+ rc = amdshpc_save_base_addr_length(ctrl, next);
+
+ if (rc)
+ return(rc);
+
+ next = next->next;
+ }
+
+ //FIXME: this loop is duplicated in the non-bridge case. The two could be rolled together
+ // Figure out IO and memory base lengths
+ for (cloop = 0x10; cloop <= 0x14; cloop += 4) {
+ temp_register = 0xFFFFFFFF;
+ pci_bus_write_config_dword(ctrl->pci_bus, devfn, cloop, temp_register);
+ pci_bus_read_config_dword(ctrl->pci_bus, devfn, cloop, &base);
+
+ if (base) { // If this register is implemented
+ if (base & 0x01L) {
+ // IO base
+ // set base = amount of IO space requested
+ base = base & 0xFFFFFFFE;
+ base = (~base) + 1;
+
+ type = 1;
+ } else {
+ // memory base
+ base = base & 0xFFFFFFF0;
+ base = (~base) + 1;
+
+ type = 0;
+ }
+ } else {
+ base = 0x0L;
+ type = 0;
+ }
+
+ // Save information in slot structure
+ func->base_length[(cloop - 0x10) >> 2] = base;
+ func->base_type[(cloop - 0x10) >> 2] = type;
+
+ } // End of base register loop
+
+
+ } else if ((header_type & 0x7F) == 0x00) { // PCI-PCI Bridge
+ // Figure out IO and memory base lengths
+ for (cloop = 0x10; cloop <= 0x24; cloop += 4) {
+ temp_register = 0xFFFFFFFF;
+ pci_bus_write_config_dword(ctrl->pci_bus, devfn, cloop, temp_register);
+ pci_bus_read_config_dword(ctrl->pci_bus, devfn, cloop, &base);
+
+ if (base) { // If this register is implemented
+ if (base & 0x01L) {
+ // IO base
+ // base = amount of IO space requested
+ base = base & 0xFFFFFFFE;
+ base = (~base) + 1;
+
+ type = 1;
+ } else {
+ // memory base
+ // base = amount of memory space requested
+ base = base & 0xFFFFFFF0;
+ base = (~base) + 1;
+
+ type = 0;
+ }
+ } else {
+ base = 0x0L;
+ type = 0;
+ }
+
+ // Save information in slot structure
+ func->base_length[(cloop - 0x10) >> 2] = base;
+ func->base_type[(cloop - 0x10) >> 2] = type;
+
+ } // End of base register loop
+
+ } else { // Some other unknown header type
+ }
+
+ // find the next device in this slot
+ func = amdshpc_slot_find(func->bus, func->device, index++);
+ }
+
+ return(0);
+}
+
+
+/*
+ * amdshpc_save_used_resources
+ *
+ * Stores used resource information for existing boards. this is
+ * for boards that were in the system when this driver was loaded.
+ * this function is for hot plug ADD
+ *
+ * returns 0 if success
+ */
+int amdshpc_save_used_resources (struct controller *ctrl, struct pci_func * func)
+{
+ u8 cloop;
+ u8 header_type;
+ u8 secondary_bus;
+ u8 temp_byte;
+ u8 b_base;
+ u8 b_length;
+ u16 command;
+ u16 save_command;
+ u16 w_base;
+ u16 w_length;
+ u32 temp_register;
+ u32 save_base;
+ u32 base;
+ int index = 0;
+ struct pci_resource *mem_node;
+ struct pci_resource *p_mem_node;
+ struct pci_resource *io_node;
+ struct pci_resource *bus_node;
+
+ dbg("%s", __FUNCTION__);
+ func = amdshpc_slot_find(func->bus, func->device, index++);
+
+ while ((func != NULL) && func->is_a_board) {
+ int devfn = PCI_DEVFN(func->device, func->function);
+
+ // Save the command register
+ pci_bus_read_config_word(ctrl->pci_bus, devfn, PCI_COMMAND, &save_command);
+
+ // disable card
+ command = 0x00;
+ pci_bus_write_config_word(ctrl->pci_bus, devfn, PCI_COMMAND, command);
+
+ // Check for Bridge
+ pci_bus_read_config_byte(ctrl->pci_bus, devfn, PCI_HEADER_TYPE, &header_type);
+
+ if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { // PCI-PCI Bridge
+ // Clear Bridge Control Register
+ command = 0x00;
+ pci_bus_write_config_word(ctrl->pci_bus, devfn, PCI_BRIDGE_CONTROL, command);
+ pci_bus_read_config_byte(ctrl->pci_bus, devfn, PCI_SECONDARY_BUS, &secondary_bus);
+ pci_bus_read_config_byte(ctrl->pci_bus, devfn, PCI_SUBORDINATE_BUS, &temp_byte);
+
+ bus_node =(struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
+ if (!bus_node)
+ return -ENOMEM;
+
+ bus_node->base = secondary_bus;
+ bus_node->length = temp_byte - secondary_bus + 1;
+
+ bus_node->next = func->bus_head;
+ func->bus_head = bus_node;
+
+ // Save IO base and Limit registers
+ pci_bus_read_config_byte(ctrl->pci_bus, devfn, PCI_IO_BASE, &b_base);
+ pci_bus_read_config_byte(ctrl->pci_bus, devfn, PCI_IO_LIMIT, &b_length);
+
+ if ((b_base <= b_length) && (save_command & 0x01)) {
+ io_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
+ if (!io_node)
+ return -ENOMEM;
+
+ io_node->base = (b_base & 0xF0) << 8;
+ io_node->length = (b_length - b_base + 0x10) << 8;
+
+ io_node->next = func->io_head;
+ func->io_head = io_node;
+ }
+ // Save memory base and Limit registers
+ pci_bus_read_config_word(ctrl->pci_bus, devfn, PCI_MEMORY_BASE, &w_base);
+ pci_bus_read_config_word(ctrl->pci_bus, devfn, PCI_MEMORY_LIMIT, &w_length);
+
+ if ((w_base <= w_length) && (save_command & 0x02)) {
+ mem_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
+ if (!mem_node)
+ return -ENOMEM;
+
+ mem_node->base = w_base << 16;
+ mem_node->length = (w_length - w_base + 0x10) << 16;
+
+ mem_node->next = func->mem_head;
+ func->mem_head = mem_node;
+ }
+ // Save prefetchable memory base and Limit registers
+ pci_bus_read_config_word(ctrl->pci_bus, devfn, PCI_PREF_MEMORY_BASE, &w_base);
+ pci_bus_read_config_word(ctrl->pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, &w_length);
+
+ if ((w_base <= w_length) && (save_command & 0x02)) {
+ p_mem_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
+ if (!p_mem_node)
+ return -ENOMEM;
+
+ p_mem_node->base = w_base << 16;
+ p_mem_node->length = (w_length - w_base + 0x10) << 16;
+
+ p_mem_node->next = func->p_mem_head;
+ func->p_mem_head = p_mem_node;
+ }
+ // Figure out IO and memory base lengths
+ for (cloop = 0x10; cloop <= 0x14; cloop += 4) {
+ pci_bus_read_config_dword(ctrl->pci_bus, devfn, cloop, &save_base);
+
+ temp_register = 0xFFFFFFFF;
+ pci_bus_write_config_dword(ctrl->pci_bus, devfn, cloop, temp_register);
+ pci_bus_read_config_dword(ctrl->pci_bus, devfn, cloop, &base);
+
+ temp_register = base;
+
+ if (base) { // If this register is implemented
+ if (((base & 0x03L) == 0x01)
+ && (save_command & 0x01)) {
+ // IO base
+ // set temp_register = amount of IO space requested
+ temp_register = base & 0xFFFFFFFE;
+ temp_register = (~temp_register) + 1;
+
+ io_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
+ if (!io_node)
+ return -ENOMEM;
+
+ io_node->base =
+ save_base & (~0x03L);
+ io_node->length = temp_register;
+
+ io_node->next = func->io_head;
+ func->io_head = io_node;
+ } else
+ if (((base & 0x0BL) == 0x08)
+ && (save_command & 0x02)) {
+ // prefetchable memory base
+ temp_register = base & 0xFFFFFFF0;
+ temp_register = (~temp_register) + 1;
+
+ p_mem_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
+ if (!p_mem_node)
+ return -ENOMEM;
+
+ p_mem_node->base = save_base & (~0x0FL);
+ p_mem_node->length = temp_register;
+
+ p_mem_node->next = func->p_mem_head;
+ func->p_mem_head = p_mem_node;
+ } else
+ if (((base & 0x0BL) == 0x00)
+ && (save_command & 0x02)) {
+ // prefetchable memory base
+ temp_register = base & 0xFFFFFFF0;
+ temp_register = (~temp_register) + 1;
+
+ mem_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
+ if (!mem_node)
+ return -ENOMEM;
+
+ mem_node->base = save_base & (~0x0FL);
+ mem_node->length = temp_register;
+
+ mem_node->next = func->mem_head;
+ func->mem_head = mem_node;
+ } else
+ return(1);
+ }
+ } // End of base register loop
+ } else if ((header_type & 0x7F) == 0x00) { // Standard header
+ // Figure out IO and memory base lengths
+ for (cloop = 0x10; cloop <= 0x24; cloop += 4) {
+ pci_bus_read_config_dword(ctrl->pci_bus, devfn, cloop, &save_base);
+
+ temp_register = 0xFFFFFFFF;
+ pci_bus_write_config_dword(ctrl->pci_bus, devfn, cloop, temp_register);
+ pci_bus_read_config_dword(ctrl->pci_bus, devfn, cloop, &base);
+
+ temp_register = base;
+
+ if (base) { // If this register is implemented
+ if (((base & 0x03L) == 0x01)
+ && (save_command & 0x01)) {
+ // IO base
+ // set temp_register = amount of IO space requested
+ temp_register = base & 0xFFFFFFFE;
+ temp_register = (~temp_register) + 1;
+
+ io_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
+ if (!io_node)
+ return -ENOMEM;
+
+ io_node->base = save_base & (~0x01L);
+ io_node->length = temp_register;
+
+ io_node->next = func->io_head;
+ func->io_head = io_node;
+ } else
+ if (((base & 0x0BL) == 0x08)
+ && (save_command & 0x02)) {
+ // prefetchable memory base
+ temp_register = base & 0xFFFFFFF0;
+ temp_register = (~temp_register) + 1;
+
+ p_mem_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
+ if (!p_mem_node)
+ return -ENOMEM;
+
+ p_mem_node->base = save_base & (~0x0FL);
+ p_mem_node->length = temp_register;
+
+ p_mem_node->next = func->p_mem_head;
+ func->p_mem_head = p_mem_node;
+ } else
+ if (((base & 0x0BL) == 0x00)
+ && (save_command & 0x02)) {
+ // prefetchable memory base
+ temp_register = base & 0xFFFFFFF0;
+ temp_register = (~temp_register) + 1;
+
+ mem_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
+ if (!mem_node)
+ return -ENOMEM;
+
+ mem_node->base = save_base & (~0x0FL);
+ mem_node->length = temp_register;
+
+ mem_node->next = func->mem_head;
+ func->mem_head = mem_node;
+ } else
+ return(1);
+ }
+ } // End of base register loop
+ } else { // Some other unknown header type
+ }
+
+ // find the next device in this slot
+ func = amdshpc_slot_find(func->bus, func->device, index++);
+ }
+
+ return(0);
+}
+
+
+/*
+ * amdshpc_configure_board
+ *
+ * Copies saved configuration information to one slot.
+ * this is called recursively for bridge devices.
+ * this is for hot plug REPLACE!
+ *
+ * returns 0 if success
+ */
+int amdshpc_configure_board(struct controller *ctrl, struct pci_func * func)
+{
+ int cloop;
+ u8 header_type;
+ u8 secondary_bus;
+ int sub_bus;
+ struct pci_func *next;
+ u32 temp;
+ u32 rc;
+ int index = 0;
+
+ dbg("%s", __FUNCTION__);
+ func = amdshpc_slot_find(func->bus, func->device, index++);
+
+ while (func != NULL) {
+ int devfn = PCI_DEVFN(func->device, func->function);
+
+ // Start at the top of config space so that the control
+ // registers are programmed last
+ for (cloop = 0x3C; cloop > 0; cloop -= 4) {
+ pci_bus_write_config_dword(ctrl->pci_bus, devfn, cloop, func->config_space[cloop >> 2]);
+ }
+
+ pci_bus_read_config_byte(ctrl->pci_bus, devfn, PCI_HEADER_TYPE, &header_type);
+
+ // If this is a bridge device, restore subordinate devices
+ if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { // PCI-PCI Bridge
+ pci_bus_read_config_byte(ctrl->pci_bus, devfn, PCI_SECONDARY_BUS, &secondary_bus);
+
+ sub_bus = (int) secondary_bus;
+
+ next = amdshpc_slot_list[sub_bus];
+
+ while (next != NULL) {
+ rc = amdshpc_configure_board(ctrl, next);
+ if (rc)
+ return rc;
+
+ next = next->next;
+ }
+ } else {
+ // Check all the base Address Registers to make sure
+ // they are the same. If not, the board is different.
+ for (cloop = 16; cloop < 40; cloop += 4) {
+ pci_bus_read_config_dword(ctrl->pci_bus, devfn, cloop, &temp);
+ if (temp != func->config_space[cloop >> 2]) {
+ dbg("Config space compare failure!!! offset = %x\n", cloop);
+ dbg("bus = %x, device = %x, function = %x\n", func->bus, func->device, func->function);
+ dbg("temp = %x, config space = %x\n\n", temp, func->config_space[cloop]);
+ return 1;
+ }
+ }
+ }
+
+ func->configured = 1;
+
+ func = amdshpc_slot_find(func->bus, func->device, index++);
+ }
+
+ return 0;
+}
+
+
+/*
+ * amdshpc_valid_replace
+ *
+ * this function checks to see if a board is the same as the
+ * one it is replacing. this check will detect if the device's
+ * vendor or device id's are the same
+ *
+ * returns 0 if the board is the same nonzero otherwise
+ */
+int amdshpc_valid_replace(struct controller *ctrl, struct pci_func * func)
+{
+ u8 cloop;
+ u8 header_type;
+ u8 secondary_bus;
+ u8 type;
+ u32 temp_register = 0;
+ u32 base;
+ u32 rc;
+ struct pci_func *next;
+ int index = 0;
+
+ dbg("%s", __FUNCTION__);
+ if (!func->is_a_board)
+ return(ADD_NOT_SUPPORTED);
+
+ func = amdshpc_slot_find(func->bus, func->device, index++);
+
+ while (func != NULL) {
+ int devfn = PCI_DEVFN(func->device, func->function);
+
+ pci_bus_read_config_dword(ctrl->pci_bus, devfn, PCI_VENDOR_ID, &temp_register);
+
+ // No adapter present
+ if (temp_register == 0xFFFFFFFF)
+ return(NO_ADAPTER_PRESENT);
+
+ if (temp_register != func->config_space[0])
+ return(ADAPTER_NOT_SAME);
+
+ // Check for same revision number and class code
+ pci_bus_read_config_dword(ctrl->pci_bus, devfn, PCI_CLASS_REVISION, &temp_register);
+
+ // Adapter not the same
+ if (temp_register != func->config_space[0x08 >> 2])
+ return(ADAPTER_NOT_SAME);
+
+ // Check for Bridge
+ pci_bus_read_config_byte(ctrl->pci_bus, devfn, PCI_HEADER_TYPE, &header_type);
+
+ if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { // PCI-PCI Bridge
+ // In order to continue checking, we must program the
+ // bus registers in the bridge to respond to accesses
+ // for it's subordinate bus(es)
+
+ temp_register = func->config_space[0x18 >> 2];
+ pci_bus_write_config_dword(ctrl->pci_bus, devfn, PCI_PRIMARY_BUS, temp_register);
+
+ secondary_bus = (temp_register >> 8) & 0xFF;
+
+ next = amdshpc_slot_list[secondary_bus];
+
+ while (next != NULL) {
+ rc = amdshpc_valid_replace(ctrl, next);
+ if (rc)
+ return(rc);
+
+ next = next->next;
+ }
+
+ }
+ // Check to see if it is a standard config header
+ else if ((header_type & 0x7F) == PCI_HEADER_TYPE_NORMAL) {
+ // Check subsystem vendor and ID
+ pci_bus_read_config_dword(ctrl->pci_bus, devfn, PCI_SUBSYSTEM_VENDOR_ID, &temp_register);
+
+ if (temp_register != func->config_space[0x2C >> 2]) {
+ // If it's a SMART-2 and the register isn't filled
+ // in, ignore the difference because
+ // they just have an old rev of the firmware
+
+ if (!((func->config_space[0] == 0xAE100E11)
+ && (temp_register == 0x00L)))
+ return(ADAPTER_NOT_SAME);
+ }
+ // Figure out IO and memory base lengths
+ for (cloop = 0x10; cloop <= 0x24; cloop += 4) {
+ temp_register = 0xFFFFFFFF;
+ pci_bus_write_config_dword(ctrl->pci_bus, devfn, cloop, temp_register);
+ pci_bus_read_config_dword(ctrl->pci_bus, devfn, cloop, &base);
+
+ if (base) { // If this register is implemented
+ if (base & 0x01L) {
+ // IO base
+ // set base = amount of IO space requested
+ base = base & 0xFFFFFFFE;
+ base = (~base) + 1;
+
+ type = 1;
+ } else {
+ // memory base
+ base = base & 0xFFFFFFF0;
+ base = (~base) + 1;
+
+ type = 0;
+ }
+ } else {
+ base = 0x0L;
+ type = 0;
+ }
+
+ // Check information in slot structure
+ if (func->base_length[(cloop - 0x10) >> 2] != base)
+ return(ADAPTER_NOT_SAME);
+
+ if (func->base_type[(cloop - 0x10) >> 2] != type)
+ return(ADAPTER_NOT_SAME);
+
+ } // End of base register loop
+
+ } // End of (type 0 config space) else
+ else {
+ // this is not a type 0 or 1 config space header so
+ // we don't know how to do it
+ return(DEVICE_TYPE_NOT_SUPPORTED);
+ }
+
+ // Get the next function
+ func = amdshpc_slot_find(func->bus, func->device, index++);
+ }
+
+ return(0);
+}
+
+
+
+static int update_slot_info (struct controller *ctrl, struct slot *slot)
+{
+// TO_DO_amd_update_slot_info();
+ dbg("%s THIS FUNCTION IS STUBBED OUT!!!!!!!!!!!",__FUNCTION__);
+ return 0;
+ /* struct hotplug_slot_info *info;
+ char buffer[SLOT_NAME_SIZE];
+ int result;
+
+ info = kmalloc (sizeof (struct hotplug_slot_info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ make_slot_name (&buffer[0], SLOT_NAME_SIZE, slot);
+ info->power_status = get_slot_enabled(ctrl, slot);
+ info->attention_status = cpq_get_attention_status(ctrl, slot);
+ info->latch_status = cpq_get_latch_status(ctrl, slot);
+ info->adapter_status = get_presence_status(ctrl, slot);
+ result = pci_hp_change_slot_info(buffer, info);
+ kfree (info);
+ return result;
+*/
+}
+
+
+/*
+ * slot_remove - Removes a node from the linked list of slots.
+ * @old_slot: slot to remove
+ *
+ * Returns 0 if successful, !0 otherwise.
+ */
+static int slot_remove(struct pci_func * old_slot)
+{
+ struct pci_func *next;
+
+ dbg("%s", __FUNCTION__);
+ if (old_slot == NULL)
+ return(1);
+
+ next = amdshpc_slot_list[old_slot->bus];
+
+ if (next == NULL) {
+ return(1);
+ }
+
+ if (next == old_slot) {
+ amdshpc_slot_list[old_slot->bus] = old_slot->next;
+ amdshpc_destroy_board_resources(old_slot);
+ kfree(old_slot);
+ return(0);
+ }
+
+ while ((next->next != old_slot) && (next->next != NULL)) {
+ next = next->next;
+ }
+
+ if (next->next == old_slot) {
+ next->next = old_slot->next;
+ amdshpc_destroy_board_resources(old_slot);
+ kfree(old_slot);
+ return(0);
+ } else
+ return(2);
+}
+
+// DJZ: I don't think is_bridge will work as is.
+//FIXME
+static int is_bridge(struct pci_func * func)
+{
+ dbg("%s", __FUNCTION__);
+ // Check the header type
+ if (((func->config_space[0x03] >> 16) & 0xFF) == 0x01)
+ return 1;
+ else
+ return 0;
+}
+
+
+/**
+ * bridge_slot_remove - Removes a node from the linked list of slots.
+ * @bridge: bridge to remove
+ *
+ * Returns 0 if successful, !0 otherwise.
+ */
+static int bridge_slot_remove(struct pci_func *bridge)
+{
+ u8 subordinateBus, secondaryBus;
+ u8 tempBus;
+ struct pci_func *next;
+
+ dbg("%s", __FUNCTION__);
+ if (bridge == NULL)
+ return(1);
+
+ secondaryBus = (bridge->config_space[0x06] >> 8) & 0xFF;
+ subordinateBus = (bridge->config_space[0x06] >> 16) & 0xFF;
+
+ for (tempBus = secondaryBus; tempBus <= subordinateBus; tempBus++) {
+ next = amdshpc_slot_list[tempBus];
+
+ while (!slot_remove(next)) {
+ next = amdshpc_slot_list[tempBus];
+ }
+ }
+
+ next = amdshpc_slot_list[bridge->bus];
+
+ if (next == NULL) {
+ return(1);
+ }
+
+ if (next == bridge) {
+ amdshpc_slot_list[bridge->bus] = bridge->next;
+ kfree(bridge);
+ return(0);
+ }
+
+ while ((next->next != bridge) && (next->next != NULL)) {
+ next = next->next;
+ }
+
+ if (next->next == bridge) {
+ next->next = bridge->next;
+ kfree(bridge);
+ return(0);
+ } else
+ return(2);
+}
+
+
--- /dev/null
+/*
+ * Dummy PCI Hot Plug Controller Driver
+ *
+ * Copyright (C) 1995,2001 Compaq Computer Corporation
+ * Copyright (C) 2001,2003 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (C) 2001,2003 IBM Corp.
+ * Copyright (C) 2002 Hiroshi Aono (h-aono@ap.jp.nec.com)
+ * Copyright (C) 2002 Takayoshi Kochi (t-kouchi@cq.jp.nec.com)
+ * Copyright (C) 2002 NEC Corporation
+ * Copyright (C) 2002 Vladimir Kondratiev (vladimir.kondratiev@intel.com)
+ * Copyright (C) 2003 Rolf Eike Beer (eike-kernel@sf-tec.de)
+ *
+ * All rights reserved.
+ *
+ * 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, GOOD TITLE or
+ * NON INFRINGEMENT. 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.
+ *
+ * Send feedback to <eike-kernel@sf-tec.de>
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include "pci_hotplug.h"
+#include "../pci.h"
+
+#if !defined(CONFIG_HOTPLUG_PCI_DUMMY_MODULE)
+ #define MY_NAME "dummyphp"
+#else
+ #define MY_NAME THIS_MODULE->name
+#endif
+
+#define dbg(format, arg...) \
+ do { \
+ if (debug) \
+ printk(KERN_DEBUG "%s: " format, \
+ MY_NAME , ## arg); \
+ } while (0)
+#define err(format, arg...) printk(KERN_ERR "%s: " format, MY_NAME , ## arg)
+#define info(format, arg...) printk(KERN_INFO "%s: " format, MY_NAME , ## arg)
+
+/* name size which is used for entries in pcihpfs */
+#define SLOT_NAME_SIZE 32 /* DUMMY-{BUS}:{DEV} */
+
+struct dummy_slot {
+ struct pci_bus *bus;
+ int devfn;
+};
+
+static int debug;
+
+#define DRIVER_VERSION "0.1"
+#define DRIVER_AUTHOR "Rolf Eike Beer <eike-kernel@sf-tec.de>"
+#define DRIVER_DESC "Dummy PCI Hot Plug Controller Driver"
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+MODULE_PARM(debug, "i");
+MODULE_PARM_DESC(debug, "Debugging mode enabled or not");
+
+static int enable_slot (struct hotplug_slot *slot);
+static int disable_slot (struct hotplug_slot *slot);
+
+/*
+ This is a dummy driver, so we make us live as easy as possible:
+ -if there is an adapter registered in the linux PCI system, then it is present
+ -if there is an adapter present, the power is on (and vice versa!)
+ -if the power is on, the latch is closed (and vice versa)
+ -the attention LED is always off
+
+ So:
+ => latch_status = adapter_status = power_status
+ */
+
+static struct hotplug_slot_ops dummy_hotplug_slot_ops = {
+ .owner = THIS_MODULE,
+ .enable_slot = enable_slot,
+ .disable_slot = disable_slot,
+};
+
+/**
+ * enable_slot - power on and enable a slot
+ * @hotplug_slot: slot to enable
+ */
+static int
+enable_slot(struct hotplug_slot *hotplug_slot)
+{
+ struct dummy_slot *slot;
+
+ if (!hotplug_slot)
+ return -ENODEV;
+
+ dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
+
+ /* enable the specified slot */
+ slot = (struct dummy_slot *) hotplug_slot->private;
+
+ if (pci_scan_slot(slot->bus, slot->devfn)) {
+ hotplug_slot->info->power_status = 1;
+ hotplug_slot->info->adapter_status = 1;
+ hotplug_slot->info->latch_status = 1;
+ return 0;
+ }
+ return -ENODEV;
+}
+
+
+/**
+ * disable_slot - disable any adapter in this slot
+ * @hotplug_slot: slot to disable
+ */
+static int
+disable_slot(struct hotplug_slot *hotplug_slot)
+{
+ struct pci_dev* old_dev;
+ int func;
+ struct dummy_slot *slot;
+
+ if (!hotplug_slot)
+ return -ENODEV;
+
+ slot = (struct dummy_slot *) hotplug_slot->private;
+
+ dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
+
+ /* disable the specified slot */
+
+ for (func=0; func<8; func++) {
+ old_dev = pci_find_slot(slot->bus->number, slot->devfn+func);
+ if (old_dev) {
+ printk(KERN_INFO "Slot %s removed\n", old_dev->slot_name);
+ pci_remove_device_safe(old_dev);
+ }
+ }
+ hotplug_slot->info->power_status = 0;
+ hotplug_slot->info->adapter_status = 0;
+ hotplug_slot->info->latch_status = 0;
+
+
+ return 0;
+}
+
+
+/**
+ * dummyphp_get_power_status - look if an adapter is configured in this slot
+ * @slot: the slot to test
+ */
+static int dummyphp_get_power_status(struct dummy_slot *slot)
+{
+ struct pci_dev* old_dev;
+ int func;
+
+ for (func = 0; func < 8; func++) {
+ old_dev = pci_find_slot(slot->bus->number, slot->devfn+func);
+ if (old_dev)
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * scan_pci_bus - add an entry for every slot on this bus
+ * @bus: bus to scan
+ */
+static int __init
+scan_pci_bus(const struct pci_bus *bus)
+{
+ struct dummy_slot *dslot;
+ struct hotplug_slot *hp;
+ int retval = 0;
+ unsigned int devfn;
+ struct pci_dev dev0;
+
+ memset(&dev0, 0, sizeof(dev0));
+ dev0.bus = (struct pci_bus*)bus;
+ dev0.sysdata = bus->sysdata;
+ for (devfn = 0; devfn < 0x100; devfn += 8) {
+ hp = kmalloc(sizeof(struct hotplug_slot), GFP_KERNEL);
+ if (!hp) {
+ return -ENOMEM;
+ }
+ memset(hp, 0, sizeof(struct hotplug_slot));
+
+ hp->info = kmalloc(sizeof(struct hotplug_slot_info), GFP_KERNEL);
+ if (!hp->info) {
+ kfree(hp);
+ return -ENOMEM;
+ }
+ memset(hp->info, 0, sizeof(struct hotplug_slot_info));
+
+ hp->name = kmalloc(SLOT_NAME_SIZE, GFP_KERNEL);
+ dslot = kmalloc(sizeof(struct dummy_slot), GFP_KERNEL);
+ if ( (!hp->name) || (!dslot) ) {
+ kfree(hp->info);
+ kfree(hp->name);
+ kfree(dslot);
+ kfree(hp);
+ return -ENOMEM;
+ }
+ dslot->bus = (struct pci_bus *) bus;
+ dslot->devfn = devfn;
+
+ hp->ops = &dummy_hotplug_slot_ops;
+ hp->private = (void *) dslot;
+
+ hp->info->power_status = dummyphp_get_power_status(dslot);
+ hp->info->latch_status = hp->info->power_status;
+ hp->info->adapter_status = hp->info->power_status;
+
+ /* set fixed values so we do not need callbacks for these */
+ hp->info->attention_status = 0;
+ hp->info->cur_bus_speed = PCI_SPEED_UNKNOWN;
+ hp->info->max_bus_speed = PCI_SPEED_UNKNOWN;
+
+ snprintf(hp->name, SLOT_NAME_SIZE, "DUMMY-%02x:%02x",
+ dslot->bus->number,
+ dslot->devfn / 8);
+
+ retval = pci_hp_register(hp);
+ if (retval) {
+ err("pci_hp_register failed with error %d\n", retval);
+ kfree(hp->info);
+ kfree(hp->name);
+ kfree(hp);
+ kfree(dslot);
+ return retval;
+ }
+
+ }
+ return 0;
+}
+
+/**
+ * scan_pci_buses - scan this bus and all child buses for slots
+ * @list: list of buses to scan
+ */
+static int __init
+pci_scan_buses(const struct list_head *list)
+{
+ int retval;
+ const struct list_head *l;
+
+ list_for_each(l,list) {
+ const struct pci_bus *b = pci_bus_b(l);
+ retval = scan_pci_bus(b);
+ if (retval)
+ return retval;
+ retval = pci_scan_buses(&b->children);
+ if (retval)
+ return retval;
+ }
+ return 0;
+}
+
+static void __exit
+cleanup_slots (void)
+{
+/*FIXME: look at every hotplug_slot if name begins with "DUMMY-" an kick them*/
+}
+
+
+static int __init
+dummyphp_init(void)
+{
+ info(DRIVER_DESC " version: " DRIVER_VERSION "\n");
+
+ return pci_scan_buses(&pci_root_buses);
+}
+
+
+static void __exit
+dummyphp_exit(void)
+{
+ cleanup_slots();
+}
+
+module_init(dummyphp_init);
+module_exit(dummyphp_exit);
obj-$(CONFIG_USB_SE401) += media/
obj-$(CONFIG_USB_STV680) += media/
obj-$(CONFIG_USB_VICAM) += media/
+obj-$(CONFIG_USB_W9968CF) += media/
obj-$(CONFIG_USB_CATC) += net/
obj-$(CONFIG_USB_KAWETH) += net/
obj-$(CONFIG_USB_TEST) += misc/
obj-$(CONFIG_USB_TIGL) += misc/
obj-$(CONFIG_USB_USS720) += misc/
-
+obj-$(CONFIG_USB_LEGOTOWER) += misc/
kfree(as);
return;
}
- if ((as->dev_audio = register_sound_dsp(&usb_audio_fops, -1)) < 0) {
+ if ((as->dev_audio = register_sound_dsp(&usb_audio_fops, -1, &dev->dev)) < 0) {
printk(KERN_ERR "usbaudio: cannot register dsp\n");
usb_free_urb(as->usbin.durb[0].urb);
usb_free_urb(as->usbin.durb[1].urb);
ms->state = s;
ms->iface = ctrlif;
ms->numch = state.nrmixch;
- if ((ms->dev_mixer = register_sound_mixer(&usb_mixer_fops, -1)) < 0) {
+ if ((ms->dev_mixer = register_sound_mixer(&usb_mixer_fops, -1, &s->usbdev->dev)) < 0) {
printk(KERN_ERR "usbaudio: cannot register mixer\n");
kfree(ms);
return;
-#define USB_DT_CS_DEVICE 0x21
-#define USB_DT_CS_CONFIG 0x22
-#define USB_DT_CS_STRING 0x23
-#define USB_DT_CS_INTERFACE 0x24
-#define USB_DT_CS_ENDPOINT 0x25
-
#define CS_AUDIO_UNDEFINED 0x20
#define CS_AUDIO_DEVICE 0x21
#define CS_AUDIO_CONFIGURATION 0x22
/*
- * acm.c Version 0.22
+ * cdc-acm.c
*
* Copyright (c) 1999 Armin Fuerst <fuerst@in.tum.de>
* Copyright (c) 1999 Pavel Machek <pavel@suse.cz>
* v0.21 - revert to probing on device for devices with multiple configs
* v0.22 - probe only the control interface. if usbcore doesn't choose the
* config we want, sysadmin changes bConfigurationValue in sysfs.
+ * v0.23 - use softirq for rx processing, as needed by tty layer
*/
/*
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+#undef DEBUG
+
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/smp_lock.h>
#include <asm/uaccess.h>
-#undef DEBUG
#include <linux/usb.h>
#include <asm/byteorder.h>
/*
* Version Information
*/
-#define DRIVER_VERSION "v0.21"
+#define DRIVER_VERSION "v0.23"
#define DRIVER_AUTHOR "Armin Fuerst, Pavel Machek, Johannes Erdfelt, Vojtech Pavlik"
#define DRIVER_DESC "USB Abstract Control Model driver for USB modems and ISDN adapters"
struct tty_struct *tty; /* the corresponding tty */
struct urb *ctrlurb, *readurb, *writeurb; /* urbs */
struct acm_line line; /* line coding (bits, stop, parity) */
- struct work_struct work; /* work queue entry for line discipline waking up */
+ struct work_struct work; /* work queue entry for line discipline waking up */
+ struct tasklet_struct bh; /* rx processing */
unsigned int ctrlin; /* input control lines (DCD, DSR, RI, break, overruns) */
unsigned int ctrlout; /* output control lines (DTR, RTS) */
unsigned int writesize; /* max packet size for the output bulk endpoint */
#define acm_send_break(acm, ms) acm_ctrl_msg(acm, ACM_REQ_SEND_BREAK, ms, NULL, 0)
/*
- * Interrupt handler for various ACM control events
+ * Interrupt handlers for various ACM device responses
*/
+/* control interface reports status changes with "interrupt" transfers */
static void acm_ctrl_irq(struct urb *urb, struct pt_regs *regs)
{
struct acm *acm = urb->context;
__FUNCTION__, status);
}
+/* data interface returns incoming bytes, or we got unthrottled */
static void acm_read_bulk(struct urb *urb, struct pt_regs *regs)
{
struct acm *acm = urb->context;
- struct tty_struct *tty = acm->tty;
- unsigned char *data = urb->transfer_buffer;
- int i = 0;
if (!ACM_READY(acm))
return;
if (urb->status)
- dbg("nonzero read bulk status received: %d", urb->status);
+ dev_dbg(&acm->data->dev, "bulk rx status %d\n", urb->status);
+
+ /* calling tty_flip_buffer_push() in_irq() isn't allowed */
+ tasklet_schedule(&acm->bh);
+}
+
+static void acm_rx_tasklet(unsigned long _acm)
+{
+ struct acm *acm = (void *)_acm;
+ struct urb *urb = acm->readurb;
+ struct tty_struct *tty = acm->tty;
+ unsigned char *data = urb->transfer_buffer;
+ int i = 0;
- if (!urb->status && !acm->throttle) {
+ if (urb->actual_length > 0 && !acm->throttle) {
for (i = 0; i < urb->actual_length && !acm->throttle; i++) {
/* if we insert more than TTY_FLIPBUF_SIZE characters,
* we drop them. */
urb->actual_length = 0;
urb->dev = acm->dev;
- if (usb_submit_urb(urb, GFP_ATOMIC))
- dbg("failed resubmitting read urb");
+ i = usb_submit_urb(urb, GFP_ATOMIC);
+ if (i)
+ dev_dbg(&acm->data->dev, "bulk rx resubmit %d\n", i);
}
+/* data interface wrote those outgoing bytes */
static void acm_write_bulk(struct urb *urb, struct pt_regs *regs)
{
struct acm *acm = (struct acm *)urb->context;
acm->minor = minor;
acm->dev = dev;
+ acm->bh.func = acm_rx_tasklet;
+ acm->bh.data = (unsigned long) acm;
INIT_WORK(&acm->work, acm_softint, acm);
if (!(buf = kmalloc(ctrlsize + readsize + acm->writesize, GFP_KERNEL))) {
memset(m, 0, sizeof(struct usb_mididev));
- if ((m->dev_midi = register_sound_midi(&usb_midi_fops, -1)) < 0) {
+ if ((m->dev_midi = register_sound_midi(&usb_midi_fops, -1, &s->usbdev->dev)) < 0) {
printk(KERN_ERR "usbmidi: cannot register midi device\n");
kfree(m);
return NULL;
#define USB_SUBCLASS_MIDISTREAMING 3
#endif
-#define USB_DT_CS_DEVICE 0x21
-#define USB_DT_CS_CONFIG 0x22
-#define USB_DT_CS_STRING 0x23
-#define USB_DT_CS_INTERFACE 0x24
-#define USB_DT_CS_ENDPOINT 0x25
-
/* ------------------------------------------------------------------------- */
/* Roland MIDI Devices */
goto done;
}
- // FIXME: only use urb->status inside completion
- // callbacks; this way is racey...
add_wait_queue(&usblp->wait, &wait);
while (1==1) {
if (signal_pending(current)) {
static void hub_irq(struct urb *urb, struct pt_regs *regs)
{
struct usb_hub *hub = (struct usb_hub *)urb->context;
- unsigned long flags;
int status;
+ spin_lock(&hub_event_lock);
+ hub->urb_active = 0;
+ if (hub->urb_complete) { /* disconnect or rmmod */
+ complete(hub->urb_complete);
+ goto done;
+ }
+
switch (urb->status) {
case -ENOENT: /* synchronous unlink */
case -ECONNRESET: /* async unlink */
case -ESHUTDOWN: /* hardware going away */
- return;
+ goto done;
default: /* presumably an error */
/* Cause a hub reset after 10 consecutive errors */
hub->nerrors = 0;
/* Something happened, let khubd figure it out */
- spin_lock_irqsave(&hub_event_lock, flags);
if (list_empty(&hub->event_list)) {
list_add(&hub->event_list, &hub_event_list);
wake_up(&khubd_wait);
}
- spin_unlock_irqrestore(&hub_event_lock, flags);
resubmit:
if ((status = usb_submit_urb (hub->urb, GFP_ATOMIC)) != 0
/* ENODEV means we raced disconnect() */
&& status != -ENODEV)
dev_err (&hub->intf->dev, "resubmit --> %d\n", urb->status);
+ if (status == 0)
+ hub->urb_active = 1;
+done:
+ spin_unlock(&hub_event_lock);
}
/* USB 2.0 spec Section 11.24.2.3 */
message = "couldn't submit status urb";
goto fail;
}
-
+ hub->urb_active = 1;
+
/* Wake up khubd */
wake_up(&khubd_wait);
static void hub_disconnect(struct usb_interface *intf)
{
struct usb_hub *hub = usb_get_intfdata (intf);
+ DECLARE_COMPLETION(urb_complete);
unsigned long flags;
if (!hub)
usb_set_intfdata (intf, NULL);
spin_lock_irqsave(&hub_event_lock, flags);
+ hub->urb_complete = &urb_complete;
/* Delete it and then reset it */
- list_del(&hub->event_list);
- INIT_LIST_HEAD(&hub->event_list);
- list_del(&hub->hub_list);
- INIT_LIST_HEAD(&hub->hub_list);
+ list_del_init(&hub->event_list);
+ list_del_init(&hub->hub_list);
spin_unlock_irqrestore(&hub_event_lock, flags);
if (hub->urb) {
usb_unlink_urb(hub->urb);
+ if (hub->urb_active)
+ wait_for_completion(&urb_complete);
usb_free_urb(hub->urb);
hub->urb = NULL;
}
struct usb_hub *hub = usb_get_intfdata(dev->actconfig->interface[0]);
int ret;
+ if (!hub)
+ return -ENODEV;
+
ret = get_port_status(dev, port + 1, &hub->status->port);
if (ret < 0)
dev_err (hubdev (dev),
struct usb_hub {
struct usb_interface *intf; /* the "real" device */
struct urb *urb; /* for interrupt polling pipe */
+ struct completion *urb_complete; /* wait for urb to end */
+ unsigned int urb_active:1;
/* buffer for urb ... 1 bit each for hub and children, rounded up */
char (*buffer)[(USB_MAXCHILDREN + 1 + 7) / 8];
int err = -EINVAL;
int i;
int j;
+ int config;
/*
* Set the driver for the usb device to point to the "generic" driver.
/* choose and set the configuration. that registers the interfaces
* with the driver core, and lets usb device drivers bind to them.
+ * NOTE: should interact with hub power budgeting.
*/
+ config = dev->config[0].desc.bConfigurationValue;
if (dev->descriptor.bNumConfigurations != 1) {
+ for (i = 0; i < dev->descriptor.bNumConfigurations; i++) {
+ /* heuristic: Linux is more likely to have class
+ * drivers, so avoid vendor-specific interfaces.
+ */
+ if (dev->config[i].interface[0]->altsetting
+ ->desc.bInterfaceClass
+ == USB_CLASS_VENDOR_SPEC)
+ continue;
+ config = dev->config[i].desc.bConfigurationValue;
+ break;
+ }
dev_info(&dev->dev,
"configuration #%d chosen from %d choices\n",
- dev->config[0].desc.bConfigurationValue,
+ config,
dev->descriptor.bNumConfigurations);
}
- err = usb_set_configuration(dev,
- dev->config[0].desc.bConfigurationValue);
+ err = usb_set_configuration(dev, config);
if (err) {
dev_err(&dev->dev, "can't set config #%d, error %d\n",
- dev->config[0].desc.bConfigurationValue, err);
+ config, err);
+ device_del(&dev->dev);
goto fail;
}
depends on USB_GADGETFS && USB_PXA2XX
default y
+config USB_G_SERIAL
+ tristate "serial Gadget"
+ depends on USB_GADGET && (USB_DUMMY_HCD || USB_NET2280 || USB_PXA2XX || USB_SA1100)
+
+config USB_G_SERIAL_NET2280
+ bool
+ # for now, treat the "dummy" hcd as if it were a net2280
+ depends on USB_G_SERIAL && (USB_NET2280 || USB_DUMMY_HCD)
+ default y
+
+config USB_G_SERIAL_PXA2XX
+ bool
+ depends on USB_G_SERIAL && USB_PXA2XX
+ default y
+
+config USB_G_SERIAL_SA1100
+ bool
+ depends on USB_G_SERIAL && USB_SA1100
+ default y
+
+
endchoice
# endmenuconfig
#
g_zero-objs := zero.o usbstring.o
g_ether-objs := ether.o usbstring.o
+g_serial-objs := serial.o usbstring.o
gadgetfs-objs := inode.o usbstring.o
obj-$(CONFIG_USB_ZERO) += g_zero.o
obj-$(CONFIG_USB_ETH) += g_ether.o
obj-$(CONFIG_USB_GADGETFS) += gadgetfs.o
-
+obj-$(CONFIG_USB_G_SERIAL) += g_serial.o
--- /dev/null
+/*
+ * g_serial.c -- USB gadget serial driver
+ *
+ * $Id: gserial.c,v 1.17 2003/10/01 06:31:57 borchers Exp $
+ *
+ * Copyright 2003 (c) Al Borchers (alborchers@steinerpoint.com)
+ *
+ * This code is based in part on the Gadget Zero driver, which
+ * is Copyright (C) 2003 by David Brownell, all rights reserved.
+ *
+ * This code also borrows from usbserial.c, which is
+ * Copyright (C) 1999 - 2002 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (c) 2000 Peter Berger (pberger@brimson.com)
+ * Copyright (c) 2000 Al Borchers (alborchers@steinerpoint.com)
+ *
+ * This software is distributed under the terms of the GNU General
+ * Public License ("GPL") as published by the Free Software Foundation,
+ * either version 2 of that License or (at your option) any later version.
+ *
+ */
+
+/* Includes */
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/timer.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/uts.h>
+#include <linux/version.h>
+#include <linux/wait.h>
+#include <linux/list.h>
+#include <linux/proc_fs.h>
+#include <linux/device.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+
+#include <asm/byteorder.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+#include <asm/unaligned.h>
+#include <asm/uaccess.h>
+
+#include <linux/usb_ch9.h>
+#include <linux/usb_gadget.h>
+
+
+/* Wait Cond */
+
+#define __wait_cond_interruptible(wq, condition, lock, flags, ret) \
+do { \
+ wait_queue_t __wait; \
+ init_waitqueue_entry(&__wait, current); \
+ \
+ add_wait_queue(&wq, &__wait); \
+ for (;;) { \
+ set_current_state(TASK_INTERRUPTIBLE); \
+ if (condition) \
+ break; \
+ if (!signal_pending(current)) { \
+ spin_unlock_irqrestore(lock, flags); \
+ schedule(); \
+ spin_lock_irqsave(lock, flags); \
+ continue; \
+ } \
+ ret = -ERESTARTSYS; \
+ break; \
+ } \
+ current->state = TASK_RUNNING; \
+ remove_wait_queue(&wq, &__wait); \
+} while (0)
+
+#define wait_cond_interruptible(wq, condition, lock, flags) \
+({ \
+ int __ret = 0; \
+ if (!(condition)) \
+ __wait_cond_interruptible(wq, condition, lock, flags, \
+ __ret); \
+ __ret; \
+})
+
+#define __wait_cond_interruptible_timeout(wq, condition, lock, flags, \
+ timeout, ret) \
+do { \
+ signed long __timeout = timeout; \
+ wait_queue_t __wait; \
+ init_waitqueue_entry(&__wait, current); \
+ \
+ add_wait_queue(&wq, &__wait); \
+ for (;;) { \
+ set_current_state(TASK_INTERRUPTIBLE); \
+ if (__timeout == 0) \
+ break; \
+ if (condition) \
+ break; \
+ if (!signal_pending(current)) { \
+ spin_unlock_irqrestore(lock, flags); \
+ __timeout = schedule_timeout(__timeout); \
+ spin_lock_irqsave(lock, flags); \
+ continue; \
+ } \
+ ret = -ERESTARTSYS; \
+ break; \
+ } \
+ current->state = TASK_RUNNING; \
+ remove_wait_queue(&wq, &__wait); \
+} while (0)
+
+#define wait_cond_interruptible_timeout(wq, condition, lock, flags, \
+ timeout) \
+({ \
+ int __ret = 0; \
+ if (!(condition)) \
+ __wait_cond_interruptible_timeout(wq, condition, lock, \
+ flags, timeout, __ret); \
+ __ret; \
+})
+
+
+/* Defines */
+
+#define GS_VERSION_STR "v0.1"
+#define GS_VERSION_NUM 0x0001
+
+#define GS_LONG_NAME "Gadget Serial"
+#define GS_SHORT_NAME "g_serial"
+
+#define GS_MAJOR 127
+#define GS_MINOR_START 0
+
+#define GS_NUM_PORTS 16
+
+#define GS_VENDOR_ID 0x05F9
+#define GS_PRODUCT_ID 0xFFFF
+
+#define GS_NUM_CONFIGS 1
+#define GS_NO_CONFIG_ID 0
+#define GS_BULK_CONFIG_ID 2
+
+#define GS_NUM_INTERFACES 1
+#define GS_INTERFACE_ID 0
+#define GS_ALT_INTERFACE_ID 0
+
+#define GS_NUM_ENDPOINTS 2
+
+#define GS_MAX_DESC_LEN 256
+
+#define GS_DEFAULT_READ_Q_SIZE 32
+#define GS_DEFAULT_WRITE_Q_SIZE 32
+
+#define GS_DEFAULT_WRITE_BUF_SIZE 8192
+#define GS_TMP_BUF_SIZE 8192
+
+#define GS_CLOSE_TIMEOUT 15
+
+/* debug macro */
+#if G_SERIAL_DEBUG
+
+static int debug = G_SERIAL_DEBUG;
+
+#define gs_debug(format, arg...) \
+ do { if(debug) printk( KERN_DEBUG format, ## arg ); } while(0)
+#define gs_debug_level(level, format, arg...) \
+ do { if(debug>=level) printk( KERN_DEBUG format, ## arg ); } while(0)
+
+#else
+
+#define gs_debug(format, arg...) \
+ do { } while(0)
+#define gs_debug_level(level, format, arg...) \
+ do { } while(0)
+
+#endif /* G_SERIAL_DEBUG */
+
+
+/* USB Controllers */
+
+/*
+ * NetChip 2280, PCI based.
+ *
+ * This has half a dozen configurable endpoints, four with dedicated
+ * DMA channels to manage their FIFOs. It supports high speed.
+ * Those endpoints can be arranged in any desired configuration.
+ */
+#ifdef CONFIG_USB_G_SERIAL_NET2280
+#define CHIP "net2280"
+#define EP0_MAXPACKET 64
+static const char EP_OUT_NAME[] = "ep-a";
+#define EP_OUT_NUM 2
+static const char EP_IN_NAME[] = "ep-b";
+#define EP_IN_NUM 2
+#define HIGHSPEED
+#define SELFPOWER USB_CONFIG_ATT_SELFPOWER
+
+extern int net2280_set_fifo_mode( struct usb_gadget *gadget, int mode );
+
+static inline void hw_optimize( struct usb_gadget *gadget )
+{
+ /* we can have bigger ep-a/ep-b fifos (2KB each, 4 packets
+ * for highspeed bulk) because we're not using ep-c/ep-d.
+ */
+ net2280_set_fifo_mode (gadget, 1);
+}
+#endif
+
+
+/*
+ * PXA-2xx UDC: widely used in second gen Linux-capable PDAs.
+ *
+ * This has fifteen fixed-function full speed endpoints, and it
+ * can support all USB transfer types.
+ *
+ * These supports three or four configurations, with fixed numbers.
+ * The hardware interprets SET_INTERFACE, net effect is that you
+ * can't use altsettings or reset the interfaces independently.
+ * So stick to a single interface.
+ */
+#ifdef CONFIG_USB_G_SERIAL_PXA2XX
+#define CHIP "pxa2xx"
+#define EP0_MAXPACKET 16
+static const char EP_OUT_NAME[] = "ep12out-bulk";
+#define EP_OUT_NUM 12
+static const char EP_IN_NAME[] = "ep11in-bulk";
+#define EP_IN_NUM 11
+#define SELFPOWER USB_CONFIG_ATT_SELFPOWER
+
+/* no hw optimizations to apply */
+#define hw_optimize(g) do {} while (0)
+#endif
+
+
+/*
+ * SA-1100 UDC: widely used in first gen Linux-capable PDAs.
+ *
+ * This has only two fixed function endpoints, which can only
+ * be used for bulk (or interrupt) transfers. (Plus control.)
+ *
+ * Since it can't flush its TX fifos without disabling the UDC,
+ * the current configuration or altsettings can't change except
+ * in special situations. So this is a case of "choose it right
+ * during enumeration" ...
+ */
+#ifdef CONFIG_USB_G_SERIAL_SA1100
+#define CHIP "sa1100"
+#define EP0_MAXPACKET 8
+static const char EP_OUT_NAME[] = "ep1out-bulk";
+#define EP_OUT_NUM 1
+static const char EP_IN_NAME [] = "ep2in-bulk";
+#define EP_IN_NUM 2
+#define SELFPOWER USB_CONFIG_ATT_SELFPOWER
+
+/* no hw optimizations to apply */
+#define hw_optimize(g) do {} while (0)
+#endif
+
+
+/*
+ * Toshiba TC86C001 ("Goku-S") UDC
+ *
+ * This has three semi-configurable full speed bulk/interrupt endpoints.
+ */
+#ifdef CONFIG_USB_G_SERIAL_GOKU
+#define CHIP "goku"
+#define DRIVER_VERSION_NUM 0x0116
+#define EP0_MAXPACKET 8
+static const char EP_OUT_NAME [] = "ep1-bulk";
+#define EP_OUT_NUM 1
+static const char EP_IN_NAME [] = "ep2-bulk";
+#define EP_IN_NUM 2
+#define SELFPOWER USB_CONFIG_ATT_SELFPOWER
+
+/* no hw optimizations to apply */
+#define hw_optimize(g) do {} while (0)
+#endif
+
+/*
+ * USB Controller Defaults
+ */
+#ifndef EP0_MAXPACKET
+#error Configure some USB peripheral controller for g_serial!
+#endif
+
+#ifndef SELFPOWER
+/* default: say we rely on bus power */
+#define SELFPOWER 0
+/* else value must be USB_CONFIG_ATT_SELFPOWER */
+#endif
+
+#ifndef MAX_USB_POWER
+/* any hub supports this steady state bus power consumption */
+#define MAX_USB_POWER 100 /* mA */
+#endif
+
+#ifndef WAKEUP
+/* default: this driver won't do remote wakeup */
+#define WAKEUP 0
+/* else value must be USB_CONFIG_ATT_WAKEUP */
+#endif
+
+
+/* Structures */
+
+struct gs_dev;
+
+/* circular buffer */
+struct gs_buf {
+ unsigned int buf_size;
+ char *buf_buf;
+ char *buf_get;
+ char *buf_put;
+};
+
+/* list of requests */
+struct gs_req_entry {
+ struct list_head re_entry;
+ struct usb_request *re_req;
+};
+
+/* the port structure holds info for each port, one for each minor number */
+struct gs_port {
+ struct gs_dev *port_dev; /* pointer to device struct */
+ struct tty_struct *port_tty; /* pointer to tty struct */
+ spinlock_t port_lock;
+ int port_num;
+ int port_open_count;
+ int port_in_use; /* open/close in progress */
+ wait_queue_head_t port_write_wait;/* waiting to write */
+ struct gs_buf *port_write_buf;
+};
+
+/* the device structure holds info for the USB device */
+struct gs_dev {
+ struct usb_gadget *dev_gadget; /* gadget device pointer */
+ spinlock_t dev_lock; /* lock for set/reset config */
+ int dev_config; /* configuration number */
+ struct usb_ep *dev_in_ep; /* address of in endpoint */
+ struct usb_ep *dev_out_ep; /* address of out endpoint */
+ struct usb_request *dev_ctrl_req; /* control request */
+ struct list_head dev_req_list; /* list of write requests */
+ int dev_sched_port; /* round robin port scheduled */
+ struct gs_port *dev_port[GS_NUM_PORTS]; /* the ports */
+};
+
+
+/* Functions */
+
+/* module */
+static int __init gs_module_init( void );
+static void __exit gs_module_exit( void );
+
+/* tty driver */
+static int gs_open( struct tty_struct *tty, struct file *file );
+static void gs_close( struct tty_struct *tty, struct file *file );
+static int gs_write( struct tty_struct *tty, int from_user,
+ const unsigned char *buf, int count );
+static void gs_put_char( struct tty_struct *tty, unsigned char ch );
+static void gs_flush_chars( struct tty_struct *tty );
+static int gs_write_room( struct tty_struct *tty );
+static int gs_chars_in_buffer( struct tty_struct *tty );
+static void gs_throttle( struct tty_struct * tty );
+static void gs_unthrottle( struct tty_struct * tty );
+static void gs_break( struct tty_struct *tty, int break_state );
+static int gs_ioctl( struct tty_struct *tty, struct file *file,
+ unsigned int cmd, unsigned long arg );
+static void gs_set_termios( struct tty_struct *tty, struct termios *old );
+static int gs_read_proc( char *page, char **start, off_t off, int count,
+ int *eof, void *data );
+
+static int gs_send( struct gs_dev *dev );
+static int gs_send_packet( struct gs_dev *dev, char *packet,
+ unsigned int size );
+static int gs_recv_packet( struct gs_dev *dev, char *packet,
+ unsigned int size );
+static void gs_read_complete( struct usb_ep *ep, struct usb_request *req );
+static void gs_write_complete( struct usb_ep *ep, struct usb_request *req );
+
+/* gadget driver */
+static int gs_bind( struct usb_gadget *gadget );
+static void gs_unbind( struct usb_gadget *gadget );
+static int gs_setup( struct usb_gadget *gadget,
+ const struct usb_ctrlrequest *ctrl );
+static void gs_setup_complete( struct usb_ep *ep, struct usb_request *req );
+static void gs_disconnect( struct usb_gadget *gadget );
+static int gs_set_config( struct gs_dev *dev, unsigned config );
+static void gs_reset_config( struct gs_dev *dev );
+static int gs_build_config_desc( u8 *buf, enum usb_device_speed speed,
+ u8 type, unsigned int index );
+
+static struct usb_request *gs_alloc_req( struct usb_ep *ep, unsigned int len,
+ int kmalloc_flags );
+static void gs_free_req( struct usb_ep *ep, struct usb_request *req );
+
+static struct gs_req_entry *gs_alloc_req_entry( struct usb_ep *ep, unsigned len,
+ int kmalloc_flags );
+static void gs_free_req_entry( struct usb_ep *ep, struct gs_req_entry *req );
+
+static int gs_alloc_ports( struct gs_dev *dev, int kmalloc_flags );
+static void gs_free_ports( struct gs_dev *dev );
+
+/* circular buffer */
+static struct gs_buf *gs_buf_alloc( unsigned int size, int kmalloc_flags );
+static void gs_buf_free( struct gs_buf *gb );
+static void gs_buf_clear( struct gs_buf *gb );
+static unsigned int gs_buf_data_avail( struct gs_buf *gb );
+static unsigned int gs_buf_space_avail( struct gs_buf *gb );
+static unsigned int gs_buf_put( struct gs_buf *gb, const char *buf,
+ unsigned int count );
+static unsigned int gs_buf_get( struct gs_buf *gb, char *buf,
+ unsigned int count );
+
+
+/* Globals */
+
+static struct gs_dev *gs_device;
+
+static struct semaphore gs_open_close_sem[GS_NUM_PORTS];
+
+static unsigned int read_q_size = GS_DEFAULT_READ_Q_SIZE;
+static unsigned int write_q_size = GS_DEFAULT_WRITE_Q_SIZE;
+
+static unsigned int write_buf_size = GS_DEFAULT_WRITE_BUF_SIZE;
+
+static unsigned char gs_tmp_buf[GS_TMP_BUF_SIZE];
+static struct semaphore gs_tmp_buf_sem;
+
+/* tty driver struct */
+static struct tty_operations gs_tty_ops = {
+ .open = gs_open,
+ .close = gs_close,
+ .write = gs_write,
+ .put_char = gs_put_char,
+ .flush_chars = gs_flush_chars,
+ .write_room = gs_write_room,
+ .ioctl = gs_ioctl,
+ .set_termios = gs_set_termios,
+ .throttle = gs_throttle,
+ .unthrottle = gs_unthrottle,
+ .break_ctl = gs_break,
+ .chars_in_buffer = gs_chars_in_buffer,
+ .read_proc = gs_read_proc,
+};
+static struct tty_driver *gs_tty_driver;
+
+/* gadget driver struct */
+static struct usb_gadget_driver gs_gadget_driver = {
+#ifdef HIGHSPEED
+ .speed = USB_SPEED_HIGH,
+#else
+ .speed = USB_SPEED_FULL,
+#endif
+ .function = GS_LONG_NAME,
+ .bind = gs_bind,
+ .unbind = gs_unbind,
+ .setup = gs_setup,
+ .disconnect = gs_disconnect,
+ .driver = {
+ .name = GS_SHORT_NAME,
+ /* .shutdown = ... */
+ /* .suspend = ... */
+ /* .resume = ... */
+ },
+};
+
+
+/* USB descriptors */
+
+#define GS_MANUFACTURER_STR_ID 1
+#define GS_PRODUCT_STR_ID 2
+#define GS_SERIAL_STR_ID 3
+#define GS_CONFIG_STR_ID 4
+
+/* static strings, in iso 8859/1 */
+static struct usb_string gs_strings[] = {
+ { GS_MANUFACTURER_STR_ID, UTS_SYSNAME " " UTS_RELEASE " with " CHIP },
+ { GS_PRODUCT_STR_ID, GS_LONG_NAME },
+ { GS_SERIAL_STR_ID, "0" },
+ { GS_CONFIG_STR_ID, "Bulk" },
+ { } /* end of list */
+};
+
+static struct usb_gadget_strings gs_string_table = {
+ .language = 0x0409, /* en-us */
+ .strings = gs_strings,
+};
+
+static const struct usb_device_descriptor gs_device_desc = {
+ .bLength = USB_DT_DEVICE_SIZE,
+ .bDescriptorType = USB_DT_DEVICE,
+ .bcdUSB = __constant_cpu_to_le16(0x0200),
+ .bDeviceClass = USB_CLASS_VENDOR_SPEC,
+ .bMaxPacketSize0 = EP0_MAXPACKET,
+ .idVendor = __constant_cpu_to_le16(GS_VENDOR_ID),
+ .idProduct = __constant_cpu_to_le16(GS_PRODUCT_ID),
+ .bcdDevice = __constant_cpu_to_le16(GS_VERSION_NUM),
+ .iManufacturer = GS_MANUFACTURER_STR_ID,
+ .iProduct = GS_PRODUCT_STR_ID,
+ .iSerialNumber = GS_SERIAL_STR_ID,
+ .bNumConfigurations = GS_NUM_CONFIGS,
+};
+
+static const struct usb_config_descriptor gs_config_desc = {
+ .bLength = USB_DT_CONFIG_SIZE,
+ .bDescriptorType = USB_DT_CONFIG,
+ /* .wTotalLength set by gs_build_config_desc */
+ .bNumInterfaces = GS_NUM_INTERFACES,
+ .bConfigurationValue = GS_BULK_CONFIG_ID,
+ .iConfiguration = GS_CONFIG_STR_ID,
+ .bmAttributes = USB_CONFIG_ATT_ONE | SELFPOWER | WAKEUP,
+ .bMaxPower = (MAX_USB_POWER + 1) / 2,
+};
+
+static const struct usb_interface_descriptor gs_interface_desc = {
+ .bLength = USB_DT_INTERFACE_SIZE,
+ .bDescriptorType = USB_DT_INTERFACE,
+ .bNumEndpoints = GS_NUM_ENDPOINTS,
+ .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
+ .iInterface = GS_CONFIG_STR_ID,
+};
+
+static const struct usb_endpoint_descriptor gs_fullspeed_in_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = EP_IN_NUM | USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = __constant_cpu_to_le16(64),
+};
+
+static const struct usb_endpoint_descriptor gs_fullspeed_out_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = EP_OUT_NUM | USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = __constant_cpu_to_le16(64),
+};
+
+static const struct usb_endpoint_descriptor gs_highspeed_in_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = EP_IN_NUM | USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = __constant_cpu_to_le16(512),
+};
+
+static const struct usb_endpoint_descriptor gs_highspeed_out_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = EP_OUT_NUM | USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = __constant_cpu_to_le16(512),
+};
+
+#ifdef HIGHSPEED
+static const struct usb_qualifier_descriptor gs_qualifier_desc = {
+ .bLength = sizeof(struct usb_qualifier_descriptor),
+ .bDescriptorType = USB_DT_DEVICE_QUALIFIER,
+ .bcdUSB = __constant_cpu_to_le16 (0x0200),
+ .bDeviceClass = USB_CLASS_VENDOR_SPEC,
+ /* assumes ep0 uses the same value for both speeds ... */
+ .bMaxPacketSize0 = EP0_MAXPACKET,
+ .bNumConfigurations = GS_NUM_CONFIGS,
+};
+#endif
+
+
+/* Module */
+
+MODULE_DESCRIPTION( GS_LONG_NAME );
+MODULE_AUTHOR( "Al Borchers" );
+MODULE_LICENSE( "GPL" );
+
+MODULE_PARM( debug, "i" );
+MODULE_PARM_DESC( debug, "Enable debugging, 0=off, 1=on" );
+
+MODULE_PARM( read_q_size, "i" );
+MODULE_PARM_DESC( read_q_size, "Read request queue size, default=32" );
+
+MODULE_PARM( write_q_size, "i" );
+MODULE_PARM_DESC( write_q_size, "Write request queue size, default=32" );
+
+MODULE_PARM( write_buf_size, "i" );
+MODULE_PARM_DESC( write_buf_size, "Write buffer size, default=8192" );
+
+module_init( gs_module_init );
+module_exit( gs_module_exit );
+
+/*
+* gs_module_init
+*
+* Register as a USB gadget driver and a tty driver.
+*/
+
+static int __init gs_module_init(void)
+{
+ int i;
+ int retval;
+
+ retval = usb_gadget_register_driver(&gs_gadget_driver);
+ if (retval) {
+ printk(KERN_ERR "gs_module_init: cannot register gadget driver, ret=%d\n", retval);
+ return retval;
+ }
+
+ gs_tty_driver = alloc_tty_driver(GS_NUM_PORTS);
+ if (!gs_tty_driver)
+ return -ENOMEM;
+ gs_tty_driver->owner = THIS_MODULE;
+ gs_tty_driver->driver_name = GS_SHORT_NAME;
+ gs_tty_driver->name = "ttygs";
+ gs_tty_driver->devfs_name = "usb/ttygs/";
+ gs_tty_driver->major = GS_MAJOR;
+ gs_tty_driver->minor_start = GS_MINOR_START;
+ gs_tty_driver->type = TTY_DRIVER_TYPE_SERIAL;
+ gs_tty_driver->subtype = SERIAL_TYPE_NORMAL;
+ gs_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS;
+ gs_tty_driver->init_termios = tty_std_termios;
+ gs_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+ tty_set_operations(gs_tty_driver, &gs_tty_ops);
+
+ for (i=0; i < GS_NUM_PORTS; i++)
+ sema_init(&gs_open_close_sem[i], 1);
+
+ sema_init(&gs_tmp_buf_sem, 1);
+
+ retval = tty_register_driver(gs_tty_driver);
+ if (retval) {
+ usb_gadget_unregister_driver(&gs_gadget_driver);
+ put_tty_driver(gs_tty_driver);
+ printk(KERN_ERR "gs_module_init: cannot register tty driver, ret=%d\n", retval);
+ return retval;
+ }
+
+ printk(KERN_INFO "gs_module_init: %s %s loaded\n", GS_LONG_NAME, GS_VERSION_STR);
+ return 0;
+}
+
+
+/*
+* gs_module_exit
+*
+* Unregister as a tty driver and a USB gadget driver.
+*/
+
+static void __exit gs_module_exit(void)
+{
+ tty_unregister_driver(gs_tty_driver);
+ put_tty_driver(gs_tty_driver);
+ usb_gadget_unregister_driver(&gs_gadget_driver);
+
+ printk(KERN_INFO "gs_module_exit: %s %s unloaded\n", GS_LONG_NAME, GS_VERSION_STR);
+}
+
+
+/* TTY Driver */
+
+/*
+ * gs_open
+ */
+
+static int gs_open( struct tty_struct *tty, struct file *file )
+{
+
+ int port_num;
+ unsigned long flags;
+ struct gs_port *port;
+ struct gs_dev *dev;
+ struct gs_buf *buf;
+ struct semaphore *sem;
+
+
+ port_num = tty->index;
+
+ gs_debug( "gs_open: (%d,%p,%p)\n", port_num, tty, file );
+
+ tty->driver_data = NULL;
+
+ if( port_num < 0 || port_num >= GS_NUM_PORTS ) {
+ printk( KERN_ERR "gs_open: (%d,%p,%p) invalid port number\n",
+ port_num, tty, file );
+ return( -ENODEV );
+ }
+
+ dev = gs_device;
+
+ if( dev == NULL ) {
+ printk( KERN_ERR "gs_open: (%d,%p,%p) NULL device pointer\n",
+ port_num, tty, file );
+ return( -ENODEV );
+ }
+
+ sem = &gs_open_close_sem[port_num];
+ if( down_interruptible( sem ) ) {
+ printk( KERN_ERR
+ "gs_open: (%d,%p,%p) interrupted waiting for semaphore\n",
+ port_num, tty, file );
+ return( -ERESTARTSYS );
+ }
+
+ spin_lock_irqsave(&dev->dev_lock, flags );
+
+ if( dev->dev_config == GS_NO_CONFIG_ID ) {
+ printk( KERN_ERR
+ "gs_open: (%d,%p,%p) device is not connected\n",
+ port_num, tty, file );
+ spin_unlock_irqrestore( &dev->dev_lock, flags );
+ up( sem );
+ return( -ENODEV );
+ }
+
+ port = dev->dev_port[port_num];
+
+ if( port == NULL ) {
+ printk( KERN_ERR "gs_open: (%d,%p,%p) NULL port pointer\n",
+ port_num, tty, file );
+ spin_unlock_irqrestore( &dev->dev_lock, flags );
+ up( sem );
+ return( -ENODEV );
+ }
+
+ spin_lock( &port->port_lock );
+ spin_unlock( &dev->dev_lock );
+
+ if( port->port_dev == NULL ) {
+ printk( KERN_ERR "gs_open: (%d,%p,%p) port disconnected (1)\n",
+ port_num, tty, file );
+ spin_unlock_irqrestore( &port->port_lock, flags );
+ up( sem );
+ return( -EIO );
+ }
+
+ if( port->port_open_count > 0 ) {
+ ++port->port_open_count;
+ spin_unlock_irqrestore( &port->port_lock, flags );
+ gs_debug( "gs_open: (%d,%p,%p) already open\n",
+ port_num, tty, file );
+ up( sem );
+ return( 0 );
+ }
+
+ /* mark port as in use, we can drop port lock and sleep if necessary */
+ port->port_in_use = 1;
+
+ /* allocate write buffer on first open */
+ if( port->port_write_buf == NULL ) {
+
+ spin_unlock_irqrestore( &port->port_lock, flags );
+ buf = gs_buf_alloc( write_buf_size, GFP_KERNEL );
+ spin_lock_irqsave( &port->port_lock, flags );
+
+ /* might have been disconnected while asleep, check */
+ if( port->port_dev == NULL ) {
+ printk( KERN_ERR
+ "gs_open: (%d,%p,%p) port disconnected (2)\n",
+ port_num, tty, file );
+ port->port_in_use = 0;
+ spin_unlock_irqrestore( &port->port_lock, flags );
+ up( sem );
+ return( -EIO );
+ }
+
+ if( (port->port_write_buf=buf) == NULL ) {
+ printk( KERN_ERR "gs_open: (%d,%p,%p) cannot allocate port write buffer\n",
+ port_num, tty, file );
+ port->port_in_use = 0;
+ spin_unlock_irqrestore( &port->port_lock, flags );
+ up( sem );
+ return( -ENOMEM );
+ }
+
+ }
+
+ /* wait for carrier detect (not implemented) */
+
+ /* might have been disconnected while asleep, check */
+ if( port->port_dev == NULL ) {
+ printk( KERN_ERR "gs_open: (%d,%p,%p) port disconnected (3)\n",
+ port_num, tty, file );
+ port->port_in_use = 0;
+ spin_unlock_irqrestore( &port->port_lock, flags );
+ up( sem );
+ return( -EIO );
+ }
+
+ tty->driver_data = port;
+ port->port_tty = tty;
+ port->port_open_count = 1;
+ port->port_in_use = 0;
+
+ spin_unlock_irqrestore( &port->port_lock, flags );
+ up( sem );
+
+ gs_debug( "gs_open: (%d,%p,%p) completed\n", port_num, tty, file );
+
+ return( 0 );
+
+}
+
+
+/*
+ * gs_close
+ */
+
+static void gs_close( struct tty_struct *tty, struct file *file )
+{
+
+ unsigned long flags;
+ struct gs_port *port = tty->driver_data;
+ struct semaphore *sem;
+
+
+ if( port == NULL ) {
+ printk( KERN_ERR "gs_close: NULL port pointer\n" );
+ return;
+ }
+
+ gs_debug( "gs_close: (%d,%p,%p)\n", port->port_num, tty, file );
+
+ sem = &gs_open_close_sem[port->port_num];
+ down( sem );
+
+ spin_lock_irqsave( &port->port_lock, flags );
+
+ if( port->port_open_count == 0 ) {
+ printk( KERN_ERR
+ "gs_close: (%d,%p,%p) port is already closed\n",
+ port->port_num, tty, file );
+ spin_unlock_irqrestore( &port->port_lock, flags );
+ up( sem );
+ return;
+ }
+
+ if( port->port_open_count > 0 ) {
+ --port->port_open_count;
+ spin_unlock_irqrestore( &port->port_lock, flags );
+ up( sem );
+ return;
+ }
+
+ /* free disconnected port on final close */
+ if( port->port_dev == NULL ) {
+ kfree( port );
+ spin_unlock_irqrestore( &port->port_lock, flags );
+ up( sem );
+ return;
+ }
+
+ /* mark port as closed but in use, we can drop port lock */
+ /* and sleep if necessary */
+ port->port_in_use = 1;
+ port->port_open_count = 0;
+
+ /* wait for write buffer to drain, or */
+ /* at most GS_CLOSE_TIMEOUT seconds */
+ if( gs_buf_data_avail( port->port_write_buf ) > 0 ) {
+ wait_cond_interruptible_timeout( port->port_write_wait,
+ port->port_dev == NULL
+ || gs_buf_data_avail(port->port_write_buf) == 0,
+ &port->port_lock, flags, GS_CLOSE_TIMEOUT * HZ );
+ }
+
+ /* free disconnected port on final close */
+ /* (might have happened during the above sleep) */
+ if( port->port_dev == NULL ) {
+ kfree( port );
+ spin_unlock_irqrestore( &port->port_lock, flags );
+ up( sem );
+ return;
+ }
+
+ gs_buf_clear( port->port_write_buf );
+
+ tty->driver_data = NULL;
+ port->port_tty = NULL;
+ port->port_in_use = 0;
+
+ spin_unlock_irqrestore( &port->port_lock, flags );
+ up( sem );
+
+ gs_debug( "gs_close: (%d,%p,%p) completed\n",
+ port->port_num, tty, file );
+
+}
+
+
+/*
+ * gs_write
+ */
+
+static int gs_write( struct tty_struct *tty, int from_user,
+ const unsigned char *buf, int count )
+{
+
+ unsigned long flags;
+ struct gs_port *port = tty->driver_data;
+
+
+ if( port == NULL ) {
+ printk( KERN_ERR "gs_write: NULL port pointer\n" );
+ return( -EIO );
+ }
+
+ gs_debug( "gs_write: (%d,%p) writing %d bytes\n", port->port_num, tty,
+ count );
+
+ if( count == 0 )
+ return( 0 );
+
+ /* copy from user into tmp buffer, get tmp_buf semaphore */
+ if( from_user ) {
+ if( count > GS_TMP_BUF_SIZE )
+ count = GS_TMP_BUF_SIZE;
+ down( &gs_tmp_buf_sem );
+ if( copy_from_user( gs_tmp_buf, buf, count ) != 0 ) {
+ up( &gs_tmp_buf_sem );
+ printk( KERN_ERR
+ "gs_write: (%d,%p) cannot copy from user space\n",
+ port->port_num, tty );
+ return( -EFAULT );
+ }
+ buf = gs_tmp_buf;
+ }
+
+ spin_lock_irqsave( &port->port_lock, flags );
+
+ if( port->port_dev == NULL ) {
+ printk( KERN_ERR "gs_write: (%d,%p) port is not connected\n",
+ port->port_num, tty );
+ spin_unlock_irqrestore( &port->port_lock, flags );
+ if( from_user )
+ up( &gs_tmp_buf_sem );
+ return( -EIO );
+ }
+
+ if( port->port_open_count == 0 ) {
+ printk( KERN_ERR "gs_write: (%d,%p) port is closed\n",
+ port->port_num, tty );
+ spin_unlock_irqrestore( &port->port_lock, flags );
+ if( from_user )
+ up( &gs_tmp_buf_sem );
+ return( -EBADF );
+ }
+
+ count = gs_buf_put( port->port_write_buf, buf, count );
+
+ spin_unlock_irqrestore( &port->port_lock, flags );
+
+ if( from_user )
+ up( &gs_tmp_buf_sem );
+
+ gs_send( gs_device );
+
+ gs_debug( "gs_write: (%d,%p) wrote %d bytes\n", port->port_num, tty,
+ count );
+
+ return( count );
+
+}
+
+
+/*
+ * gs_put_char
+ */
+
+static void gs_put_char( struct tty_struct *tty, unsigned char ch )
+{
+
+ unsigned long flags;
+ struct gs_port *port = tty->driver_data;
+
+
+ if( port == NULL ) {
+ printk( KERN_ERR "gs_put_char: NULL port pointer\n" );
+ return;
+ }
+
+ gs_debug( "gs_put_char: (%d,%p) char=0x%x, called from %p, %p, %p\n", port->port_num, tty, ch, __builtin_return_address(0), __builtin_return_address(1), __builtin_return_address(2) );
+
+ spin_lock_irqsave( &port->port_lock, flags );
+
+ if( port->port_dev == NULL ) {
+ printk( KERN_ERR "gs_put_char: (%d,%p) port is not connected\n",
+ port->port_num, tty );
+ spin_unlock_irqrestore( &port->port_lock, flags );
+ return;
+ }
+
+ if( port->port_open_count == 0 ) {
+ printk( KERN_ERR "gs_put_char: (%d,%p) port is closed\n",
+ port->port_num, tty );
+ spin_unlock_irqrestore( &port->port_lock, flags );
+ return;
+ }
+
+ gs_buf_put( port->port_write_buf, &ch, 1 );
+
+ spin_unlock_irqrestore( &port->port_lock, flags );
+
+}
+
+
+/*
+ * gs_flush_chars
+ */
+
+static void gs_flush_chars( struct tty_struct *tty )
+{
+
+ unsigned long flags;
+ struct gs_port *port = tty->driver_data;
+
+
+ if( port == NULL ) {
+ printk( KERN_ERR "gs_flush_chars: NULL port pointer\n" );
+ return;
+ }
+
+ gs_debug( "gs_flush_chars: (%d,%p)\n", port->port_num, tty );
+
+ spin_lock_irqsave( &port->port_lock, flags );
+
+ if( port->port_dev == NULL ) {
+ printk( KERN_ERR
+ "gs_flush_chars: (%d,%p) port is not connected\n",
+ port->port_num, tty );
+ spin_unlock_irqrestore( &port->port_lock, flags );
+ return;
+ }
+
+ if( port->port_open_count == 0 ) {
+ printk( KERN_ERR "gs_flush_chars: (%d,%p) port is closed\n",
+ port->port_num, tty );
+ spin_unlock_irqrestore( &port->port_lock, flags );
+ return;
+ }
+
+ spin_unlock_irqrestore( &port->port_lock, flags );
+
+ gs_send( gs_device );
+
+}
+
+
+/*
+ * gs_write_room
+ */
+
+static int gs_write_room( struct tty_struct *tty )
+{
+
+ int room = 0;
+ unsigned long flags;
+ struct gs_port *port = tty->driver_data;
+
+
+ if( port == NULL )
+ return( 0 );
+
+ spin_lock_irqsave( &port->port_lock, flags );
+
+ if( port->port_dev != NULL && port->port_open_count > 0
+ && port->port_write_buf != NULL )
+ room = gs_buf_space_avail( port->port_write_buf );
+
+ spin_unlock_irqrestore( &port->port_lock, flags );
+
+ gs_debug( "gs_write_room: (%d,%p) room=%d\n",
+ port->port_num, tty, room );
+
+ return( room );
+
+}
+
+
+/*
+ * gs_chars_in_buffer
+ */
+
+static int gs_chars_in_buffer( struct tty_struct *tty )
+{
+
+ int chars = 0;
+ unsigned long flags;
+ struct gs_port *port = tty->driver_data;
+
+
+ if( port == NULL )
+ return( 0 );
+
+ spin_lock_irqsave( &port->port_lock, flags );
+
+ if( port->port_dev != NULL && port->port_open_count > 0
+ && port->port_write_buf != NULL )
+ chars = gs_buf_data_avail( port->port_write_buf );
+
+ spin_unlock_irqrestore( &port->port_lock, flags );
+
+ gs_debug( "gs_chars_in_buffer: (%d,%p) chars=%d\n",
+ port->port_num, tty, chars );
+
+ return( chars );
+
+}
+
+
+/*
+ * gs_throttle
+ */
+
+static void gs_throttle( struct tty_struct *tty )
+{
+
+}
+
+
+/*
+ * gs_unthrottle
+ */
+
+static void gs_unthrottle( struct tty_struct *tty )
+{
+
+}
+
+
+/*
+ * gs_break
+ */
+
+static void gs_break( struct tty_struct *tty, int break_state )
+{
+
+}
+
+
+/*
+ * gs_ioctl
+ */
+
+static int gs_ioctl( struct tty_struct *tty, struct file *file,
+ unsigned int cmd, unsigned long arg )
+{
+
+ struct gs_port *port = tty->driver_data;
+
+
+ if( port == NULL ) {
+ printk( KERN_ERR "gs_ioctl: NULL port pointer\n" );
+ return( -EIO );
+ }
+
+ gs_debug( "gs_ioctl: (%d,%p,%p) cmd=0x%4.4x, arg=%lu\n",
+ port->port_num, tty, file, cmd, arg );
+
+ /* handle ioctls */
+
+ /* could not handle ioctl */
+ return( -ENOIOCTLCMD );
+
+}
+
+
+/*
+ * gs_set_termios
+ */
+
+static void gs_set_termios( struct tty_struct *tty, struct termios *old )
+{
+
+}
+
+
+/*
+ * gs_read_proc
+ */
+
+static int gs_read_proc( char *page, char **start, off_t off, int count,
+ int *eof, void *data )
+{
+
+ return( 0 );
+
+}
+
+
+/*
+* gs_send
+*
+* This function finds available write requests, calls
+* gs_send_packet to fill these packets with data, and
+* continues until either there are no more write requests
+* available or no more data to send. This function is
+* run whenever data arrives or write requests are available.
+*/
+
+static int gs_send( struct gs_dev *dev )
+{
+
+ int ret,len;
+ unsigned long flags;
+ struct usb_ep *ep;
+ struct usb_request *req;
+ struct gs_req_entry *req_entry;
+
+
+ if( dev == NULL ) {
+ printk( KERN_ERR "gs_send: NULL device pointer\n" );
+ return( -ENODEV );
+ }
+
+ spin_lock_irqsave( &dev->dev_lock, flags );
+
+ ep = dev->dev_in_ep;
+
+ while( !list_empty( &dev->dev_req_list ) ) {
+
+ req_entry = list_entry( dev->dev_req_list.next,
+ struct gs_req_entry, re_entry );
+
+ req = req_entry->re_req;
+
+ len = gs_send_packet( dev, req->buf, ep->maxpacket );
+
+ if( len > 0 ) {
+gs_debug_level( 3, "gs_send: len=%d, 0x%2.2x 0x%2.2x 0x%2.2x ...\n", len, *((unsigned char *)req->buf), *((unsigned char *)req->buf+1), *((unsigned char *)req->buf+2) );
+ list_del( &req_entry->re_entry );
+ req->length = len;
+ if( (ret=usb_ep_queue( ep, req, GFP_ATOMIC )) ) {
+ printk( KERN_ERR
+ "gs_send: cannot queue read request, ret=%d\n",
+ ret );
+ break;
+ }
+ } else {
+ break;
+ }
+
+ }
+
+ spin_unlock_irqrestore( &dev->dev_lock, flags );
+
+ return( 0 );
+
+}
+
+
+/*
+ * gs_send_packet
+ *
+ * If there is data to send, a packet is built in the given
+ * buffer and the size is returned. If there is no data to
+ * send, 0 is returned. If there is any error a negative
+ * error number is returned.
+ *
+ * Called during USB completion routine, on interrupt time.
+ *
+ * We assume that disconnect will not happen until all completion
+ * routines have completed, so we can assume that the dev_port
+ * array does not change during the lifetime of this function.
+ */
+
+static int gs_send_packet( struct gs_dev *dev, char *packet, unsigned int size )
+{
+
+ unsigned int len;
+ struct gs_port *port;
+
+
+ /* TEMPORARY -- only port 0 is supported right now */
+ port = dev->dev_port[0];
+
+ if( port == NULL ) {
+ printk( KERN_ERR
+ "gs_send_packet: port=%d, NULL port pointer\n",
+ 0 );
+ return( -EIO );
+ }
+
+ spin_lock( &port->port_lock );
+
+ len = gs_buf_data_avail( port->port_write_buf );
+ if( len < size )
+ size = len;
+
+ if( size == 0 ) {
+ spin_unlock( &port->port_lock );
+ return( 0 );
+ }
+
+ size = gs_buf_get( port->port_write_buf, packet, size );
+
+ wake_up_interruptible( &port->port_tty->write_wait );
+
+ spin_unlock( &port->port_lock );
+
+ return( size );
+
+}
+
+
+/*
+ * gs_recv_packet
+ *
+ * Called for each USB packet received. Reads the packet
+ * header and stuffs the data in the appropriate tty buffer.
+ * Returns 0 if successful, or a negative error number.
+ *
+ * Called during USB completion routine, on interrupt time.
+ *
+ * We assume that disconnect will not happen until all completion
+ * routines have completed, so we can assume that the dev_port
+ * array does not change during the lifetime of this function.
+ */
+
+static int gs_recv_packet( struct gs_dev *dev, char *packet, unsigned int size )
+{
+
+ unsigned int len;
+ struct gs_port *port;
+
+
+ /* TEMPORARY -- only port 0 is supported right now */
+ port = dev->dev_port[0];
+
+ if( port == NULL ) {
+ printk( KERN_ERR "gs_recv_packet: port=%d, NULL port pointer\n",
+ port->port_num );
+ return( -EIO );
+ }
+
+ spin_lock( &port->port_lock );
+
+ if( port->port_tty == NULL ) {
+ printk( KERN_ERR "gs_recv_packet: port=%d, NULL tty pointer\n",
+ port->port_num );
+ spin_unlock( &port->port_lock );
+ return( -EIO );
+ }
+
+ if( port->port_tty->magic != TTY_MAGIC ) {
+ printk( KERN_ERR "gs_recv_packet: port=%d, bad tty magic\n",
+ port->port_num );
+ spin_unlock( &port->port_lock );
+ return( -EIO );
+ }
+
+ len = (unsigned int)(TTY_FLIPBUF_SIZE - port->port_tty->flip.count);
+ if( len < size )
+ size = len;
+
+ if( size > 0 ) {
+ memcpy( port->port_tty->flip.char_buf_ptr, packet, size );
+ port->port_tty->flip.char_buf_ptr += size;
+ port->port_tty->flip.count += size;
+ tty_flip_buffer_push( port->port_tty );
+ wake_up_interruptible( &port->port_tty->read_wait );
+ }
+
+ spin_unlock( &port->port_lock );
+
+ return( 0 );
+
+}
+
+
+/*
+* gs_read_complete
+*/
+
+static void gs_read_complete( struct usb_ep *ep, struct usb_request *req )
+{
+
+ int ret;
+ struct gs_dev *dev = ep->driver_data;
+
+
+ if( dev == NULL ) {
+ printk( KERN_ERR "gs_read_complete: NULL device pointer\n" );
+ return;
+ }
+
+ switch( req->status ) {
+
+ case 0:
+ /* normal completion */
+ gs_recv_packet( dev, req->buf, req->actual );
+requeue:
+ req->length = ep->maxpacket;
+ if( (ret=usb_ep_queue( ep, req, GFP_ATOMIC )) ) {
+ printk( KERN_ERR
+ "gs_read_complete: cannot queue read request, ret=%d\n",
+ ret );
+ }
+ break;
+
+ case -ESHUTDOWN:
+ /* disconnect */
+ gs_debug( "gs_read_complete: shutdown\n" );
+ gs_free_req( ep, req );
+ break;
+
+ default:
+ /* unexpected */
+ printk( KERN_ERR
+ "gs_read_complete: unexpected status error, status=%d\n",
+ req->status );
+ goto requeue;
+ break;
+
+ }
+
+}
+
+
+/*
+* gs_write_complete
+*/
+
+static void gs_write_complete( struct usb_ep *ep, struct usb_request *req )
+{
+
+ struct gs_dev *dev = ep->driver_data;
+ struct gs_req_entry *gs_req = req->context;
+
+
+ if( dev == NULL ) {
+ printk( KERN_ERR "gs_write_complete: NULL device pointer\n" );
+ return;
+ }
+
+ switch( req->status ) {
+
+ case 0:
+ /* normal completion */
+requeue:
+ if( gs_req == NULL ) {
+ printk( KERN_ERR
+ "gs_write_complete: NULL request pointer\n" );
+ return;
+ }
+
+ spin_lock( &dev->dev_lock );
+ list_add( &gs_req->re_entry, &dev->dev_req_list );
+ spin_unlock( &dev->dev_lock );
+
+ gs_send( dev );
+
+ break;
+
+ case -ESHUTDOWN:
+ /* disconnect */
+ gs_debug( "gs_write_complete: shutdown\n" );
+ gs_free_req( ep, req );
+ break;
+
+ default:
+ printk( KERN_ERR
+ "gs_write_complete: unexpected status error, status=%d\n",
+ req->status );
+ goto requeue;
+ break;
+
+ }
+
+}
+
+
+/* Gadget Driver */
+
+/*
+ * gs_bind
+ *
+ * Called on module load. Allocates and initializes the device
+ * structure and a control request.
+ */
+
+static int gs_bind( struct usb_gadget *gadget )
+{
+
+ int ret;
+ struct gs_dev *dev;
+
+
+ gs_device = dev = kmalloc( sizeof(struct gs_dev), GFP_KERNEL );
+ if( dev == NULL )
+ return( -ENOMEM );
+
+ set_gadget_data( gadget, dev );
+
+ memset( dev, 0, sizeof(struct gs_dev) );
+ dev->dev_gadget = gadget;
+ spin_lock_init( &dev->dev_lock );
+ INIT_LIST_HEAD( &dev->dev_req_list );
+
+ if( (ret=gs_alloc_ports( dev, GFP_KERNEL )) != 0 ) {
+ printk( KERN_ERR "gs_bind: cannot allocate ports\n" );
+ gs_unbind( gadget );
+ return( ret );
+ }
+
+ /* preallocate control response and buffer */
+ dev->dev_ctrl_req = gs_alloc_req( gadget->ep0, GS_MAX_DESC_LEN,
+ GFP_KERNEL );
+ if( dev->dev_ctrl_req == NULL ) {
+ gs_unbind( gadget );
+ return( -ENOMEM );
+ }
+ dev->dev_ctrl_req->complete = gs_setup_complete;
+
+ gadget->ep0->driver_data = dev;
+
+ printk( KERN_INFO "gs_bind: %s %s bound\n",
+ GS_LONG_NAME, GS_VERSION_STR );
+
+ return( 0 );
+
+}
+
+
+/*
+ * gs_unbind
+ *
+ * Called on module unload. Frees the control request and device
+ * structure.
+ */
+
+static void gs_unbind( struct usb_gadget *gadget )
+{
+
+ struct gs_dev *dev = get_gadget_data( gadget );
+
+
+ gs_device = NULL;
+
+ /* read/write requests already freed, only control request remains */
+ if( dev != NULL ) {
+ if( dev->dev_ctrl_req != NULL )
+ gs_free_req( gadget->ep0, dev->dev_ctrl_req );
+ gs_free_ports( dev );
+ kfree( dev );
+ set_gadget_data( gadget, NULL );
+ }
+
+ printk( KERN_INFO "gs_unbind: %s %s unbound\n", GS_LONG_NAME,
+ GS_VERSION_STR );
+
+}
+
+
+/*
+ * gs_setup
+ *
+ * Implements all the control endpoint functionality that's not
+ * handled in hardware or the hardware driver.
+ *
+ * Returns the size of the data sent to the host, or a negative
+ * error number.
+ */
+
+static int gs_setup( struct usb_gadget *gadget,
+ const struct usb_ctrlrequest *ctrl )
+{
+
+ int ret = -EOPNOTSUPP;
+ unsigned int sv_config;
+ struct gs_dev *dev = get_gadget_data( gadget );
+ struct usb_request *req = dev->dev_ctrl_req;
+
+
+ switch (ctrl->bRequest) {
+
+ case USB_REQ_GET_DESCRIPTOR:
+
+ if( ctrl->bRequestType != USB_DIR_IN )
+ break;
+
+ switch (ctrl->wValue >> 8) {
+
+ case USB_DT_DEVICE:
+ ret = min( ctrl->wLength,
+ (u16)sizeof(struct usb_device_descriptor) );
+ memcpy( req->buf, &gs_device_desc, ret );
+ break;
+
+#ifdef HIGHSPEED
+ case USB_DT_DEVICE_QUALIFIER:
+ ret = min( ctrl->wLength,
+ (u16)sizeof(struct usb_qualifier_descriptor) );
+ memcpy( req->buf, &gs_qualifier_desc, ret );
+ break;
+
+ case USB_DT_OTHER_SPEED_CONFIG:
+#endif /* HIGHSPEED */
+ case USB_DT_CONFIG:
+ ret = gs_build_config_desc( req->buf, gadget->speed,
+ ctrl->wValue >> 8, ctrl->wValue & 0xff );
+ if( ret >= 0 )
+ ret = min( ctrl->wLength, (u16)ret );
+ break;
+
+ case USB_DT_STRING:
+ /* wIndex == language code. */
+ ret = usb_gadget_get_string( &gs_string_table,
+ ctrl->wValue & 0xff, req->buf );
+ if( ret >= 0 )
+ ret = min( ctrl->wLength, (u16)ret );
+ break;
+ }
+ break;
+
+ case USB_REQ_SET_CONFIGURATION:
+ if( ctrl->bRequestType != 0 )
+ break;
+ spin_lock( &dev->dev_lock );
+ ret = gs_set_config( dev, ctrl->wValue );
+ spin_unlock( &dev->dev_lock );
+ break;
+
+ case USB_REQ_GET_CONFIGURATION:
+ if( ctrl->bRequestType != USB_DIR_IN )
+ break;
+ *(u8 *)req->buf = dev->dev_config;
+ ret = min( ctrl->wLength, (u16)1 );
+ break;
+
+ case USB_REQ_SET_INTERFACE:
+ if( ctrl->bRequestType != USB_RECIP_INTERFACE )
+ break;
+ spin_lock( &dev->dev_lock );
+ if( dev->dev_config == GS_BULK_CONFIG_ID
+ && ctrl->wIndex == GS_INTERFACE_ID
+ && ctrl->wValue == GS_ALT_INTERFACE_ID ) {
+ sv_config = dev->dev_config;
+ /* since there is only one interface, setting the */
+ /* interface is equivalent to setting the config */
+ gs_reset_config( dev );
+ gs_set_config( dev, sv_config );
+ ret = 0;
+ }
+ spin_unlock( &dev->dev_lock );
+ break;
+
+ case USB_REQ_GET_INTERFACE:
+ if( ctrl->bRequestType != (USB_DIR_IN|USB_RECIP_INTERFACE) )
+ break;
+ if( dev->dev_config == GS_NO_CONFIG_ID )
+ break;
+ if( ctrl->wIndex != GS_INTERFACE_ID ) {
+ ret = -EDOM;
+ break;
+ }
+ *(u8 *)req->buf = GS_ALT_INTERFACE_ID;
+ ret = min( ctrl->wLength, (u16)1 );
+ break;
+
+ default:
+ printk( KERN_ERR "gs_setup: unknown request, type=%02x, request=%02x, value=%04x, index=%04x, length=%d\n",
+ ctrl->bRequestType, ctrl->bRequest, ctrl->wValue,
+ ctrl->wIndex, ctrl->wLength );
+ break;
+
+ }
+
+ /* respond with data transfer before status phase? */
+ if( ret >= 0 ) {
+ req->length = ret;
+ ret = usb_ep_queue( gadget->ep0, req, GFP_ATOMIC );
+ if( ret < 0 ) {
+ printk( KERN_ERR
+ "gs_setup: cannot queue response, ret=%d\n",
+ ret );
+ req->status = 0;
+ gs_setup_complete( gadget->ep0, req );
+ }
+ }
+
+ /* device either stalls (ret < 0) or reports success */
+ return( ret );
+
+}
+
+
+/*
+ * gs_setup_complete
+ */
+
+static void gs_setup_complete( struct usb_ep *ep, struct usb_request *req )
+{
+ if( req->status || req->actual != req->length ) {
+ printk( KERN_ERR "gs_setup_complete: status error, status=%d, actual=%d, length=%d\n",
+ req->status, req->actual, req->length );
+ }
+}
+
+
+/*
+ * gs_disconnect
+ *
+ * Called when the device is disconnected. Frees the closed
+ * ports and disconnects open ports. Open ports will be freed
+ * on close. Then reallocates the ports for the next connection.
+ */
+
+static void gs_disconnect( struct usb_gadget *gadget )
+{
+
+ unsigned long flags;
+ struct gs_dev *dev = get_gadget_data( gadget );
+
+
+ spin_lock_irqsave( &dev->dev_lock, flags );
+
+ gs_reset_config( dev );
+
+ /* free closed ports and disconnect open ports */
+ /* (open ports will be freed when closed) */
+ gs_free_ports( dev );
+
+ /* re-allocate ports for the next connection */
+ if( gs_alloc_ports( dev, GFP_ATOMIC ) != 0 )
+ printk( KERN_ERR "gs_disconnect: cannot re-allocate ports\n" );
+
+ spin_unlock_irqrestore( &dev->dev_lock, flags );
+
+ printk( KERN_INFO "gs_disconnect: %s disconnected\n", GS_LONG_NAME );
+
+}
+
+
+/*
+ * gs_set_config
+ *
+ * Configures the device by enabling device specific
+ * optimizations, setting up the endpoints, allocating
+ * read and write requests and queuing read requests.
+ *
+ * The device lock must be held when calling this function.
+ */
+
+static int gs_set_config( struct gs_dev *dev, unsigned config )
+{
+
+ int i;
+ int ret = 0;
+ struct usb_gadget *gadget = dev->dev_gadget;
+ struct usb_ep *ep;
+ struct usb_request *req;
+ struct gs_req_entry *req_entry;
+
+
+ if( dev == NULL ) {
+ printk( KERN_ERR "gs_set_config: NULL device pointer\n" );
+ return( 0 );
+ }
+
+ if( config == dev->dev_config )
+ return( 0 );
+
+ gs_reset_config( dev );
+
+ if( config == GS_NO_CONFIG_ID )
+ return( 0 );
+
+ if( config != GS_BULK_CONFIG_ID )
+ return( -EINVAL );
+
+ hw_optimize( gadget );
+
+ gadget_for_each_ep( ep, gadget ) {
+
+ if( strcmp( ep->name, EP_IN_NAME ) == 0 ) {
+ ret = usb_ep_enable( ep,
+ gadget->speed == USB_SPEED_HIGH ?
+ &gs_highspeed_in_desc : &gs_fullspeed_in_desc );
+ if( ret == 0 ) {
+ ep->driver_data = dev;
+ dev->dev_in_ep = ep;
+ } else {
+ printk( KERN_ERR "gs_set_config: cannot enable in endpoint %s, ret=%d\n",
+ ep->name, ret );
+ gs_reset_config( dev );
+ return( ret );
+ }
+ }
+
+ else if( strcmp( ep->name, EP_OUT_NAME ) == 0 ) {
+ ret = usb_ep_enable( ep,
+ gadget->speed == USB_SPEED_HIGH ?
+ &gs_highspeed_out_desc :
+ &gs_fullspeed_out_desc );
+ if( ret == 0 ) {
+ ep->driver_data = dev;
+ dev->dev_out_ep = ep;
+ } else {
+ printk( KERN_ERR "gs_set_config: cannot enable out endpoint %s, ret=%d\n",
+ ep->name, ret );
+ gs_reset_config( dev );
+ return( ret );
+ }
+ }
+
+ }
+
+ if( dev->dev_in_ep == NULL || dev->dev_out_ep == NULL ) {
+ gs_reset_config( dev );
+ printk( KERN_ERR "gs_set_config: cannot find endpoints\n" );
+ return( -ENODEV );
+ }
+
+ /* allocate and queue read requests */
+ ep = dev->dev_out_ep;
+ for( i=0; i<read_q_size && ret == 0; i++ ) {
+ if( (req=gs_alloc_req( ep, ep->maxpacket, GFP_ATOMIC )) ) {
+ req->complete = gs_read_complete;
+ if( (ret=usb_ep_queue( ep, req, GFP_ATOMIC )) ) {
+ printk( KERN_ERR "gs_set_config: cannot queue read request, ret=%d\n",
+ ret );
+ }
+ } else {
+ gs_reset_config( dev );
+ printk( KERN_ERR
+ "gs_set_config: cannot allocate read requests\n" );
+ return( -ENOMEM );
+ }
+ }
+
+ /* allocate write requests, and put on free list */
+ ep = dev->dev_in_ep;
+ for( i=0; i<write_q_size; i++ ) {
+ if( (req_entry=gs_alloc_req_entry( ep, ep->maxpacket,
+ GFP_ATOMIC )) ) {
+ req_entry->re_req->complete = gs_write_complete;
+ list_add( &req_entry->re_entry, &dev->dev_req_list );
+ } else {
+ gs_reset_config( dev );
+ printk( KERN_ERR
+ "gs_set_config: cannot allocate write requests\n" );
+ return( -ENOMEM );
+ }
+ }
+
+ dev->dev_config = config;
+
+ printk( KERN_INFO "gs_set_config: %s configured for %s speed\n",
+ GS_LONG_NAME,
+ gadget->speed == USB_SPEED_HIGH ? "high" : "full" );
+
+ return( 0 );
+
+}
+
+
+/*
+ * gs_reset_config
+ *
+ * Mark the device as not configured, disable all endpoints,
+ * which forces completion of pending I/O and frees queued
+ * requests, and free the remaining write requests on the
+ * free list.
+ *
+ * The device lock must be held when calling this function.
+ */
+
+static void gs_reset_config( struct gs_dev *dev )
+{
+
+ struct gs_req_entry *req_entry;
+
+
+ if( dev == NULL ) {
+ printk( KERN_ERR "gs_reset_config: NULL device pointer\n" );
+ return;
+ }
+
+ if( dev->dev_config == GS_NO_CONFIG_ID )
+ return;
+
+ dev->dev_config = GS_NO_CONFIG_ID;
+
+ /* free write requests on the free list */
+ while( !list_empty( &dev->dev_req_list ) ) {
+ req_entry = list_entry( dev->dev_req_list.next,
+ struct gs_req_entry, re_entry );
+ list_del( &req_entry->re_entry );
+ gs_free_req_entry( dev->dev_in_ep, req_entry );
+ }
+
+ /* disable endpoints, forcing completion of pending i/o; */
+ /* completion handlers free their requests in this case */
+ if( dev->dev_in_ep ) {
+ usb_ep_disable( dev->dev_in_ep );
+ dev->dev_in_ep = NULL;
+ }
+ if( dev->dev_out_ep ) {
+ usb_ep_disable( dev->dev_out_ep );
+ dev->dev_out_ep = NULL;
+ }
+
+}
+
+
+/*
+ * gs_build_config_desc
+ *
+ * Builds a config descriptor in the given buffer and returns the
+ * length, or a negative error number.
+ */
+
+static int gs_build_config_desc( u8 *buf, enum usb_device_speed speed,
+ u8 type, unsigned int index )
+{
+
+ int high_speed;
+ int len = USB_DT_CONFIG_SIZE + USB_DT_INTERFACE_SIZE
+ + GS_NUM_ENDPOINTS * USB_DT_ENDPOINT_SIZE;
+
+
+ /* only one config */
+ if( index != 0 )
+ return( -EINVAL );
+
+ memcpy( buf, &gs_config_desc, USB_DT_CONFIG_SIZE );
+ ((struct usb_config_descriptor *)buf)->bDescriptorType = type;
+ ((struct usb_config_descriptor *)buf)->wTotalLength =
+ __constant_cpu_to_le16( len );
+ buf += USB_DT_CONFIG_SIZE;
+
+ memcpy( buf, &gs_interface_desc, USB_DT_INTERFACE_SIZE );
+ buf += USB_DT_INTERFACE_SIZE;
+
+ /* other speed switches high and full speed */
+ high_speed = (speed == USB_SPEED_HIGH);
+ if( type == USB_DT_OTHER_SPEED_CONFIG )
+ high_speed = !high_speed;
+
+ memcpy( buf,
+ high_speed ? &gs_highspeed_in_desc : &gs_fullspeed_in_desc,
+ USB_DT_ENDPOINT_SIZE );
+ buf += USB_DT_ENDPOINT_SIZE;
+ memcpy( buf,
+ high_speed ? &gs_highspeed_out_desc : &gs_fullspeed_out_desc,
+ USB_DT_ENDPOINT_SIZE );
+
+ return( len );
+
+}
+
+
+/*
+ * gs_alloc_req
+ *
+ * Allocate a usb_request and its buffer. Returns a pointer to the
+ * usb_request or NULL if there is an error.
+ */
+
+static struct usb_request *gs_alloc_req( struct usb_ep *ep, unsigned int len,
+ int kmalloc_flags )
+{
+
+ struct usb_request *req;
+
+
+ if( ep == NULL )
+ return( NULL );
+
+ req = usb_ep_alloc_request( ep, kmalloc_flags );
+
+ if( req != NULL ) {
+ req->length = len;
+ req->buf = usb_ep_alloc_buffer( ep, len, &req->dma,
+ kmalloc_flags );
+ if( req->buf == NULL ) {
+ usb_ep_free_request( ep, req );
+ return( NULL );
+ }
+ }
+
+ return( req );
+
+}
+
+
+/*
+ * gs_free_req
+ *
+ * Free a usb_request and its buffer.
+ */
+
+static void gs_free_req( struct usb_ep *ep, struct usb_request *req )
+{
+ if( ep != NULL && req != NULL ) {
+ if( req->buf != NULL )
+ usb_ep_free_buffer( ep, req->buf, req->dma,
+ req->length );
+ usb_ep_free_request( ep, req );
+ }
+}
+
+
+/*
+ * gs_alloc_req_entry
+ *
+ * Allocates a request and its buffer, using the given
+ * endpoint, buffer len, and kmalloc flags.
+ */
+
+static struct gs_req_entry *gs_alloc_req_entry( struct usb_ep *ep,
+ unsigned len, int kmalloc_flags )
+{
+
+ struct gs_req_entry *req;
+
+
+ req = kmalloc( sizeof(struct gs_req_entry), kmalloc_flags );
+ if( req == NULL )
+ return( NULL );
+
+ req->re_req = gs_alloc_req( ep, len, kmalloc_flags );
+ if( req->re_req == NULL ) {
+ kfree( req );
+ return( NULL );
+ }
+
+ req->re_req->context = req;
+
+ return( req );
+
+}
+
+
+/*
+ * gs_free_req_entry
+ *
+ * Frees a request and its buffer.
+ */
+
+static void gs_free_req_entry( struct usb_ep *ep, struct gs_req_entry *req )
+{
+ if( ep != NULL && req != NULL ) {
+ if( req->re_req != NULL )
+ gs_free_req( ep, req->re_req );
+ kfree( req );
+ }
+}
+
+
+/*
+ * gs_alloc_ports
+ *
+ * Allocate all ports and set the gs_dev struct to point to them.
+ * Return 0 if successful, or a negative error number.
+ *
+ * The device lock is normally held when calling this function.
+ */
+
+static int gs_alloc_ports( struct gs_dev *dev, int kmalloc_flags )
+{
+
+ int i;
+ struct gs_port *port;
+
+
+ if( dev == NULL )
+ return( -EIO );
+
+ for( i=0; i<GS_NUM_PORTS; i++ ) {
+
+ if( (port=(struct gs_port *)kmalloc( sizeof(struct gs_port),
+ kmalloc_flags )) == NULL )
+ return( -ENOMEM );
+
+ memset( port, 0, sizeof( struct gs_port ) );
+ port->port_dev = dev;
+ port->port_num = i;
+ spin_lock_init( &port->port_lock );
+ init_waitqueue_head( &port->port_write_wait );
+
+ dev->dev_port[i] = port;
+
+ }
+
+ return( 0 );
+
+}
+
+
+/*
+ * gs_free_ports
+ *
+ * Free all closed ports. Open ports are disconnected by
+ * freeing their write buffers, setting their device pointers
+ * and the pointers to them in the device to NULL. These
+ * ports will be freed when closed.
+ *
+ * The device lock is normally held when calling this function.
+ */
+
+static void gs_free_ports( struct gs_dev *dev )
+{
+
+ int i;
+ unsigned long flags;
+ struct gs_port *port;
+
+
+ if( dev == NULL )
+ return;
+
+ for( i=0; i<GS_NUM_PORTS; i++ ) {
+
+ if( (port=dev->dev_port[i]) != NULL ) {
+
+ dev->dev_port[i] = NULL;
+
+ spin_lock_irqsave( &port->port_lock, flags );
+
+ if( port->port_write_buf != NULL ) {
+ gs_buf_free( port->port_write_buf );
+ port->port_write_buf = NULL;
+ }
+
+ if( port->port_open_count > 0 || port->port_in_use ) {
+ port->port_dev = NULL;
+ wake_up_interruptible( &port->port_write_wait );
+ wake_up_interruptible( &port->port_tty->read_wait );
+ wake_up_interruptible( &port->port_tty->write_wait );
+ } else {
+ kfree( port );
+ }
+
+ spin_unlock_irqrestore( &port->port_lock, flags );
+
+ }
+
+ }
+
+}
+
+
+/* Circular Buffer */
+
+/*
+ * gs_buf_alloc
+ *
+ * Allocate a circular buffer and all associated memory.
+ */
+
+static struct gs_buf *gs_buf_alloc( unsigned int size, int kmalloc_flags )
+{
+
+ struct gs_buf *gb;
+
+
+ if( size == 0 )
+ return( NULL );
+
+ gb = (struct gs_buf *)kmalloc( sizeof(struct gs_buf), kmalloc_flags );
+ if( gb == NULL )
+ return( NULL );
+
+ gb->buf_buf = kmalloc( size, kmalloc_flags );
+ if( gb->buf_buf == NULL ) {
+ kfree( gb );
+ return( NULL );
+ }
+
+ gb->buf_size = size;
+ gb->buf_get = gb->buf_put = gb->buf_buf;
+
+ return( gb );
+
+}
+
+
+/*
+ * gs_buf_free
+ *
+ * Free the buffer and all associated memory.
+ */
+
+void gs_buf_free( struct gs_buf *gb )
+{
+ if( gb != NULL ) {
+ if( gb->buf_buf != NULL )
+ kfree( gb->buf_buf );
+ kfree( gb );
+ }
+}
+
+
+/*
+ * gs_buf_clear
+ *
+ * Clear out all data in the circular buffer.
+ */
+
+void gs_buf_clear( struct gs_buf *gb )
+{
+ if( gb != NULL )
+ gb->buf_get = gb->buf_put;
+ /* equivalent to a get of all data available */
+}
+
+
+/*
+ * gs_buf_data_avail
+ *
+ * Return the number of bytes of data available in the circular
+ * buffer.
+ */
+
+unsigned int gs_buf_data_avail( struct gs_buf *gb )
+{
+ if( gb != NULL )
+ return( (gb->buf_size + gb->buf_put - gb->buf_get)
+ % gb->buf_size );
+ else
+ return( 0 );
+}
+
+
+/*
+ * gs_buf_space_avail
+ *
+ * Return the number of bytes of space available in the circular
+ * buffer.
+ */
+
+unsigned int gs_buf_space_avail( struct gs_buf *gb )
+{
+ if( gb != NULL )
+ return( (gb->buf_size + gb->buf_get - gb->buf_put - 1)
+ % gb->buf_size );
+ else
+ return( 0 );
+}
+
+
+/*
+ * gs_buf_put
+ *
+ * Copy data data from a user buffer and put it into the circular buffer.
+ * Restrict to the amount of space available.
+ *
+ * Return the number of bytes copied.
+ */
+
+unsigned int gs_buf_put( struct gs_buf *gb, const char *buf,
+ unsigned int count )
+{
+
+ unsigned int len;
+
+
+ if( gb == NULL )
+ return( 0 );
+
+ len = gs_buf_space_avail( gb );
+ if( count > len )
+ count = len;
+
+ if( count == 0 )
+ return( 0 );
+
+ len = gb->buf_buf + gb->buf_size - gb->buf_put;
+ if( count > len ) {
+ memcpy( gb->buf_put, buf, len );
+ memcpy( gb->buf_buf, buf+len, count - len );
+ gb->buf_put = gb->buf_buf + count - len;
+ } else {
+ memcpy( gb->buf_put, buf, count );
+ if( count < len )
+ gb->buf_put += count;
+ else /* count == len */
+ gb->buf_put = gb->buf_buf;
+ }
+
+ return( count );
+
+}
+
+
+/*
+ * gs_buf_get
+ *
+ * Get data from the circular buffer and copy to the given buffer.
+ * Restrict to the amount of data available.
+ *
+ * Return the number of bytes copied.
+ */
+
+unsigned int gs_buf_get( struct gs_buf *gb, char *buf, unsigned int count )
+{
+
+ unsigned int len;
+
+
+ if( gb == NULL )
+ return( 0 );
+
+ len = gs_buf_data_avail( gb );
+ if( count > len )
+ count = len;
+
+ if( count == 0 )
+ return( 0 );
+
+ len = gb->buf_buf + gb->buf_size - gb->buf_get;
+ if( count > len ) {
+ memcpy( buf, gb->buf_get, len );
+ memcpy( buf+len, gb->buf_buf, count - len );
+ gb->buf_get = gb->buf_buf + count - len;
+ } else {
+ memcpy( buf, gb->buf_get, count );
+ if( count < len )
+ gb->buf_get += count;
+ else /* count == len */
+ gb->buf_get = gb->buf_buf;
+ }
+
+ return( count );
+
+}
ohci_dump_status (controller, NULL, 0);
if (controller->hcca)
ohci_dbg (controller,
- "hcca frame #%04x\n", controller->hcca->frame_no);
+ "hcca frame #%04x\n", OHCI_FRAME_NO(controller->hcca));
ohci_dump_roothub (controller, 1, NULL, 0);
}
static const char data0 [] = "DATA0";
static const char data1 [] = "DATA1";
-static void ohci_dump_td (struct ohci_hcd *ohci, char *label, struct td *td)
+static void ohci_dump_td (const struct ohci_hcd *ohci, const char *label,
+ const struct td *td)
{
u32 tmp = le32_to_cpup (&td->hwINFO);
- ohci_dbg (ohci, "%s td %p%s; urb %p index %d; hw next td %08x",
+ ohci_dbg (ohci, "%s td %p%s; urb %p index %d; hw next td %08x\n",
label, td,
(tmp & TD_DONE) ? " (DONE)" : "",
td->urb, td->index,
case TD_DP_OUT: pid = "OUT"; break;
default: pid = "(bad pid)"; break;
}
- ohci_dbg (ohci, " info %08x CC=%x %s DI=%d %s %s", tmp,
+ ohci_dbg (ohci, " info %08x CC=%x %s DI=%d %s %s\n", tmp,
TD_CC_GET(tmp), /* EC, */ toggle,
(tmp & TD_DI) >> 21, pid,
(tmp & TD_R) ? "R" : "");
cbp = le32_to_cpup (&td->hwCBP);
be = le32_to_cpup (&td->hwBE);
- ohci_dbg (ohci, " cbp %08x be %08x (len %d)", cbp, be,
+ ohci_dbg (ohci, " cbp %08x be %08x (len %d)\n", cbp, be,
cbp ? (be + 1 - cbp) : 0);
} else {
unsigned i;
- ohci_dbg (ohci, " info %08x CC=%x FC=%d DI=%d SF=%04x", tmp,
+ ohci_dbg (ohci, " info %08x CC=%x FC=%d DI=%d SF=%04x\n", tmp,
TD_CC_GET(tmp),
(tmp >> 24) & 0x07,
(tmp & TD_DI) >> 21,
tmp & 0x0000ffff);
- ohci_dbg (ohci, " bp0 %08x be %08x",
+ ohci_dbg (ohci, " bp0 %08x be %08x\n",
le32_to_cpup (&td->hwCBP) & ~0x0fff,
le32_to_cpup (&td->hwBE));
for (i = 0; i < MAXPSW; i++) {
u16 psw = le16_to_cpup (&td->hwPSW [i]);
int cc = (psw >> 12) & 0x0f;
- ohci_dbg (ohci, " psw [%d] = %2x, CC=%x %s=%d", i,
+ ohci_dbg (ohci, " psw [%d] = %2x, CC=%x %s=%d\n", i,
psw, cc,
(cc >= 0x0e) ? "OFFSET" : "SIZE",
psw & 0x0fff);
/* caller MUST own hcd spinlock if verbose is set! */
static void __attribute__((unused))
-ohci_dump_ed (struct ohci_hcd *ohci, char *label, struct ed *ed, int verbose)
+ohci_dump_ed (const struct ohci_hcd *ohci, const char *label,
+ const struct ed *ed, int verbose)
{
u32 tmp = ed->hwINFO;
char *type = "";
- ohci_dbg (ohci, "%s, ed %p state 0x%x type %s; next ed %08x",
+ ohci_dbg (ohci, "%s, ed %p state 0x%x type %s; next ed %08x\n",
label,
ed, ed->state, edstring (ed->type),
le32_to_cpup (&ed->hwNextED));
/* else from TDs ... control */
}
ohci_dbg (ohci,
- " info %08x MAX=%d%s%s%s%s EP=%d%s DEV=%d", le32_to_cpu (tmp),
+ " info %08x MAX=%d%s%s%s%s EP=%d%s DEV=%d\n", le32_to_cpu (tmp),
0x03ff & (le32_to_cpu (tmp) >> 16),
(tmp & ED_DEQUEUE) ? " DQ" : "",
(tmp & ED_ISO) ? " ISO" : "",
0x000f & (le32_to_cpu (tmp) >> 7),
type,
0x007f & le32_to_cpu (tmp));
- ohci_dbg (ohci, " tds: head %08x %s%s tail %08x%s",
+ ohci_dbg (ohci, " tds: head %08x %s%s tail %08x%s\n",
tmp = le32_to_cpup (&ed->hwHeadP),
(ed->hwHeadP & ED_C) ? data1 : data0,
(ed->hwHeadP & ED_H) ? " HALT" : "",
if (temp == seen_count) {
u32 info = ed->hwINFO;
u32 scratch = cpu_to_le32p (&ed->hwINFO);
+ struct list_head *entry;
+ unsigned qlen = 0;
+
+ /* qlen measured here in TDs, not urbs */
+ list_for_each (entry, &ed->td_list)
+ qlen++;
temp = snprintf (next, size,
- " (%cs dev%d%s ep%d%s"
+ " (%cs dev%d ep%d%s-%s qlen %u"
" max %d %08x%s%s)",
(info & ED_LOWSPEED) ? 'l' : 'f',
scratch & 0x7f,
- (info & ED_ISO) ? " iso" : "",
(scratch >> 7) & 0xf,
(info & ED_IN) ? "in" : "out",
+ (info & ED_ISO) ? "iso" : "int",
+ qlen,
0x03ff & (scratch >> 16),
scratch,
- (info & ED_SKIP) ? " s" : "",
+ (info & ED_SKIP) ? " K" : "",
(ed->hwHeadP & ED_H) ? " H" : "");
size -= temp;
next += temp;
- // FIXME some TD info too
-
if (seen_count < DBG_SCHED_LIMIT)
seen [seen_count++] = ed;
/* hcca */
if (ohci->hcca)
ohci_dbg_sw (ohci, &next, &size,
- "hcca frame 0x%04x\n", ohci->hcca->frame_no);
+ "hcca frame 0x%04x\n", OHCI_FRAME_NO(ohci->hcca));
/* other registers mostly affect frame timings */
rdata = readl (®s->fminterval);
if (retval < 0)
goto fail;
if (ed->type == PIPE_ISOCHRONOUS) {
- u16 frame = le16_to_cpu (ohci->hcca->frame_no);
+ u16 frame = OHCI_FRAME_NO(ohci->hcca);
/* delay a few frames before the first TD */
frame += max_t (u16, 8, ed->interval);
urb_priv = urb->hcpriv;
if (urb_priv) {
if (urb_priv->ed->state == ED_OPER)
- start_urb_unlink (ohci, urb_priv->ed);
+ start_ed_unlink (ohci, urb_priv->ed);
}
} else {
/*
{
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
- return le16_to_cpu (ohci->hcca->frame_no);
+ return OHCI_FRAME_NO(ohci->hcca);
}
/*-------------------------------------------------------------------------*
*/
spin_lock (&ohci->lock);
if (ohci->ed_rm_list)
- finish_unlinks (ohci, le16_to_cpu (ohci->hcca->frame_no),
+ finish_unlinks (ohci, OHCI_FRAME_NO(ohci->hcca),
ptregs);
if ((ints & OHCI_INTR_SF) != 0 && !ohci->ed_rm_list
&& HCD_IS_RUNNING(ohci->hcd.state))
* put the ep on the rm_list
* real work is done at the next start frame (SF) hardware interrupt
*/
-static void start_urb_unlink (struct ohci_hcd *ohci, struct ed *ed)
+static void start_ed_unlink (struct ohci_hcd *ohci, struct ed *ed)
{
ed->hwINFO |= ED_DEQUEUE;
ed->state = ED_UNLINK;
* behave. frame_no wraps every 2^16 msec, and changes right before
* SF is triggered.
*/
- ed->tick = le16_to_cpu (ohci->hcca->frame_no) + 1;
+ ed->tick = OHCI_FRAME_NO(ohci->hcca) + 1;
/* rm_list is just singly linked, for simplicity */
ed->ed_next = ohci->ed_rm_list;
* and iso; other urbs rarely need more than one TD per urb.
* this way, only final tds (or ones with an error) cause IRQs.
* at least immediately; use DI=6 in case any control request is
- * tempted to die part way through.
+ * tempted to die part way through. (and to force the hc to flush
+ * its donelist soonish, even on unlink paths.)
*
* NOTE: could delay interrupts even for the last TD, and get fewer
* interrupts ... increasing per-urb latency by sharing interrupts.
u32 *prev;
/* only take off EDs that the HC isn't using, accounting for
- * frame counter wraps.
+ * frame counter wraps and EDs with partially retired TDs
*/
- if (tick_before (tick, ed->tick)
- && HCD_IS_RUNNING(ohci->hcd.state)) {
- last = &ed->ed_next;
- continue;
+ if (likely (HCD_IS_RUNNING(ohci->hcd.state))) {
+ if (tick_before (tick, ed->tick)) {
+skip_ed:
+ last = &ed->ed_next;
+ continue;
+ }
+
+ if (!list_empty (&ed->td_list)) {
+ struct td *td;
+ u32 head;
+
+ td = list_entry (ed->td_list.next, struct td,
+ td_list);
+ head = cpu_to_le32 (ed->hwHeadP) & TD_MASK;
+
+ /* INTR_WDH may need to clean up first */
+ if (td->td_dma != head)
+ goto skip_ed;
+ }
}
/* reentrancy: if we drop the schedule lock, someone might
struct ohci_hcca {
#define NUM_INTS 32
__u32 int_table [NUM_INTS]; /* periodic schedule */
- __u16 frame_no; /* current frame number */
- __u16 pad1; /* set to 0 on each frame_no change */
+
+ /*
+ * OHCI defines u16 frame_no, followed by u16 zero pad.
+ * Since some processors can't do 16 bit bus accesses,
+ * portable access must be a 32 bit byteswapped access.
+ */
+ u32 frame_no; /* current frame number */
+#define OHCI_FRAME_NO(hccap) ((u16)le32_to_cpup(&(hccap)->frame_no))
__u32 done_head; /* info returned for an interrupt */
u8 reserved_for_hc [116];
u8 what [4]; /* spec only identifies 252 bytes :) */
* Visioneer scanners.
* - Added test for USB_CLASS_CDC_DATA which is used by some fingerprint scanners.
*
+ * 0.4.16 2003-11-04
+ * - Added vendor/product ids for Epson, Genius, Microtek, Plustek, Reflecta, and
+ * Visioneer scanners. Removed ids for HP PSC devices as these are supported by
+ * the hpoj userspace driver.
*
* TODO
* - Performance
// #define DEBUG
-#define DRIVER_VERSION "0.4.15"
+#define DRIVER_VERSION "0.4.16"
#define DRIVER_DESC "USB Scanner Driver"
#include <linux/usb.h>
{ USB_DEVICE(0x0458, 0x2015) }, /* ColorPage HR7LE */
{ USB_DEVICE(0x0458, 0x2016) }, /* ColorPage HR6X */
{ USB_DEVICE(0x0458, 0x2018) }, /* ColorPage HR7X */
+ { USB_DEVICE(0x0458, 0x201b) }, /* Colorpage Vivid 4x */
/* Hewlett Packard */
+ /* IMPORTANT: Hewlett-Packard multi-function peripherals (OfficeJet,
+ Printer/Scanner/Copier (PSC), LaserJet, or PhotoSmart printer)
+ should not be added to this table because they are accessed by a
+ userspace driver (hpoj) */
{ USB_DEVICE(0x03f0, 0x0101) }, /* ScanJet 4100C */
{ USB_DEVICE(0x03f0, 0x0102) }, /* PhotoSmart S20 */
{ USB_DEVICE(0x03f0, 0x0105) }, /* ScanJet 4200C */
{ USB_DEVICE(0x03F0, 0x1105) }, /* ScanJet 5470C */
{ USB_DEVICE(0x03f0, 0x1205) }, /* ScanJet 5550C */
{ USB_DEVICE(0x03f0, 0x1305) }, /* Scanjet 4570c */
- { USB_DEVICE(0x03f0, 0x1411) }, /* PSC 750 */
+ // { USB_DEVICE(0x03f0, 0x1411) }, /* PSC 750 - NOT SUPPORTED - use hpoj userspace driver */
{ USB_DEVICE(0x03f0, 0x2005) }, /* ScanJet 3570c */
{ USB_DEVICE(0x03f0, 0x2205) }, /* ScanJet 3500c */
- { USB_DEVICE(0x03f0, 0x2f11) }, /* PSC 1210 */
+ // { USB_DEVICE(0x03f0, 0x2f11) }, /* PSC 1210 - NOT SUPPORTED - use hpoj userspace driver */
/* Lexmark */
{ USB_DEVICE(0x043d, 0x002d) }, /* X70/X73 */
{ USB_DEVICE(0x043d, 0x003d) }, /* X83 */
{ USB_DEVICE(0x05da, 0x30ce) }, /* ScanMaker 3800 */
{ USB_DEVICE(0x05da, 0x30cf) }, /* ScanMaker 4800 */
{ USB_DEVICE(0x05da, 0x30d4) }, /* ScanMaker 3830 + 3840 */
+ { USB_DEVICE(0x05da, 0x30d8) }, /* ScanMaker 5900 */
{ USB_DEVICE(0x04a7, 0x0224) }, /* Scanport 3000 (actually Visioneer?)*/
/* The following SCSI-over-USB Microtek devices are supported by the
microtek driver: Enable SCSI and USB Microtek in kernel config */
{ USB_DEVICE(0x07b3, 0x0400) }, /* OpticPro 1248U */
{ USB_DEVICE(0x07b3, 0x0401) }, /* OpticPro 1248U (another one) */
{ USB_DEVICE(0x07b3, 0x0403) }, /* U16B */
+ { USB_DEVICE(0x07b3, 0x0413) }, /* OpticSlim 1200 */
/* Primax/Colorado */
{ USB_DEVICE(0x0461, 0x0300) }, /* G2-300 #1 */
{ USB_DEVICE(0x0461, 0x0301) }, /* G2E-300 #1 */
{ USB_DEVICE(0x0461, 0x0383) }, /* G2E-600 */
/* Prolink */
{ USB_DEVICE(0x06dc, 0x0014) }, /* Winscan Pro 2448U */
+ /* Reflecta */
+ { USB_DEVICE(0x05e3, 0x0120) }, /* iScan 1800 */
/* Relisis */
// { USB_DEVICE(0x0475, 0x0103) }, /* Episode - undetected endpoint */
{ USB_DEVICE(0x0475, 0x0210) }, /* Scorpio Ultra 3 */
{ USB_DEVICE(0x04b8, 0x011c) }, /* Perfection 3200 */
{ USB_DEVICE(0x04b8, 0x011d) }, /* Perfection 1260 */
{ USB_DEVICE(0x04b8, 0x011e) }, /* Perfection 1660 Photo */
+ { USB_DEVICE(0x04b8, 0x011f) }, /* Perfection 1670 */
{ USB_DEVICE(0x04b8, 0x0801) }, /* Stylus CX5200 */
{ USB_DEVICE(0x04b8, 0x0802) }, /* Stylus CX3200 */
/* Siemens */
{ USB_DEVICE(0x04a7, 0x0221) }, /* OneTouch 5300 USB */
{ USB_DEVICE(0x04a7, 0x0224) }, /* OneTouch 4800 USB */
{ USB_DEVICE(0x04a7, 0x0226) }, /* OneTouch 5800 USB */
+ { USB_DEVICE(0x04a7, 0x0229) }, /* OneTouch 7100 USB */
{ USB_DEVICE(0x04a7, 0x022c) }, /* OneTouch 9020 USB */
{ USB_DEVICE(0x04a7, 0x0231) }, /* 6100 USB */
{ USB_DEVICE(0x04a7, 0x0311) }, /* 6200 EPP/USB */
To compile this driver as a module, choose M here: the
module will be called stv680.
+config USB_W9968CF
+ tristate "USB W996[87]CF JPEG Dual Mode Camera support"
+ depends on USB && VIDEO_DEV && I2C
+ ---help---
+ Say Y here if you want support for cameras based on
+ Winbond W9967CF/W9968CF JPEG USB Dual Mode Camera Chips.
+
+ This driver has an optional plugin, which is distributed as a
+ separate module only (released under GPL). It contains code that
+ allows you to use higher resolutions and framerates, and can't
+ be included into the official Linux kernel for performance
+ purposes.
+ At the moment the driver needs a third-part module for the CMOS
+ sensors, which is available on internet: it is recommended to read
+ <file:Documentation/usb/w9968cf.txt> for more informations and for
+ a list of supported cameras.
+
+ This driver uses the Video For Linux and the I2C APIs.
+ You must say Y or M to both "Video For Linux" and
+ "I2C Support" to use this driver.
+ Information on this API and pointers to "v4l" programs may be found
+ on the WWW at <http://roadrunner.swansea.uk.linux.org/v4l.shtml>.
+
+ This code is also available as a module ( = code which can be
+ inserted in and removed from the running kernel whenever you want).
+ The module will be called w9968cf.o. If you want to compile it as a
+ module, say M here and read <file:Documentation/modules.txt>.
obj-$(CONFIG_USB_SE401) += se401.o
obj-$(CONFIG_USB_STV680) += stv680.o
obj-$(CONFIG_USB_VICAM) += vicam.o usbvideo.o
+obj-$(CONFIG_USB_W9968CF) += w9968cf.o
--- /dev/null
+/***************************************************************************
+ * Video4Linux driver for W996[87]CF JPEG USB Dual Mode Camera Chip. *
+ * *
+ * Copyright (C) 2002 2003 by Luca Risolia <luca_ing@libero.it> *
+ * *
+ * - Memory management code from bttv driver by Ralph Metzler, *
+ * Marcus Metzler and Gerd Knorr. *
+ * - I2C interface to kernel, high-level CMOS sensor control routines and *
+ * some symbolic names from OV511 driver by Mark W. McClelland. *
+ * - Low-level I2C fast write function by Piotr Czerczak. *
+ * - Low-level I2C read function by Frédéric Jouault. *
+ * *
+ * 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 <linux/version.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/vmalloc.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/ctype.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/ioctl.h>
+#include <linux/delay.h>
+#include <asm/page.h>
+#include <asm/uaccess.h>
+
+#include "w9968cf.h"
+#include "w9968cf_decoder.h"
+
+
+
+/****************************************************************************
+ * Modules paramaters *
+ ****************************************************************************/
+
+static u8 vppmod_load = W9968CF_VPPMOD_LOAD;
+static u8 simcams = W9968CF_SIMCAMS;
+static int video_nr[]={[0 ... W9968CF_MAX_DEVICES-1] = -1}; /* -1=first free */
+static u16 packet_size[] = {[0 ... W9968CF_MAX_DEVICES-1]=W9968CF_PACKET_SIZE};
+static u8 max_buffers[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_BUFFERS};
+static u8 double_buffer[] = {[0 ... W9968CF_MAX_DEVICES-1] =
+ W9968CF_DOUBLE_BUFFER};
+static u8 clamping[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_CLAMPING};
+static u8 filter_type[]= {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_FILTER_TYPE};
+static u8 largeview[]= {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_LARGEVIEW};
+static u8 decompression[] = {[0 ... W9968CF_MAX_DEVICES-1] =
+ W9968CF_DECOMPRESSION};
+static u8 upscaling[]= {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_UPSCALING};
+static u8 force_palette[] = {[0 ... W9968CF_MAX_DEVICES-1] = 0};
+static u8 force_rgb[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_FORCE_RGB};
+static u8 autobright[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_AUTOBRIGHT};
+static u8 autoexp[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_AUTOEXP};
+static u8 lightfreq[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_LIGHTFREQ};
+static u8 bandingfilter[] = {[0 ... W9968CF_MAX_DEVICES-1]=
+ W9968CF_BANDINGFILTER};
+static int clockdiv[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_CLOCKDIV};
+static u8 backlight[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_BACKLIGHT};
+static u8 mirror[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_MIRROR};
+static u8 sensor_mono[] = {[0 ... W9968CF_MAX_DEVICES-1]=W9968CF_SENSOR_MONO};
+static u16 brightness[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_BRIGHTNESS};
+static u16 hue[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_HUE};
+static u16 colour[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_COLOUR};
+static u16 contrast[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_CONTRAST};
+static u16 whiteness[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_WHITENESS};
+#ifdef W9968CF_DEBUG
+static u8 debug = W9968CF_DEBUG_LEVEL;
+static u8 specific_debug = W9968CF_SPECIFIC_DEBUG;
+#endif
+
+MODULE_AUTHOR("Luca Risolia <luca_ing@libero.it>");
+
+MODULE_DESCRIPTION("Video4Linux driver for "
+ "W996[87]CF JPEG USB Dual Mode Camera Chip");
+
+MODULE_SUPPORTED_DEVICE("Video");
+
+MODULE_LICENSE("GPL");
+
+MODULE_PARM(vppmod_load, "i");
+MODULE_PARM(simcams, "i");
+MODULE_PARM(video_nr, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "i");
+MODULE_PARM(packet_size, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "i");
+MODULE_PARM(max_buffers, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "i");
+MODULE_PARM(double_buffer, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "i");
+MODULE_PARM(clamping, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "i");
+MODULE_PARM(filter_type, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "i");
+MODULE_PARM(largeview, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "i");
+MODULE_PARM(decompression, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "i");
+MODULE_PARM(upscaling, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "i");
+MODULE_PARM(force_palette, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "i");
+MODULE_PARM(force_rgb, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "i");
+MODULE_PARM(autobright, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "l");
+MODULE_PARM(autoexp, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "l");
+MODULE_PARM(lightfreq, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "l");
+MODULE_PARM(bandingfilter, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "l");
+MODULE_PARM(clockdiv, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "l");
+MODULE_PARM(backlight, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "l");
+MODULE_PARM(mirror, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "l");
+MODULE_PARM(sensor_mono, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "l");
+MODULE_PARM(brightness, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "l");
+MODULE_PARM(hue, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "l");
+MODULE_PARM(colour, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "l");
+MODULE_PARM(contrast, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "l");
+MODULE_PARM(whiteness, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "l");
+#ifdef W9968CF_DEBUG
+MODULE_PARM(debug, "i");
+MODULE_PARM(specific_debug, "i");
+#endif
+
+MODULE_PARM_DESC(vppmod_load,
+ "\n<0|1> Automatic 'w9968cf-vpp' module loading."
+ "\n0 disable, 1 enable."
+ "\nIf enabled, every time an application attempts to open a"
+ "\ncamera, 'insmod' searches for the video post-processing"
+ "\nmodule in the system and loads it automatically (if"
+ "\npresent). The 'w9968cf-vpp' module adds extra image"
+ "\nmanipulation functions to the 'w9968cf' module, like"
+ "\nsoftware up-scaling,colour conversions and video decoding."
+ "\nDefault value is "__MODULE_STRING(W9968CF_VPPMOD_LOAD)"."
+ "\n");
+MODULE_PARM_DESC(simcams,
+ "\n<n> Number of cameras allowed to stream simultaneously."
+ "\nn may vary from 0 to "
+ __MODULE_STRING(W9968CF_MAX_DEVICES)"."
+ "\nDefault value is "__MODULE_STRING(W9968CF_SIMCAMS)"."
+ "\n");
+MODULE_PARM_DESC(video_nr,
+ "\n<-1|n[,...]> Specify V4L minor mode number."
+ "\n -1 = use next available (default)"
+ "\n n = use minor number n (integer >= 0)"
+ "\nYou can specify " __MODULE_STRING(W9968CF_MAX_DEVICES)
+ " cameras this way."
+ "\nFor example:"
+ "\nvideo_nr=-1,2,-1 would assign minor number 2 to"
+ "\nthe second camera and use auto for the first"
+ "\none and for every other camera."
+ "\n");
+MODULE_PARM_DESC(packet_size,
+ "\n<n[,...]> Specify the maximum data payload"
+ "\nsize in bytes for alternate settings, for each device."
+ "\nn is scaled between 63 and 1023 "
+ "(default is "__MODULE_STRING(W9968CF_PACKET_SIZE)")."
+ "\n");
+MODULE_PARM_DESC(max_buffers,
+ "\n<n[,...]> Only for advanced users."
+ "\nSpecify the maximum number of video frame buffers"
+ "\nto allocate for each device, from 2 to "
+ __MODULE_STRING(W9968CF_MAX_BUFFERS)
+ ". (default is "__MODULE_STRING(W9968CF_BUFFERS)")."
+ "\n");
+MODULE_PARM_DESC(double_buffer,
+ "\n<0|1[,...]> "
+ "Hardware double buffering: 0 disabled, 1 enabled."
+ "\nIt should be enabled if you want smooth video output: if"
+ "\nyou obtain out of sync. video, disable it at all, or try"
+ "\nto decrease the 'clockdiv' module paramater value."
+ "\nDefault value is "__MODULE_STRING(W9968CF_DOUBLE_BUFFER)
+ " for every device."
+ "\n");
+MODULE_PARM_DESC(clamping,
+ "\n<0|1[,...]> Video data clamping: 0 disabled, 1 enabled."
+ "\nDefault value is "__MODULE_STRING(W9968CF_CLAMPING)
+ " for every device."
+ "\n");
+MODULE_PARM_DESC(filter_type,
+ "\n<0|1|2[,...]> Video filter type."
+ "\n0 none, 1 (1-2-1) 3-tap filter, "
+ "2 (2-3-6-3-2) 5-tap filter."
+ "\nDefault value is "__MODULE_STRING(W9968CF_FILTER_TYPE)
+ " for every device."
+ "\nThe filter is used to reduce noise and aliasing artifacts"
+ "\nproduced by the CCD or CMOS sensor, and the scaling"
+ " process."
+ "\n");
+MODULE_PARM_DESC(largeview,
+ "\n<0|1[,...]> Large view: 0 disabled, 1 enabled."
+ "\nDefault value is "__MODULE_STRING(W9968CF_LARGEVIEW)
+ " for every device."
+ "\n");
+MODULE_PARM_DESC(upscaling,
+ "\n<0|1[,...]> Software scaling (for non-compressed video):"
+ "\n0 disabled, 1 enabled."
+ "\nDisable it if you have a slow CPU or you don't have"
+ " enough memory."
+ "\nDefault value is "__MODULE_STRING(W9968CF_UPSCALING)
+ " for every device."
+ "\nIf 'w9968cf-vpp' is not loaded, this paramater is"
+ " set to 0.");
+MODULE_PARM_DESC(decompression,
+ "\n<0|1|2[,...]> Software video decompression:"
+ "\n- 0 disables decompression (doesn't allow formats needing"
+ " decompression)"
+ "\n- 1 forces decompression (allows formats needing"
+ " decompression only);"
+ "\n- 2 allows any permitted formats."
+ "\nFormats supporting compressed video are YUV422P and"
+ " YUV420P/YUV420 "
+ "\nin any resolutions where both width and height are "
+ "a multiple of 16."
+ "\nDefault value is "__MODULE_STRING(W9968CF_DECOMPRESSION)
+ " for every device."
+ "\nIf 'w9968cf-vpp' is not loaded, forcing decompression is "
+ "\nnot allowed; in this case this paramater is set to 2.");
+MODULE_PARM_DESC(force_palette,
+ "\n<0"
+ "|" __MODULE_STRING(VIDEO_PALETTE_UYVY)
+ "|" __MODULE_STRING(VIDEO_PALETTE_YUV420)
+ "|" __MODULE_STRING(VIDEO_PALETTE_YUV422P)
+ "|" __MODULE_STRING(VIDEO_PALETTE_YUV420P)
+ "|" __MODULE_STRING(VIDEO_PALETTE_YUYV)
+ "|" __MODULE_STRING(VIDEO_PALETTE_YUV422)
+ "|" __MODULE_STRING(VIDEO_PALETTE_GREY)
+ "|" __MODULE_STRING(VIDEO_PALETTE_RGB555)
+ "|" __MODULE_STRING(VIDEO_PALETTE_RGB565)
+ "|" __MODULE_STRING(VIDEO_PALETTE_RGB24)
+ "|" __MODULE_STRING(VIDEO_PALETTE_RGB32)
+ "[,...]>"
+ " Force picture palette."
+ "\nIn order:"
+ "\n- 0 allows any of the following formats:"
+ "\n- UYVY 16 bpp - Original video, compression disabled"
+ "\n- YUV420 12 bpp - Original video, compression enabled"
+ "\n- YUV422P 16 bpp - Original video, compression enabled"
+ "\n- YUV420P 12 bpp - Original video, compression enabled"
+ "\n- YUVY 16 bpp - Software conversion from UYVY"
+ "\n- YUV422 16 bpp - Software conversion from UYVY"
+ "\n- GREY 8 bpp - Software conversion from UYVY"
+ "\n- RGB555 16 bpp - Software conversion from UYVY"
+ "\n- RGB565 16 bpp - Software conversion from UYVY"
+ "\n- RGB24 24 bpp - Software conversion from UYVY"
+ "\n- RGB32 32 bpp - Software conversion from UYVY"
+ "\nWhen not 0, this paramater will override 'decompression'."
+ "\nDefault value is 0 for every device."
+ "\nInitial palette is "
+ __MODULE_STRING(W9968CF_PALETTE_DECOMP_ON)"."
+ "\nIf 'w9968cf-vpp' is not loaded, this paramater is"
+ " set to 9 (UYVY).");
+MODULE_PARM_DESC(force_rgb,
+ "\n<0|1[,...]> Read RGB video data instead of BGR:"
+ "\n 1 = use RGB component ordering."
+ "\n 0 = use BGR component ordering."
+ "\nThis parameter has effect when using RGBX palettes only."
+ "\nDefault value is "__MODULE_STRING(W9968CF_FORCE_RGB)
+ " for every device."
+ "\n");
+MODULE_PARM_DESC(autobright,
+ "\n<0|1[,...]> CMOS sensor automatically changes brightness:"
+ "\n 0 = no, 1 = yes"
+ "\nDefault value is "__MODULE_STRING(W9968CF_AUTOBRIGHT)
+ " for every device."
+ "\n");
+MODULE_PARM_DESC(autoexp,
+ "\n<0|1[,...]> CMOS sensor automatically changes exposure:"
+ "\n 0 = no, 1 = yes"
+ "\nDefault value is "__MODULE_STRING(W9968CF_AUTOEXP)
+ " for every device."
+ "\n");
+MODULE_PARM_DESC(lightfreq,
+ "\n<50|60[,...]> Light frequency in Hz:"
+ "\n 50 for European and Asian lighting,"
+ " 60 for American lighting."
+ "\nDefault value is "__MODULE_STRING(W9968CF_LIGHTFREQ)
+ " for every device."
+ "\n");
+MODULE_PARM_DESC(bandingfilter,
+ "\n<0|1[,...]> Banding filter to reduce effects of"
+ " fluorescent lighting:"
+ "\n 0 disabled, 1 enabled."
+ "\nThis filter tries to reduce the pattern of horizontal"
+ "\nlight/dark bands caused by some (usually fluorescent)"
+ " lighting."
+ "\nDefault value is "__MODULE_STRING(W9968CF_BANDINGFILTER)
+ " for every device."
+ "\n");
+MODULE_PARM_DESC(clockdiv,
+ "\n<-1|n[,...]> "
+ "Force pixel clock divisor to a specific value (for experts):"
+ "\n n may vary from 0 to 127."
+ "\n -1 for automatic value."
+ "\nSee also the 'double_buffer' module paramater."
+ "\nDefault value is "__MODULE_STRING(W9968CF_CLOCKDIV)
+ " for every device."
+ "\n");
+MODULE_PARM_DESC(backlight,
+ "\n<0|1[,...]> Objects are lit from behind:"
+ "\n 0 = no, 1 = yes"
+ "\nDefault value is "__MODULE_STRING(W9968CF_BACKLIGHT)
+ " for every device."
+ "\n");
+MODULE_PARM_DESC(mirror,
+ "\n<0|1[,...]> Reverse image horizontally:"
+ "\n 0 = no, 1 = yes"
+ "\nDefault value is "__MODULE_STRING(W9968CF_MIRROR)
+ " for every device."
+ "\n");
+MODULE_PARM_DESC(sensor_mono,
+ "\n<0|1[,...]> The OV CMOS sensor is monochrome:"
+ "\n 0 = no, 1 = yes"
+ "\nDefault value is "__MODULE_STRING(W9968CF_SENSOR_MONO)
+ " for every device."
+ "\n");
+MODULE_PARM_DESC(brightness,
+ "\n<n[,...]> Set picture brightness (0-65535)."
+ "\nDefault value is "__MODULE_STRING(W9968CF_BRIGHTNESS)
+ " for every device."
+ "\nThis parameter has no effect if 'autobright' is enabled."
+ "\n");
+MODULE_PARM_DESC(hue,
+ "\n<n[,...]> Set picture hue (0-65535)."
+ "\nDefault value is "__MODULE_STRING(W9968CF_HUE)
+ " for every device."
+ "\n");
+MODULE_PARM_DESC(colour,
+ "\n<n[,...]> Set picture saturation (0-65535)."
+ "\nDefault value is "__MODULE_STRING(W9968CF_COLOUR)
+ " for every device."
+ "\n");
+MODULE_PARM_DESC(contrast,
+ "\n<n[,...]> Set picture contrast (0-65535)."
+ "\nDefault value is "__MODULE_STRING(W9968CF_CONTRAST)
+ " for every device."
+ "\n");
+MODULE_PARM_DESC(whiteness,
+ "\n<n[,...]> Set picture whiteness (0-65535)."
+ "\nDefault value is "__MODULE_STRING(W9968CF_WHITENESS)
+ " for every device."
+ "\n");
+#ifdef W9968CF_DEBUG
+MODULE_PARM_DESC(debug,
+ "\n<n> Debugging information level, from 0 to 6:"
+ "\n0 = none (be cautious)"
+ "\n1 = critical errors"
+ "\n2 = significant informations"
+ "\n3 = configuration or general messages"
+ "\n4 = warnings"
+ "\n5 = called functions"
+ "\n6 = function internals"
+ "\nLevel 5 and 6 are useful for testing only, when just "
+ "one device is used."
+ "\nDefault value is "__MODULE_STRING(W9968CF_DEBUG_LEVEL)"."
+ "\n");
+MODULE_PARM_DESC(specific_debug,
+ "\n<0|1> Enable or disable specific debugging messages:"
+ "\n0 = print messages concerning every level"
+ " <= 'debug' level."
+ "\n1 = print messages concerning the level"
+ " indicated by 'debug'."
+ "\nDefault value is "
+ __MODULE_STRING(W9968CF_SPECIFIC_DEBUG)"."
+ "\n");
+#endif /* W9968CF_DEBUG */
+
+
+
+/****************************************************************************
+ * Some prototypes *
+ ****************************************************************************/
+
+/* Video4linux interface */
+static struct file_operations w9968cf_fops;
+static int w9968cf_open(struct inode*, struct file*);
+static int w9968cf_release(struct inode*, struct file*);
+static ssize_t w9968cf_read(struct file*, char*, size_t, loff_t*);
+static int w9968cf_mmap(struct file*, struct vm_area_struct*);
+static int w9968cf_ioctl(struct inode*, struct file*,
+ unsigned int, unsigned long);
+static int w9968cf_do_ioctl(struct w9968cf_device*, unsigned int, void*);
+
+/* USB-specific */
+static void w9968cf_urb_complete(struct urb *urb, struct pt_regs *regs);
+static int w9968cf_start_transfer(struct w9968cf_device*);
+static int w9968cf_stop_transfer(struct w9968cf_device*);
+static int w9968cf_write_reg(struct w9968cf_device*, u16 value, u16 index);
+static int w9968cf_read_reg(struct w9968cf_device*, u16 index);
+static int w9968cf_write_fsb(struct w9968cf_device*, u16* data);
+static int w9968cf_write_sb(struct w9968cf_device*, u16 value);
+static int w9968cf_read_sb(struct w9968cf_device*);
+static int w9968cf_upload_quantizationtables(struct w9968cf_device*);
+
+/* Low-level I2C (SMBus) I/O */
+static int w9968cf_smbus_start(struct w9968cf_device*);
+static int w9968cf_smbus_stop(struct w9968cf_device*);
+static int w9968cf_smbus_write_byte(struct w9968cf_device*, u8 v);
+static int w9968cf_smbus_read_byte(struct w9968cf_device*, u8* v);
+static int w9968cf_smbus_write_ack(struct w9968cf_device*);
+static int w9968cf_smbus_read_ack(struct w9968cf_device*);
+static int w9968cf_i2c_adap_read_byte(struct w9968cf_device* cam,
+ u16 address, u8* value);
+static int w9968cf_i2c_adap_read_byte_data(struct w9968cf_device*, u16 address,
+ u8 subaddress, u8* value);
+static int w9968cf_i2c_adap_write_byte(struct w9968cf_device*,
+ u16 address, u8 subaddress);
+static int w9968cf_i2c_adap_fastwrite_byte_data(struct w9968cf_device*,
+ u16 address, u8 subaddress,
+ u8 value);
+
+/* I2C interface to kernel */
+static int w9968cf_i2c_init(struct w9968cf_device*);
+static int w9968cf_i2c_smbus_xfer(struct i2c_adapter*, u16 addr,
+ unsigned short flags, char read_write,
+ u8 command, int size, union i2c_smbus_data*);
+static u32 w9968cf_i2c_func(struct i2c_adapter*);
+static int w9968cf_i2c_attach_inform(struct i2c_client*);
+static int w9968cf_i2c_detach_inform(struct i2c_client*);
+static int w9968cf_i2c_control(struct i2c_adapter*, unsigned int cmd,
+ unsigned long arg);
+
+/* Memory management */
+static inline unsigned long kvirt_to_pa(unsigned long adr);
+static void* rvmalloc(unsigned long size);
+static void rvfree(void *mem, unsigned long size);
+static void w9968cf_deallocate_memory(struct w9968cf_device*);
+static int w9968cf_allocate_memory(struct w9968cf_device*);
+static inline unsigned long w9968cf_get_max_bufsize(struct w9968cf_device*);
+
+/* High-level CMOS sensor control functions */
+static int w9968cf_sensor_set_control(struct w9968cf_device*,int cid,int val);
+static int w9968cf_sensor_get_control(struct w9968cf_device*,int cid,int *val);
+static inline int w9968cf_sensor_cmd(struct w9968cf_device*,
+ unsigned int cmd, void *arg);
+static void w9968cf_sensor_configure(struct w9968cf_device*);
+static int w9968cf_sensor_change_settings(struct w9968cf_device*);
+static int w9968cf_sensor_get_picture(struct w9968cf_device*,
+ struct video_picture*);
+static int w9968cf_sensor_set_picture(struct w9968cf_device*,
+ struct video_picture pict);
+
+/* Other helper functions */
+static void w9968cf_configure_camera(struct w9968cf_device*,struct usb_device*,
+ enum w9968cf_model_id,
+ const unsigned short dev_nr);
+static int w9968cf_turn_on_led(struct w9968cf_device*);
+static int w9968cf_init_chip(struct w9968cf_device*);
+static int w9968cf_set_picture(struct w9968cf_device*, struct video_picture);
+static int w9968cf_set_window(struct w9968cf_device*, struct video_window);
+static inline u16 w9968cf_valid_palette(u16 palette);
+static inline u16 w9968cf_valid_depth(u16 palette);
+static inline u8 w9968cf_need_decompression(u16 palette);
+static int w9968cf_postprocess_frame(struct w9968cf_device*,
+ struct w9968cf_frame_t*);
+static int w9968cf_adjust_window_size(struct w9968cf_device*, u16* w, u16* h);
+static void w9968cf_init_framelist(struct w9968cf_device*);
+static void w9968cf_push_frame(struct w9968cf_device*, u8 f_num);
+static void w9968cf_pop_frame(struct w9968cf_device*,struct w9968cf_frame_t**);
+static void w9968cf_release_resources(struct w9968cf_device*);
+
+/* Intermodule communication */
+static int w9968cf_vppmod_detect(void);
+static void w9968cf_vppmod_release(void);
+
+/* Pointers to registered video post-processing functions */
+static void (*w9968cf_vpp_init_decoder)(void);
+static int (*w9968cf_vpp_check_headers)(const unsigned char*,
+ const unsigned long);
+static int (*w9968cf_vpp_decode)(const char*, const unsigned,
+ const unsigned, const unsigned, char*);
+static void (*w9968cf_vpp_swap_yuvbytes)(void*, unsigned long);
+static void (*w9968cf_vpp_uyvy_to_rgbx)(u8*, unsigned long, u8*, u16, u8);
+static void (*w9968cf_vpp_scale_up)(u8*, u8*, u16, u16, u16, u16, u16);
+
+
+
+/****************************************************************************
+ * Symbolic names *
+ ****************************************************************************/
+
+/* Used to represent a list of values and their respective symbolic names */
+struct w9968cf_symbolic_list {
+ const int num;
+ const char *name;
+};
+
+/*--------------------------------------------------------------------------
+ Returns the name of the matching element in the symbolic_list array. The
+ end of the list must be marked with an element that has a NULL name.
+ --------------------------------------------------------------------------*/
+static inline const char *
+symbolic(struct w9968cf_symbolic_list list[], const int num)
+{
+ int i;
+
+ for (i = 0; list[i].name != NULL; i++)
+ if (list[i].num == num)
+ return (list[i].name);
+
+ return "Unknown";
+}
+
+static struct w9968cf_symbolic_list camlist[] = {
+ { W9968CF_MOD_GENERIC, "W996[87]CF JPEG USB Dual Mode Camera" },
+ { W9968CF_MOD_CLVBWGP, "Creative Labs Video Blaster WebCam Go Plus" },
+
+ /* Other cameras (having the same descriptors as Generic W996[87]CF) */
+ { W9968CF_MOD_ADPA5R, "Aroma Digi Pen ADG-5000 Refurbished" },
+ { W9986CF_MOD_AU, "AVerTV USB" },
+ { W9968CF_MOD_CLVBWG, "Creative Labs Video Blaster WebCam Go" },
+ { W9968CF_MOD_DLLDK, "Die Lebon LDC-D35A Digital Kamera" },
+ { W9968CF_MOD_EEEMC, "Ezonics EZ-802 EZMega Cam" },
+ { W9968CF_MOD_ODPVDMPC, "OPCOM Digi Pen VGA Dual Mode Pen Camera" },
+
+ { -1, NULL }
+};
+
+static struct w9968cf_symbolic_list senlist[] = {
+ { CC_OV76BE, "OV76BE" },
+ { CC_OV7610, "OV7610" },
+ { CC_OV7620, "OV7620" },
+ { CC_OV7620AE, "OV7620AE" },
+ { CC_OV6620, "OV6620" },
+ { CC_OV6630, "OV6630" },
+ { CC_OV6630AE, "OV6630AE" },
+ { CC_OV6630AF, "OV6630AF" },
+ { -1, NULL }
+};
+
+/* Video4Linux1 palettes */
+static struct w9968cf_symbolic_list v4l1_plist[] = {
+ { VIDEO_PALETTE_GREY, "GREY" },
+ { VIDEO_PALETTE_HI240, "HI240" },
+ { VIDEO_PALETTE_RGB565, "RGB565" },
+ { VIDEO_PALETTE_RGB24, "RGB24" },
+ { VIDEO_PALETTE_RGB32, "RGB32" },
+ { VIDEO_PALETTE_RGB555, "RGB555" },
+ { VIDEO_PALETTE_YUV422, "YUV422" },
+ { VIDEO_PALETTE_YUYV, "YUYV" },
+ { VIDEO_PALETTE_UYVY, "UYVY" },
+ { VIDEO_PALETTE_YUV420, "YUV420" },
+ { VIDEO_PALETTE_YUV411, "YUV411" },
+ { VIDEO_PALETTE_RAW, "RAW" },
+ { VIDEO_PALETTE_YUV422P, "YUV422P" },
+ { VIDEO_PALETTE_YUV411P, "YUV411P" },
+ { VIDEO_PALETTE_YUV420P, "YUV420P" },
+ { VIDEO_PALETTE_YUV410P, "YUV410P" },
+ { -1, NULL }
+};
+
+/* Decoder error codes: */
+static struct w9968cf_symbolic_list decoder_errlist[] = {
+ { W9968CF_DEC_ERR_CORRUPTED_DATA, "Corrupted data" },
+ { W9968CF_DEC_ERR_BUF_OVERFLOW, "Buffer overflow" },
+ { W9968CF_DEC_ERR_NO_SOI, "SOI marker not found" },
+ { W9968CF_DEC_ERR_NO_SOF0, "SOF0 marker not found" },
+ { W9968CF_DEC_ERR_NO_SOS, "SOS marker not found" },
+ { W9968CF_DEC_ERR_NO_EOI, "EOI marker not found" },
+ { -1, NULL }
+};
+
+/* URB error codes: */
+static struct w9968cf_symbolic_list urb_errlist[] = {
+ { -ENOMEM, "No memory for allocation of internal structures" },
+ { -ENOSPC, "The host controller's bandwidth is already consumed" },
+ { -ENOENT, "URB was canceled by unlink_urb" },
+ { -EXDEV, "ISO transfer only partially completed" },
+ { -EAGAIN, "Too match scheduled for the future" },
+ { -ENXIO, "URB already queued" },
+ { -EFBIG, "Too much ISO frames requested" },
+ { -ENOSR, "Buffer error (overrun)" },
+ { -EPIPE, "Specified endpoint is stalled (device not responding)"},
+ { -EOVERFLOW, "Babble (bad cable?)" },
+ { -EPROTO, "Bit-stuff error (bad cable?)" },
+ { -EILSEQ, "CRC/Timeout" },
+ { -ETIMEDOUT, "NAK (device does not respond)" },
+ { -1, NULL }
+};
+
+
+
+/****************************************************************************
+ * Memory management functions *
+ ****************************************************************************/
+
+/* Shameless copy from bttv-driver.c */
+
+/* Here we want the physical address of the memory.
+ This is used when initializing the contents of the area. */
+static inline unsigned long kvirt_to_pa(unsigned long adr)
+{
+ unsigned long kva, ret;
+
+ kva = (unsigned long) page_address(vmalloc_to_page((void *)adr));
+ kva |= adr & (PAGE_SIZE-1); /* restore the offset */
+ ret = __pa(kva);
+ return ret;
+}
+
+
+static void* rvmalloc(unsigned long size)
+{
+ void* mem;
+ unsigned long adr;
+
+ size = PAGE_ALIGN(size);
+ mem = vmalloc_32(size);
+ if (!mem)
+ return NULL;
+
+ memset(mem, 0, size); /* Clear the ram out, no junk to the user */
+ adr = (unsigned long) mem;
+ while (size > 0) {
+ SetPageReserved(vmalloc_to_page((void *)adr));
+ adr += PAGE_SIZE;
+ size -= PAGE_SIZE;
+ }
+
+ return mem;
+}
+
+
+static void rvfree(void* mem, unsigned long size)
+{
+ unsigned long adr;
+
+ if (!mem)
+ return;
+
+ adr = (unsigned long) mem;
+ while ((long) size > 0) {
+ ClearPageReserved(vmalloc_to_page((void *)adr));
+ adr += PAGE_SIZE;
+ size -= PAGE_SIZE;
+ }
+ vfree(mem);
+}
+/* End of shameless copy */
+
+
+/*--------------------------------------------------------------------------
+ Return the maximum size (in bytes) of a frame buffer.
+ --------------------------------------------------------------------------*/
+static inline unsigned long w9968cf_get_max_bufsize(struct w9968cf_device* cam)
+{
+ u8 bpp = (w9968cf_vppmod_present) ? 4 : 2;
+ return (cam->upscaling) ? W9968CF_MAX_WIDTH*W9968CF_MAX_HEIGHT*bpp :
+ cam->maxwidth*cam->maxheight*bpp;
+}
+
+
+/*--------------------------------------------------------------------------
+ Deallocate previously allocated memory.
+ --------------------------------------------------------------------------*/
+static void w9968cf_deallocate_memory(struct w9968cf_device* cam)
+{
+ u8 i;
+
+ /* Free the isochronous transfer buffers */
+ for (i = 0; i < W9968CF_URBS; i++) {
+ kfree(cam->transfer_buffer[i]);
+ cam->transfer_buffer[i] = NULL;
+ }
+
+ /* Free temporary frame buffer */
+ if (cam->frame_tmp.buffer) {
+ rvfree(cam->frame_tmp.buffer, W9968CF_HW_BUF_SIZE);
+ cam->frame_tmp.buffer = NULL;
+ }
+
+ /* Free helper buffer */
+ if (cam->vpp_buffer) {
+ rvfree(cam->vpp_buffer, w9968cf_get_max_bufsize(cam));
+ cam->vpp_buffer = NULL;
+ }
+
+ /* Free video frame buffers */
+ if (cam->frame[0].buffer) {
+ rvfree(cam->frame[0].buffer,
+ cam->nbuffers * w9968cf_get_max_bufsize(cam));
+ cam->frame[0].buffer = NULL;
+ }
+
+ cam->nbuffers = 0;
+
+ DBG(5, "Memory successfully deallocated.")
+}
+
+
+/*--------------------------------------------------------------------------
+ Allocate memory buffers for USB transfers and video frames.
+ This function is called by open() only.
+ Return 0 on success, a negative number otherwise.
+ --------------------------------------------------------------------------*/
+static int w9968cf_allocate_memory(struct w9968cf_device* cam)
+{
+ const unsigned long bufsize = w9968cf_get_max_bufsize(cam);
+ const u16 p_size = wMaxPacketSize[cam->altsetting-1];
+ void* buff = NULL;
+ u8 i;
+
+ /* NOTE: Deallocation is done elsewhere in case of error */
+
+ /* Allocate memory for the isochronous transfer buffers */
+ for (i = 0; i < W9968CF_URBS; i++) {
+ if (!(cam->transfer_buffer[i] =
+ kmalloc(W9968CF_ISO_PACKETS*p_size, GFP_KERNEL))) {
+ DBG(1, "Couldn't allocate memory for the isochronous "
+ "transfer buffers (%d bytes).",
+ p_size * W9968CF_ISO_PACKETS)
+ return -ENOMEM;
+ }
+ memset(cam->transfer_buffer[i], 0, W9968CF_ISO_PACKETS*p_size);
+ }
+
+ /* Allocate memory for the temporary frame buffer */
+ if (!(cam->frame_tmp.buffer = rvmalloc(W9968CF_HW_BUF_SIZE))) {
+ DBG(1, "Couldn't allocate memory for the temporary "
+ "video frame buffer (%i bytes).", W9968CF_HW_BUF_SIZE)
+ return -ENOMEM;
+ }
+
+ /* Allocate memory for the helper buffer */
+ if (w9968cf_vppmod_present) {
+ if (!(cam->vpp_buffer = rvmalloc(bufsize))) {
+ DBG(1, "Couldn't allocate memory for the helper buffer"
+ " (%li bytes).", bufsize)
+ return -ENOMEM;
+ }
+ } else
+ cam->vpp_buffer = NULL;
+
+ /* Allocate memory for video frame buffers */
+ cam->nbuffers = cam->max_buffers;
+ while (cam->nbuffers >= 2) {
+ if ((buff = rvmalloc(cam->nbuffers * bufsize)))
+ break;
+ else
+ cam->nbuffers--;
+ }
+
+ if (!buff) {
+ DBG(1, "Couldn't allocate memory for the video frame buffers.")
+ cam->nbuffers = 0;
+ return -ENOMEM;
+ }
+
+ if (cam->nbuffers != cam->max_buffers)
+ DBG(2, "Couldn't allocate memory for %d video frame buffers. "
+ "Only memory for %d buffers has been allocated.",
+ cam->max_buffers, cam->nbuffers)
+
+ for (i = 0; i < cam->nbuffers; i++) {
+ cam->frame[i].buffer = buff + i*bufsize;
+ /* Circular list */
+ if (i != cam->nbuffers-1)
+ cam->frame[i].next = &cam->frame[i+1];
+ else
+ cam->frame[i].next = &cam->frame[0];
+ cam->frame[i].status = F_UNUSED;
+ }
+
+ DBG(5, "Memory successfully allocated.")
+ return 0;
+}
+
+
+
+/****************************************************************************
+ * USB-specific functions *
+ ****************************************************************************/
+
+/*--------------------------------------------------------------------------
+ This is an handler function which is called after the URBs are completed.
+ It collects multiple data packets coming from the camera by putting them
+ into frame buffers: one or more zero data length data packets are used to
+ mark the end of a video frame; the first non-zero data packet is the start
+ of the next video frame; if an error is encountered in a packet, the entire
+ video frame is discarded and grabbed again.
+ If there are no requested frames in the FIFO list, packets are collected into
+ a temporary buffer.
+ --------------------------------------------------------------------------*/
+static void w9968cf_urb_complete(struct urb *urb, struct pt_regs *regs)
+{
+ struct w9968cf_device* cam = (struct w9968cf_device*)urb->context;
+ struct w9968cf_frame_t** f;
+ unsigned long maxbufsize;
+ unsigned int len, status;
+ void* pos;
+ u8 i;
+ int err = 0;
+
+ if ((!cam->streaming) || cam->disconnected) {
+ DBG(4, "Got interrupt, but not streaming.")
+ return;
+ }
+
+ maxbufsize = min( (unsigned long)W9968CF_HW_BUF_SIZE,
+ w9968cf_get_max_bufsize(cam) );
+
+ /* "(*f)" will be used instead of "cam->frame_current" */
+ f = &cam->frame_current;
+
+ /* If a frame has been requested and we are grabbing into
+ the temporary frame, we'll switch to that requested frame */
+ if ((*f) == &cam->frame_tmp && *cam->requested_frame) {
+ if (cam->frame_tmp.status == F_GRABBING) {
+ w9968cf_pop_frame(cam, &cam->frame_current);
+ (*f)->status = F_GRABBING;
+ (*f)->length = cam->frame_tmp.length;
+ memcpy((*f)->buffer, cam->frame_tmp.buffer,
+ (*f)->length);
+ DBG(6, "Switched from temp. frame to frame #%d",
+ (*f) - &cam->frame[0])
+ }
+ }
+
+ for (i = 0; i < urb->number_of_packets; i++) {
+ len = urb->iso_frame_desc[i].actual_length;
+ status = urb->iso_frame_desc[i].status;
+ pos = urb->iso_frame_desc[i].offset + urb->transfer_buffer;
+
+ if (status && len != 0) {
+ DBG(4, "URB failed, error in data packet "
+ "(error #%d, %s).",
+ status, symbolic(urb_errlist, status))
+ (*f)->status = F_ERROR;
+ continue;
+ }
+
+ if (len) { /* start of frame */
+
+ if ((*f)->status == F_UNUSED) {
+ (*f)->status = F_GRABBING;
+ (*f)->length = 0;
+ }
+
+ /* Buffer overflows shouldn't happen, however...*/
+ if ((*f)->length + len > maxbufsize) {
+ DBG(4, "Buffer overflow: bad data packets.")
+ (*f)->status = F_ERROR;
+ }
+
+ if ((*f)->status == F_GRABBING) {
+ memcpy((*f)->buffer + (*f)->length, pos, len);
+ (*f)->length += len;
+ }
+
+ } else if ((*f)->status == F_GRABBING) { /* end of frame */
+
+ DBG(6, "Frame #%d successfully grabbed.",
+ ((*f)==&cam->frame_tmp ? -1 : (*f)-&cam->frame[0]))
+
+ if (cam->vpp_flag & VPP_DECOMPRESSION) {
+ err=(*w9968cf_vpp_check_headers)((*f)->buffer,
+ (*f)->length);
+ if (err) {
+ DBG(4, "Skip corrupted frame: %s",
+ symbolic(decoder_errlist, err))
+ (*f)->status = F_UNUSED;
+ continue; /* grab this frame again */
+ }
+ }
+
+ (*f)->status = F_READY;
+ (*f)->queued = 0;
+
+ /* Take a pointer to the new frame from the FIFO list.
+ If the list is empty,we'll use the temporary frame*/
+ if (*cam->requested_frame)
+ w9968cf_pop_frame(cam, &cam->frame_current);
+ else {
+ cam->frame_current = &cam->frame_tmp;
+ (*f)->status = F_UNUSED;
+ }
+
+ } else if ((*f)->status == F_ERROR)
+ (*f)->status = F_UNUSED; /* grab it again */
+
+ PDBGG("Frame length %li | pack.#%d | pack.len. %d | state %d",
+ (unsigned long)(*f)->length, i, len, (*f)->status)
+
+ } /* end for */
+
+ /* Resubmit this URB */
+ urb->dev = cam->usbdev;
+ urb->status = 0;
+ spin_lock(&cam->urb_lock);
+ if (cam->streaming)
+ if ((err = usb_submit_urb(urb, GFP_ATOMIC))) {
+ cam->misconfigured = 1;
+ DBG(1, "Couldn't resubmit the URB: error %d, %s",
+ err, symbolic(urb_errlist, err));
+ }
+ spin_unlock(&cam->urb_lock);
+
+ /* Wake up the user process */
+ if (waitqueue_active(&cam->wait_queue))
+ wake_up_interruptible(&cam->wait_queue);
+}
+
+
+/*---------------------------------------------------------------------------
+ Setup the URB structures for the isochronous transfer.
+ Submit the URBs so that the data transfer begins.
+ Return 0 on success, a negative number otherwise.
+ ---------------------------------------------------------------------------*/
+static int w9968cf_start_transfer(struct w9968cf_device* cam)
+{
+ struct usb_device *udev = cam->usbdev;
+ struct urb* urb;
+ const u16 p_size = wMaxPacketSize[cam->altsetting-1];
+ u16 w, h, d;
+ int vidcapt;
+ u32 t_size;
+ int err = 0;
+ s8 i, j;
+
+ for (i = 0; i < W9968CF_URBS; i++) {
+ urb = usb_alloc_urb(W9968CF_ISO_PACKETS, GFP_KERNEL);
+ cam->urb[i] = urb;
+
+ if (!urb) {
+ for (j = 0; j < i; j++)
+ usb_free_urb(cam->urb[j]);
+ DBG(1, "Couldn't allocate the URB structures.")
+ return -ENOMEM;
+ }
+
+ urb->dev = udev;
+ urb->context = (void*)cam;
+ urb->pipe = usb_rcvisocpipe(udev, 1);
+ urb->transfer_flags = URB_ISO_ASAP;
+ urb->number_of_packets = W9968CF_ISO_PACKETS;
+ urb->complete = w9968cf_urb_complete;
+ urb->transfer_buffer = cam->transfer_buffer[i];
+ urb->transfer_buffer_length = p_size*W9968CF_ISO_PACKETS;
+ urb->interval = 1;
+ for (j = 0; j < W9968CF_ISO_PACKETS; j++) {
+ urb->iso_frame_desc[j].offset = p_size*j;
+ urb->iso_frame_desc[j].length = p_size;
+ }
+ }
+
+ /* Transfer size per frame, in WORD ! */
+ d = cam->hw_depth;
+ w = cam->hw_width;
+ h = cam->hw_height;
+
+ t_size = (w*h*d)/16;
+
+ err = w9968cf_write_reg(cam, 0xbf17, 0x00); /* reset everything */
+ err += w9968cf_write_reg(cam, 0xbf10, 0x00); /* normal operation */
+
+ /* Transfer size */
+ err += w9968cf_write_reg(cam, t_size & 0xffff, 0x3d); /* low bits */
+ err += w9968cf_write_reg(cam, t_size >> 16, 0x3e); /* high bits */
+
+ if (cam->vpp_flag & VPP_DECOMPRESSION)
+ err += w9968cf_upload_quantizationtables(cam);
+
+ vidcapt = w9968cf_read_reg(cam, 0x16); /* read picture settings */
+ err += w9968cf_write_reg(cam, vidcapt|0x8000, 0x16); /* capt. enable */
+
+ err += usb_set_interface(udev, 0, cam->altsetting);
+ err += w9968cf_write_reg(cam, 0x8a05, 0x3c); /* USB FIFO enable */
+
+ if (err || (vidcapt < 0)) {
+ for (i = 0; i < W9968CF_URBS; i++)
+ usb_free_urb(cam->urb[i]);
+ DBG(1, "Couldn't tell the camera to start the data transfer.")
+ return err;
+ }
+
+ w9968cf_init_framelist(cam);
+
+ /* Begin to grab into the temporary buffer */
+ cam->frame_tmp.status = F_UNUSED;
+ cam->frame_tmp.queued = 0;
+ cam->frame_current = &cam->frame_tmp;
+
+ if (!(cam->vpp_flag & VPP_DECOMPRESSION))
+ DBG(5, "Isochronous transfer size: %li bytes/frame.",
+ (unsigned long)t_size*2)
+
+ DBG(5, "Starting the isochronous transfer...")
+
+ /* Submit the URBs */
+ for (i = 0; i < W9968CF_URBS; i++) {
+ err = usb_submit_urb(cam->urb[i], GFP_KERNEL);
+ if (err) {
+ for (j = i-1; j >= 0; j--)
+ if (!usb_unlink_urb(cam->urb[j]))
+ usb_free_urb(cam->urb[j]);
+ DBG(1, "Couldn't send a transfer request to the "
+ "USB core (error #%d, %s).", err,
+ symbolic(urb_errlist, err))
+ }
+ }
+
+ cam->streaming = 1;
+
+ return 0;
+}
+
+
+/*--------------------------------------------------------------------------
+ Stop the isochronous transfer and set alternate setting to 0 (0Mb/s).
+ Return 0 on success, a negative number otherwise.
+ --------------------------------------------------------------------------*/
+static int w9968cf_stop_transfer(struct w9968cf_device* cam)
+{
+ struct usb_device *udev = cam->usbdev;
+ unsigned long lock_flags;
+ int err = 0;
+ s8 i;
+
+ /* This avoids race conditions with usb_submit_urb()
+ in the URB completition handler */
+ spin_lock_irqsave(&cam->urb_lock, lock_flags);
+ cam->streaming = 0;
+ spin_unlock_irqrestore(&cam->urb_lock, lock_flags);
+
+ for (i = W9968CF_URBS-1; i >= 0; i--)
+ if (cam->urb[i])
+ if (!usb_unlink_urb(cam->urb[i])) {
+ usb_free_urb(cam->urb[i]);
+ cam->urb[i] = NULL;
+ }
+
+ if (cam->disconnected)
+ goto exit;
+
+ err = w9968cf_write_reg(cam, 0x0a05, 0x3c); /* stop USB transfer */
+ err += usb_set_interface(udev, 0, 0); /* 0 Mb/s */
+ err += w9968cf_write_reg(cam, 0x0000, 0x39); /* disable JPEG encoder */
+ err += w9968cf_write_reg(cam, 0x0000, 0x16); /* stop video capture */
+
+ if (err) {
+ DBG(2, "Failed to tell the camera to stop the isochronous "
+ "transfer. However this is not a critical error.")
+ return -EIO;
+ }
+
+exit:
+ DBG(5, "Isochronous transfer stopped.")
+ return 0;
+}
+
+
+/*--------------------------------------------------------------------------
+ Write a W9968CF register.
+ Return 0 on success, -1 otherwise.
+ --------------------------------------------------------------------------*/
+static int w9968cf_write_reg(struct w9968cf_device* cam, u16 value, u16 index)
+{
+ struct usb_device* udev = cam->usbdev;
+ int res;
+
+ res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0,
+ USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE,
+ value, index, NULL, 0, W9968CF_USB_CTRL_TIMEOUT);
+
+ if (res < 0)
+ DBG(4, "Failed to write a register "
+ "(value 0x%04X, index 0x%02X, error #%d, %s).",
+ value, index, res, symbolic(urb_errlist, res))
+
+ return (res >= 0) ? 0 : -1;
+}
+
+
+/*--------------------------------------------------------------------------
+ Read a W9968CF register.
+ Return the register value on success, -1 otherwise.
+ --------------------------------------------------------------------------*/
+static int w9968cf_read_reg(struct w9968cf_device* cam, u16 index)
+{
+ struct usb_device* udev = cam->usbdev;
+ u16* buff = cam->control_buffer;
+ int res;
+
+ res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 1,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0, index, (void*)buff,
+ 2, W9968CF_USB_CTRL_TIMEOUT);
+
+ if (res < 0)
+ DBG(4, "Failed to read a register "
+ "(index 0x%02X, error #%d, %s).",
+ index, res, symbolic(urb_errlist, res))
+
+ return (res >= 0) ? (int)(*buff) : -1;
+}
+
+
+/*--------------------------------------------------------------------------
+ Write data to the fast serial bus registers.
+ Return 0 on success, -1 otherwise.
+ --------------------------------------------------------------------------*/
+static int w9968cf_write_fsb(struct w9968cf_device* cam, u16* data)
+{
+ struct usb_device* udev = cam->usbdev;
+ u16 value;
+ int res;
+
+ value = *data++;
+
+ res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0,
+ USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE,
+ value, 0x06, (void*)data, 6,
+ W9968CF_USB_CTRL_TIMEOUT);
+
+ if (res < 0)
+ DBG(4, "Failed to write the FSB registers "
+ "(error #%d, %s).", res, symbolic(urb_errlist, res))
+
+ return (res >= 0) ? 0 : -1;
+}
+
+
+/*--------------------------------------------------------------------------
+ Write data to the serial bus control register.
+ Return 0 on success, a negative number otherwise.
+ --------------------------------------------------------------------------*/
+static int w9968cf_write_sb(struct w9968cf_device* cam, u16 value)
+{
+ int err = 0;
+
+ err = w9968cf_write_reg(cam, value, 0x01);
+ udelay(W9968CF_I2C_BUS_DELAY);
+
+ return err;
+}
+
+
+/*--------------------------------------------------------------------------
+ Read data from the serial bus control register.
+ Return 0 on success, a negative number otherwise.
+ --------------------------------------------------------------------------*/
+static int w9968cf_read_sb(struct w9968cf_device* cam)
+{
+ int v = 0;
+
+ v = w9968cf_read_reg(cam, 0x01);
+ udelay(W9968CF_I2C_BUS_DELAY);
+
+ return v;
+}
+
+
+/*--------------------------------------------------------------------------
+ Upload quantization tables for the JPEG compression.
+ This function is called by w9968cf_start_transfer().
+ Return 0 on success, a negative number otherwise.
+ --------------------------------------------------------------------------*/
+static int w9968cf_upload_quantizationtables(struct w9968cf_device* cam)
+{
+ u16 a, b;
+ int err = 0, i, j;
+
+ err += w9968cf_write_reg(cam, 0x0010, 0x39); /* JPEG clock enable */
+
+ for (i = 0, j = 0; i < 32; i++, j += 2) {
+ a = Y_QUANTABLE[j] | ((unsigned)(Y_QUANTABLE[j+1]) << 8);
+ b = UV_QUANTABLE[j] | ((unsigned)(UV_QUANTABLE[j+1]) << 8);
+ err += w9968cf_write_reg(cam, a, 0x40+i);
+ err += w9968cf_write_reg(cam, b, 0x60+i);
+ }
+ err += w9968cf_write_reg(cam, 0x0012, 0x39); /* JPEG encoder enable */
+
+ return err;
+}
+
+
+
+/****************************************************************************
+ * Low-level I2C I/O functions. *
+ * The adapter supports the following I2C transfer functions: *
+ * i2c_adap_fastwrite_byte_data() (at 400 kHz bit frequency only) *
+ * i2c_adap_read_byte_data() *
+ * i2c_adap_read_byte() *
+ ****************************************************************************/
+
+static int w9968cf_smbus_start(struct w9968cf_device* cam)
+{
+ int err = 0;
+
+ err += w9968cf_write_sb(cam, 0x0011); /* SDE=1, SDA=0, SCL=1 */
+ err += w9968cf_write_sb(cam, 0x0010); /* SDE=1, SDA=0, SCL=0 */
+
+ return err;
+}
+
+
+static int w9968cf_smbus_stop(struct w9968cf_device* cam)
+{
+ int err = 0;
+
+ err += w9968cf_write_sb(cam, 0x0011); /* SDE=1, SDA=0, SCL=1 */
+ err += w9968cf_write_sb(cam, 0x0013); /* SDE=1, SDA=1, SCL=1 */
+
+ return err;
+}
+
+
+static int w9968cf_smbus_write_byte(struct w9968cf_device* cam, u8 v)
+{
+ u8 bit;
+ int err = 0, sda;
+
+ for (bit = 0 ; bit < 8 ; bit++) {
+ sda = (v & 0x80) ? 2 : 0;
+ v <<= 1;
+ /* SDE=1, SDA=sda, SCL=0 */
+ err += w9968cf_write_sb(cam, 0x10 | sda);
+ /* SDE=1, SDA=sda, SCL=1 */
+ err += w9968cf_write_sb(cam, 0x11 | sda);
+ /* SDE=1, SDA=sda, SCL=0 */
+ err += w9968cf_write_sb(cam, 0x10 | sda);
+ }
+
+ return err;
+}
+
+
+static int w9968cf_smbus_read_byte(struct w9968cf_device* cam, u8* v)
+{
+ u8 bit;
+ int err = 0;
+
+ *v = 0;
+ for (bit = 0 ; bit < 8 ; bit++) {
+ *v <<= 1;
+ err += w9968cf_write_sb(cam, 0x0013);
+ *v |= (w9968cf_read_sb(cam) & 0x0008) ? 1 : 0;
+ err += w9968cf_write_sb(cam, 0x0012);
+ }
+
+ return err;
+}
+
+
+static int w9968cf_smbus_write_ack(struct w9968cf_device* cam)
+{
+ int err = 0;
+
+ err += w9968cf_write_sb(cam, 0x0010); /* SDE=1, SDA=0, SCL=0 */
+ err += w9968cf_write_sb(cam, 0x0011); /* SDE=1, SDA=0, SCL=1 */
+ err += w9968cf_write_sb(cam, 0x0010); /* SDE=1, SDA=0, SCL=0 */
+
+ return err;
+}
+
+
+static int w9968cf_smbus_read_ack(struct w9968cf_device* cam)
+{
+ int err = 0, sda;
+
+ err += w9968cf_write_sb(cam, 0x0013); /* SDE=1, SDA=1, SCL=1 */
+ sda = (w9968cf_read_sb(cam) & 0x08) ? 1 : 0; /* sda = SDA */
+ err += w9968cf_write_sb(cam, 0x0012); /* SDE=1, SDA=1, SCL=0 */
+ if (sda < 0)
+ err += sda;
+ if (sda == 1) {
+ DBG(6, "Couldn't receive the ACK.")
+ err += -1;
+ }
+
+ return err;
+}
+
+
+/* SMBus protocol: S Addr Wr [A] Subaddr [A] Value [A] P */
+static int
+w9968cf_i2c_adap_fastwrite_byte_data(struct w9968cf_device* cam,
+ u16 address, u8 subaddress,u8 value)
+{
+ u16* data = cam->data_buffer;
+ int err = 0;
+
+ /* Enable SBUS outputs */
+ err += w9968cf_write_reg(cam, 0x0020, 0x01);
+
+ data[0] = 0x082f | ((address & 0x80) ? 0x1500 : 0x0);
+ data[0] |= (address & 0x40) ? 0x4000 : 0x0;
+ data[1] = 0x2082 | ((address & 0x40) ? 0x0005 : 0x0);
+ data[1] |= (address & 0x20) ? 0x0150 : 0x0;
+ data[1] |= (address & 0x10) ? 0x5400 : 0x0;
+ data[2] = 0x8208 | ((address & 0x08) ? 0x0015 : 0x0);
+ data[2] |= (address & 0x04) ? 0x0540 : 0x0;
+ data[2] |= (address & 0x02) ? 0x5000 : 0x0;
+ data[3] = 0x1d20 | ((address & 0x02) ? 0x0001 : 0x0);
+ data[3] |= (address & 0x01) ? 0x0054 : 0x0;
+
+ err += w9968cf_write_fsb(cam, data);
+
+ data[0] = 0x8208 | ((subaddress & 0x80) ? 0x0015 : 0x0);
+ data[0] |= (subaddress & 0x40) ? 0x0540 : 0x0;
+ data[0] |= (subaddress & 0x20) ? 0x5000 : 0x0;
+ data[1] = 0x0820 | ((subaddress & 0x20) ? 0x0001 : 0x0);
+ data[1] |= (subaddress & 0x10) ? 0x0054 : 0x0;
+ data[1] |= (subaddress & 0x08) ? 0x1500 : 0x0;
+ data[1] |= (subaddress & 0x04) ? 0x4000 : 0x0;
+ data[2] = 0x2082 | ((subaddress & 0x04) ? 0x0005 : 0x0);
+ data[2] |= (subaddress & 0x02) ? 0x0150 : 0x0;
+ data[2] |= (subaddress & 0x01) ? 0x5400 : 0x0;
+ data[3] = 0x001d;
+
+ err += w9968cf_write_fsb(cam, data);
+
+ data[0] = 0x8208 | ((value & 0x80) ? 0x0015 : 0x0);
+ data[0] |= (value & 0x40) ? 0x0540 : 0x0;
+ data[0] |= (value & 0x20) ? 0x5000 : 0x0;
+ data[1] = 0x0820 | ((value & 0x20) ? 0x0001 : 0x0);
+ data[1] |= (value & 0x10) ? 0x0054 : 0x0;
+ data[1] |= (value & 0x08) ? 0x1500 : 0x0;
+ data[1] |= (value & 0x04) ? 0x4000 : 0x0;
+ data[2] = 0x2082 | ((value & 0x04) ? 0x0005 : 0x0);
+ data[2] |= (value & 0x02) ? 0x0150 : 0x0;
+ data[2] |= (value & 0x01) ? 0x5400 : 0x0;
+ data[3] = 0xfe1d;
+
+ err += w9968cf_write_fsb(cam, data);
+
+ /* Disable SBUS outputs */
+ err += w9968cf_write_reg(cam, 0x0000, 0x01);
+
+ if (!err)
+ DBG(5, "I2C write byte data done, addr.0x%04X, subaddr.0x%02X "
+ "value 0x%02X.", address, subaddress, value)
+ else
+ DBG(5, "I2C write byte data failed, addr.0x%04X, "
+ "subaddr.0x%02X, value 0x%02X.",
+ address, subaddress, value)
+
+ return err;
+}
+
+
+/* SMBus protocol: S Addr Wr [A] Subaddr [A] P S Addr+1 Rd [A] [Value] NA P */
+static int
+w9968cf_i2c_adap_read_byte_data(struct w9968cf_device* cam,
+ u16 address, u8 subaddress,
+ u8* value)
+{
+ int err = 0;
+
+ /* Serial data enable */
+ err += w9968cf_write_sb(cam, 0x0013); /* don't change ! */
+
+ err += w9968cf_smbus_start(cam);
+ err += w9968cf_smbus_write_byte(cam, address);
+ err += w9968cf_smbus_read_ack(cam);
+ err += w9968cf_smbus_write_byte(cam, subaddress);
+ err += w9968cf_smbus_read_ack(cam);
+ err += w9968cf_smbus_stop(cam);
+ err += w9968cf_smbus_start(cam);
+ err += w9968cf_smbus_write_byte(cam, address + 1);
+ err += w9968cf_smbus_read_ack(cam);
+ err += w9968cf_smbus_read_byte(cam, value);
+ err += w9968cf_smbus_write_ack(cam);
+ err += w9968cf_smbus_stop(cam);
+
+ /* Serial data disable */
+ err += w9968cf_write_sb(cam, 0x0000);
+
+ if (!err)
+ DBG(5, "I2C read byte data done, addr.0x%04X, "
+ "subaddr.0x%02X, value 0x%02X.",
+ address, subaddress, *value)
+ else
+ DBG(5, "I2C read byte data failed, addr.0x%04X, "
+ "subaddr.0x%02X, wrong value 0x%02X.",
+ address, subaddress, *value)
+
+ return err;
+}
+
+
+/* SMBus protocol: S Addr+1 Rd [A] [Value] NA P */
+static int
+w9968cf_i2c_adap_read_byte(struct w9968cf_device* cam,
+ u16 address, u8* value)
+{
+ int err = 0;
+
+ /* Serial data enable */
+ err += w9968cf_write_sb(cam, 0x0013);
+
+ err += w9968cf_smbus_start(cam);
+ err += w9968cf_smbus_write_byte(cam, address + 1);
+ err += w9968cf_smbus_read_ack(cam);
+ err += w9968cf_smbus_read_byte(cam, value);
+ err += w9968cf_smbus_write_ack(cam);
+ err += w9968cf_smbus_stop(cam);
+
+ /* Serial data disable */
+ err += w9968cf_write_sb(cam, 0x0000);
+
+ if (!err)
+ DBG(5, "I2C read byte done, addr.0x%04X."
+ "value 0x%02X.", address, *value)
+ else
+ DBG(5, "I2C read byte failed, addr.0x%04X."
+ "wrong value 0x%02X.", address, *value)
+
+ return err;
+}
+
+
+/* SMBus protocol: S Addr Wr [A] Value [A] P */
+static int
+w9968cf_i2c_adap_write_byte(struct w9968cf_device* cam,
+ u16 address, u8 value)
+{
+ DBG(4, "i2c_write_byte() is an unsupported transfer mode.")
+ return -EINVAL;
+}
+
+
+
+/****************************************************************************
+ * I2C interface to kernel *
+ ****************************************************************************/
+
+static int
+w9968cf_i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr,
+ unsigned short flags, char read_write, u8 command,
+ int size, union i2c_smbus_data *data)
+{
+ struct w9968cf_device* cam = i2c_get_adapdata(adapter);
+ u8 i, j;
+ int rc = 0, err = 0;
+
+ switch (addr) {
+ case OV6xx0_SID:
+ case OV7xx0_SID:
+ break;
+ default:
+ DBG(4, "Rejected slave ID 0x%04X", addr)
+ return -EINVAL;
+ }
+
+ if (size == I2C_SMBUS_BYTE) {
+ /* Why addr <<= 1? See OVXXX0_SID defines in ovcamchip.h */
+ addr <<= 1;
+
+ if (read_write == I2C_SMBUS_WRITE)
+ rc = w9968cf_i2c_adap_write_byte(cam, addr, command);
+ else if (read_write == I2C_SMBUS_READ)
+ rc = w9968cf_i2c_adap_read_byte(cam,addr, &data->byte);
+
+ } else if (size == I2C_SMBUS_BYTE_DATA) {
+ addr <<= 1;
+
+ if (read_write == I2C_SMBUS_WRITE)
+ rc = w9968cf_i2c_adap_fastwrite_byte_data(cam, addr,
+ command, data->byte);
+ else if (read_write == I2C_SMBUS_READ) {
+ for (i = 1; i <= W9968CF_I2C_RW_RETRIES; i++) {
+ rc = w9968cf_i2c_adap_read_byte_data(cam, addr,
+ command, &data->byte);
+ if (rc < 0) {
+ /* Work around: this seems to wake up
+ the EEPROM from the stall state */
+ for (j = 0; j <= 10; j++) {
+ err += w9968cf_write_sb(cam,0x0020);
+ err += w9968cf_write_sb(cam,0x0000);
+ if (err)
+ break;
+ }
+ }
+ else
+ break;
+ }
+
+ } else
+ return -EINVAL;
+
+ } else {
+ DBG(4, "Unsupported I2C transfer mode (%d)", size)
+ return -EINVAL;
+ }
+
+ /* This works around a bug in the I2C core */
+ if (rc > 0)
+ rc = 0;
+
+ return rc;
+}
+
+
+static u32 w9968cf_i2c_func(struct i2c_adapter* adap)
+{
+ return I2C_FUNC_SMBUS_READ_BYTE |
+ I2C_FUNC_SMBUS_READ_BYTE_DATA |
+ I2C_FUNC_SMBUS_WRITE_BYTE_DATA;
+}
+
+
+static int w9968cf_i2c_attach_inform(struct i2c_client* client)
+{
+ struct w9968cf_device* cam = i2c_get_adapdata(client->adapter);
+ const char* clientname = i2c_clientname(client);
+ int id = client->driver->id;
+
+ if (id == I2C_DRIVERID_OVCAMCHIP) {
+ int rc = 0;
+
+ cam->sensor_client = client;
+
+ rc = w9968cf_sensor_cmd(cam, OVCAMCHIP_CMD_INITIALIZE,
+ &cam->sensor_mono);
+ if (rc < 0) {
+ DBG(1, "CMOS sensor initialization failed (rc=%d)",rc);
+ cam->sensor_client = NULL;
+ return rc;
+ }
+
+ if (w9968cf_sensor_cmd(cam, OVCAMCHIP_CMD_Q_SUBTYPE,
+ &cam->sensor) < 0)
+ rc = -EIO;
+ else if (client->addr==OV7xx0_SID || client->addr==OV6xx0_SID)
+ w9968cf_sensor_configure(cam);
+ else
+ rc = -EINVAL;
+
+ if (rc < 0) {
+ cam->sensor_client = NULL;
+ cam->sensor = CC_UNKNOWN;
+ return rc;
+ }
+ } else {
+ DBG(4, "Rejected client [%s] with [%s]",
+ clientname, client->driver->name)
+ return -1;
+ }
+
+ DBG(2, "I2C attach client [%s] with [%s]",
+ clientname, client->driver->name)
+
+ return 0;
+}
+
+
+static int w9968cf_i2c_detach_inform(struct i2c_client* client)
+{
+ struct w9968cf_device* cam = i2c_get_adapdata(client->adapter);
+ const char* clientname = i2c_clientname(client);
+
+ if (cam->sensor_client == client) {
+ cam->sensor_client = NULL;
+ }
+
+ DBG(2, "I2C detach [%s]", clientname)
+
+ return 0;
+}
+
+
+static int
+w9968cf_i2c_control(struct i2c_adapter* adapter, unsigned int cmd,
+ unsigned long arg)
+{
+ return 0;
+}
+
+
+static int w9968cf_i2c_init(struct w9968cf_device* cam)
+{
+ int rc = 0;
+
+ static struct i2c_algorithm algo = {
+ .name = "W996[87]CF algorithm",
+ .id = I2C_ALGO_SMBUS,
+ .smbus_xfer = w9968cf_i2c_smbus_xfer,
+ .algo_control = w9968cf_i2c_control,
+ .functionality = w9968cf_i2c_func,
+ };
+
+ static struct i2c_adapter adap = {
+ .id = I2C_ALGO_SMBUS | I2C_HW_SMBUS_W9968CF,
+ .class = I2C_ADAP_CLASS_CAM_DIGITAL,
+ .owner = THIS_MODULE,
+ .client_register = w9968cf_i2c_attach_inform,
+ .client_unregister = w9968cf_i2c_detach_inform,
+ .algo = &algo,
+ };
+
+ memcpy(&cam->i2c_adapter, &adap, sizeof(struct i2c_adapter));
+ strcpy(cam->i2c_adapter.name, "w9968cf");
+ i2c_set_adapdata(&cam->i2c_adapter, cam);
+
+ DBG(6, "Registering I2C bus with kernel...")
+
+ rc = i2c_add_adapter(&cam->i2c_adapter);
+ if (rc)
+ DBG(5, "Failed to register the I2C bus.")
+ else
+ DBG(5, "I2C bus registered.")
+
+ return rc;
+}
+
+
+
+/****************************************************************************
+ * Helper functions *
+ ****************************************************************************/
+
+/*--------------------------------------------------------------------------
+ Turn on the LED on some webcams. A beep should be heard too.
+ Return 0 on success, a negative number otherwise.
+ --------------------------------------------------------------------------*/
+static int w9968cf_turn_on_led(struct w9968cf_device* cam)
+{
+ int err = 0;
+
+ err += w9968cf_write_reg(cam, 0xff00, 0x00); /* power-down */
+ err += w9968cf_write_reg(cam, 0xbf17, 0x00); /* reset everything */
+ err += w9968cf_write_reg(cam, 0xbf10, 0x00); /* normal operation */
+ err += w9968cf_write_reg(cam, 0x0010, 0x01); /* serial bus, SDS high */
+ err += w9968cf_write_reg(cam, 0x0000, 0x01); /* serial bus, SDS low */
+ err += w9968cf_write_reg(cam, 0x0010, 0x01); /* ..high 'beep-beep' */
+
+ if (err)
+ DBG(2, "Couldn't turn on the LED.")
+
+ DBG(5, "LED turned on.")
+
+ return err;
+}
+
+
+/*--------------------------------------------------------------------------
+ Write some registers for the device initialization.
+ This function is called once on open().
+ Return 0 on success, a negative number otherwise.
+ --------------------------------------------------------------------------*/
+static int w9968cf_init_chip(struct w9968cf_device* cam)
+{
+ int err = 0, rc = 0;
+
+ err += w9968cf_write_reg(cam, 0xff00, 0x00); /* power off */
+ err += w9968cf_write_reg(cam, 0xbf10, 0x00); /* power on */
+
+ err += w9968cf_write_reg(cam, 0x405d, 0x03); /* DRAM timings */
+ err += w9968cf_write_reg(cam, 0x0030, 0x04); /* SDRAM timings */
+
+ err += w9968cf_write_reg(cam, 0x0000, 0x20); /* Y frame buf.0, low */
+ err += w9968cf_write_reg(cam, 0x0000, 0x21); /* Y frame buf.0, high */
+ err += w9968cf_write_reg(cam, 0xb000, 0x22); /* Y frame buf.1, low */
+ err += w9968cf_write_reg(cam, 0x0004, 0x23); /* Y frame buf.1, high */
+ err += w9968cf_write_reg(cam, 0x5800, 0x24); /* U frame buf.0, low */
+ err += w9968cf_write_reg(cam, 0x0002, 0x25); /* U frame buf.0, high */
+ err += w9968cf_write_reg(cam, 0x0800, 0x26); /* U frame buf.1, low */
+ err += w9968cf_write_reg(cam, 0x0007, 0x27); /* U frame buf.1, high */
+ err += w9968cf_write_reg(cam, 0x8400, 0x28); /* V frame buf.0, low */
+ err += w9968cf_write_reg(cam, 0x0003, 0x29); /* V frame buf.0, high */
+ err += w9968cf_write_reg(cam, 0x3400, 0x2a); /* V frame buf.1, low */
+ err += w9968cf_write_reg(cam, 0x0008, 0x2b); /* V frame buf.1, high */
+
+ err += w9968cf_write_reg(cam, 0x6000, 0x32); /* JPEG bitstream buf 0 */
+ err += w9968cf_write_reg(cam, 0x0009, 0x33); /* JPEG bitstream buf 0 */
+ err += w9968cf_write_reg(cam, 0x2000, 0x34); /* JPEG bitstream buf 1 */
+ err += w9968cf_write_reg(cam, 0x000d, 0x35); /* JPEG bitstream buf 1 */
+
+ err += w9968cf_write_reg(cam, 0x0000, 0x36);/* JPEG restart interval */
+ err += w9968cf_write_reg(cam, 0x0804, 0x37);/*JPEG VLE FIFO threshold*/
+ err += w9968cf_write_reg(cam, 0x0000, 0x38);/* disable hw up-scaling */
+ err += w9968cf_write_reg(cam, 0x0000, 0x3f); /* JPEG/MCTL test data */
+
+ err += w9968cf_set_picture(cam, cam->picture); /* this before */
+ err += w9968cf_set_window(cam, cam->window);
+
+ if (err)
+ goto error;
+
+ rc = w9968cf_sensor_change_settings(cam);
+ if (rc)
+ goto error;
+
+ DBG(5, "Chip successfully initialized.");
+
+ return 0;
+
+error:
+ DBG(1, "Chip initialization failed.")
+ if (err)
+ return err;
+ else
+ return rc;
+}
+
+
+/*--------------------------------------------------------------------------
+ Change the picture settings of the camera.
+ Return 0 on success, a negative number otherwise.
+ --------------------------------------------------------------------------*/
+static int
+w9968cf_set_picture(struct w9968cf_device* cam, struct video_picture pict)
+{
+ u16 fmt, hw_depth, hw_palette, reg_v = 0x0000;
+ int err = 0, rc = 0;
+
+ /* Make sure we are using a valid depth */
+ pict.depth = w9968cf_valid_depth(pict.palette);
+
+ fmt = pict.palette;
+
+ hw_depth = pict.depth; /* depth used by the winbond chip */
+ hw_palette = pict.palette; /* palette used by the winbond chip */
+
+ /* VS & HS polarities */
+ reg_v = (cam->vs_polarity << 12) | (cam->hs_polarity << 11);
+
+ switch (fmt)
+ {
+ case VIDEO_PALETTE_UYVY:
+ reg_v |= 0x0000;
+ cam->vpp_flag = VPP_NONE;
+ break;
+ case VIDEO_PALETTE_YUV422P:
+ reg_v |= 0x0002;
+ cam->vpp_flag = VPP_DECOMPRESSION;
+ break;
+ case VIDEO_PALETTE_YUV420:
+ case VIDEO_PALETTE_YUV420P:
+ reg_v |= 0x0003;
+ cam->vpp_flag = VPP_DECOMPRESSION;
+ break;
+ case VIDEO_PALETTE_YUYV:
+ case VIDEO_PALETTE_YUV422:
+ reg_v |= 0x0000;
+ cam->vpp_flag = VPP_SWAP_YUV_BYTES;
+ hw_palette = VIDEO_PALETTE_UYVY;
+ break;
+ /* Original video is used instead of RGBX palettes.
+ Software conversion later. */
+ case VIDEO_PALETTE_GREY:
+ case VIDEO_PALETTE_RGB555:
+ case VIDEO_PALETTE_RGB565:
+ case VIDEO_PALETTE_RGB24:
+ case VIDEO_PALETTE_RGB32:
+ reg_v |= 0x0000; /* UYVY 16 bit is used */
+ hw_depth = 16;
+ hw_palette = VIDEO_PALETTE_UYVY;
+ cam->vpp_flag = VPP_UYVY_TO_RGBX;
+ break;
+ }
+
+ /* FIXME: 'hardware double buffer' doesn't work when compressed video
+ is enabled (corrupted frames). */
+ if (cam->double_buffer && !(cam->vpp_flag & VPP_DECOMPRESSION))
+ reg_v |= 0x0080;
+
+ if (cam->clamping)
+ reg_v |= 0x0020;
+
+ if (cam->filter_type == 1)
+ reg_v |= 0x0008;
+ else if (cam->filter_type == 2)
+ reg_v |= 0x000c;
+
+ err = w9968cf_write_reg(cam, reg_v, 0x16);
+ if (err)
+ goto error;
+
+ rc = w9968cf_sensor_set_picture(cam, pict);
+ if (rc)
+ goto error;
+
+ /* If all went well, update the device data structure */
+ memcpy(&cam->picture, &pict, sizeof(pict));
+ cam->hw_depth = hw_depth;
+ cam->hw_palette = hw_palette;
+
+ /* Settings changed, so we clear the frame buffers */
+ memset(cam->frame[0].buffer, 0,
+ cam->nbuffers*w9968cf_get_max_bufsize(cam));
+
+ DBG(4, "Palette is %s, depth is %d bpp.",
+ symbolic(v4l1_plist, pict.palette), pict.depth)
+
+ return 0;
+
+error:
+ DBG(1, "Failed to change picture settings.")
+ if (err)
+ return err;
+ else
+ return rc;
+}
+
+
+/*--------------------------------------------------------------------------
+ Change the capture area size of the camera.
+ This function _must_ be called _after_ w9968cf_set_picture().
+ Return 0 on success, a negative number otherwise.
+ --------------------------------------------------------------------------*/
+static int
+w9968cf_set_window(struct w9968cf_device* cam, struct video_window win)
+{
+ u16 x, y, w, h, scx, scy, cw, ch, ax, ay;
+ unsigned long fw, fh;
+ struct ovcamchip_window s_win;
+ int err=0, rc=0;
+
+ /* Work around to avoid FP arithmetics */
+ #define __SC(x) ((x) << 10)
+ #define __UNSC(x) ((x) >> 10)
+
+ /* Make sure we are using a supported resolution */
+ if ((err = w9968cf_adjust_window_size(cam, (u16*)&win.width,
+ (u16*)&win.height)))
+ goto error;
+
+ /* Scaling factors */
+ fw = __SC(win.width) / cam->maxwidth;
+ fh = __SC(win.height) / cam->maxheight;
+
+ /* Set up the width and height values used by the chip */
+ if ((win.width > cam->maxwidth) || (win.height > cam->maxheight)) {
+ cam->vpp_flag |= VPP_UPSCALE;
+ /* Calculate largest w,h mantaining the same w/h ratio */
+ w = (fw >= fh) ? cam->maxwidth : __SC(win.width)/fh;
+ h = (fw >= fh) ? __SC(win.height)/fw : cam->maxheight;
+ if (w < cam->minwidth) /* just in case */
+ w = cam->minwidth;
+ if (h < cam->minheight) /* just in case */
+ h = cam->minheight;
+ } else {
+ cam->vpp_flag &= ~VPP_UPSCALE;
+ w = win.width;
+ h = win.height;
+ }
+
+ /* x,y offsets of the cropped area */
+ scx = cam->start_cropx;
+ scy = cam->start_cropy;
+
+ /* Calculate cropped area manteining the right w/h ratio */
+ if (cam->largeview && !(cam->vpp_flag & VPP_UPSCALE)) {
+ cw = (fw >= fh) ? cam->maxwidth : __SC(win.width)/fh;
+ ch = (fw >= fh) ? __SC(win.height)/fw : cam->maxheight;
+ } else {
+ cw = w;
+ ch = h;
+ }
+
+ /* Setup the sensor window */
+ s_win.format = SENSOR_FORMAT;
+ s_win.width = cam->maxwidth;
+ s_win.height = cam->maxheight;
+ s_win.quarter = 0; /* full progressive video */
+
+ /* Center it */
+ s_win.x = (s_win.width - cw) / 2;
+ s_win.y = (s_win.height - ch) / 2;
+
+ /* Clock divisor */
+ if (cam->clockdiv >= 0)
+ s_win.clockdiv = cam->clockdiv; /* manual override */
+ else
+ switch (cam->sensor) {
+ case CC_OV6620:
+ s_win.clockdiv = 0;
+ break;
+ case CC_OV6630:
+ s_win.clockdiv = 0;
+ break;
+ case CC_OV76BE:
+ case CC_OV7610:
+ case CC_OV7620:
+ s_win.clockdiv = 0;
+ break;
+ default:
+ s_win.clockdiv = W9968CF_DEF_CLOCKDIVISOR;
+ }
+
+ /* We have to scale win.x and win.y offsets */
+ if ( (cam->largeview && !(cam->vpp_flag & VPP_UPSCALE))
+ || (cam->vpp_flag & VPP_UPSCALE) ) {
+ ax = __SC(win.x)/fw;
+ ay = __SC(win.y)/fh;
+ } else {
+ ax = win.x;
+ ay = win.y;
+ }
+
+ if ((ax + cw) > cam->maxwidth)
+ ax = cam->maxwidth - cw;
+
+ if ((ay + ch) > cam->maxheight)
+ ay = cam->maxheight - ch;
+
+ /* Adjust win.x, win.y */
+ if ( (cam->largeview && !(cam->vpp_flag & VPP_UPSCALE))
+ || (cam->vpp_flag & VPP_UPSCALE) ) {
+ win.x = __UNSC(ax*fw);
+ win.y = __UNSC(ay*fh);
+ } else {
+ win.x = ax;
+ win.y = ay;
+ }
+
+ /* Offsets used by the chip */
+ x = ax + s_win.x;
+ y = ay + s_win.y;
+
+ /* Go ! */
+ if ((rc = w9968cf_sensor_cmd(cam, OVCAMCHIP_CMD_S_MODE, &s_win)))
+ goto error;
+
+ err += w9968cf_write_reg(cam, scx + x, 0x10);
+ err += w9968cf_write_reg(cam, scy + y, 0x11);
+ err += w9968cf_write_reg(cam, scx + x + cw, 0x12);
+ err += w9968cf_write_reg(cam, scy + y + ch, 0x13);
+ err += w9968cf_write_reg(cam, w, 0x14);
+ err += w9968cf_write_reg(cam, h, 0x15);
+
+ /* JPEG width & height */
+ err += w9968cf_write_reg(cam, w, 0x30);
+ err += w9968cf_write_reg(cam, h, 0x31);
+
+ /* Y & UV frame buffer strides (in WORD) */
+ if (cam->vpp_flag & VPP_DECOMPRESSION) {
+ err += w9968cf_write_reg(cam, w/2, 0x2c);
+ err += w9968cf_write_reg(cam, w/4, 0x2d);
+ } else
+ err += w9968cf_write_reg(cam, w, 0x2c);
+
+ if (err)
+ goto error;
+
+ /* If all went well, update the device data structure */
+ memcpy(&cam->window, &win, sizeof(win));
+ cam->hw_width = w;
+ cam->hw_height = h;
+
+ /* Settings changed, so we clear the frame buffers */
+ memset(cam->frame[0].buffer, 0,
+ cam->nbuffers*w9968cf_get_max_bufsize(cam));
+
+ DBG(4, "The capture area is %dx%d, Offset (x,y)=(%d,%d).",
+ win.width, win.height, win.x, win.y)
+
+ PDBGG("x=%d ,y=%d, w=%d, h=%d, ax=%d, ay=%d, s_win.x=%d, s_win.y=%d, "
+ "cw=%d, ch=%d, win.x=%d ,win.y=%d, win.width=%d, win.height=%d",
+ x, y, w, h, ax, ay, s_win.x, s_win.y, cw, ch, win.x, win.y,
+ win.width, win.height)
+
+ return 0;
+
+error:
+ DBG(1, "Failed to change the capture area size.")
+ if (err)
+ return err;
+ else
+ return rc;
+}
+
+
+/*--------------------------------------------------------------------------
+ Return non-zero if the palette is supported, 0 otherwise.
+ --------------------------------------------------------------------------*/
+static inline u16 w9968cf_valid_palette(u16 palette)
+{
+ u8 i = 0;
+ while (w9968cf_formatlist[i].palette != 0) {
+ if (palette == w9968cf_formatlist[i].palette)
+ return palette;
+ i++;
+ }
+ return 0;
+}
+
+
+/*--------------------------------------------------------------------------
+ Return the depth corresponding to the given palette.
+ Palette _must_ be supported !
+ --------------------------------------------------------------------------*/
+static inline u16 w9968cf_valid_depth(u16 palette)
+{
+ u8 i=0;
+ while (w9968cf_formatlist[i].palette != palette)
+ i++;
+
+ return w9968cf_formatlist[i].depth;
+}
+
+
+/*--------------------------------------------------------------------------
+ Return non-zero if the format requires decompression, 0 otherwise.
+ --------------------------------------------------------------------------*/
+static inline u8 w9968cf_need_decompression(u16 palette)
+{
+ u8 i = 0;
+ while (w9968cf_formatlist[i].palette != 0) {
+ if (palette == w9968cf_formatlist[i].palette)
+ return w9968cf_formatlist[i].compression;
+ i++;
+ }
+ return 0;
+}
+
+
+/*--------------------------------------------------------------------------
+ Adjust the asked values for window width and height.
+ Return 0 on success, -1 otherwise.
+ --------------------------------------------------------------------------*/
+static int
+w9968cf_adjust_window_size(struct w9968cf_device* cam, u16* width, u16* height)
+{
+ u16 maxw, maxh;
+
+ if ((*width < cam->minwidth) || (*height < cam->minheight))
+ return -ERANGE;
+
+ maxw = cam->upscaling && !(cam->vpp_flag & VPP_DECOMPRESSION)
+ && w9968cf_vppmod_present ? W9968CF_MAX_WIDTH : cam->maxwidth;
+ maxh = cam->upscaling && !(cam->vpp_flag & VPP_DECOMPRESSION)
+ && w9968cf_vppmod_present ? W9968CF_MAX_HEIGHT : cam->maxheight;
+
+ if (*width > maxw)
+ *width = maxw;
+ if (*height > maxh)
+ *height = maxh;
+
+ if (cam->vpp_flag & VPP_DECOMPRESSION) {
+ *width &= ~15L; /* multiple of 16 */
+ *height &= ~15L;
+ }
+
+ PDBGG("Window size adjusted w=%d, h=%d ", *width, *height)
+
+ return 0;
+}
+
+
+/*--------------------------------------------------------------------------
+ Initialize the FIFO list of requested frames.
+ --------------------------------------------------------------------------*/
+static void w9968cf_init_framelist(struct w9968cf_device* cam)
+{
+ u8 i;
+
+ for (i = 0; i < cam->nbuffers; i++) {
+ cam->requested_frame[i] = NULL;
+ cam->frame[i].queued = 0;
+ cam->frame[i].status = F_UNUSED;
+ }
+}
+
+
+/*--------------------------------------------------------------------------
+ Add a frame in the FIFO list of requested frames.
+ This function is called in process context.
+ --------------------------------------------------------------------------*/
+static void w9968cf_push_frame(struct w9968cf_device* cam, u8 f_num)
+{
+ u8 f;
+ unsigned long lock_flags;
+
+ spin_lock_irqsave(&cam->flist_lock, lock_flags);
+
+ for (f=0; cam->requested_frame[f] != NULL; f++);
+ cam->requested_frame[f] = &cam->frame[f_num];
+ cam->frame[f_num].queued = 1;
+ cam->frame[f_num].status = F_UNUSED; /* clear the status */
+
+ spin_unlock_irqrestore(&cam->flist_lock, lock_flags);
+
+ DBG(6, "Frame #%d pushed into the FIFO list. Position %d.", f_num, f)
+}
+
+
+/*--------------------------------------------------------------------------
+ Read, store and remove the first pointer in the FIFO list of requested
+ frames. This function is called in interrupt context.
+ --------------------------------------------------------------------------*/
+static void
+w9968cf_pop_frame(struct w9968cf_device* cam, struct w9968cf_frame_t** framep)
+{
+ u8 i;
+
+ spin_lock(&cam->flist_lock);
+
+ *framep = cam->requested_frame[0];
+
+ /* Shift the list of pointers */
+ for (i = 0; i < cam->nbuffers-1; i++)
+ cam->requested_frame[i] = cam->requested_frame[i+1];
+ cam->requested_frame[i] = NULL;
+
+ spin_unlock(&cam->flist_lock);
+
+ DBG(6,"Popped frame #%d from the list.",*framep-&cam->frame[0])
+}
+
+
+/*--------------------------------------------------------------------------
+ High-level video post-processing routine on grabbed frames.
+ Return 0 on success, a negative number otherwise.
+ --------------------------------------------------------------------------*/
+static int
+w9968cf_postprocess_frame(struct w9968cf_device* cam,
+ struct w9968cf_frame_t* fr)
+{
+ void *pIn = fr->buffer, *pOut = cam->vpp_buffer, *tmp;
+ u16 w = cam->window.width,
+ h = cam->window.height,
+ d = cam->picture.depth,
+ fmt = cam->picture.palette,
+ rgb = cam->force_rgb,
+ hw_w = cam->hw_width,
+ hw_h = cam->hw_height,
+ hw_d = cam->hw_depth;
+ int err = 0;
+
+ #define _PSWAP(pIn, pOut) {tmp = (pIn); (pIn) = (pOut); (pOut) = tmp;}
+
+ if (cam->vpp_flag & VPP_DECOMPRESSION) {
+ memcpy(pOut, pIn, fr->length);
+ _PSWAP(pIn, pOut)
+ err = (*w9968cf_vpp_decode)(pIn, fr->length, hw_w, hw_h, pOut);
+ PDBGG("Compressed frame length: %li",(unsigned long)fr->length)
+ fr->length = (hw_w*hw_h*hw_d)/8;
+ _PSWAP(pIn, pOut)
+ if (err) {
+ DBG(4, "An error occurred while decoding the frame: "
+ "%s.", symbolic(decoder_errlist, err))
+ return err;
+ } else
+ DBG(6, "Frame decoded")
+ }
+
+ if (cam->vpp_flag & VPP_SWAP_YUV_BYTES) {
+ (*w9968cf_vpp_swap_yuvbytes)(pIn, fr->length);
+ DBG(6, "Original UYVY component ordering changed.")
+ }
+
+ if (cam->vpp_flag & VPP_UPSCALE) {
+ (*w9968cf_vpp_scale_up)(pIn, pOut, hw_w, hw_h, hw_d, w, h);
+ fr->length = (w*h*hw_d)/8;
+ _PSWAP(pIn, pOut)
+ DBG(6, "Vertical up-scaling done: %d,%d,%dbpp->%d,%d",
+ hw_w, hw_h, hw_d, w, h)
+ }
+
+ if (cam->vpp_flag & VPP_UYVY_TO_RGBX) {
+ (*w9968cf_vpp_uyvy_to_rgbx)(pIn, fr->length, pOut, fmt, rgb);
+ fr->length = (w*h*d)/8;
+ _PSWAP(pIn, pOut)
+ DBG(6, "UYVY-16bit to %s conversion done.",
+ symbolic(v4l1_plist, fmt))
+ }
+
+ if (pOut == fr->buffer)
+ memcpy(fr->buffer, cam->vpp_buffer, fr->length);
+
+ return 0;
+}
+
+
+
+/****************************************************************************
+ * CMOS sensor control routines *
+ ****************************************************************************/
+
+static int
+w9968cf_sensor_set_control(struct w9968cf_device* cam, int cid, int val)
+{
+ struct ovcamchip_control ctl;
+ int rc;
+
+ ctl.id = cid;
+ ctl.value = val;
+
+ rc = w9968cf_sensor_cmd(cam, OVCAMCHIP_CMD_S_CTRL, &ctl);
+
+ return rc;
+}
+
+static int
+w9968cf_sensor_get_control(struct w9968cf_device* cam, int cid, int *val)
+{
+ struct ovcamchip_control ctl;
+ int rc;
+
+ ctl.id = cid;
+
+ rc = w9968cf_sensor_cmd(cam, OVCAMCHIP_CMD_G_CTRL, &ctl);
+ if (rc >= 0)
+ *val = ctl.value;
+
+ return rc;
+}
+
+
+static inline int
+w9968cf_sensor_cmd(struct w9968cf_device* cam, unsigned int cmd, void *arg)
+{
+ struct i2c_client* c = cam->sensor_client;
+
+ DBG(6, "Executing CMOS sensor command...")
+
+ if (c && c->driver->command)
+ return c->driver->command(cam->sensor_client, cmd, arg);
+ else
+ return -ENODEV;
+}
+
+
+/*--------------------------------------------------------------------------
+ Change some settings of the CMOS sensor.
+ Returns: 0 for success, a negative number otherwise.
+ --------------------------------------------------------------------------*/
+static int w9968cf_sensor_change_settings(struct w9968cf_device* cam)
+{
+ int rc;
+
+ /* Auto brightness */
+ rc = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_AUTOBRIGHT,
+ cam->auto_brt);
+ if (SENSOR_FATAL_ERROR(rc))
+ return rc;
+
+ /* Auto exposure */
+ rc = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_AUTOEXP,
+ cam->auto_exp);
+ if (SENSOR_FATAL_ERROR(rc))
+ return rc;
+
+ /* Banding filter */
+ rc = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_BANDFILT,
+ cam->bandfilt);
+ if (SENSOR_FATAL_ERROR(rc))
+ return rc;
+
+ /* Light frequency */
+ rc = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_FREQ,
+ cam->lightfreq);
+ if (SENSOR_FATAL_ERROR(rc))
+ return rc;
+
+ /* Back light */
+ rc = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_BACKLIGHT,
+ cam->backlight);
+ if (SENSOR_FATAL_ERROR(rc))
+ return rc;
+
+ /* Mirror */
+ rc = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_MIRROR,
+ cam->mirror);
+ if (SENSOR_FATAL_ERROR(rc))
+ return rc;
+
+ return 0;
+}
+
+
+/*--------------------------------------------------------------------------
+ Get some current picture settings from the CMOS sensor.
+ Returns: 0 for success, a negative number otherwise.
+ --------------------------------------------------------------------------*/
+static int
+w9968cf_sensor_get_picture(struct w9968cf_device* cam,
+ struct video_picture* pict)
+{
+ int rc, v;
+
+ /* Don't return error if a setting is unsupported, or rest of settings
+ will not be performed */
+
+ rc = w9968cf_sensor_get_control(cam, OVCAMCHIP_CID_CONT, &v);
+ if (SENSOR_FATAL_ERROR(rc))
+ return rc;
+ pict->contrast = v;
+
+ rc = w9968cf_sensor_get_control(cam, OVCAMCHIP_CID_BRIGHT, &v);
+ if (SENSOR_FATAL_ERROR(rc))
+ return rc;
+ pict->brightness = v;
+
+ rc = w9968cf_sensor_get_control(cam, OVCAMCHIP_CID_SAT, &v);
+ if (SENSOR_FATAL_ERROR(rc))
+ return rc;
+ pict->colour = v;
+
+ rc = w9968cf_sensor_get_control(cam, OVCAMCHIP_CID_HUE, &v);
+ if (SENSOR_FATAL_ERROR(rc))
+ return rc;
+ pict->hue = v;
+
+ pict->whiteness = W9968CF_WHITENESS; /* to do! */
+
+ DBG(5, "Got picture settings from the CMOS sensor.")
+
+ PDBGG("Brightness, contrast, hue, colour, whiteness are "
+ "%d,%d,%d,%d,%d.", pict->brightness, pict->contrast,
+ pict->hue, pict->colour, pict->whiteness)
+
+ return 0;
+}
+
+
+/*--------------------------------------------------------------------------
+ Change picture settings of the CMOS sensor.
+ Returns: 0 for success, a negative number otherwise.
+ --------------------------------------------------------------------------*/
+static int
+w9968cf_sensor_set_picture(struct w9968cf_device* cam,
+ struct video_picture pict)
+{
+ int rc;
+
+ rc = w9968cf_sensor_set_control(cam,OVCAMCHIP_CID_CONT, pict.contrast);
+ if (SENSOR_FATAL_ERROR(rc))
+ return rc;
+
+ if (!cam->auto_brt) {
+ rc = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_BRIGHT,
+ pict.brightness);
+ if (SENSOR_FATAL_ERROR(rc))
+ return rc;
+ }
+
+ rc = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_SAT, pict.colour);
+ if (SENSOR_FATAL_ERROR(rc))
+ return rc;
+
+ rc = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_HUE, pict.hue);
+ if (SENSOR_FATAL_ERROR(rc))
+ return rc;
+
+ PDBGG("Brightness, contrast, hue, colour, whiteness are "
+ "%d,%d,%d,%d,%d.", pict.brightness, pict.contrast,
+ pict.hue, pict.colour, pict.whiteness)
+
+ return 0;
+}
+
+
+
+/****************************************************************************
+ * Camera configuration *
+ ****************************************************************************/
+
+/*--------------------------------------------------------------------------
+ This function is called when the CMOS sensor is detected.
+ --------------------------------------------------------------------------*/
+static void w9968cf_sensor_configure(struct w9968cf_device* cam)
+{
+ /* NOTE: Make sure width and height are a multiple of 16 */
+
+ switch (cam->sensor_client->addr) {
+ case OV6xx0_SID:
+ cam->maxwidth = 352;
+ cam->maxheight = 288;
+ cam->minwidth = 64;
+ cam->minheight = 48;
+ break;
+ case OV7xx0_SID:
+ cam->maxwidth = 640;
+ cam->maxheight = 480;
+ cam->minwidth = 64;
+ cam->minheight = 48;
+ break;
+ }
+
+ /* These values depend on the ones in the ovxxx0.c sources */
+ switch (cam->sensor) {
+ case CC_OV7620:
+ cam->start_cropx = 287;
+ cam->start_cropy = 35;
+ /* Seems to work around a bug in the CMOS sensor */
+ cam->vs_polarity = 1;
+ cam->hs_polarity = 1;
+ break;
+ default:
+ cam->start_cropx = 320;
+ cam->start_cropy = 35;
+ cam->vs_polarity = 1;
+ cam->hs_polarity = 0;
+ }
+
+ DBG(5, "CMOS sensor %s configured.", symbolic(senlist, cam->sensor))
+}
+
+
+/*--------------------------------------------------------------------------
+ Fill some basic fields in the main device data structure.
+ This function is called once on w9968cf_usb_probe() for each recognized
+ camera.
+ --------------------------------------------------------------------------*/
+static void
+w9968cf_configure_camera(struct w9968cf_device* cam,
+ struct usb_device* udev,
+ enum w9968cf_model_id mod_id,
+ const unsigned short dev_nr)
+{
+ init_MUTEX(&cam->fileop_sem);
+ init_waitqueue_head(&cam->open);
+ spin_lock_init(&cam->urb_lock);
+ spin_lock_init(&cam->flist_lock);
+
+ cam->users = 0;
+ cam->disconnected = 0;
+ cam->usbdev = udev;
+ cam->id = mod_id;
+ cam->sensor = CC_UNKNOWN;
+
+ strcpy(cam->v4ldev.name, symbolic(camlist, mod_id));
+ cam->v4ldev.type = VID_TYPE_CAPTURE | VID_TYPE_SCALES;
+ cam->v4ldev.hardware = VID_HARDWARE_W9968CF;
+ cam->v4ldev.fops = &w9968cf_fops;
+ cam->v4ldev.priv = (void*)cam;
+ cam->v4ldev.minor = video_nr[dev_nr];
+
+ /* Calculate the alternate setting number (from 1 to 16)
+ according to the 'packet_size' module parameter */
+ if (packet_size[dev_nr] < W9968CF_MIN_PACKET_SIZE)
+ packet_size[dev_nr] = W9968CF_MIN_PACKET_SIZE;
+ for (cam->altsetting = 1;
+ packet_size[dev_nr] < wMaxPacketSize[cam->altsetting-1];
+ cam->altsetting++);
+
+ cam->max_buffers = (max_buffers[dev_nr] < 2 ||
+ max_buffers[dev_nr] > W9968CF_MAX_BUFFERS)
+ ? W9968CF_BUFFERS : max_buffers[dev_nr];
+
+ cam->double_buffer = (double_buffer[dev_nr] == 0 ||
+ double_buffer[dev_nr] == 1)
+ ? double_buffer[dev_nr] : W9968CF_DOUBLE_BUFFER;
+
+ cam->clamping = (clamping[dev_nr] == 0 || clamping[dev_nr] == 1)
+ ? clamping[dev_nr] : W9968CF_CLAMPING;
+
+ cam->filter_type = (filter_type[dev_nr] == 0 ||
+ filter_type[dev_nr] == 1 ||
+ filter_type[dev_nr] == 2)
+ ? filter_type[dev_nr] : W9968CF_FILTER_TYPE;
+
+ cam->capture = 1;
+
+ cam->largeview = (largeview[dev_nr] == 0 || largeview[dev_nr] == 1)
+ ? largeview[dev_nr] : W9968CF_LARGEVIEW;
+
+ cam->decompression = (decompression[dev_nr] == 0 ||
+ decompression[dev_nr] == 1 ||
+ decompression[dev_nr] == 2)
+ ? decompression[dev_nr] : W9968CF_DECOMPRESSION;
+
+ cam->upscaling = (upscaling[dev_nr] == 0 ||
+ upscaling[dev_nr] == 1)
+ ? upscaling[dev_nr] : W9968CF_UPSCALING;
+
+ cam->auto_brt = (autobright[dev_nr] == 0 || autobright[dev_nr] == 1)
+ ? autobright[dev_nr] : W9968CF_AUTOBRIGHT;
+
+ cam->auto_exp = (autoexp[dev_nr] == 0 || autoexp[dev_nr] == 1)
+ ? autoexp[dev_nr] : W9968CF_AUTOEXP;
+
+ cam->lightfreq = (lightfreq[dev_nr] == 50 || lightfreq[dev_nr] == 60)
+ ? lightfreq[dev_nr] : W9968CF_LIGHTFREQ;
+
+ cam->bandfilt = (bandingfilter[dev_nr] == 0 ||
+ bandingfilter[dev_nr] == 1)
+ ? bandingfilter[dev_nr] : W9968CF_BANDINGFILTER;
+
+ cam->backlight = (backlight[dev_nr] == 0 || backlight[dev_nr] == 1)
+ ? backlight[dev_nr] : W9968CF_BACKLIGHT;
+
+ cam->clockdiv = (clockdiv[dev_nr] == -1 || clockdiv[dev_nr] >= 0)
+ ? clockdiv[dev_nr] : W9968CF_CLOCKDIV;
+
+ cam->mirror = (mirror[dev_nr] == 0 || mirror[dev_nr] == 1)
+ ? mirror[dev_nr] : W9968CF_MIRROR;
+
+ cam->sensor_mono = (sensor_mono[dev_nr]==0 || sensor_mono[dev_nr]==1)
+ ? sensor_mono[dev_nr] : W9968CF_SENSOR_MONO;
+
+ cam->picture.brightness = brightness[dev_nr];
+ cam->picture.hue = hue[dev_nr];
+ cam->picture.colour = colour[dev_nr];
+ cam->picture.contrast = contrast[dev_nr];
+ cam->picture.whiteness = whiteness[dev_nr];
+ if (w9968cf_valid_palette(force_palette[dev_nr])) {
+ cam->picture.palette = force_palette[dev_nr];
+ cam->force_palette = 1;
+ } else {
+ cam->force_palette = 0;
+ if (cam->decompression == 0)
+ cam->picture.palette = W9968CF_PALETTE_DECOMP_OFF;
+ else if (cam->decompression == 1)
+ cam->picture.palette = W9968CF_PALETTE_DECOMP_FORCE;
+ else
+ cam->picture.palette = W9968CF_PALETTE_DECOMP_ON;
+ }
+
+ cam->force_rgb = (force_rgb[dev_nr] == 0 || force_rgb[dev_nr] == 1)
+ ? force_rgb[dev_nr] : W9968CF_FORCE_RGB;
+
+ cam->window.x = 0;
+ cam->window.y = 0;
+ cam->window.width = W9968CF_WIDTH;
+ cam->window.height = W9968CF_HEIGHT;
+ cam->window.chromakey = 0;
+ cam->window.clipcount = 0;
+ cam->window.flags = 0;
+
+ /* If the video post-processing module is not present, some paramaters
+ must be overridden: */
+ if (!w9968cf_vppmod_present) {
+ if (cam->decompression == 1)
+ cam->decompression = 2;
+ cam->upscaling = 0;
+ if (cam->picture.palette != VIDEO_PALETTE_UYVY)
+ cam->force_palette = 0;
+ cam->picture.palette = VIDEO_PALETTE_UYVY;
+ }
+
+ cam->picture.depth = w9968cf_valid_depth(cam->picture.palette);
+
+ DBG(3, "%s configured with settings #%d:",
+ symbolic(camlist, cam->id), dev_nr)
+
+ DBG(3, "- Data packet size for USB isochrnous transfer: %d bytes.",
+ wMaxPacketSize[cam->altsetting-1])
+
+ DBG(3, "- Number of requested video frame buffers: %d",
+ cam->max_buffers)
+
+ if (cam->double_buffer)
+ DBG(3, "- Hardware double buffering enabled.")
+ else
+ DBG(3, "- Hardware double buffering disabled.")
+
+ if (cam->filter_type == 0)
+ DBG(3, "- Video filtering disabled.")
+ else if (cam->filter_type == 1)
+ DBG(3, "- Video filtering enabled: type 1-2-1.")
+ else if (cam->filter_type == 2)
+ DBG(3, "- Video filtering enabled: type 2-3-6-3-2.")
+
+ if (cam->clamping)
+ DBG(3, "- Video data clamping (CCIR-601 format) enabled.")
+ else
+ DBG(3, "- Video data clamping (CCIR-601 format) disabled.")
+
+ if (cam->largeview)
+ DBG(3, "- Large view enabled.")
+ else
+ DBG(3, "- Large view disabled.")
+
+ if ((cam->decompression) == 0 && (!cam->force_palette))
+ DBG(3, "- Decompression disabled.")
+ else if ((cam->decompression) == 1 && (!cam->force_palette))
+ DBG(3, "- Decompression forced.")
+ else if ((cam->decompression) == 2 && (!cam->force_palette))
+ DBG(3, "- Decompression allowed.")
+
+ if (cam->upscaling)
+ DBG(3, "- Software image scaling enabled.")
+ else
+ DBG(3, "- Software image scaling disabled.")
+
+ if (cam->force_palette)
+ DBG(3, "- Image palette forced to %s.",
+ symbolic(v4l1_plist, cam->picture.palette))
+
+ if (cam->force_rgb)
+ DBG(3, "- RGB component ordering will be used instead of BGR.")
+
+ if (cam->auto_brt)
+ DBG(3, "- Auto brightness enabled.")
+ else
+ DBG(3, "- Auto brightness disabled.")
+
+ if (cam->auto_exp)
+ DBG(3, "- Auto exposure enabled.")
+ else
+ DBG(3, "- Auto exposure disabled.")
+
+ if (cam->backlight)
+ DBG(3, "- Backlight exposure algorithm enabled.")
+ else
+ DBG(3, "- Backlight exposure algorithm disabled.")
+
+ if (cam->mirror)
+ DBG(3, "- Mirror enabled.")
+ else
+ DBG(3, "- Mirror disabled.")
+
+ if (cam->bandfilt)
+ DBG(3, "- Banding filter enabled.")
+ else
+ DBG(3, "- Banding filter disabled.")
+
+ DBG(3, "- Power lighting frequency: %d", cam->lightfreq)
+
+ if (cam->clockdiv == -1)
+ DBG(3, "- Automatic clock divisor enabled.")
+ else
+ DBG(3, "- Clock divisor: %d", cam->clockdiv)
+
+ if (cam->sensor_mono)
+ DBG(3, "- CMOS sensor used as monochrome.")
+ else
+ DBG(3, "- CMOS sensor not used as monochrome.")
+}
+
+
+/*--------------------------------------------------------------------------
+ Release the resources used by the driver.
+ This function is called on disconnect
+ (or on close if deallocation has been deferred)
+ --------------------------------------------------------------------------*/
+static void w9968cf_release_resources(struct w9968cf_device* cam)
+{
+ down(&w9968cf_devlist_sem);
+
+ DBG(2, "V4L device deregistered: /dev/video%d", cam->v4ldev.minor)
+
+ video_unregister_device(&cam->v4ldev);
+ list_del(&cam->v4llist);
+ i2c_del_adapter(&cam->i2c_adapter);
+ w9968cf_deallocate_memory(cam);
+ kfree(cam->control_buffer);
+ kfree(cam->data_buffer);
+
+ up(&w9968cf_devlist_sem);
+
+ DBG(5, "Resources released.")
+}
+
+
+
+/****************************************************************************
+ * Video4Linux interface *
+ ****************************************************************************/
+
+static int w9968cf_open(struct inode* inode, struct file* filp)
+{
+ struct w9968cf_device* cam =
+ (struct w9968cf_device*)video_devdata(filp)->priv;
+ int err;
+
+ down(&cam->dev_sem);
+
+ if (cam->sensor == CC_UNKNOWN) {
+ DBG(2, "No supported CMOS sensor has been detected by the "
+ "'ovcamchip' module for the %s (/dev/video%d). Make "
+ "sure it is loaded *before* the 'w9968cf' module.",
+ symbolic(camlist, cam->id),cam->v4ldev.minor)
+ up(&cam->dev_sem);
+ return -ENODEV;
+ }
+
+ if (cam->users) {
+ DBG(2, "%s (/dev/video%d) has been already occupied by '%s'.",
+ symbolic(camlist, cam->id),cam->v4ldev.minor, cam->command)
+ if ((filp->f_flags & O_NONBLOCK)||(filp->f_flags & O_NDELAY)) {
+ up(&cam->dev_sem);
+ return -EWOULDBLOCK;
+ }
+ up(&cam->dev_sem);
+ err = wait_event_interruptible(cam->open, cam->disconnected ||
+ (cam->users == 0));
+ if (err)
+ return err;
+ if (cam->disconnected)
+ return -ENODEV;
+ down(&cam->dev_sem);
+ }
+
+ DBG(5, "Opening the %s, /dev/video%d ...",
+ symbolic(camlist, cam->id), cam->v4ldev.minor)
+
+ cam->streaming = 0;
+ cam->misconfigured = 0;
+
+ if (!w9968cf_vppmod_present)
+ w9968cf_vppmod_detect();
+
+ if ((err = w9968cf_allocate_memory(cam)))
+ goto deallocate_memory;
+
+ if ((err = w9968cf_init_chip(cam)))
+ goto deallocate_memory;
+
+ if ((err = w9968cf_start_transfer(cam)))
+ goto deallocate_memory;
+
+ filp->private_data = (void*)cam;
+
+ cam->users++;
+ strcpy(cam->command, current->comm);
+
+ init_waitqueue_head(&cam->wait_queue);
+
+ up(&cam->dev_sem);
+
+ DBG(5, "Video device is open.")
+ return 0;
+
+deallocate_memory:
+ w9968cf_deallocate_memory(cam);
+ DBG(2, "Failed to open the video device.")
+ up(&cam->dev_sem);
+ return err;
+}
+
+
+static int w9968cf_release(struct inode* inode, struct file* filp)
+{
+ struct w9968cf_device* cam =
+ (struct w9968cf_device*)video_devdata(filp)->priv;
+
+ down(&cam->dev_sem); /* prevent disconnect() to be called */
+
+ w9968cf_stop_transfer(cam);
+
+ if (cam->disconnected) {
+ w9968cf_release_resources(cam);
+ up(&cam->dev_sem);
+ kfree(cam);
+ return 0;
+ }
+
+ cam->users--;
+ w9968cf_deallocate_memory(cam);
+
+ if (waitqueue_active(&cam->open))
+ wake_up_interruptible(&cam->open);
+
+ DBG(5, "Video device closed.")
+ up(&cam->dev_sem);
+ return 0;
+}
+
+
+static ssize_t
+w9968cf_read(struct file* filp, char* buf, size_t count, loff_t* f_pos)
+{
+ struct w9968cf_device* cam =
+ (struct w9968cf_device*)video_devdata(filp)->priv;
+ struct w9968cf_frame_t* fr;
+ int err = 0;
+
+ if (filp->f_flags & O_NONBLOCK)
+ return -EWOULDBLOCK;
+
+ if (down_interruptible(&cam->fileop_sem))
+ return -ERESTARTSYS;
+
+ if (cam->disconnected) {
+ DBG(2, "Device not present.")
+ up(&cam->fileop_sem);
+ return -ENODEV;
+ }
+
+ if (cam->misconfigured) {
+ DBG(2, "The camera is misconfigured. Close and open it again.")
+ up(&cam->fileop_sem);
+ return -EIO;
+ }
+
+ if (!cam->frame[0].queued)
+ w9968cf_push_frame(cam, 0);
+
+ if (!cam->frame[1].queued)
+ w9968cf_push_frame(cam, 1);
+
+ err = wait_event_interruptible(cam->wait_queue,
+ cam->frame[0].status == F_READY ||
+ cam->frame[1].status == F_READY ||
+ cam->disconnected);
+ if (err) {
+ up(&cam->fileop_sem);
+ return err;
+ }
+ if (cam->disconnected) {
+ up(&cam->fileop_sem);
+ return -ENODEV;
+ }
+
+ fr = (cam->frame[0].status == F_READY) ? &cam->frame[0]:&cam->frame[1];
+
+ if (w9968cf_vppmod_present)
+ w9968cf_postprocess_frame(cam, fr);
+
+ if (count > fr->length)
+ count = fr->length;
+
+ if (copy_to_user(buf, fr->buffer, count)) {
+ fr->status = F_UNUSED;
+ up(&cam->fileop_sem);
+ return -EFAULT;
+ }
+ *f_pos += count;
+
+ fr->status = F_UNUSED;
+
+ DBG(5, "%d bytes read.", count)
+
+ up(&cam->fileop_sem);
+ return count;
+}
+
+
+static int w9968cf_mmap(struct file* filp, struct vm_area_struct *vma)
+{
+ struct w9968cf_device* cam =
+ (struct w9968cf_device*)video_devdata(filp)->priv;
+
+ unsigned long vsize = vma->vm_end - vma->vm_start,
+ psize = cam->nbuffers * w9968cf_get_max_bufsize(cam),
+ start = vma->vm_start,
+ pos = (unsigned long)cam->frame[0].buffer,
+ page;
+
+ if (cam->disconnected) {
+ DBG(2, "Device not present.")
+ return -ENODEV;
+ }
+
+ if (cam->misconfigured) {
+ DBG(2, "The camera is misconfigured. Close and open it again.")
+ return -EIO;
+ }
+
+ PDBGG("mmapping %li bytes...", vsize)
+
+ if (vsize > psize - (vma->vm_pgoff << PAGE_SHIFT))
+ return -EAGAIN;
+
+ while (vsize > 0) {
+ page = kvirt_to_pa(pos) + vma->vm_pgoff;
+ if (remap_page_range(vma, start, page, PAGE_SIZE,
+ vma->vm_page_prot))
+ return -EAGAIN;
+ start += PAGE_SIZE;
+ pos += PAGE_SIZE;
+ vsize = (vsize > PAGE_SIZE) ? vsize-PAGE_SIZE : 0;
+ }
+
+ DBG(5, "mmap method successfully called.")
+ return 0;
+}
+
+
+static int
+w9968cf_ioctl(struct inode* inode, struct file* filp,
+ unsigned int cmd, unsigned long arg)
+{
+ struct w9968cf_device* cam =
+ (struct w9968cf_device*)video_devdata(filp)->priv;
+ int err;
+
+ if (down_interruptible(&cam->fileop_sem))
+ return -ERESTARTSYS;
+
+ if (cam->disconnected) {
+ DBG(2, "Device not present.")
+ up(&cam->fileop_sem);
+ return -ENODEV;
+ }
+
+ if (cam->misconfigured) {
+ DBG(2, "The camera is misconfigured. Close and open it again.")
+ up(&cam->fileop_sem);
+ return -EIO;
+ }
+
+ err = w9968cf_do_ioctl(cam, cmd, (void*)arg);
+
+ up(&cam->fileop_sem);
+ return err;
+}
+
+
+static int
+w9968cf_do_ioctl(struct w9968cf_device* cam, unsigned cmd, void* arg)
+{
+ const char* v4l1_ioctls[] = {
+ "?", "CGAP", "GCHAN", "SCHAN", "GTUNER", "STUNER",
+ "GPICT", "SPICT", "CCAPTURE", "GWIN", "SWIN", "GFBUF",
+ "SFBUF", "KEY", "GFREQ", "SFREQ", "GAUDIO", "SAUDIO",
+ "SYNC", "MCAPTURE", "GMBUF", "GUNIT", "GCAPTURE", "SCAPTURE",
+ "SPLAYMODE", "SWRITEMODE", "GPLAYINFO", "SMICROCODE",
+ "GVBIFMT", "SVBIFMT"
+ };
+
+ #define V4L1_IOCTL(cmd) \
+ ((_IOC_NR((cmd)) < sizeof(v4l1_ioctls)/sizeof(char*)) ? \
+ v4l1_ioctls[_IOC_NR((cmd))] : "???")
+
+ switch (cmd) {
+
+ case VIDIOCGCAP: /* get video capability */
+ {
+ struct video_capability cap = {
+ .type = VID_TYPE_CAPTURE | VID_TYPE_SCALES,
+ .channels = 1,
+ .audios = 0,
+ .minwidth = cam->minwidth,
+ .minheight = cam->minheight,
+ };
+ sprintf(cap.name, "W996[87]CF USB Camera #%d",
+ cam->v4ldev.minor);
+ cap.maxwidth = (cam->upscaling && w9968cf_vppmod_present)
+ ? W9968CF_MAX_WIDTH : cam->maxwidth;
+ cap.maxheight = (cam->upscaling && w9968cf_vppmod_present)
+ ? W9968CF_MAX_HEIGHT : cam->maxheight;
+
+ if (copy_to_user(arg, &cap, sizeof(cap)))
+ return -EFAULT;
+
+ DBG(5, "VIDIOCGCAP successfully called.")
+ return 0;
+ }
+
+ case VIDIOCGCHAN: /* get video channel informations */
+ {
+ struct video_channel chan;
+ if (copy_from_user(&chan, arg, sizeof(chan)))
+ return -EFAULT;
+
+ if (chan.channel != 0)
+ return -EINVAL;
+
+ strcpy(chan.name, "Camera");
+ chan.tuners = 0;
+ chan.flags = 0;
+ chan.type = VIDEO_TYPE_CAMERA;
+ chan.norm = VIDEO_MODE_AUTO;
+
+ if (copy_to_user(arg, &chan, sizeof(chan)))
+ return -EFAULT;
+
+ DBG(5, "VIDIOCGCHAN successfully called.")
+ return 0;
+ }
+
+ case VIDIOCSCHAN: /* set active channel */
+ {
+ struct video_channel chan;
+
+ if (copy_from_user(&chan, arg, sizeof(chan)))
+ return -EFAULT;
+
+ if (chan.channel != 0)
+ return -EINVAL;
+
+ DBG(5, "VIDIOCSCHAN successfully called.")
+ return 0;
+ }
+
+ case VIDIOCGPICT: /* get image properties of the picture */
+ {
+ struct video_picture pict;
+
+ if (w9968cf_sensor_get_picture(cam, &pict))
+ return -EIO;
+
+ pict.depth = cam->picture.depth;
+ pict.palette = cam->picture.palette;
+
+ if (copy_to_user(arg, &pict, sizeof(pict)))
+ return -EFAULT;
+
+ DBG(5, "VIDIOCGPICT successfully called.")
+ return 0;
+ }
+
+ case VIDIOCSPICT: /* change picture settings */
+ {
+ struct video_picture pict;
+ int err = 0;
+
+ if (copy_from_user(&pict, arg, sizeof(pict)))
+ return -EFAULT;
+
+ if ( (cam->force_palette || !w9968cf_vppmod_present)
+ && pict.palette != cam->picture.palette ) {
+ DBG(4, "Palette %s rejected. Only %s is allowed.",
+ symbolic(v4l1_plist, pict.palette),
+ symbolic(v4l1_plist, cam->picture.palette))
+ return -EINVAL;
+ }
+
+ if (!w9968cf_valid_palette(pict.palette)) {
+ DBG(4, "Palette %s not supported. VIDIOCSPICT failed.",
+ symbolic(v4l1_plist, pict.palette))
+ return -EINVAL;
+ }
+
+ if (pict.depth != w9968cf_valid_depth(pict.palette)) {
+ DBG(4, "Depth %d bpp is not supported for %s palette. "
+ "VIDIOCSPICT failed.",
+ pict.depth, symbolic(v4l1_plist, pict.palette))
+ return -EINVAL;
+ }
+
+ if (!cam->force_palette) {
+ if (cam->decompression == 0) {
+ if (w9968cf_need_decompression(pict.palette)) {
+ DBG(4, "Decompression disabled: palette %s is not "
+ "allowed. VIDIOCSPICT failed.",
+ symbolic(v4l1_plist, pict.palette))
+ return -EINVAL;
+ }
+ } else if (cam->decompression == 1) {
+ if (!w9968cf_need_decompression(pict.palette)) {
+ DBG(4, "Decompression forced: palette %s is not "
+ "allowed. VIDIOCSPICT failed.",
+ symbolic(v4l1_plist, pict.palette))
+ return -EINVAL;
+ }
+ }
+ }
+
+ if (pict.palette != cam->picture.palette ||
+ pict.depth != cam->picture.depth)
+ {
+ if(*cam->requested_frame
+ || cam->frame_current->queued) {
+ err = wait_event_interruptible
+ ( cam->wait_queue,
+ cam->disconnected ||
+ (!*cam->requested_frame &&
+ !cam->frame_current->queued) );
+ if (err)
+ return err;
+ if (cam->disconnected)
+ return -ENODEV;
+ }
+
+ if (w9968cf_stop_transfer(cam))
+ goto ioctl_fail;
+
+ if (w9968cf_set_picture(cam, pict))
+ goto ioctl_fail;
+
+ if (w9968cf_start_transfer(cam))
+ goto ioctl_fail;
+
+ } else if ( ((pict.brightness != cam->picture.brightness) &&
+ (!cam->auto_brt)) ||
+ pict.hue != cam->picture.hue ||
+ pict.colour != cam->picture.colour ||
+ pict.contrast != cam->picture.contrast ||
+ pict.whiteness != cam->picture.whiteness ) {
+ if (w9968cf_sensor_set_picture(cam, pict))
+ return -EIO;
+ }
+
+ DBG(5, "VIDIOCSPICT successfully called.")
+ return 0;
+ }
+
+ case VIDIOCSWIN: /* set capture area */
+ {
+ struct video_window win;
+ int err = 0;
+
+ if (copy_from_user(&win, arg, sizeof(win)))
+ return -EFAULT;
+
+ DBG(6, "VIDIOCSWIN called: clipcount=%d, flags=%d, "
+ "x=%d, y=%d, %dx%d", win.clipcount, win.flags,
+ win.x, win.y, win.width, win.height)
+
+ if (win.clipcount != 0 || win.flags != 0)
+ return -EINVAL;
+
+ if ((err = w9968cf_adjust_window_size(cam, (u16*)&win.width,
+ (u16*)&win.height))) {
+ DBG(4, "Resolution not supported (%dx%d)."
+ "VIDIOCSWIN failed.", win.width, win.height)
+ return err;
+ }
+
+ if (win.x != cam->window.x ||
+ win.y != cam->window.y ||
+ win.width != cam->window.width ||
+ win.height != cam->window.height) {
+
+ if(*cam->requested_frame
+ || cam->frame_current->queued) {
+ err = wait_event_interruptible
+ ( cam->wait_queue,
+ cam->disconnected ||
+ (!*cam->requested_frame &&
+ !cam->frame_current->queued) );
+ if (err)
+ return err;
+ if (cam->disconnected)
+ return -ENODEV;
+ }
+
+ if (w9968cf_stop_transfer(cam))
+ goto ioctl_fail;
+
+ /* This _must_ be called before set_window() */
+ if (w9968cf_set_picture(cam, cam->picture))
+ goto ioctl_fail;
+
+ if (w9968cf_set_window(cam, win))
+ goto ioctl_fail;
+
+ if (w9968cf_start_transfer(cam))
+ goto ioctl_fail;
+ }
+
+ DBG(5, "VIDIOCSWIN successfully called. ")
+ return 0;
+ }
+
+ case VIDIOCGWIN: /* get current window properties */
+ {
+ if (copy_to_user(arg,&cam->window,sizeof(struct video_window)))
+ return -EFAULT;
+
+ DBG(5, "VIDIOCGWIN successfully called.")
+ return 0;
+ }
+
+ case VIDIOCGMBUF: /* request for memory (mapped) buffer */
+ {
+ struct video_mbuf mbuf;
+ u8 i;
+
+ mbuf.size = cam->nbuffers * w9968cf_get_max_bufsize(cam);
+ mbuf.frames = cam->nbuffers;
+ for (i = 0; i < cam->nbuffers; i++)
+ mbuf.offsets[i] = (unsigned long)cam->frame[i].buffer -
+ (unsigned long)cam->frame[0].buffer;
+
+ if (copy_to_user(arg, &mbuf, sizeof(mbuf)))
+ return -EFAULT;
+
+ DBG(5, "VIDIOCGMBUF successfully called.")
+ return 0;
+ }
+
+ case VIDIOCMCAPTURE: /* start the capture to a frame */
+ {
+ struct video_mmap mmap;
+ struct w9968cf_frame_t* fr;
+ int err = 0;
+
+ if (copy_from_user(&mmap, arg, sizeof(mmap)))
+ return -EFAULT;
+
+ DBG(6, "VIDIOCMCAPTURE called: frame #%d, format=%s, %dx%d",
+ mmap.frame, symbolic(v4l1_plist, mmap.format),
+ mmap.width, mmap.height)
+
+ if (mmap.frame >= cam->nbuffers) {
+ DBG(4, "Invalid frame number (%d). "
+ "VIDIOCMCAPTURE failed.", mmap.frame)
+ return -EINVAL;
+ }
+
+ if (mmap.format!=cam->picture.palette &&
+ (cam->force_palette || !w9968cf_vppmod_present)) {
+ DBG(4, "Palette %s rejected. Only %s is allowed.",
+ symbolic(v4l1_plist, mmap.format),
+ symbolic(v4l1_plist, cam->picture.palette))
+ return -EINVAL;
+ }
+
+ if (!w9968cf_valid_palette(mmap.format)) {
+ DBG(4, "Palette %s not supported. "
+ "VIDIOCMCAPTURE failed.",
+ symbolic(v4l1_plist, mmap.format))
+ return -EINVAL;
+ }
+
+ if (!cam->force_palette) {
+ if (cam->decompression == 0) {
+ if (w9968cf_need_decompression(mmap.format)) {
+ DBG(4, "Decompression disabled: palette %s is not "
+ "allowed. VIDIOCSPICT failed.",
+ symbolic(v4l1_plist, mmap.format))
+ return -EINVAL;
+ }
+ } else if (cam->decompression == 1) {
+ if (!w9968cf_need_decompression(mmap.format)) {
+ DBG(4, "Decompression forced: palette %s is not "
+ "allowed. VIDIOCSPICT failed.",
+ symbolic(v4l1_plist, mmap.format))
+ return -EINVAL;
+ }
+ }
+ }
+
+ if (w9968cf_adjust_window_size(cam, (u16*)&mmap.width,
+ (u16*)&mmap.height)) {
+ DBG(4, "Resolution not supported (%dx%d). "
+ "VIDIOCMCAPTURE failed.",
+ mmap.width, mmap.height)
+ return -EINVAL;
+ }
+
+ fr = &cam->frame[mmap.frame];
+
+ if (mmap.width != cam->window.width ||
+ mmap.height != cam->window.height ||
+ mmap.format != cam->picture.palette) {
+
+ struct video_window win;
+ struct video_picture pict;
+
+ if(*cam->requested_frame
+ || cam->frame_current->queued) {
+ DBG(6, "VIDIOCMCAPTURE. Change settings for "
+ "frame #%d: %dx%d, format %s. Wait...",
+ mmap.frame, mmap.width, mmap.height,
+ symbolic(v4l1_plist, mmap.format))
+ err = wait_event_interruptible
+ ( cam->wait_queue,
+ cam->disconnected ||
+ (!*cam->requested_frame &&
+ !cam->frame_current->queued) );
+ if (err)
+ return err;
+ if (cam->disconnected)
+ return -ENODEV;
+ }
+
+ memcpy(&win, &cam->window, sizeof(win));
+ memcpy(&pict, &cam->picture, sizeof(pict));
+ win.width = mmap.width;
+ win.height = mmap.height;
+ pict.palette = mmap.format;
+
+ if (w9968cf_stop_transfer(cam))
+ goto ioctl_fail;
+
+ /* This before set_window */
+ if (w9968cf_set_picture(cam, pict))
+ goto ioctl_fail;
+
+ if (w9968cf_set_window(cam, win))
+ goto ioctl_fail;
+
+ if (w9968cf_start_transfer(cam))
+ goto ioctl_fail;
+
+ } else if (fr->queued) {
+
+ DBG(6, "Wait until frame #%d is free.", mmap.frame)
+
+ err = wait_event_interruptible(cam->wait_queue,
+ cam->disconnected ||
+ (!fr->queued));
+ if (err)
+ return err;
+ if (cam->disconnected)
+ return -ENODEV;
+ }
+
+ w9968cf_push_frame(cam, mmap.frame);
+ DBG(5, "VIDIOCMCAPTURE(%d): successfully called.", mmap.frame)
+ return 0;
+ }
+
+ case VIDIOCSYNC: /* wait until the capture of a frame is finished */
+ {
+ unsigned int f_num = *((unsigned int *) arg);
+ struct w9968cf_frame_t* fr;
+ int err = 0;
+
+ if (f_num >= cam->nbuffers) {
+ DBG(4, "Invalid frame number (%d). "
+ "VIDIOCMCAPTURE failed.", f_num)
+ return -EINVAL;
+ }
+
+ DBG(6, "VIDIOCSYNC called for frame #%d", f_num)
+
+ fr = &cam->frame[f_num];
+
+ switch (fr->status) {
+ case F_UNUSED:
+ if (!fr->queued) {
+ DBG(4, "VIDIOSYNC: Frame #%d not requested!",
+ f_num)
+ return -EFAULT;
+ }
+ case F_ERROR:
+ case F_GRABBING:
+ err = wait_event_interruptible(cam->wait_queue,
+ (fr->status == F_READY)
+ || cam->disconnected);
+ if (err)
+ return err;
+ if (cam->disconnected)
+ return -ENODEV;
+ break;
+ case F_READY:
+ break;
+ }
+
+ if (w9968cf_vppmod_present)
+ w9968cf_postprocess_frame(cam, fr);
+
+ fr->status = F_UNUSED;
+
+ DBG(5, "VIDIOCSYNC(%d) successfully called.", f_num)
+ return 0;
+ }
+
+ case VIDIOCGUNIT:/* report the unit numbers of the associated devices*/
+ {
+ struct video_unit unit = {
+ .video = cam->v4ldev.minor,
+ .vbi = VIDEO_NO_UNIT,
+ .radio = VIDEO_NO_UNIT,
+ .audio = VIDEO_NO_UNIT,
+ .teletext = VIDEO_NO_UNIT,
+ };
+
+ if (copy_to_user(arg, &unit, sizeof(unit)))
+ return -EFAULT;
+
+ DBG(5, "VIDIOCGUNIT successfully called.")
+ return 0;
+ }
+
+ case VIDIOCKEY:
+ return 0;
+
+ case VIDIOCGFBUF:
+ {
+ struct video_buffer* buffer = (struct video_buffer*)arg;
+
+ memset(buffer, 0, sizeof(struct video_buffer));
+
+ DBG(5, "VIDIOCGFBUF successfully called.")
+ return 0;
+ }
+
+ case VIDIOCGTUNER:
+ {
+ struct video_tuner tuner;
+ if (copy_from_user(&tuner, arg, sizeof(tuner)))
+ return -EFAULT;
+
+ if (tuner.tuner != 0);
+ return -EINVAL;
+
+ strcpy(tuner.name, "no_tuner");
+ tuner.rangelow = 0;
+ tuner.rangehigh = 0;
+ tuner.flags = VIDEO_TUNER_NORM;
+ tuner.mode = VIDEO_MODE_AUTO;
+ tuner.signal = 0xffff;
+
+ if (copy_to_user(arg, &tuner, sizeof(tuner)))
+ return -EFAULT;
+
+ DBG(5, "VIDIOCGTUNER successfully called.")
+ return 0;
+ }
+
+ case VIDIOCSTUNER:
+ {
+ struct video_tuner tuner;
+ if (copy_from_user(&tuner, arg, sizeof(tuner)))
+ return -EFAULT;
+
+ if (tuner.tuner != 0)
+ return -EINVAL;
+
+ if (tuner.mode != VIDEO_MODE_AUTO)
+ return -EINVAL;
+
+ DBG(5, "VIDIOCSTUNER successfully called.")
+ return 0;
+ }
+
+ case VIDIOCSFBUF:
+ case VIDIOCCAPTURE:
+ case VIDIOCGFREQ:
+ case VIDIOCSFREQ:
+ case VIDIOCGAUDIO:
+ case VIDIOCSAUDIO:
+ case VIDIOCSPLAYMODE:
+ case VIDIOCSWRITEMODE:
+ case VIDIOCGPLAYINFO:
+ case VIDIOCSMICROCODE:
+ case VIDIOCGVBIFMT:
+ case VIDIOCSVBIFMT:
+ DBG(4, "Unsupported V4L1 IOCtl: VIDIOC%s "
+ "(type 0x%01X, "
+ "n. 0x%01X, "
+ "dir. 0x%01X, "
+ "size 0x%02X).",
+ V4L1_IOCTL(cmd),
+ _IOC_TYPE(cmd),_IOC_NR(cmd),_IOC_DIR(cmd),_IOC_SIZE(cmd))
+
+ return -EINVAL;
+
+ default:
+ DBG(4, "Invalid V4L1 IOCtl: VIDIOC%s "
+ "type 0x%01X, "
+ "n. 0x%01X, "
+ "dir. 0x%01X, "
+ "size 0x%02X.",
+ V4L1_IOCTL(cmd),
+ _IOC_TYPE(cmd),_IOC_NR(cmd),_IOC_DIR(cmd),_IOC_SIZE(cmd))
+
+ return -ENOIOCTLCMD;
+
+ } /* end of switch */
+
+ioctl_fail:
+ cam->misconfigured = 1;
+ DBG(1, "VIDIOC%s failed because of hardware problems. "
+ "To use the camera, close and open it again.", V4L1_IOCTL(cmd))
+ return -EFAULT;
+}
+
+
+static struct file_operations w9968cf_fops = {
+ .owner = THIS_MODULE,
+ .open = w9968cf_open,
+ .release = w9968cf_release,
+ .read = w9968cf_read,
+ .ioctl = w9968cf_ioctl,
+ .mmap = w9968cf_mmap,
+ .llseek = no_llseek,
+};
+
+
+
+/****************************************************************************
+ * USB probe and V4L registration, disconnect and id_table[] definition *
+ ****************************************************************************/
+
+static int
+w9968cf_usb_probe(struct usb_interface* intf, const struct usb_device_id* id)
+{
+ struct usb_device *udev = interface_to_usbdev(intf);
+ struct w9968cf_device* cam;
+ int err = 0;
+ enum w9968cf_model_id mod_id;
+ struct list_head* ptr;
+ u8 sc = 0; /* number of simultaneous cameras */
+ static unsigned short dev_nr = 0; /* we are handling device number n */
+
+ if (udev->descriptor.idVendor == winbond_id_table[0].idVendor &&
+ udev->descriptor.idProduct == winbond_id_table[0].idProduct)
+ mod_id = W9968CF_MOD_CLVBWGP; /* see camlist[] table */
+
+ else if (udev->descriptor.idVendor == winbond_id_table[1].idVendor &&
+ udev->descriptor.idProduct == winbond_id_table[1].idProduct)
+ mod_id = W9968CF_MOD_GENERIC; /* see camlist[] table */
+
+ else
+ return -ENODEV;
+
+ /* We don't handle multi-config cameras */
+ if (udev->descriptor.bNumConfigurations != 1)
+ return -ENODEV;
+
+ DBG(2, "%s detected.", symbolic(camlist, mod_id))
+
+ if (simcams > W9968CF_MAX_DEVICES)
+ simcams = W9968CF_SIMCAMS;
+
+ /* How many cameras are connected ? */
+ down(&w9968cf_devlist_sem);
+ list_for_each(ptr, &w9968cf_dev_list)
+ sc++;
+ up(&w9968cf_devlist_sem);
+
+ if (sc >= simcams) {
+ DBG(2, "Device rejected: too many connected cameras "
+ "(max. %d)", simcams)
+ return -EPERM;
+ }
+
+ err = usb_set_configuration(udev, 1);
+ err += usb_set_interface(udev, 0, 0);
+
+ if (err) {
+ DBG(1, "Device configuration failed.")
+ return -EIO;
+ }
+
+ cam = (struct w9968cf_device*)
+ kmalloc(sizeof(struct w9968cf_device), GFP_KERNEL);
+
+ if (!cam) {
+ DBG(1, "Couldn't allocate %d bytes of kernel memory.",
+ sizeof(struct w9968cf_device))
+ err = -ENOMEM;
+ goto fail;
+ }
+ memset(cam, 0, sizeof(*cam));
+
+ init_MUTEX(&cam->dev_sem);
+ down(&cam->dev_sem);
+
+ /* Allocate 2 bytes of memory for camera control USB transfers */
+ if (!(cam->control_buffer = (u16*)kmalloc(2, GFP_KERNEL))) {
+ DBG(1,"Couldn't allocate memory for camera control transfers.")
+ err = -ENOMEM;
+ goto fail;
+ }
+ memset(cam->control_buffer, 0, 2);
+
+ /* Allocate 8 bytes of memory for USB data transfers to the FSB */
+ if (!(cam->data_buffer = (u16*)kmalloc(8, GFP_KERNEL))) {
+ DBG(1, "Couldn't allocate memory for data "
+ "transfers to the FSB.")
+ err = -ENOMEM;
+ goto fail;
+ }
+ memset(cam->data_buffer, 0, 8);
+
+ /* Set some basic constants */
+ w9968cf_configure_camera(cam, udev, mod_id, dev_nr);
+
+ err = video_register_device(&cam->v4ldev, VFL_TYPE_GRABBER,
+ video_nr[dev_nr]);
+ if (err) {
+ DBG(1, "V4L device registration failed.")
+ if (err == -ENFILE && video_nr[dev_nr] == -1)
+ DBG(2, "Couldn't find a free /dev/videoX node.")
+ video_nr[dev_nr] = -1;
+ dev_nr = (dev_nr < W9968CF_MAX_DEVICES-1) ? dev_nr+1 : 0;
+ goto fail;
+ }
+
+ DBG(2, "V4L device registered as /dev/video%d", cam->v4ldev.minor)
+
+ /* Ok, add a new entry into the list of V4L registered devices */
+ down(&w9968cf_devlist_sem);
+ list_add(&cam->v4llist, &w9968cf_dev_list);
+ up(&w9968cf_devlist_sem);
+
+ dev_nr = (dev_nr < W9968CF_MAX_DEVICES-1) ? dev_nr+1 : 0;
+
+ w9968cf_turn_on_led(cam);
+
+ w9968cf_i2c_init(cam);
+
+ up(&cam->dev_sem);
+
+ dev_set_drvdata(&intf->dev, (void*)cam);
+
+ return 0;
+
+fail: /* Free unused memory */
+ if (cam) {
+ if (cam->control_buffer)
+ kfree(cam->control_buffer);
+ if (cam->data_buffer)
+ kfree(cam->data_buffer);
+ up(&cam->dev_sem);
+ kfree(cam);
+ }
+ return err;
+}
+
+
+static void w9968cf_usb_disconnect(struct usb_interface* intf)
+{
+ struct w9968cf_device* cam =
+ (struct w9968cf_device*)dev_get_drvdata(&intf->dev);
+
+ dev_set_drvdata(&intf->dev, NULL);
+
+ if (cam) {
+ /* Prevent concurrent accesses to data */
+ down(&cam->dev_sem);
+
+ cam->streaming = 0;
+ cam->disconnected = 1;
+
+ DBG(2, "Disconnecting %s...", symbolic(camlist, cam->id))
+
+ if (waitqueue_active(&cam->open))
+ wake_up_interruptible(&cam->open);
+
+ if (cam->users) {
+ DBG(2, "The device is open (/dev/video%d)! "
+ "Process name: %s. Deregistration and memory "
+ "deallocation are deferred on close.",
+ cam->v4ldev.minor, cam->command)
+
+ cam->misconfigured = 1;
+
+ if (waitqueue_active(&cam->wait_queue))
+ wake_up_interruptible(&cam->wait_queue);
+ } else
+ w9968cf_release_resources(cam);
+
+ up(&cam->dev_sem);
+
+ if (!cam->users)
+ kfree(cam);
+ }
+}
+
+
+static struct usb_driver w9968cf_usb_driver = {
+ .owner = THIS_MODULE,
+ .name = "w9968cf",
+ .id_table = winbond_id_table,
+ .probe = w9968cf_usb_probe,
+ .disconnect = w9968cf_usb_disconnect,
+};
+
+
+
+/****************************************************************************
+ * Module init, exit and intermodule communication *
+ ****************************************************************************/
+
+static int w9968cf_vppmod_detect(void)
+{
+ w9968cf_vpp_init_decoder = inter_module_get("w9968cf_init_decoder");
+
+ if (!w9968cf_vpp_init_decoder) {
+ if (vppmod_load)
+ w9968cf_vpp_init_decoder = inter_module_get_request
+ ( "w9968cf_init_decoder",
+ "w9968cf-vpp" );
+ if (!w9968cf_vpp_init_decoder) {
+ w9968cf_vppmod_present = 0;
+ DBG(4, "Video post-processing module not detected.")
+ return -ENODEV;
+ }
+ }
+
+ w9968cf_vpp_check_headers = inter_module_get("w9968cf_check_headers");
+ w9968cf_vpp_decode = inter_module_get("w9968cf_decode");
+ w9968cf_vpp_swap_yuvbytes = inter_module_get("w9968cf_swap_yuvbytes");
+ w9968cf_vpp_uyvy_to_rgbx = inter_module_get("w9968cf_uyvy_to_rgbx");
+ w9968cf_vpp_scale_up = inter_module_get("w9968cf_scale_up");
+
+ w9968cf_vppmod_present = 1;
+
+ /* Initialization */
+ (*w9968cf_vpp_init_decoder)();
+
+ DBG(2, "Video post-processing module detected.")
+ return 0;
+}
+
+
+static void w9968cf_vppmod_release(void)
+{
+ inter_module_put("w9968cf_init_decoder");
+ inter_module_put("w9968cf_check_headers");
+ inter_module_put("w9968cf_decode");
+ inter_module_put("w9968cf_swap_yuvbytes");
+ inter_module_put("w9968cf_uyvy_to_rgbx");
+ inter_module_put("w9968cf_scale_up");
+
+ DBG(2, "Video post-processing module released.")
+}
+
+
+static int __init w9968cf_module_init(void)
+{
+ int err;
+
+ DBG(2, W9968CF_MODULE_NAME" "W9968CF_MODULE_VERSION)
+ DBG(3, W9968CF_MODULE_AUTHOR)
+
+ init_MUTEX(&w9968cf_devlist_sem);
+
+ w9968cf_vppmod_detect();
+
+ if ((err = usb_register(&w9968cf_usb_driver))) {
+ if (w9968cf_vppmod_present)
+ w9968cf_vppmod_release();
+ return err;
+ }
+
+ return 0;
+}
+
+
+static void __exit w9968cf_module_exit(void)
+{
+ /* w9968cf_usb_disconnect() will be called */
+ usb_deregister(&w9968cf_usb_driver);
+
+ if (w9968cf_vppmod_present)
+ w9968cf_vppmod_release();
+
+ DBG(2, W9968CF_MODULE_NAME" deregistered.")
+}
+
+
+module_init(w9968cf_module_init);
+module_exit(w9968cf_module_exit);
--- /dev/null
+/***************************************************************************
+ * Video4Linux driver for W996[87]CF JPEG USB Dual Mode Camera Chip. *
+ * *
+ * Copyright (C) 2002 2003 by Luca Risolia <luca_ing@libero.it> *
+ * *
+ * 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 _W9968CF_H_
+#define _W9968CF_H_
+
+#include <linux/videodev.h>
+#include <linux/usb.h>
+#include <linux/i2c.h>
+#include <linux/spinlock.h>
+#include <linux/list.h>
+#include <linux/wait.h>
+#include <linux/config.h>
+#include <asm/semaphore.h>
+#include <asm/types.h>
+
+#include "w9968cf_externaldef.h"
+
+
+/****************************************************************************
+ * Default values *
+ ****************************************************************************/
+
+#define W9968CF_VPPMOD_LOAD 1 /* automatic 'w9968cf-vpp' module loading */
+
+/* Comment/uncomment the following line to enable/disable debugging messages */
+#define W9968CF_DEBUG
+
+/* These have effect only if W9968CF_DEBUG is defined */
+#define W9968CF_DEBUG_LEVEL 2 /* from 0 to 6. 0 for no debug informations */
+#define W9968CF_SPECIFIC_DEBUG 0 /* 0 or 1 */
+
+#define W9968CF_MAX_DEVICES 32
+#define W9968CF_SIMCAMS W9968CF_MAX_DEVICES /* simultaneous cameras */
+
+#define W9968CF_MAX_BUFFERS 32
+#define W9968CF_BUFFERS 2 /* n. of frame buffers from 2 to MAX_BUFFERS */
+
+/* Maximum data payload sizes in bytes for alternate settings */
+static const u16 wMaxPacketSize[] = {1023, 959, 895, 831, 767, 703, 639, 575,
+ 511, 447, 383, 319, 255, 191, 127, 63};
+#define W9968CF_PACKET_SIZE 1023 /* according to wMaxPacketSizes[] */
+#define W9968CF_MIN_PACKET_SIZE 63 /* minimum value */
+#define W9968CF_ISO_PACKETS 5 /* n.of packets for isochronous transfers */
+#define W9968CF_USB_CTRL_TIMEOUT HZ /* timeout for usb control commands */
+#define W9968CF_URBS 2 /* n. of scheduled URBs for ISO transfer */
+
+#define W9968CF_I2C_BUS_DELAY 4 /* delay in us for I2C bit r/w operations */
+#define W9968CF_I2C_RW_RETRIES 15 /* number of max I2C r/w retries */
+
+/* Available video formats */
+struct w9968cf_format {
+ const u16 palette;
+ const u16 depth;
+ const u8 compression;
+};
+
+static const struct w9968cf_format w9968cf_formatlist[] = {
+ { VIDEO_PALETTE_UYVY, 16, 0 }, /* original video */
+ { VIDEO_PALETTE_YUV422P, 16, 1 }, /* with JPEG compression */
+ { VIDEO_PALETTE_YUV420P, 12, 1 }, /* with JPEG compression */
+ { VIDEO_PALETTE_YUV420, 12, 1 }, /* same as YUV420P */
+ { VIDEO_PALETTE_YUYV, 16, 0 }, /* software conversion */
+ { VIDEO_PALETTE_YUV422, 16, 0 }, /* software conversion */
+ { VIDEO_PALETTE_GREY, 8, 0 }, /* software conversion */
+ { VIDEO_PALETTE_RGB555, 16, 0 }, /* software conversion */
+ { VIDEO_PALETTE_RGB565, 16, 0 }, /* software conversion */
+ { VIDEO_PALETTE_RGB24, 24, 0 }, /* software conversion */
+ { VIDEO_PALETTE_RGB32, 32, 0 }, /* software conversion */
+ { 0, 0, 0 } /* 0 is a terminating entry */
+};
+
+#define W9968CF_DECOMPRESSION 2 /* decomp:0=disable,1=force,2=any formats */
+#define W9968CF_PALETTE_DECOMP_OFF VIDEO_PALETTE_UYVY /* when decomp=0 */
+#define W9968CF_PALETTE_DECOMP_FORCE VIDEO_PALETTE_YUV420P /* when decomp=1 */
+#define W9968CF_PALETTE_DECOMP_ON VIDEO_PALETTE_UYVY /* when decomp=2 */
+
+#define W9968CF_FORCE_RGB 0 /* read RGB instead of BGR, yes=1/no=0 */
+
+#define W9968CF_MAX_WIDTH 800 /* should be >= 640 */
+#define W9968CF_MAX_HEIGHT 600 /* should be >= 480 */
+#define W9968CF_WIDTH 320 /* from 128 to 352, multiple of 16 */
+#define W9968CF_HEIGHT 240 /* from 96 to 288, multiple of 16 */
+
+#define W9968CF_CLAMPING 0 /* 0 disable, 1 enable video data clamping */
+#define W9968CF_FILTER_TYPE 0 /* 0 disable 1 (1-2-1), 2 (2-3-6-3-2) */
+#define W9968CF_DOUBLE_BUFFER 1 /* 0 disable, 1 enable double buffer */
+#define W9968CF_LARGEVIEW 1 /* 0 disable, 1 enable */
+#define W9968CF_UPSCALING 0 /* 0 disable, 1 enable */
+
+#define W9968CF_SENSOR_MONO 0 /* 0 not monochrome, 1 monochrome sensor */
+#define W9968CF_BRIGHTNESS 31000 /* from 0 to 65535 */
+#define W9968CF_HUE 32768 /* from 0 to 65535 */
+#define W9968CF_COLOUR 32768 /* from 0 to 65535 */
+#define W9968CF_CONTRAST 50000 /* from 0 to 65535 */
+#define W9968CF_WHITENESS 32768 /* from 0 to 65535 */
+
+#define W9968CF_AUTOBRIGHT 0 /* 0 disable, 1 enable automatic brightness */
+#define W9968CF_AUTOEXP 1 /* 0 disable, 1 enable automatic exposure */
+#define W9968CF_LIGHTFREQ 50 /* light frequency. 50Hz (Europe) or 60Hz */
+#define W9968CF_BANDINGFILTER 0 /* 0 disable, 1 enable banding filter */
+#define W9968CF_BACKLIGHT 0 /* 0 or 1, 1=object is lit from behind */
+#define W9968CF_MIRROR 0 /* 0 or 1 [don't] reverse image horizontally*/
+
+#define W9968CF_CLOCKDIV -1 /* -1 = automatic clock divisor */
+#define W9968CF_DEF_CLOCKDIVISOR 0 /* default sensor clock divisor value */
+
+
+/****************************************************************************
+ * Globals *
+ ****************************************************************************/
+
+#define W9968CF_MODULE_NAME "V4L driver for W996[87]CF JPEG USB " \
+ "Dual Mode Camera Chip"
+#define W9968CF_MODULE_VERSION "v1.22"
+#define W9968CF_MODULE_AUTHOR "(C) 2002 2003 Luca Risolia"
+#define W9968CF_AUTHOR_EMAIL "<luca_ing@libero.it>"
+
+static u8 w9968cf_vppmod_present; /* status flag: yes=1, no=0 */
+
+static const struct usb_device_id winbond_id_table[] = {
+ {
+ /* Creative Labs Video Blaster WebCam Go Plus */
+ USB_DEVICE(0x041e, 0x4003),
+ .driver_info = (unsigned long)"w9968cf",
+ },
+ {
+ /* Generic W996[87]CF JPEG USB Dual Mode Camera */
+ USB_DEVICE(0x1046, 0x9967),
+ .driver_info = (unsigned long)"w9968cf",
+ },
+ { } /* terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, winbond_id_table);
+
+/* W996[87]CF camera models, internal ids: */
+enum w9968cf_model_id {
+ W9968CF_MOD_GENERIC = 1, /* Generic W996[87]CF based device */
+ W9968CF_MOD_CLVBWGP = 11,/*Creative Labs Video Blaster WebCam Go Plus*/
+ W9968CF_MOD_ADPA5R = 21, /* Aroma Digi Pen ADG-5000 Refurbished */
+ W9986CF_MOD_AU = 31, /* AVerTV USB */
+ W9968CF_MOD_CLVBWG = 34, /* Creative Labs Video Blaster WebCam Go */
+ W9968CF_MOD_DLLDK = 37, /* Die Lebon LDC-D35A Digital Kamera */
+ W9968CF_MOD_EEEMC = 40, /* Ezonics EZ-802 EZMega Cam */
+ W9968CF_MOD_ODPVDMPC = 43,/* OPCOM Digi Pen VGA Dual Mode Pen Camera */
+};
+
+enum w9968cf_frame_status {
+ F_READY, /* finished grabbing & ready to be read/synced */
+ F_GRABBING, /* in the process of being grabbed into */
+ F_ERROR, /* something bad happened while processing */
+ F_UNUSED /* unused (no VIDIOCMCAPTURE) */
+};
+
+struct w9968cf_frame_t {
+ void* buffer;
+ u32 length;
+ enum w9968cf_frame_status status;
+ struct w9968cf_frame_t* next;
+ u8 queued;
+};
+
+enum w9968cf_vpp_flag {
+ VPP_NONE = 0x00,
+ VPP_UPSCALE = 0x01,
+ VPP_SWAP_YUV_BYTES = 0x02,
+ VPP_DECOMPRESSION = 0x04,
+ VPP_UYVY_TO_RGBX = 0x08,
+};
+
+struct list_head w9968cf_dev_list; /* head of V4L registered cameras list */
+LIST_HEAD(w9968cf_dev_list);
+struct semaphore w9968cf_devlist_sem; /* semaphore for list traversal */
+
+/* Main device driver structure */
+struct w9968cf_device {
+ enum w9968cf_model_id id; /* private device identifier */
+
+ struct video_device v4ldev; /* V4L structure */
+ struct list_head v4llist; /* entry of the list of V4L cameras */
+
+ struct usb_device* usbdev; /* -> main USB structure */
+ struct urb* urb[W9968CF_URBS]; /* -> USB request block structs */
+ void* transfer_buffer[W9968CF_URBS]; /* -> ISO transfer buffers */
+ u16* control_buffer; /* -> buffer for control req.*/
+ u16* data_buffer; /* -> data to send to the FSB */
+
+ struct w9968cf_frame_t frame[W9968CF_MAX_BUFFERS];
+ struct w9968cf_frame_t frame_tmp; /* temporary frame */
+ struct w9968cf_frame_t* frame_current; /* -> frame being grabbed */
+ struct w9968cf_frame_t* requested_frame[W9968CF_MAX_BUFFERS];
+ void* vpp_buffer; /* -> helper buffer for post-processing routines */
+
+ u8 max_buffers, /* number of requested buffers */
+ force_palette, /* yes=1/no=0 */
+ force_rgb, /* read RGB instead of BGR, yes=1, no=0 */
+ double_buffer, /* hardware double buffering yes=1/no=0 */
+ clamping, /* video data clamping yes=1/no=0 */
+ filter_type, /* 0=disabled, 1=3 tap, 2=5 tap filter */
+ capture, /* 0=disabled, 1=enabled */
+ largeview, /* 0=disabled, 1=enabled */
+ decompression, /* 0=disabled, 1=forced, 2=allowed */
+ upscaling; /* software image scaling, 0=enabled, 1=disabled */
+
+ struct video_picture picture; /* current window settings */
+ struct video_window window; /* current picture settings */
+
+ u16 hw_depth, /* depth (used by the chip) */
+ hw_palette, /* palette (used by the chip) */
+ hw_width, /* width (used by the chip) */
+ hw_height, /* height (used by the chip) */
+ hs_polarity, /* 0=negative sync pulse, 1=positive sync pulse */
+ vs_polarity; /* 0=negative sync pulse, 1=positive sync pulse */
+
+ enum w9968cf_vpp_flag vpp_flag; /* post-processing routines in use */
+
+ u8 nbuffers, /* number of allocated frame buffers */
+ altsetting, /* camera alternate setting */
+ disconnected, /* flag: yes=1, no=0 */
+ misconfigured, /* flag: yes=1, no=0 */
+ users, /* flag: number of users holding the device */
+ streaming; /* flag: yes=1, no=0 */
+
+ int sensor; /* type of image CMOS sensor chip (CC_*) */
+
+ /* Determined by CMOS sensor type */
+ u16 maxwidth,
+ maxheight,
+ minwidth,
+ minheight,
+ start_cropx,
+ start_cropy;
+
+ u8 auto_brt, /* auto brightness enabled flag */
+ auto_exp, /* auto exposure enabled flag */
+ backlight, /* backlight exposure algorithm flag */
+ mirror, /* image is reversed horizontally */
+ lightfreq, /* power (lighting) frequency */
+ bandfilt; /* banding filter enabled flag */
+ s8 clockdiv; /* clock divisor */
+ int sensor_mono; /* CMOS sensor is (probably) monochrome */
+
+ /* I2C interface to kernel */
+ struct i2c_adapter i2c_adapter;
+ struct i2c_client* sensor_client;
+
+ /* Locks */
+ struct semaphore dev_sem, /* for probe, disconnect,open and close */
+ fileop_sem; /* for read and ioctl */
+ spinlock_t urb_lock, /* for submit_urb() and unlink_urb() */
+ flist_lock; /* for requested frame list accesses */
+ char command[16]; /* name of the program holding the device */
+ wait_queue_head_t open, wait_queue;
+};
+
+#define W9968CF_HW_BUF_SIZE 640*480*2 /* buf. size for original video frames */
+
+#define SENSOR_FORMAT VIDEO_PALETTE_UYVY
+#define SENSOR_FATAL_ERROR(rc) ((rc) < 0 && (rc) != -EPERM)
+
+
+/****************************************************************************
+ * Macros and other constants *
+ ****************************************************************************/
+
+#undef DBG
+#ifdef W9968CF_DEBUG
+# define DBG(level, fmt, args...) \
+{ \
+if ( ((specific_debug) && (debug == (level))) || \
+ ((!specific_debug) && (debug >= (level))) ) { \
+ if ((level) == 1) \
+ err(fmt, ## args); \
+ else if ((level) == 2 || (level) == 3) \
+ info(fmt, ## args); \
+ else if ((level) == 4) \
+ warn(fmt, ## args); \
+ else if ((level) >= 5) \
+ info("[%s,%d] " fmt, \
+ __PRETTY_FUNCTION__, __LINE__ , ## args); \
+} \
+}
+#else
+ /* Not debugging: nothing */
+# define DBG(level, fmt, args...) do {;} while(0);
+#endif
+
+#undef PDBG
+#undef PDBGG
+#define PDBG(fmt, args...) info("[%s, %d] "fmt, \
+ __PRETTY_FUNCTION__, __LINE__ , ## args);
+#define PDBGG(fmt, args...) do {;} while(0); /* nothing: it's a placeholder */
+
+#endif /* _W9968CF_H_ */
--- /dev/null
+/***************************************************************************
+ * Video decoder for the W996[87]CF driver for Linux. *
+ * *
+ * Copyright (C) 2003 by Luca Risolia <luca_ing@libero.it> *
+ * *
+ * 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 _W9968CF_DECODER_H_
+#define _W9968CF_DECODER_H_
+
+/* Comment/uncomment this for high/low quality of compressed video */
+#define W9968CF_DEC_FAST_LOWQUALITY_VIDEO
+
+#ifdef W9968CF_DEC_FAST_LOWQUALITY_VIDEO
+static const unsigned char Y_QUANTABLE[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 unsigned char UV_QUANTABLE[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
+};
+#else
+static const unsigned char Y_QUANTABLE[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 unsigned char UV_QUANTABLE[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
+};
+#endif
+
+#define W9968CF_DEC_ERR_CORRUPTED_DATA -1
+#define W9968CF_DEC_ERR_BUF_OVERFLOW -2
+#define W9968CF_DEC_ERR_NO_SOI -3
+#define W9968CF_DEC_ERR_NO_SOF0 -4
+#define W9968CF_DEC_ERR_NO_SOS -5
+#define W9968CF_DEC_ERR_NO_EOI -6
+
+extern void w9968cf_init_decoder(void);
+extern int w9968cf_check_headers(const unsigned char* Pin,
+ const unsigned long BUF_SIZE);
+extern int w9968cf_decode(const char* Pin, const unsigned long BUF_SIZE,
+ const unsigned W, const unsigned H, char* Pout);
+
+#endif /* _W9968CF_DECODER_H_ */
--- /dev/null
+/***************************************************************************
+ * Various definitions for compatibility with external modules. *
+ * This file is part of the W996[87]CF driver for Linux. *
+ * *
+ * Copyright (C) 2002 2003 by Luca Risolia <luca_ing@libero.it> *
+ * *
+ * 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 _W9968CF_EXTERNALDEF_H_
+#define _W9968CF_EXTERNALDEF_H_
+
+#include <linux/videodev.h>
+#include <linux/i2c.h>
+#include <asm/ioctl.h>
+#include <asm/types.h>
+
+/* The following values have been copied from the "ovcamchip" module. */
+
+#ifndef I2C_DRIVERID_OVCAMCHIP
+# define I2C_DRIVERID_OVCAMCHIP 0xf00f
+#endif
+
+/* Controls */
+enum {
+ OVCAMCHIP_CID_CONT, /* Contrast */
+ OVCAMCHIP_CID_BRIGHT, /* Brightness */
+ OVCAMCHIP_CID_SAT, /* Saturation */
+ OVCAMCHIP_CID_HUE, /* Hue */
+ OVCAMCHIP_CID_EXP, /* Exposure */
+ OVCAMCHIP_CID_FREQ, /* Light frequency */
+ OVCAMCHIP_CID_BANDFILT, /* Banding filter */
+ OVCAMCHIP_CID_AUTOBRIGHT, /* Auto brightness */
+ OVCAMCHIP_CID_AUTOEXP, /* Auto exposure */
+ OVCAMCHIP_CID_BACKLIGHT, /* Back light compensation */
+ OVCAMCHIP_CID_MIRROR, /* Mirror horizontally */
+};
+
+/* I2C addresses */
+#define OV7xx0_SID (0x42 >> 1)
+#define OV6xx0_SID (0xC0 >> 1)
+
+/* Sensor types */
+enum {
+ CC_UNKNOWN,
+ CC_OV76BE,
+ CC_OV7610,
+ CC_OV7620,
+ CC_OV7620AE,
+ CC_OV6620,
+ CC_OV6630,
+ CC_OV6630AE,
+ CC_OV6630AF,
+};
+
+/* API */
+struct ovcamchip_control {
+ __u32 id;
+ __s32 value;
+};
+
+struct ovcamchip_window {
+ int x;
+ int y;
+ int width;
+ int height;
+ int format;
+ int quarter; /* Scale width and height down 2x */
+
+ /* This stuff will be removed eventually */
+ int clockdiv; /* Clock divisor setting */
+};
+
+/* Commands.
+ You must call OVCAMCHIP_CMD_INITIALIZE before any of other commands */
+#define OVCAMCHIP_CMD_Q_SUBTYPE _IOR (0x88, 0x00, int)
+#define OVCAMCHIP_CMD_INITIALIZE _IOW (0x88, 0x01, int)
+#define OVCAMCHIP_CMD_S_CTRL _IOW (0x88, 0x02, struct ovcamchip_control)
+#define OVCAMCHIP_CMD_G_CTRL _IOWR (0x88, 0x03, struct ovcamchip_control)
+#define OVCAMCHIP_CMD_S_MODE _IOW (0x88, 0x04, struct ovcamchip_window)
+#define OVCAMCHIP_MAX_CMD _IO (0x88, 0x3f)
+
+#endif /* _W9968CF_EXTERNALDEF_H_ */
To compile this driver as a module, choose M here: the
module will be called rio500.
+config USB_LEGOTOWER
+ tristate "USB Lego Infrared Tower support (EXPERIMENTAL)"
+ depends on USB && EXPERIMENTAL
+ help
+ Say Y here if you want to connect a USB Lego Infrared Tower to your
+ computer's USB port.
+
+ This code is also available as a module ( = code which can be
+ inserted in and removed from the running kernel whenever you want).
+ The module will be called legousbtower. If you want to compile it as
+ a module, say M here and read <file:Documentation/modules.txt>.
+
config USB_BRLVGER
tristate "Tieman Voyager USB Braille display support (EXPERIMENTAL)"
depends on USB && EXPERIMENTAL
obj-$(CONFIG_USB_TEST) += usbtest.o
obj-$(CONFIG_USB_TIGL) += tiglusb.o
obj-$(CONFIG_USB_USS720) += uss720.o
+obj-$(CONFIG_USB_LEGOTOWER) += legousbtower.o
--- /dev/null
+/*
+ * LEGO USB Tower driver
+ *
+ * Copyright (C) 2003 David Glance <davidgsf@sourceforge.net>
+ * 2001 Juergen Stuber <stuber@loria.fr>
+ *
+ * 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.
+ *
+ * derived from USB Skeleton driver - 0.5
+ * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
+ *
+ * History:
+ *
+ * 2001-10-13 - 0.1 js
+ * - first version
+ * 2001-11-03 - 0.2 js
+ * - simplified buffering, one-shot URBs for writing
+ * 2001-11-10 - 0.3 js
+ * - removed IOCTL (setting power/mode is more complicated, postponed)
+ * 2001-11-28 - 0.4 js
+ * - added vendor commands for mode of operation and power level in open
+ * 2001-12-04 - 0.5 js
+ * - set IR mode by default (by oversight 0.4 set VLL mode)
+ * 2002-01-11 - 0.5? pcchan
+ * - make read buffer reusable and work around bytes_to_write issue between
+ * uhci and legusbtower
+ * 2002-09-23 - 0.52 david (david@csse.uwa.edu.au)
+ * - imported into lejos project
+ * - changed wake_up to wake_up_interruptible
+ * - changed to use lego0 rather than tower0
+ * - changed dbg() to use __func__ rather than deprecated __FUNCTION__
+ * 2003-01-12 - 0.53 david (david@csse.uwa.edu.au)
+ * - changed read and write to write everything or timeout (from a patch by Chris Riesen and
+ * Brett Thaeler driver)
+ * - added ioctl functionality to set timeouts
+ * 2003-07-18 - 0.54 davidgsf (david@csse.uwa.edu.au)
+ * - initial import into LegoUSB project
+ * - merge of existing LegoUSB.c driver
+ * 2003-07-18 - 0.56 davidgsf (david@csse.uwa.edu.au)
+ * - port to 2.6 style driver
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/smp_lock.h>
+#include <linux/completion.h>
+#include <asm/uaccess.h>
+#include <linux/usb.h>
+
+
+#ifdef CONFIG_USB_DEBUG
+ static int debug = 4;
+#else
+ static int debug = 1;
+#endif
+
+/* Use our own dbg macro */
+#undef dbg
+#define dbg(lvl, format, arg...) do { if (debug >= lvl) printk(KERN_DEBUG __FILE__ " : " format " \n", ## arg); } while (0)
+
+
+/* Version Information */
+#define DRIVER_VERSION "v0.56"
+#define DRIVER_AUTHOR "David Glance, davidgsf@sourceforge.net"
+#define DRIVER_DESC "LEGO USB Tower Driver"
+
+/* Module paramaters */
+MODULE_PARM(debug, "i");
+MODULE_PARM_DESC(debug, "Debug enabled or not");
+
+
+/* Define these values to match your device */
+#define LEGO_USB_TOWER_VENDOR_ID 0x0694
+#define LEGO_USB_TOWER_PRODUCT_ID 0x0001
+
+/* table of devices that work with this driver */
+static struct usb_device_id tower_table [] = {
+ { USB_DEVICE(LEGO_USB_TOWER_VENDOR_ID, LEGO_USB_TOWER_PRODUCT_ID) },
+ { } /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE (usb, tower_table);
+
+#define LEGO_USB_TOWER_MINOR_BASE 160
+
+/* we can have up to this number of device plugged in at once */
+#define MAX_DEVICES 16
+
+#define COMMAND_TIMEOUT (2*HZ) /* 2 second timeout for a command */
+
+/* Structure to hold all of our device specific stuff */
+struct lego_usb_tower {
+ struct semaphore sem; /* locks this structure */
+ struct usb_device* udev; /* save off the usb device pointer */
+ struct usb_interface* interface;
+ unsigned char minor; /* the starting minor number for this device */
+
+ int open_count; /* number of times this port has been opened */
+
+ char* read_buffer;
+ int read_buffer_length;
+
+ wait_queue_head_t read_wait;
+ wait_queue_head_t write_wait;
+
+ char* interrupt_in_buffer;
+ struct usb_endpoint_descriptor* interrupt_in_endpoint;
+ struct urb* interrupt_in_urb;
+
+ char* interrupt_out_buffer;
+ struct usb_endpoint_descriptor* interrupt_out_endpoint;
+ struct urb* interrupt_out_urb;
+
+};
+
+/* Note that no locking is needed:
+ * read_buffer is arbitrated by read_buffer_length == 0
+ * interrupt_out_buffer is arbitrated by interrupt_out_urb->status == -EINPROGRESS
+ * interrupt_in_buffer belongs to urb alone and is overwritten on overflow
+ */
+
+/* local function prototypes */
+static ssize_t tower_read (struct file *file, char *buffer, size_t count, loff_t *ppos);
+static ssize_t tower_write (struct file *file, const char *buffer, size_t count, loff_t *ppos);
+static inline void tower_delete (struct lego_usb_tower *dev);
+static int tower_open (struct inode *inode, struct file *file);
+static int tower_release (struct inode *inode, struct file *file);
+static int tower_release_internal (struct lego_usb_tower *dev);
+static void tower_abort_transfers (struct lego_usb_tower *dev);
+static void tower_interrupt_in_callback (struct urb *urb, struct pt_regs *regs);
+static void tower_interrupt_out_callback (struct urb *urb, struct pt_regs *regs);
+
+static int tower_probe (struct usb_interface *interface, const struct usb_device_id *id);
+static void tower_disconnect (struct usb_interface *interface);
+
+
+/* prevent races between open() and disconnect */
+static DECLARE_MUTEX (disconnect_sem);
+
+/* file operations needed when we register this driver */
+static struct file_operations tower_fops = {
+ .owner = THIS_MODULE,
+ .read = tower_read,
+ .write = tower_write,
+ .open = tower_open,
+ .release = tower_release,
+};
+
+/*
+ * usb class driver info in order to get a minor number from the usb core,
+ * and to have the device registered with devfs and the driver core
+ */
+static struct usb_class_driver tower_class = {
+ .name = "usb/legousbtower%d",
+ .fops = &tower_fops,
+ .mode = S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH,
+ .minor_base = LEGO_USB_TOWER_MINOR_BASE,
+};
+
+
+/* usb specific object needed to register this driver with the usb subsystem */
+static struct usb_driver tower_driver = {
+ .owner = THIS_MODULE,
+ .name = "legousbtower",
+ .probe = tower_probe,
+ .disconnect = tower_disconnect,
+ .id_table = tower_table,
+};
+
+
+/**
+ * lego_usb_tower_debug_data
+ */
+static inline void lego_usb_tower_debug_data (int level, const char *function, int size, const unsigned char *data)
+{
+ int i;
+
+ if (debug < level)
+ return;
+
+ printk (KERN_DEBUG __FILE__": %s - length = %d, data = ", function, size);
+ for (i = 0; i < size; ++i) {
+ printk ("%.2x ", data[i]);
+ }
+ printk ("\n");
+}
+
+
+/**
+ * tower_delete
+ */
+static inline void tower_delete (struct lego_usb_tower *dev)
+{
+ dbg(2, "%s enter", __func__);
+
+ tower_abort_transfers (dev);
+
+ /* free data structures */
+ if (dev->interrupt_in_urb != NULL) {
+ usb_free_urb (dev->interrupt_in_urb);
+ }
+ if (dev->interrupt_out_urb != NULL) {
+ usb_free_urb (dev->interrupt_out_urb);
+ }
+ kfree (dev->read_buffer);
+ kfree (dev->interrupt_in_buffer);
+ kfree (dev->interrupt_out_buffer);
+ kfree (dev);
+
+ dbg(2, "%s : leave", __func__);
+}
+
+
+/**
+ * tower_open
+ */
+static int tower_open (struct inode *inode, struct file *file)
+{
+ struct lego_usb_tower *dev = NULL;
+ int subminor;
+ int retval = 0;
+ struct usb_interface *interface;
+
+ dbg(2,"%s : enter", __func__);
+
+ subminor = iminor(inode);
+
+ down (&disconnect_sem);
+
+ interface = usb_find_interface (&tower_driver, subminor);
+
+ if (!interface) {
+ err ("%s - error, can't find device for minor %d",
+ __FUNCTION__, subminor);
+ retval = -ENODEV;
+ goto exit_no_device;
+ }
+
+ dev = usb_get_intfdata(interface);
+
+ if (!dev) {
+ retval = -ENODEV;
+ goto exit_no_device;
+ }
+
+ /* lock this device */
+ down (&dev->sem);
+
+
+ /* increment our usage count for the device */
+ ++dev->open_count;
+
+ /* save device in the file's private structure */
+ file->private_data = dev;
+
+
+ /* initialize in direction */
+ dev->read_buffer_length = 0;
+
+ up (&dev->sem);
+
+exit_no_device:
+
+ up (&disconnect_sem);
+
+ dbg(2,"%s : leave, return value %d ", __func__, retval);
+
+ return retval;
+}
+
+/**
+ * tower_release
+ */
+static int tower_release (struct inode *inode, struct file *file)
+{
+ struct lego_usb_tower *dev;
+ int retval = 0;
+
+ dbg(2," %s : enter", __func__);
+
+ dev = (struct lego_usb_tower *)file->private_data;
+
+ if (dev == NULL) {
+ dbg(1," %s : object is NULL", __func__);
+ retval = -ENODEV;
+ goto exit;
+ }
+
+
+ /* lock our device */
+ down (&dev->sem);
+
+ if (dev->open_count <= 0) {
+ dbg(1," %s : device not opened", __func__);
+ retval = -ENODEV;
+ goto exit;
+ }
+
+ /* do the work */
+ retval = tower_release_internal (dev);
+
+exit:
+ up (&dev->sem);
+ dbg(2," %s : leave, return value %d", __func__, retval);
+ return retval;
+}
+
+
+/**
+ * tower_release_internal
+ */
+static int tower_release_internal (struct lego_usb_tower *dev)
+{
+ int retval = 0;
+
+ dbg(2," %s : enter", __func__);
+
+ if (dev->udev == NULL) {
+ /* the device was unplugged before the file was released */
+ tower_delete (dev);
+ goto exit;
+ }
+
+ /* decrement our usage count for the device */
+ --dev->open_count;
+ if (dev->open_count <= 0) {
+ tower_abort_transfers (dev);
+ dev->open_count = 0;
+ }
+
+exit:
+ dbg(2," %s : leave", __func__);
+ return retval;
+}
+
+
+/**
+ * tower_abort_transfers
+ * aborts transfers and frees associated data structures
+ */
+static void tower_abort_transfers (struct lego_usb_tower *dev)
+{
+ dbg(2," %s : enter", __func__);
+
+ if (dev == NULL) {
+ dbg(1," %s : dev is null", __func__);
+ goto exit;
+ }
+
+ /* shutdown transfer */
+ if (dev->interrupt_in_urb != NULL) {
+ usb_unlink_urb (dev->interrupt_in_urb);
+ }
+ if (dev->interrupt_out_urb != NULL) {
+ usb_unlink_urb (dev->interrupt_out_urb);
+ }
+
+exit:
+ dbg(2," %s : leave", __func__);
+}
+
+
+/**
+ * tower_read
+ */
+static ssize_t tower_read (struct file *file, char *buffer, size_t count, loff_t *ppos)
+{
+ struct lego_usb_tower *dev;
+ size_t bytes_read = 0;
+ size_t bytes_to_read;
+ int i;
+ int retval = 0;
+ int timeout = 0;
+
+ dbg(2," %s : enter, count = %Zd", __func__, count);
+
+ dev = (struct lego_usb_tower *)file->private_data;
+
+ /* lock this object */
+ down (&dev->sem);
+
+ /* verify that the device wasn't unplugged */
+ if (dev->udev == NULL) {
+ retval = -ENODEV;
+ err("No device or device unplugged %d", retval);
+ goto exit;
+ }
+
+ /* verify that we actually have some data to read */
+ if (count == 0) {
+ dbg(1," %s : read request of 0 bytes", __func__);
+ goto exit;
+ }
+
+
+ timeout = COMMAND_TIMEOUT;
+
+ while (1) {
+ if (dev->read_buffer_length == 0) {
+
+ /* start reading */
+ usb_fill_int_urb (dev->interrupt_in_urb,dev->udev,
+ usb_rcvintpipe(dev->udev, dev->interrupt_in_endpoint->bEndpointAddress),
+ dev->interrupt_in_buffer,
+ dev->interrupt_in_endpoint->wMaxPacketSize,
+ tower_interrupt_in_callback,
+ dev,
+ dev->interrupt_in_endpoint->bInterval);
+
+ retval = usb_submit_urb (dev->interrupt_in_urb, GFP_KERNEL);
+
+ if (retval < 0) {
+ err("Couldn't submit interrupt_in_urb");
+ goto exit;
+ }
+
+ if (timeout <= 0) {
+ retval = -ETIMEDOUT;
+ goto exit;
+ }
+
+ if (signal_pending(current)) {
+ retval = -EINTR;
+ goto exit;
+ }
+
+ up (&dev->sem);
+ timeout = interruptible_sleep_on_timeout (&dev->read_wait, timeout);
+ down (&dev->sem);
+
+ } else {
+ /* copy the data from read_buffer into userspace */
+ bytes_to_read = count > dev->read_buffer_length ? dev->read_buffer_length : count;
+ if (copy_to_user (buffer, dev->read_buffer, bytes_to_read) != 0) {
+ retval = -EFAULT;
+ goto exit;
+ }
+ dev->read_buffer_length -= bytes_to_read;
+ for (i=0; i<dev->read_buffer_length; i++) {
+ dev->read_buffer[i] = dev->read_buffer[i+bytes_to_read];
+ }
+
+ buffer += bytes_to_read;
+ count -= bytes_to_read;
+ bytes_read += bytes_to_read;
+ if (count == 0) {
+ break;
+ }
+ }
+ }
+
+ retval = bytes_read;
+
+exit:
+ /* unlock the device */
+ up (&dev->sem);
+
+ dbg(2," %s : leave, return value %d", __func__, retval);
+ return retval;
+}
+
+
+/**
+ * tower_write
+ */
+static ssize_t tower_write (struct file *file, const char *buffer, size_t count, loff_t *ppos)
+{
+ struct lego_usb_tower *dev;
+ size_t bytes_written = 0;
+ size_t bytes_to_write;
+ size_t buffer_size;
+ int retval = 0;
+ int timeout = 0;
+
+ dbg(2," %s : enter, count = %Zd", __func__, count);
+
+ dev = (struct lego_usb_tower *)file->private_data;
+
+ /* lock this object */
+ down (&dev->sem);
+
+ /* verify that the device wasn't unplugged */
+ if (dev->udev == NULL) {
+ retval = -ENODEV;
+ err("No device or device unplugged %d", retval);
+ goto exit;
+ }
+
+ /* verify that we actually have some data to write */
+ if (count == 0) {
+ dbg(1," %s : write request of 0 bytes", __func__);
+ goto exit;
+ }
+
+
+ while (count > 0) {
+ if (dev->interrupt_out_urb->status == -EINPROGRESS) {
+ timeout = COMMAND_TIMEOUT;
+
+ while (timeout > 0) {
+ if (signal_pending(current)) {
+ dbg(1," %s : interrupted", __func__);
+ retval = -EINTR;
+ goto exit;
+ }
+ up (&dev->sem);
+ timeout = interruptible_sleep_on_timeout (&dev->write_wait, timeout);
+ down (&dev->sem);
+ if (timeout > 0) {
+ break;
+ }
+ dbg(1," %s : interrupted timeout: %d", __func__, timeout);
+ }
+
+
+ dbg(1," %s : final timeout: %d", __func__, timeout);
+
+ if (timeout == 0) {
+ dbg(1, "%s - command timed out.", __func__);
+ retval = -ETIMEDOUT;
+ goto exit;
+ }
+
+ dbg(4," %s : in progress, count = %Zd", __func__, count);
+ } else {
+ dbg(4," %s : sending, count = %Zd", __func__, count);
+
+ /* write the data into interrupt_out_buffer from userspace */
+ buffer_size = dev->interrupt_out_endpoint->wMaxPacketSize;
+ bytes_to_write = count > buffer_size ? buffer_size : count;
+ dbg(4," %s : buffer_size = %Zd, count = %Zd, bytes_to_write = %Zd", __func__, buffer_size, count, bytes_to_write);
+
+ if (copy_from_user (dev->interrupt_out_buffer, buffer, bytes_to_write) != 0) {
+ retval = -EFAULT;
+ goto exit;
+ }
+
+ /* send off the urb */
+ usb_fill_int_urb(dev->interrupt_out_urb,
+ dev->udev,
+ usb_sndintpipe(dev->udev, dev->interrupt_out_endpoint->bEndpointAddress),
+ dev->interrupt_out_buffer,
+ bytes_to_write,
+ tower_interrupt_out_callback,
+ dev,
+ dev->interrupt_in_endpoint->bInterval);
+
+ dev->interrupt_out_urb->actual_length = bytes_to_write;
+ retval = usb_submit_urb (dev->interrupt_out_urb, GFP_KERNEL);
+
+ if (retval < 0) {
+ err("Couldn't submit interrupt_out_urb %d", retval);
+ goto exit;
+ }
+
+ buffer += bytes_to_write;
+ count -= bytes_to_write;
+
+ bytes_written += bytes_to_write;
+ }
+ }
+
+ retval = bytes_written;
+
+exit:
+ /* unlock the device */
+ up (&dev->sem);
+
+ dbg(2," %s : leave, return value %d", __func__, retval);
+
+ return retval;
+}
+
+
+/**
+ * tower_interrupt_in_callback
+ */
+static void tower_interrupt_in_callback (struct urb *urb, struct pt_regs *regs)
+{
+ struct lego_usb_tower *dev = (struct lego_usb_tower *)urb->context;
+
+ dbg(4," %s : enter, status %d", __func__, urb->status);
+
+ lego_usb_tower_debug_data(5,__func__, urb->actual_length, urb->transfer_buffer);
+
+ if (urb->status != 0) {
+ if ((urb->status != -ENOENT) && (urb->status != -ECONNRESET)) {
+ dbg(1," %s : nonzero status received: %d", __func__, urb->status);
+ }
+ goto exit;
+ }
+
+ down (&dev->sem);
+
+ if (urb->actual_length > 0) {
+ if (dev->read_buffer_length < (4 * dev->interrupt_in_endpoint->wMaxPacketSize) - (urb->actual_length)) {
+
+ memcpy (dev->read_buffer+dev->read_buffer_length, dev->interrupt_in_buffer, urb->actual_length);
+
+ dev->read_buffer_length += urb->actual_length;
+ dbg(1," %s reading %d ", __func__, urb->actual_length);
+ wake_up_interruptible (&dev->read_wait);
+
+ } else {
+ dbg(1," %s : read_buffer overflow", __func__);
+ }
+ }
+
+ up (&dev->sem);
+
+exit:
+ lego_usb_tower_debug_data(5,__func__, urb->actual_length, urb->transfer_buffer);
+ dbg(4," %s : leave, status %d", __func__, urb->status);
+}
+
+
+/**
+ * tower_interrupt_out_callback
+ */
+static void tower_interrupt_out_callback (struct urb *urb, struct pt_regs *regs)
+{
+ struct lego_usb_tower *dev = (struct lego_usb_tower *)urb->context;
+
+ dbg(4," %s : enter, status %d", __func__, urb->status);
+ lego_usb_tower_debug_data(5,__func__, urb->actual_length, urb->transfer_buffer);
+
+ if (urb->status != 0) {
+ if ((urb->status != -ENOENT) &&
+ (urb->status != -ECONNRESET)) {
+ dbg(1, " %s :nonzero status received: %d", __func__, urb->status);
+ }
+ goto exit;
+ }
+
+ wake_up_interruptible(&dev->write_wait);
+exit:
+
+ lego_usb_tower_debug_data(5,__func__, urb->actual_length, urb->transfer_buffer);
+ dbg(4," %s : leave, status %d", __func__, urb->status);
+}
+
+
+/**
+ * tower_probe
+ *
+ * Called by the usb core when a new device is connected that it thinks
+ * this driver might be interested in.
+ */
+static int tower_probe (struct usb_interface *interface, const struct usb_device_id *id)
+{
+ struct usb_device *udev = interface_to_usbdev(interface);
+ struct lego_usb_tower *dev = NULL;
+ struct usb_host_interface *iface_desc;
+ struct usb_endpoint_descriptor* endpoint;
+ int i;
+ int retval = -ENOMEM;
+
+ dbg(2," %s : enter", __func__);
+
+ if (udev == NULL) {
+ info ("udev is NULL.");
+ }
+
+ /* See if the device offered us matches what we can accept */
+ if ((udev->descriptor.idVendor != LEGO_USB_TOWER_VENDOR_ID) ||
+ (udev->descriptor.idProduct != LEGO_USB_TOWER_PRODUCT_ID)) {
+ return -ENODEV;
+ }
+
+
+ /* allocate memory for our device state and intialize it */
+
+ dev = kmalloc (sizeof(struct lego_usb_tower), GFP_KERNEL);
+
+ if (dev == NULL) {
+ err ("Out of memory");
+ goto exit;
+ }
+
+ init_MUTEX (&dev->sem);
+
+ dev->udev = udev;
+ dev->open_count = 0;
+
+ dev->read_buffer = NULL;
+ dev->read_buffer_length = 0;
+
+ init_waitqueue_head (&dev->read_wait);
+ init_waitqueue_head (&dev->write_wait);
+
+ dev->interrupt_in_buffer = NULL;
+ dev->interrupt_in_endpoint = NULL;
+ dev->interrupt_in_urb = NULL;
+
+ dev->interrupt_out_buffer = NULL;
+ dev->interrupt_out_endpoint = NULL;
+ dev->interrupt_out_urb = NULL;
+
+
+ iface_desc = &interface->altsetting[0];
+
+ /* set up the endpoint information */
+ for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
+ endpoint = &iface_desc->endpoint[i].desc;
+
+ if (((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN) &&
+ ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT)) {
+ dev->interrupt_in_endpoint = endpoint;
+ }
+
+ if (((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT) &&
+ ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT)) {
+ dev->interrupt_out_endpoint = endpoint;
+ }
+ }
+ if(dev->interrupt_in_endpoint == NULL) {
+ err("interrupt in endpoint not found");
+ goto error;
+ }
+ if (dev->interrupt_out_endpoint == NULL) {
+ err("interrupt out endpoint not found");
+ goto error;
+ }
+
+ dev->read_buffer = kmalloc ((4*dev->interrupt_in_endpoint->wMaxPacketSize), GFP_KERNEL);
+ if (!dev->read_buffer) {
+ err("Couldn't allocate read_buffer");
+ goto error;
+ }
+ dev->interrupt_in_buffer = kmalloc (dev->interrupt_in_endpoint->wMaxPacketSize, GFP_KERNEL);
+ if (!dev->interrupt_in_buffer) {
+ err("Couldn't allocate interrupt_in_buffer");
+ goto error;
+ }
+ dev->interrupt_in_urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!dev->interrupt_in_urb) {
+ err("Couldn't allocate interrupt_in_urb");
+ goto error;
+ }
+ dev->interrupt_out_buffer = kmalloc (dev->interrupt_out_endpoint->wMaxPacketSize, GFP_KERNEL);
+ if (!dev->interrupt_out_buffer) {
+ err("Couldn't allocate interrupt_out_buffer");
+ goto error;
+ }
+ dev->interrupt_out_urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!dev->interrupt_out_urb) {
+ err("Couldn't allocate interrupt_out_urb");
+ goto error;
+ }
+
+ /* we can register the device now, as it is ready */
+ usb_set_intfdata (interface, dev);
+
+ retval = usb_register_dev (interface, &tower_class);
+
+ if (retval) {
+ /* something prevented us from registering this driver */
+ err ("Not able to get a minor for this device.");
+ usb_set_intfdata (interface, NULL);
+ goto error;
+ }
+
+ dev->minor = interface->minor;
+
+ /* let the user know what node this device is now attached to */
+ info ("LEGO USB Tower device now attached to /dev/usb/lego%d", (dev->minor - LEGO_USB_TOWER_MINOR_BASE));
+
+
+
+exit:
+ dbg(2," %s : leave, return value 0x%.8lx (dev)", __func__, (long) dev);
+
+ return retval;
+
+error:
+ tower_delete(dev);
+ return retval;
+}
+
+
+/**
+ * tower_disconnect
+ *
+ * Called by the usb core when the device is removed from the system.
+ */
+static void tower_disconnect (struct usb_interface *interface)
+{
+ struct lego_usb_tower *dev;
+ int minor;
+
+ dbg(2," %s : enter", __func__);
+
+ down (&disconnect_sem);
+
+ dev = usb_get_intfdata (interface);
+ usb_set_intfdata (interface, NULL);
+
+
+ down (&dev->sem);
+
+ minor = dev->minor;
+
+ /* give back our minor */
+ usb_deregister_dev (interface, &tower_class);
+
+ /* if the device is not opened, then we clean up right now */
+ if (!dev->open_count) {
+ up (&dev->sem);
+ tower_delete (dev);
+ } else {
+ dev->udev = NULL;
+ up (&dev->sem);
+ }
+
+ up (&disconnect_sem);
+
+ info("LEGO USB Tower #%d now disconnected", (minor - LEGO_USB_TOWER_MINOR_BASE));
+
+ dbg(2," %s : leave", __func__);
+}
+
+
+
+/**
+ * lego_usb_tower_init
+ */
+static int __init lego_usb_tower_init(void)
+{
+ int result;
+ int retval = 0;
+
+ dbg(2," %s : enter", __func__);
+
+ /* register this driver with the USB subsystem */
+ result = usb_register(&tower_driver);
+ if (result < 0) {
+ err("usb_register failed for the "__FILE__" driver. Error number %d", result);
+ retval = -1;
+ goto exit;
+ }
+
+ info(DRIVER_DESC " " DRIVER_VERSION);
+
+exit:
+ dbg(2," %s : leave, return value %d", __func__, retval);
+
+ return retval;
+}
+
+
+/**
+ * lego_usb_tower_exit
+ */
+static void __exit lego_usb_tower_exit(void)
+{
+ dbg(2," %s : enter", __func__);
+
+ /* deregister this driver with the USB subsystem */
+ usb_deregister (&tower_driver);
+
+ dbg(2," %s : leave", __func__);
+}
+
+module_init (lego_usb_tower_init);
+module_exit (lego_usb_tower_exit);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+#ifdef MODULE_LICENSE
+MODULE_LICENSE("GPL");
+#endif
memset (info, 0, sizeof *info);
info->control = intf;
while (len > 3) {
- /* ignore bDescriptorType != CS_INTERFACE */
- if (buf [1] != 0x24)
+ if (buf [1] != USB_DT_CS_INTERFACE)
goto next_desc;
/* bDescriptorSubType identifies three "must have" descriptors;
{ USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2803_8_PID, 0, 0x3ff) },
{ USB_DEVICE_VER(IDTECH_VID, IDTECH_IDT1221U_PID, 0, 0x3ff) },
{ USB_DEVICE_VER(OCT_VID, OCT_US101_PID, 0, 0x3ff) },
+ { USB_DEVICE_VER(FTDI_VID, PROTEGO_SPECIAL_1, 0, 0x3ff) },
+ { USB_DEVICE_VER(FTDI_VID, PROTEGO_R2X0, 0, 0x3ff) },
+ { USB_DEVICE_VER(FTDI_VID, PROTEGO_SPECIAL_3, 0, 0x3ff) },
+ { USB_DEVICE_VER(FTDI_VID, PROTEGO_SPECIAL_4, 0, 0x3ff) },
{ } /* Terminating entry */
};
{ USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2803_8_PID, 0x400, 0xffff) },
{ USB_DEVICE_VER(IDTECH_VID, IDTECH_IDT1221U_PID, 0x400, 0xffff) },
{ USB_DEVICE_VER(OCT_VID, OCT_US101_PID, 0x400, 0xffff) },
+ { USB_DEVICE_VER(FTDI_VID, PROTEGO_SPECIAL_1, 0x400, 0xffff) },
+ { USB_DEVICE_VER(FTDI_VID, PROTEGO_R2X0, 0x400, 0xffff) },
+ { USB_DEVICE_VER(FTDI_VID, PROTEGO_SPECIAL_3, 0x400, 0xffff) },
+ { USB_DEVICE_VER(FTDI_VID, PROTEGO_SPECIAL_4, 0x400, 0xffff) },
{ } /* Terminating entry */
};
{ USB_DEVICE(OCT_VID, OCT_US101_PID) },
{ USB_DEVICE_VER(FTDI_VID, FTDI_HE_TIRA1_PID, 0x400, 0xffff) },
{ USB_DEVICE(FTDI_VID, FTDI_USB_UIRT_PID) },
+ { USB_DEVICE(FTDI_VID, PROTEGO_SPECIAL_1) },
+ { USB_DEVICE(FTDI_VID, PROTEGO_R2X0) },
+ { USB_DEVICE(FTDI_VID, PROTEGO_SPECIAL_3) },
+ { USB_DEVICE(FTDI_VID, PROTEGO_SPECIAL_4) },
{ } /* Terminating entry */
};
/* Note: OCT US101 is also rebadged as Dick Smith Electronics (NZ) XH6381 */
#define OCT_US101_PID 0x0421 /* OCT US101 USB to RS-232 */
+/*
+ * Protego product ids
+ */
+#define PROTEGO_SPECIAL_1 0xFC70 /* special/unknown device */
+#define PROTEGO_R2X0 0xFC71 /* R200-USB TRNG unit (R210, R220, and R230) */
+#define PROTEGO_SPECIAL_3 0xFC72 /* special/unknown device */
+#define PROTEGO_SPECIAL_4 0xFC73 /* special/unknown device */
+
/* Commands */
#define FTDI_SIO_RESET 0 /* Reset the port */
#define FTDI_SIO_MODEM_CTRL 1 /* Set the modem control register */
{ USB_DEVICE(TRIPP_VENDOR_ID, TRIPP_PRODUCT_ID) },
{ USB_DEVICE(RADIOSHACK_VENDOR_ID, RADIOSHACK_PRODUCT_ID) },
{ USB_DEVICE(DCU10_VENDOR_ID, DCU10_PRODUCT_ID) },
+ { USB_DEVICE(SITECOM_VENDOR_ID, SITECOM_PRODUCT_ID) },
{ } /* Terminating entry */
};
struct pl2303_private {
spinlock_t lock;
+ wait_queue_head_t delta_msr_wait;
u8 line_control;
u8 line_status;
u8 termios_initialized;
return -ENOMEM;
memset (priv, 0x00, sizeof (struct pl2303_private));
spin_lock_init(&priv->lock);
+ init_waitqueue_head(&priv->delta_msr_wait);
usb_set_serial_port_data(serial->port[i], priv);
}
return 0;
return result;
}
+static int wait_modem_info(struct usb_serial_port *port, unsigned int arg)
+{
+ struct pl2303_private *priv = usb_get_serial_port_data(port);
+ unsigned long flags;
+ unsigned int prevstatus;
+ unsigned int status;
+ unsigned int changed;
+
+ spin_lock_irqsave (&priv->lock, flags);
+ prevstatus = priv->line_status;
+ spin_unlock_irqrestore (&priv->lock, flags);
+
+ while (1) {
+ interruptible_sleep_on(&priv->delta_msr_wait);
+ /* see if a signal did it */
+ if (signal_pending(current))
+ return -ERESTARTSYS;
+
+ spin_lock_irqsave (&priv->lock, flags);
+ status = priv->line_status;
+ spin_unlock_irqrestore (&priv->lock, flags);
+
+ changed=prevstatus^status;
+
+ if (((arg & TIOCM_RNG) && (changed & UART_RING)) ||
+ ((arg & TIOCM_DSR) && (changed & UART_DSR)) ||
+ ((arg & TIOCM_CD) && (changed & UART_DCD)) ||
+ ((arg & TIOCM_CTS) && (changed & UART_CTS)) ) {
+ return 0;
+ }
+ prevstatus = status;
+ }
+ /* NOTREACHED */
+ return 0;
+}
+
static int pl2303_ioctl (struct usb_serial_port *port, struct file *file, unsigned int cmd, unsigned long arg)
{
dbg("%s (%d) cmd = 0x%04x", __FUNCTION__, port->number, cmd);
switch (cmd) {
+ case TIOCMIWAIT:
+ dbg("%s (%d) TIOCMIWAIT", __FUNCTION__, port->number);
+ return wait_modem_info(port, arg);
+
default:
dbg("%s not supported = 0x%04x", __FUNCTION__, cmd);
break;
spin_lock_irqsave(&priv->lock, flags);
status = priv->line_status;
spin_unlock_irqrestore(&priv->lock, flags);
+ wake_up_interruptible (&priv->delta_msr_wait);
/* break takes precedence over parity, */
/* which takes precedence over framing errors */
#define DCU10_VENDOR_ID 0x0731
#define DCU10_PRODUCT_ID 0x0528
+
+#define SITECOM_VENDOR_ID 0x6189
+#define SITECOM_PRODUCT_ID 0x2068
return retval;
}
-static void __serial_close(struct usb_serial_port *port, struct file *filp)
+static void serial_close(struct tty_struct *tty, struct file * filp)
{
- if (!port->open_count) {
- dbg ("%s - port not opened", __FUNCTION__);
+ struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data;
+ struct usb_serial *serial = get_usb_serial (port, __FUNCTION__);
+
+ if (!serial)
return;
- }
+
+ dbg("%s - port %d", __FUNCTION__, port->number);
--port->open_count;
if (port->open_count <= 0) {
* port is being closed by the last owner */
port->serial->type->close(port, filp);
port->open_count = 0;
+
+ if (port->tty) {
+ if (port->tty->driver_data)
+ port->tty->driver_data = NULL;
+ port->tty = NULL;
+ }
}
module_put(port->serial->type->owner);
kobject_put(&port->serial->kobj);
}
-static void serial_close(struct tty_struct *tty, struct file * filp)
-{
- struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data;
- struct usb_serial *serial = get_usb_serial (port, __FUNCTION__);
-
- if (!serial)
- return;
-
- dbg("%s - port %d", __FUNCTION__, port->number);
-
- /* if disconnect beat us to the punch here, there's nothing to do */
- if (tty && tty->driver_data) {
- __serial_close(port, filp);
- tty->driver_data = NULL;
- }
- port->tty = NULL;
-}
-
static int serial_write (struct tty_struct * tty, int from_user, const unsigned char *buf, int count)
{
struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data;
dbg ("%s - %s", __FUNCTION__, kobj->name);
serial = to_usb_serial(kobj);
-
- /* fail all future close/read/write/ioctl/etc calls */
- for (i = 0; i < serial->num_ports; ++i) {
- port = serial->port[i];
- if (port->tty != NULL) {
- port->tty->driver_data = NULL;
- while (port->open_count > 0) {
- __serial_close(port, NULL);
- }
- port->tty = NULL;
- }
- }
-
serial_shutdown (serial);
/* return the minor range that this device had */
/* register all of the individual ports with the driver core */
for (i = 0; i < num_ports; ++i) {
port = serial->port[i];
- port->dev.parent = &serial->dev->dev;
+ port->dev.parent = &interface->dev;
port->dev.driver = NULL;
port->dev.bus = &usb_serial_bus_type;
port->dev.release = &port_release;
US_DEBUGPX("\n");
}
-void usb_stor_print_Scsi_Cmnd(Scsi_Cmnd *cmd)
-{
- int i=0, bufferSize = cmd->request_bufflen;
- u8 *buffer = cmd->request_buffer;
- struct scatterlist *sg = (struct scatterlist*)cmd->request_buffer;
-
- US_DEBUGP("Dumping information about %p.\n", cmd);
- US_DEBUGP("cmd->cmnd[0] value is %d.\n", cmd->cmnd[0]);
- US_DEBUGP("(MODE_SENSE is %d and MODE_SENSE_10 is %d)\n",
- MODE_SENSE, MODE_SENSE_10);
-
- US_DEBUGP("buffer is %p with length %d.\n", buffer, bufferSize);
- for (i=0; i<bufferSize; i+=16) {
- US_DEBUGP("%02x %02x %02x %02x %02x %02x %02x %02x\n"
- "%02x %02x %02x %02x %02x %02x %02x %02x\n",
- buffer[i],
- buffer[i+1],
- buffer[i+2],
- buffer[i+3],
- buffer[i+4],
- buffer[i+5],
- buffer[i+6],
- buffer[i+7],
- buffer[i+8],
- buffer[i+9],
- buffer[i+10],
- buffer[i+11],
- buffer[i+12],
- buffer[i+13],
- buffer[i+14],
- buffer[i+15] );
- }
-
- US_DEBUGP("Buffer has %d scatterlists.\n", cmd->use_sg );
- for (i=0; i<cmd->use_sg; i++) {
- char *adr = sg_address(sg[i]);
-
- US_DEBUGP("Length of scatterlist %d is %d.\n",i,sg[i].length);
- US_DEBUGP("%02x %02x %02x %02x %02x %02x %02x %02x\n"
- "%02x %02x %02x %02x %02x %02x %02x %02x\n",
- adr[0],
- adr[1],
- adr[2],
- adr[3],
- adr[4],
- adr[5],
- adr[6],
- adr[7],
- adr[8],
- adr[9],
- adr[10],
- adr[11],
- adr[12],
- adr[13],
- adr[14],
- adr[15]);
- }
-}
-
void usb_stor_show_sense(
unsigned char key,
unsigned char asc,
#ifdef CONFIG_USB_STORAGE_DEBUG
void usb_stor_show_command(Scsi_Cmnd *srb);
-void usb_stor_print_Scsi_Cmnd( Scsi_Cmnd* cmd );
void usb_stor_show_sense( unsigned char key,
unsigned char asc, unsigned char ascq );
#define US_DEBUGP(x...) printk( KERN_DEBUG USB_STORAGE x )
* 675 Mass Ave, Cambridge, MA 02139, USA.
*/
+#include <linux/highmem.h>
#include "protocol.h"
#include "usb.h"
#include "debug.h"
* Helper routines
***********************************************************************/
-static void *
-find_data_location(Scsi_Cmnd *srb) {
- if (srb->use_sg) {
- /*
- * This piece of code only works if the first page is
- * big enough to hold more than 3 bytes -- which is
- * _very_ likely.
- */
- struct scatterlist *sg;
-
- sg = (struct scatterlist *) srb->request_buffer;
- return (void *) sg_address(sg[0]);
- } else
- return (void *) srb->request_buffer;
-}
-
/*
* Fix-up the return data from an INQUIRY command to show
* ANSI SCSI rev 2 so we don't confuse the SCSI layers above us
*/
static void fix_inquiry_data(Scsi_Cmnd *srb)
{
- unsigned char *data_ptr;
+ unsigned char databuf[3];
+ unsigned int index, offset;
/* verify that it's an INQUIRY command */
if (srb->cmnd[0] != INQUIRY)
return;
- /* oddly short buffer -- bail out */
- if (srb->request_bufflen < 3)
+ index = offset = 0;
+ if (usb_stor_access_xfer_buf(databuf, sizeof(databuf), srb,
+ &index, &offset, FROM_XFER_BUF) != sizeof(databuf))
return;
- data_ptr = find_data_location(srb);
-
- if ((data_ptr[2] & 7) == 2)
+ if ((databuf[2] & 7) == 2)
return;
US_DEBUGP("Fixing INQUIRY data to show SCSI rev 2 - was %d\n",
- data_ptr[2] & 7);
+ databuf[2] & 7);
/* Change the SCSI revision number */
- data_ptr[2] = (data_ptr[2] & ~7) | 2;
+ databuf[2] = (databuf[2] & ~7) | 2;
+
+ index = offset = 0;
+ usb_stor_access_xfer_buf(databuf, sizeof(databuf), srb,
+ &index, &offset, TO_XFER_BUF);
}
/*
*/
static void fix_read_capacity(Scsi_Cmnd *srb)
{
- unsigned char *dp;
+ unsigned int index, offset;
+ u32 c;
unsigned long capacity;
/* verify that it's a READ CAPACITY command */
if (srb->cmnd[0] != READ_CAPACITY)
return;
- dp = find_data_location(srb);
+ index = offset = 0;
+ if (usb_stor_access_xfer_buf((unsigned char *) &c, 4, srb,
+ &index, &offset, FROM_XFER_BUF) != 4)
+ return;
- capacity = (dp[0]<<24) + (dp[1]<<16) + (dp[2]<<8) + (dp[3]);
+ capacity = be32_to_cpu(c);
US_DEBUGP("US: Fixing capacity: from %ld to %ld\n",
capacity+1, capacity);
- capacity--;
- dp[0] = (capacity >> 24);
- dp[1] = (capacity >> 16);
- dp[2] = (capacity >> 8);
- dp[3] = (capacity);
+ c = cpu_to_be32(capacity - 1);
+
+ index = offset = 0;
+ usb_stor_access_xfer_buf((unsigned char *) &c, 4, srb,
+ &index, &offset, TO_XFER_BUF);
}
/***********************************************************************
fix_read_capacity(srb);
}
}
+
+/***********************************************************************
+ * Scatter-gather transfer buffer access routines
+ ***********************************************************************/
+
+/* Copy a buffer of length buflen to/from the srb's transfer buffer.
+ * Update the index and offset variables so that the next copy will
+ * pick up from where this one left off. */
+
+unsigned int usb_stor_access_xfer_buf(unsigned char *buffer,
+ unsigned int buflen, Scsi_Cmnd *srb, unsigned int *index,
+ unsigned int *offset, enum xfer_buf_dir dir)
+{
+ unsigned int cnt;
+
+ if (srb->use_sg == 0) {
+ if (*offset >= srb->request_bufflen)
+ return 0;
+ cnt = min(buflen, srb->request_bufflen - *offset);
+ if (dir == TO_XFER_BUF)
+ memcpy((unsigned char *) srb->request_buffer + *offset,
+ buffer, cnt);
+ else
+ memcpy(buffer, (unsigned char *) srb->request_buffer +
+ *offset, cnt);
+ *offset += cnt;
+
+ } else {
+ struct scatterlist *sg =
+ (struct scatterlist *) srb->request_buffer
+ + *index;
+
+ cnt = 0;
+ while (cnt < buflen && *index < srb->use_sg) {
+ struct page *page = sg->page +
+ ((sg->offset + *offset) >> PAGE_SHIFT);
+ unsigned int poff =
+ (sg->offset + *offset) & (PAGE_SIZE-1);
+ unsigned int sglen = sg->length - *offset;
+
+ if (sglen > buflen - cnt) {
+ sglen = buflen - cnt;
+ *offset += sglen;
+ } else {
+ *offset = 0;
+ ++sg;
+ ++*index;
+ }
+
+ while (sglen > 0) {
+ unsigned int plen = min(sglen, (unsigned int)
+ PAGE_SIZE - poff);
+ unsigned char *ptr = kmap(page);
+
+ if (dir == TO_XFER_BUF)
+ memcpy(ptr + poff, buffer + cnt, plen);
+ else
+ memcpy(buffer + cnt, ptr + poff, plen);
+ kunmap(page);
+ poff = 0;
+ ++page;
+ cnt += plen;
+ sglen -= plen;
+ }
+ }
+ }
+ return cnt;
+}
#define US_SC_DEVICE 0xff /* Use device's value */
+/* Protocol handling routines */
extern void usb_stor_ATAPI_command(Scsi_Cmnd*, struct us_data*);
extern void usb_stor_qic157_command(Scsi_Cmnd*, struct us_data*);
extern void usb_stor_ufi_command(Scsi_Cmnd*, struct us_data*);
extern void usb_stor_transparent_scsi_command(Scsi_Cmnd*, struct us_data*);
+/* Scsi_Cmnd transfer buffer access utilities */
+enum xfer_buf_dir {TO_XFER_BUF, FROM_XFER_BUF};
+
+extern unsigned int usb_stor_access_xfer_buf(unsigned char *buffer,
+ unsigned int buflen, Scsi_Cmnd *srb, unsigned int *index,
+ unsigned int *offset, enum xfer_buf_dir dir);
+
#endif
length = room;
memcpy(ptr, buffer+transferred, length);
- transferred += sg[i].length;
+ transferred += length;
*offset += length;
if (length == room) {
i++;
*/
#include "transport.h"
-#include "raw_bulk.h"
#include "protocol.h"
#include "usb.h"
#include "debug.h"
* NAND Flash Manufacturer ID Codes
*/
#define NAND_MFR_AMD 0x01
-#define NAND_MFR_NS 0x8f
+#define NAND_MFR_NATSEMI 0x8f
#define NAND_MFR_TOSHIBA 0x98
#define NAND_MFR_SAMSUNG 0xec
switch(manuf_id) {
case NAND_MFR_AMD:
return "AMD";
- case NAND_MFR_NS:
- return "NS";
+ case NAND_MFR_NATSEMI:
+ return "NATSEMI";
case NAND_MFR_TOSHIBA:
return "Toshiba";
case NAND_MFR_SAMSUNG:
if (result != USB_STOR_XFER_GOOD) {
US_DEBUGP("request sense bulk in failed\n");
return USB_STOR_TRANSPORT_ERROR;
- }
- else {
+ } else {
US_DEBUGP("request sense worked\n");
return USB_STOR_TRANSPORT_GOOD;
}
unsigned char *command = us->iobuf;
int result;
+ US_DEBUGP("sddr09_erase: erase address %lu\n", Eaddress);
+
memset(command, 0, 12);
command[0] = 0xEA;
command[1] = LUNBITS;
sddr09_read_data(struct us_data *us,
unsigned long address,
unsigned int sectors,
- unsigned char *content,
+ unsigned char *buffer,
int use_sg) {
struct sddr09_card_info *info = (struct sddr09_card_info *) us->extra;
unsigned int lba, maxlba, pba;
unsigned int page, pages;
- unsigned char *buffer = NULL;
- unsigned char *ptr;
- int result, len;
-
- // If we're using scatter-gather, we have to create a new
- // buffer to read all of the data in first, since a
- // scatter-gather buffer could in theory start in the middle
- // of a page, which would be bad. A developer who wants a
- // challenge might want to write a limited-buffer
- // version of this code.
-
- len = sectors*info->pagesize;
+ unsigned int len, index, offset;
+ int result;
- buffer = (use_sg ? kmalloc(len, GFP_NOIO) : content);
- if (buffer == NULL)
- return USB_STOR_TRANSPORT_ERROR;
+ // Since we only read in one block at a time, we have to create
+ // a bounce buffer if the transfer uses scatter-gather.
- ptr = buffer;
+ if (use_sg) {
+ len = min(sectors, (unsigned int) info->blocksize) *
+ info->pagesize;
+ buffer = kmalloc(len, GFP_NOIO);
+ if (buffer == NULL) {
+ printk("sddr09_read_data: Out of memory\n");
+ return USB_STOR_TRANSPORT_ERROR;
+ }
+ }
// Figure out the initial LBA and page
lba = address >> info->blockshift;
// contiguous LBA's. Another exercise left to the student.
result = USB_STOR_TRANSPORT_GOOD;
+ index = offset = 0;
while (sectors > 0) {
/* Find number of pages we can read in this block */
- pages = info->blocksize - page;
- if (pages > sectors)
- pages = sectors;
+ pages = min(sectors, info->blocksize - page);
+ len = pages << info->pageshift;
/* Not overflowing capacity? */
if (lba >= maxlba) {
Instead of returning USB_STOR_TRANSPORT_ERROR
it is better to return all zero data. */
- memset(ptr, 0, pages << info->pageshift);
+ memset(buffer, 0, len);
} else {
US_DEBUGP("Read %d pages, from PBA %d"
info->pageshift;
result = sddr09_read20(us, address>>1,
- pages, info->pageshift, ptr, 0);
+ pages, info->pageshift, buffer, 0);
if (result != USB_STOR_TRANSPORT_GOOD)
break;
}
+ if (use_sg)
+ usb_stor_access_xfer_buf(buffer, len, us->srb,
+ &index, &offset, TO_XFER_BUF);
+ else
+ buffer += len;
page = 0;
lba++;
sectors -= pages;
- ptr += (pages << info->pageshift);
}
- if (use_sg && result == USB_STOR_TRANSPORT_GOOD)
- us_copy_to_sgbuf_all(buffer, len, content, use_sg);
-
if (use_sg)
kfree(buffer);
return result;
}
-/* we never free blocks, so lastpba can only increase */
static unsigned int
-sddr09_find_unused_pba(struct sddr09_card_info *info) {
+sddr09_find_unused_pba(struct sddr09_card_info *info, unsigned int lba) {
static unsigned int lastpba = 1;
- int numblocks = info->capacity >> (info->blockshift + info->pageshift);
- int i;
+ int zonestart, end, i;
+
+ zonestart = (lba/1000) << 10;
+ end = info->capacity >> (info->blockshift + info->pageshift);
+ end -= zonestart;
+ if (end > 1024)
+ end = 1024;
- for (i = lastpba+1; i < numblocks; i++) {
- if (info->pba_to_lba[i] == UNDEF) {
+ for (i = lastpba+1; i < end; i++) {
+ if (info->pba_to_lba[zonestart+i] == UNDEF) {
+ lastpba = i;
+ return zonestart+i;
+ }
+ }
+ for (i = 0; i <= lastpba; i++) {
+ if (info->pba_to_lba[zonestart+i] == UNDEF) {
lastpba = i;
- return i;
+ return zonestart+i;
}
}
return 0;
static int
sddr09_write_lba(struct us_data *us, unsigned int lba,
unsigned int page, unsigned int pages,
- unsigned char *ptr) {
+ unsigned char *ptr, unsigned char *blockbuffer) {
struct sddr09_card_info *info = (struct sddr09_card_info *) us->extra;
unsigned long address;
unsigned int pba, lbap;
- unsigned int pagelen, blocklen;
- unsigned char *blockbuffer, *bptr, *cptr, *xptr;
+ unsigned int pagelen;
+ unsigned char *bptr, *cptr, *xptr;
unsigned char ecc[3];
- int i, result;
+ int i, result, isnew;
- lbap = ((lba & 0x3ff) << 1) | 0x1000;
+ lbap = ((lba % 1000) << 1) | 0x1000;
if (parity[MSB_of(lbap) ^ LSB_of(lbap)])
lbap ^= 1;
pba = info->lba_to_pba[lba];
+ isnew = 0;
if (pba == UNDEF) {
- pba = sddr09_find_unused_pba(info);
+ pba = sddr09_find_unused_pba(info, lba);
if (!pba) {
printk("sddr09_write_lba: Out of unused blocks\n");
return USB_STOR_TRANSPORT_ERROR;
}
info->pba_to_lba[pba] = lba;
info->lba_to_pba[lba] = pba;
+ isnew = 1;
}
if (pba == 1) {
}
pagelen = (1 << info->pageshift) + (1 << CONTROL_SHIFT);
- blocklen = (pagelen << info->blockshift);
- blockbuffer = kmalloc(blocklen, GFP_NOIO);
- if (!blockbuffer) {
- printk("sddr09_write_lba: Out of memory\n");
- return USB_STOR_TRANSPORT_ERROR;
- }
/* read old contents */
address = (pba << (info->pageshift + info->blockshift));
result = sddr09_read22(us, address>>1, info->blocksize,
info->pageshift, blockbuffer, 0);
if (result != USB_STOR_TRANSPORT_GOOD)
- goto err;
+ return result;
- /* check old contents */
- for (i = 0; i < info->blockshift; i++) {
+ /* check old contents and fill lba */
+ for (i = 0; i < info->blocksize; i++) {
bptr = blockbuffer + i*pagelen;
cptr = bptr + info->pagesize;
nand_compute_ecc(bptr, ecc);
i, pba);
nand_store_ecc(cptr+8, ecc);
}
+ cptr[6] = cptr[11] = MSB_of(lbap);
+ cptr[7] = cptr[12] = LSB_of(lbap);
}
/* copy in new stuff and compute ECC */
nand_store_ecc(cptr+13, ecc);
nand_compute_ecc(bptr+(info->pagesize / 2), ecc);
nand_store_ecc(cptr+8, ecc);
- cptr[6] = cptr[11] = MSB_of(lbap);
- cptr[7] = cptr[12] = LSB_of(lbap);
}
US_DEBUGP("Rewrite PBA %d (LBA %d)\n", pba, lba);
int result2 = sddr09_test_unit_ready(us);
}
#endif
- err:
- kfree(blockbuffer);
-
- /* TODO: instead of doing kmalloc/kfree for each block,
- add a bufferpointer to the info structure */
return result;
}
sddr09_write_data(struct us_data *us,
unsigned long address,
unsigned int sectors,
- unsigned char *content,
+ unsigned char *buffer,
int use_sg) {
struct sddr09_card_info *info = (struct sddr09_card_info *) us->extra;
unsigned int lba, page, pages;
- unsigned char *buffer = NULL;
- unsigned char *ptr;
- int result, len;
+ unsigned int pagelen, blocklen;
+ unsigned char *blockbuffer;
+ unsigned int len, index, offset;
+ int result;
- len = sectors*info->pagesize;
+ // blockbuffer is used for reading in the old data, overwriting
+ // with the new data, and performing ECC calculations
- buffer = us_copy_from_sgbuf_all(content, len, use_sg);
- if (buffer == NULL)
+ /* TODO: instead of doing kmalloc/kfree for each write,
+ add a bufferpointer to the info structure */
+
+ pagelen = (1 << info->pageshift) + (1 << CONTROL_SHIFT);
+ blocklen = (pagelen << info->blockshift);
+ blockbuffer = kmalloc(blocklen, GFP_NOIO);
+ if (!blockbuffer) {
+ printk("sddr09_write_data: Out of memory\n");
return USB_STOR_TRANSPORT_ERROR;
+ }
- ptr = buffer;
+ // Since we don't write the user data directly to the device,
+ // we have to create a bounce buffer if the transfer uses
+ // scatter-gather.
+
+ if (use_sg) {
+ len = min(sectors, (unsigned int) info->blocksize) *
+ info->pagesize;
+ buffer = kmalloc(len, GFP_NOIO);
+ if (buffer == NULL) {
+ printk("sddr09_write_data: Out of memory\n");
+ kfree(blockbuffer);
+ return USB_STOR_TRANSPORT_ERROR;
+ }
+ }
// Figure out the initial LBA and page
lba = address >> info->blockshift;
page = (address & info->blockmask);
result = USB_STOR_TRANSPORT_GOOD;
+ index = offset = 0;
while (sectors > 0) {
// Write as many sectors as possible in this block
- pages = info->blocksize - page;
- if (pages > sectors)
- pages = sectors;
+ pages = min(sectors, info->blocksize - page);
+ len = (pages << info->pageshift);
+ if (use_sg)
+ usb_stor_access_xfer_buf(buffer, len, us->srb,
+ &index, &offset, FROM_XFER_BUF);
- result = sddr09_write_lba(us, lba, page, pages, ptr);
+ result = sddr09_write_lba(us, lba, page, pages,
+ buffer, blockbuffer);
if (result != USB_STOR_TRANSPORT_GOOD)
break;
+ if (!use_sg)
+ buffer += len;
page = 0;
lba++;
sectors -= pages;
- ptr += (pages << info->pageshift);
}
if (use_sg)
kfree(buffer);
+ kfree(blockbuffer);
return result;
}
unsigned char *content,
int use_sg) {
- US_DEBUGP("Read control address %08lX blocks %04X\n",
+ US_DEBUGP("Read control address %lu, blocks %d\n",
address, blocks);
- return sddr09_read21(us, address, blocks, CONTROL_SHIFT, content, use_sg);
+ return sddr09_read21(us, address, blocks,
+ CONTROL_SHIFT, content, use_sg);
}
/*
US_DEBUGP("sddr09_get_wp: read_status fails\n");
return result;
}
- US_DEBUGP("sddr09_get_wp: status %02X", status);
+ US_DEBUGP("sddr09_get_wp: status 0x%02X", status);
if ((status & 0x80) == 0) {
info->flags |= SDDR09_WP; /* write protected */
US_DEBUGP(" WP");
static int
sddr09_read_map(struct us_data *us) {
- struct scatterlist *sg;
struct sddr09_card_info *info = (struct sddr09_card_info *) us->extra;
int numblocks, alloc_len, alloc_blocks;
int i, j, result;
- unsigned char *ptr;
+ unsigned char *buffer, *buffer_end, *ptr;
unsigned int lba, lbact;
if (!info->capacity)
return -1;
- // read 64 (1<<6) bytes for every block
- // ( 1 << ( blockshift + pageshift ) bytes)
- // of capacity:
- // (1<<6)*capacity/(1<<(b+p)) =
- // ((1<<6)*capacity)>>(b+p) =
- // capacity>>(b+p-6)
-
- alloc_len = info->capacity >>
- (info->blockshift + info->pageshift - CONTROL_SHIFT);
-
- // Allocate a number of scatterlist structures according to
- // the number of 128k blocks in the alloc_len. Adding 128k-1
- // and then dividing by 128k gives the correct number of blocks.
- // 128k = 1<<17
-
- alloc_blocks = (alloc_len + (1<<17) - 1) >> 17;
- sg = kmalloc(alloc_blocks*sizeof(struct scatterlist),
- GFP_NOIO);
- if (sg == NULL)
- return 0;
-
- for (i=0; i<alloc_blocks; i++) {
- int alloc_req = (i < alloc_blocks-1 ? 1 << 17 : alloc_len);
- char *vaddr = kmalloc(alloc_req, GFP_NOIO);
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,3)
- sg[i].page = virt_to_page(vaddr);
- sg[i].offset = offset_in_page(vaddr);
-#else
- sg[i].address = vaddr;
-#endif
- sg[i].length = alloc_req;
- alloc_len -= alloc_req;
- }
-
- for (i=0; i<alloc_blocks; i++)
- if (sg[i].page == NULL) {
- for (i=0; i<alloc_blocks; i++)
- if (sg[i].page != NULL)
- kfree(sg_address(sg[i]));
- kfree(sg);
- return 0;
- }
+ // size of a block is 1 << (blockshift + pageshift) bytes
+ // divide into the total capacity to get the number of blocks
numblocks = info->capacity >> (info->blockshift + info->pageshift);
- result = sddr09_read_control(us, 0, numblocks,
- (unsigned char *)sg, alloc_blocks);
- if (result != USB_STOR_TRANSPORT_GOOD) {
- for (i=0; i<alloc_blocks; i++)
- kfree(sg_address(sg[i]));
- kfree(sg);
- return -1;
+ // read 64 bytes for every block (actually 1 << CONTROL_SHIFT)
+ // but only use a 64 KB buffer
+ // buffer size used must be a multiple of (1 << CONTROL_SHIFT)
+#define SDDR09_READ_MAP_BUFSZ 65536
+
+ alloc_blocks = min(numblocks, SDDR09_READ_MAP_BUFSZ >> CONTROL_SHIFT);
+ alloc_len = (alloc_blocks << CONTROL_SHIFT);
+ buffer = kmalloc(alloc_len, GFP_NOIO);
+ if (buffer == NULL) {
+ printk("sddr09_read_map: out of memory\n");
+ result = -1;
+ goto done;
}
+ buffer_end = buffer + alloc_len;
+
+#undef SDDR09_READ_MAP_BUFSZ
kfree(info->lba_to_pba);
kfree(info->pba_to_lba);
info->pba_to_lba = kmalloc(numblocks*sizeof(int), GFP_NOIO);
if (info->lba_to_pba == NULL || info->pba_to_lba == NULL) {
- kfree(info->lba_to_pba);
- kfree(info->pba_to_lba);
- info->lba_to_pba = NULL;
- info->pba_to_lba = NULL;
- for (i=0; i<alloc_blocks; i++)
- kfree(sg_address(sg[i]));
- kfree(sg);
- return 0;
+ printk("sddr09_read_map: out of memory\n");
+ result = -1;
+ goto done;
}
for (i = 0; i < numblocks; i++)
info->lba_to_pba[i] = info->pba_to_lba[i] = UNDEF;
- ptr = sg_address(sg[0]);
-
/*
* Define lba-pba translation table
*/
- // Each block is 64 bytes of control data, so block i is located in
- // scatterlist block i*64/128k = i*(2^6)*(2^-17) = i*(2^-11)
- for (i=0; i<numblocks; i++) {
- ptr = sg_address(sg[i>>11]) + ((i&0x7ff)<<6);
+ ptr = buffer_end;
+ for (i = 0; i < numblocks; i++) {
+ ptr += (1 << CONTROL_SHIFT);
+ if (ptr >= buffer_end) {
+ unsigned long address;
+
+ address = i << (info->pageshift + info->blockshift);
+ result = sddr09_read_control(
+ us, address>>1,
+ min(alloc_blocks, numblocks - i),
+ buffer, 0);
+ if (result != USB_STOR_TRANSPORT_GOOD) {
+ result = -1;
+ goto done;
+ }
+ ptr = buffer;
+ }
if (i == 0 || i == 1) {
info->pba_to_lba[i] = UNUSABLE;
if (ptr[j] != 0)
goto nonz;
info->pba_to_lba[i] = UNUSABLE;
- printk("sddr09: PBA %04X has no logical mapping\n", i);
+ printk("sddr09: PBA %d has no logical mapping\n", i);
continue;
nonz:
nonff:
/* normal PBAs start with six FFs */
if (j < 6) {
- printk("sddr09: PBA %04X has no logical mapping: "
+ printk("sddr09: PBA %d has no logical mapping: "
"reserved area = %02X%02X%02X%02X "
"data status %02X block status %02X\n",
i, ptr[0], ptr[1], ptr[2], ptr[3],
}
if ((ptr[6] >> 4) != 0x01) {
- printk("sddr09: PBA %04X has invalid address field "
+ printk("sddr09: PBA %d has invalid address field "
"%02X%02X/%02X%02X\n",
i, ptr[6], ptr[7], ptr[11], ptr[12]);
info->pba_to_lba[i] = UNUSABLE;
/* check even parity */
if (parity[ptr[6] ^ ptr[7]]) {
- printk("sddr09: Bad parity in LBA for block %04X"
+ printk("sddr09: Bad parity in LBA for block %d"
" (%02X %02X)\n", i, ptr[6], ptr[7]);
info->pba_to_lba[i] = UNUSABLE;
continue;
*/
if (lba >= 1000) {
- unsigned long address;
-
- printk("sddr09: Bad LBA %04X for block %04X\n",
+ printk("sddr09: Bad low LBA %d for block %d\n",
lba, i);
- info->pba_to_lba[i] = UNDEF /* UNUSABLE */;
- if (erase_bad_lba_entries) {
- /* some cameras cannot erase a card if it has
- bad entries, so we supply this function */
- address = (i << (info->pageshift + info->blockshift));
- sddr09_erase(us, address>>1);
- }
- continue;
+ goto possibly_erase;
}
lba += 1000*(i/0x400);
- if (lba<0x10 || (lba >= 0x3E0 && lba < 0x3EF))
- US_DEBUGP("LBA %04X <-> PBA %04X\n", lba, i);
+ if (info->lba_to_pba[lba] != UNDEF) {
+ printk("sddr09: LBA %d seen for PBA %d and %d\n",
+ lba, info->lba_to_pba[lba], i);
+ goto possibly_erase;
+ }
info->pba_to_lba[i] = lba;
info->lba_to_pba[lba] = i;
+ continue;
+
+ possibly_erase:
+ if (erase_bad_lba_entries) {
+ unsigned long address;
+
+ address = (i << (info->pageshift + info->blockshift));
+ sddr09_erase(us, address>>1);
+ info->pba_to_lba[i] = UNDEF;
+ } else
+ info->pba_to_lba[i] = UNUSABLE;
}
/*
}
info->lbact = lbact;
US_DEBUGP("Found %d LBA's\n", lbact);
+ result = 0;
- for (i=0; i<alloc_blocks; i++)
- kfree(sg_address(sg[i]));
- kfree(sg);
- return 0;
+ done:
+ if (result != 0) {
+ kfree(info->lba_to_pba);
+ kfree(info->pba_to_lba);
+ info->lba_to_pba = NULL;
+ info->pba_to_lba = NULL;
+ }
+ kfree(buffer);
+ return result;
}
static void
cardinfo = sddr09_get_cardinfo(us, info->flags);
if (!cardinfo) {
/* probably no media */
+ init_error:
sensekey = 0x02; /* not ready */
sensecode = 0x3a; /* medium not present */
return USB_STOR_TRANSPORT_FAILED;
info->blockmask = info->blocksize - 1;
// map initialization, must follow get_cardinfo()
- sddr09_read_map(us);
+ if (sddr09_read_map(us)) {
+ /* probably out of memory */
+ goto init_error;
+ }
// Report capacity
return USB_STOR_TRANSPORT_GOOD;
}
- if (srb->cmnd[0] == MODE_SENSE) {
+ if (srb->cmnd[0] == MODE_SENSE || srb->cmnd[0] == MODE_SENSE_10) {
int modepage = (srb->cmnd[2] & 0x3F);
int len;
/* They ask for the Read/Write error recovery page,
or for all pages. Give as much as they have room for. */
+ /* %% We should check DBD %% */
if (modepage == 0x01 || modepage == 0x3F) {
US_DEBUGP("SDDR09: Dummy up request for "
return USB_STOR_TRANSPORT_GOOD;
}
- return USB_STOR_TRANSPORT_ERROR;
+ sensekey = 0x05; /* illegal request */
+ sensecode = 0x24; /* invalid field in CDB */
+ return USB_STOR_TRANSPORT_FAILED;
}
- if (srb->cmnd[0] == ALLOW_MEDIUM_REMOVAL) {
-
- US_DEBUGP(
- "SDDR09: %s medium removal. Not that I can do"
- " anything about it...\n",
- (srb->cmnd[4]&0x03) ? "Prevent" : "Allow");
-
+ if (srb->cmnd[0] == ALLOW_MEDIUM_REMOVAL)
return USB_STOR_TRANSPORT_GOOD;
- }
-
havefakesense = 0;
if (srb->cmnd[0] == READ_10) {
if (srb->cmnd[0] != TEST_UNIT_READY &&
srb->cmnd[0] != REQUEST_SENSE) {
+ sensekey = 0x05; /* illegal request */
+ sensecode = 0x20; /* invalid command */
havefakesense = 1;
- return USB_STOR_TRANSPORT_ERROR;
+ return USB_STOR_TRANSPORT_FAILED;
}
for (; srb->cmd_len<12; srb->cmd_len++)
for (i=0; i<12; i++)
sprintf(string+strlen(string), "%02X ", srb->cmnd[i]);
- US_DEBUGP("SDDR09: Send control for command %s\n",
- string);
+ US_DEBUGP("SDDR09: Send control for command %s\n", string);
result = sddr09_send_scsi_command(us, srb->cmnd, 12);
if (result != USB_STOR_TRANSPORT_GOOD) {
int usb_stor_CBI_transport(Scsi_Cmnd *srb, struct us_data *us)
{
unsigned int transfer_length = srb->request_bufflen;
+ unsigned int pipe = 0;
int result;
/* COMMAND STAGE */
/* DATA STAGE */
/* transfer the data payload for this command, if one exists*/
if (transfer_length) {
- unsigned int pipe = srb->sc_data_direction == SCSI_DATA_READ ?
+ pipe = srb->sc_data_direction == SCSI_DATA_READ ?
us->recv_bulk_pipe : us->send_bulk_pipe;
result = usb_stor_bulk_transfer_sg(us, pipe,
srb->request_buffer, transfer_length,
if (srb->cmnd[0] == REQUEST_SENSE ||
srb->cmnd[0] == INQUIRY)
return USB_STOR_TRANSPORT_GOOD;
- else {
- if (us->iobuf[0])
- return USB_STOR_TRANSPORT_FAILED;
- else
- return USB_STOR_TRANSPORT_GOOD;
- }
+ if (us->iobuf[0])
+ goto Failed;
+ return USB_STOR_TRANSPORT_GOOD;
}
/* If not UFI, we interpret the data as a result code
case 0x00:
return USB_STOR_TRANSPORT_GOOD;
case 0x01:
- return USB_STOR_TRANSPORT_FAILED;
- default:
- return USB_STOR_TRANSPORT_ERROR;
+ goto Failed;
}
-
- /* we should never get here, but if we do, we're in trouble */
return USB_STOR_TRANSPORT_ERROR;
+
+ /* the CBI spec requires that the bulk pipe must be cleared
+ * following any data-in/out command failure (section 2.4.3.1.3)
+ */
+ Failed:
+ if (pipe)
+ usb_stor_clear_halt(us, pipe);
+ return USB_STOR_TRANSPORT_FAILED;
}
/*
struct bulk_cb_wrap *bcb = (struct bulk_cb_wrap *) us->iobuf;
struct bulk_cs_wrap *bcs = (struct bulk_cs_wrap *) us->iobuf;
unsigned int transfer_length = srb->request_bufflen;
+ unsigned int residue;
int result;
int fake_sense = 0;
return USB_STOR_TRANSPORT_ERROR;
/* check bulk status */
- US_DEBUGP("Bulk Status S 0x%x T 0x%x R %d Stat 0x%x\n",
+ residue = le32_to_cpu(bcs->Residue);
+ US_DEBUGP("Bulk Status S 0x%x T 0x%x R %u Stat 0x%x\n",
le32_to_cpu(bcs->Signature), bcs->Tag,
- bcs->Residue, bcs->Status);
+ residue, bcs->Status);
if ((bcs->Signature != cpu_to_le32(US_BULK_CS_SIGN) &&
bcs->Signature != cpu_to_le32(US_BULK_CS_OLYMPUS_SIGN)) ||
bcs->Tag != srb->serial_number ||
return USB_STOR_TRANSPORT_ERROR;
}
+ /* try to compute the actual residue, based on how much data
+ * was really transferred and what the device tells us */
+ residue = min(residue, transfer_length);
+ srb->resid = max(srb->resid, (int) residue);
+
/* based on the status code, we report good or bad */
switch (bcs->Status) {
case US_BULK_STAT_OK:
*/
void fill_inquiry_response(struct us_data *us, unsigned char *data,
- unsigned int data_len) {
-
- int i;
- struct scatterlist *sg;
- int len =
- us->srb->request_bufflen > data_len ? data_len :
- us->srb->request_bufflen;
- int transferred;
- int amt;
+ unsigned int data_len)
+{
+ unsigned int index, offset;
if (data_len<36) // You lose.
return;
data[35] = 0x30 + ((us->pusb_dev->descriptor.bcdDevice) & 0x0F);
}
- if (us->srb->use_sg) {
- sg = (struct scatterlist *)us->srb->request_buffer;
- for (i=0; i<us->srb->use_sg; i++)
- memset(sg_address(sg[i]), 0, sg[i].length);
- for (i=0, transferred=0;
- i<us->srb->use_sg && transferred < len;
- i++) {
- amt = sg[i].length > len-transferred ?
- len-transferred : sg[i].length;
- memcpy(sg_address(sg[i]), data+transferred, amt);
- transferred -= amt;
- }
- } else {
- memset(us->srb->request_buffer, 0, us->srb->request_bufflen);
- memcpy(us->srb->request_buffer, data, len);
- }
+ index = offset = 0;
+ usb_stor_access_xfer_buf(data, data_len, us->srb,
+ &index, &offset, TO_XFER_BUF);
+ if (data_len < us->srb->request_bufflen)
+ us->srb->resid = us->srb->request_bufflen - data_len;
}
static int usb_stor_control_thread(void * __us)
/* fill in info */
info->fbops = &aty128fb_ops;
info->flags = FBINFO_FLAG_DEFAULT;
+ info->dev = &pdev->dev;
#ifdef CONFIG_PMAC_PBOOK
par->lcd_on = default_lcd_on;
fb_info->gen.info.switch_con = &fbgen_switch;
fb_info->gen.info.updatevar = &fbgen_update_var;
fb_info->gen.info.flags = FBINFO_FLAG_DEFAULT;
+ fb_info->gen.info.dev = fb_info->pdev;
for (j = 0; j < 256; j++) {
if (j < 16) {
cfb->fb.fix.smem_len = smem_size;
cfb->fb.fix.mmio_len = MMIO_SIZE;
cfb->fb.screen_base = cfb->region;
+ cfb->fb.dev = &cfb->dev->dev;
err = -EINVAL;
if (!fb_find_mode(&cfb->fb.var, &cfb->fb, NULL, NULL, 0,
#include <linux/kmod.h>
#endif
#include <linux/devfs_fs_kernel.h>
+#include <linux/device.h>
#if defined(__mc68000__) || defined(CONFIG_APUS)
#include <asm/setup.h>
#endif
};
+struct fb_dev {
+ struct list_head node;
+ dev_t dev;
+ struct class_device class_dev;
+};
+#define to_fb_dev(d) container_of(d, struct fb_dev, class_dev)
+
+static void release_fb_dev(struct class_device *class_dev)
+{
+ struct fb_dev *fb_dev = to_fb_dev(class_dev);
+ kfree(fb_dev);
+}
+
+static struct class fb_class = {
+ .name = "video",
+ .release = &release_fb_dev,
+};
+
+static LIST_HEAD(fb_dev_list);
+static spinlock_t fb_dev_list_lock = SPIN_LOCK_UNLOCKED;
+
+static ssize_t show_dev(struct class_device *class_dev, char *buf)
+{
+ struct fb_dev *fb_dev = to_fb_dev(class_dev);
+ return print_dev_t(buf, fb_dev->dev);
+}
+static CLASS_DEVICE_ATTR(dev, S_IRUGO, show_dev, NULL);
+
+static void fb_add_class_device(int minor, struct device *device)
+{
+ struct fb_dev *fb_dev = NULL;
+ int retval;
+
+ fb_dev = kmalloc(sizeof(*fb_dev), GFP_KERNEL);
+ if (!fb_dev)
+ return;
+ memset(fb_dev, 0x00, sizeof(*fb_dev));
+
+ fb_dev->dev = MKDEV(FB_MAJOR, minor);
+ fb_dev->class_dev.dev = device;
+ fb_dev->class_dev.class = &fb_class;
+ snprintf(fb_dev->class_dev.class_id, BUS_ID_SIZE, "fb%d", minor);
+ retval = class_device_register(&fb_dev->class_dev);
+ if (retval)
+ goto error;
+ class_device_create_file(&fb_dev->class_dev, &class_device_attr_dev);
+ spin_lock(&fb_dev_list_lock);
+ list_add(&fb_dev->node, &fb_dev_list);
+ spin_unlock(&fb_dev_list_lock);
+ return;
+error:
+ kfree(fb_dev);
+}
+
+void fb_remove_class_device(int minor)
+{
+ struct fb_dev *fb_dev = NULL;
+ struct list_head *tmp;
+ int found = 0;
+
+ spin_lock(&fb_dev_list_lock);
+ list_for_each(tmp, &fb_dev_list) {
+ fb_dev = list_entry(tmp, struct fb_dev, node);
+ if ((MINOR(fb_dev->dev) == minor)) {
+ found = 1;
+ break;
+ }
+ }
+ if (found) {
+ list_del(&fb_dev->node);
+ spin_unlock(&fb_dev_list_lock);
+ class_device_unregister(&fb_dev->class_dev);
+ } else {
+ spin_unlock(&fb_dev_list_lock);
+ }
+}
+
/**
* register_framebuffer - registers a frame buffer device
* @fb_info: frame buffer info structure
devfs_mk_cdev(MKDEV(FB_MAJOR, i),
S_IFCHR | S_IRUGO | S_IWUGO, "fb/%d", i);
+
+ fb_add_class_device(i, fb_info->dev);
return 0;
}
kfree(fb_info->pixmap.addr);
registered_fb[i]=NULL;
num_registered_fb--;
+ fb_remove_class_device(i);
return 0;
}
if (register_chrdev(FB_MAJOR,"fb",&fb_fops))
printk("unable to get major %d for fb devs\n", FB_MAJOR);
+ class_register(&fb_class);
+
#ifdef CONFIG_FB_OF
if (ofonly) {
offb_init();
info->fbops = &par->i810fb_ops;
info->pseudo_palette = par->pseudo_palette;
info->flags = FBINFO_FLAG_DEFAULT;
+ info->dev = &dev->dev;
fb_alloc_cmap(&info->cmap, 256, 0);
#endif
};
-static int __init iga_init(struct fb_info *info, struct iga_par *par)
+static int __init iga_init(struct fb_info *info, struct iga_par *par, struct pci_dev *dev)
{
char vramsz = iga_inb(par, IGA_EXT_CNTRL, IGA_IDX_EXT_BUS_CNTL)
& MEM_SIZE_ALIAS;
info->fbops = &igafb_ops;
info->flags = FBINFO_FLAG_DEFAULT;
+ info->dev = &dev->dev;
fb_alloc_cmap(&info->cmap, video_cmap_len, 0);
info->fix = igafb_fix;
info->pseudo_palette = (void *)(par + 1);
- if (!iga_init(info, par)) {
+ if (!iga_init(info, par, pdev)) {
iounmap((void *)par->io_base);
iounmap(info->screen_base);
if (par->mmap_map)
};
static void __init
-init_imstt(struct fb_info *info)
+init_imstt(struct fb_info *info, struct pci_dev *pdev)
{
struct imstt_par *par = (struct imstt_par *) info->par;
__u32 i, tmp, *ip, *end;
info->fbops = &imsttfb_ops;
info->flags = FBINFO_FLAG_DEFAULT;
+ info->dev = &pdev->dev;
fb_alloc_cmap(&info->cmap, 0, 0);
par->cmap_regs = (__u8 *)ioremap(addr + 0x840000, 0x1000);
info->par = par;
info->pseudo_palette = (void *) (par + 1);
- init_imstt(info);
+ init_imstt(info, pdev);
pci_set_drvdata(pdev, info);
return 0;
m2info->fbcon.flags = FBINFO_FLAG_DEFAULT;
m2info->fbcon.currcon = -1;
m2info->fbcon.pseudo_palette = m2info->cmap;
+ m2info->fbcon.dev = &m2info->primary_dev->pcidev->dev;
fb_alloc_cmap(&m2info->fbcon.cmap, 256, 1);
if (mem < 64)
info->flags = FBINFO_FLAG_DEFAULT;
info->par = par;
info->pseudo_palette = (void *) (par + 1);
+ info->dev = &dev->dev;
fb_alloc_cmap(&info->cmap, NR_PALETTE, 0);
pci_set_drvdata(pdev, rinfo);
rinfo->next = board_list;
board_list = rinfo;
+ rinfo->info.dev = &pdev->dev;
if (register_framebuffer ((struct fb_info *) rinfo) < 0) {
printk ("radeonfb: could not register framebuffer\n");
if (info->pixmap.addr == NULL)
goto err_out_kfree1;
memset(info->pixmap.addr, 0, 64 * 1024);
+ info->dev = &pd->dev;
strcat(rivafb_fix.id, rci->name);
default_par->riva.Architecture = rci->arch_rev;
sis_fb_info.par = &ivideo;
sis_fb_info.screen_base = ivideo.video_vbase;
sis_fb_info.fbops = &sisfb_ops;
+ sis_fb_info.dev = &pdev->dev;
sisfb_get_fix(&sis_fb_info.fix, -1, &sis_fb_info);
sis_fb_info.pseudo_palette = pseudo_palette;
info->fbops = &sstfb_ops;
info->currcon = -1;
info->pseudo_palette = &all->pseudo_palette;
+ info->dev = &pdev->dev;
fix->type = FB_TYPE_PACKED_PIXELS;
fix->visual = FB_VISUAL_TRUECOLOR;
info->par = default_par;
info->pseudo_palette = (void *)(default_par + 1);
info->flags = FBINFO_FLAG_DEFAULT;
+ info->dev = &pdev->dev;
#ifndef MODULE
if (!mode_option)
}
#endif
+
+
all->info.currcon = -1;
all->info.par = &all->par;
all->info.pseudo_palette = all->pseudo_palette;
+ all->info.dev = &pdev->dev;
/* This should give a reasonable default video mode. */
default_var.accel_flags &= ~FB_ACCELF_TEXT;
default_var.activate |= FB_ACTIVATE_NOW;
fb_info.var = default_var;
+ fb_info.dev = &dev->dev;
if (register_framebuffer(&fb_info) < 0) {
output("Could not register Trident framebuffer\n");
return -EINVAL;
struct dentry * parent = dget(d->d_parent);
down(&parent->d_inode->i_sem);
d_delete(d);
- simple_rmdir(parent->d_inode,d);
+ if (d->d_inode)
+ simple_rmdir(parent->d_inode,d);
pr_debug(" o %s removing done (%d)\n",d->d_name.name,
atomic_read(&d->d_count));
extern void firmware_unregister(struct subsystem *);
/* debugging and troubleshooting/diagnostic helpers. */
+#ifdef CONFIG_DEBUG_DEV_PRINTK
+#define dev_printk(level, dev, format, arg...) \
+ do { \
+ if (!(dev) || !(dev)->driver) \
+ WARN_ON(1); \
+ else \
+ printk(level "%s %s: " format , \
+ (dev)->driver->name , \
+ (dev)->bus_id , ## arg); \
+ } while (0)
+#else
#define dev_printk(level, dev, format, arg...) \
printk(level "%s %s: " format , (dev)->driver->name , (dev)->bus_id , ## arg)
+#endif
#ifdef DEBUG
#define dev_dbg(dev, format, arg...) \
struct fb_info;
struct vm_area_struct;
struct file;
+struct device;
/*
* Frame buffer operations
struct vc_data *display_fg; /* Console visible on this display */
int currcon; /* Current VC. */
void *pseudo_palette; /* Fake palette of 16 colors */
+ struct device *dev; /* pointer to the device for this fb */
#ifdef CONFIG_BOOTSPLASH
struct splash_data *splash_data;
unsigned char *splash_pic;
#define I2C_DRIVERID_FS451 1037
#define I2C_DRIVERID_W83627HF 1038
#define I2C_DRIVERID_LM85 1039
+#define I2C_DRIVERID_LM83 1040
/*
* ---- Adapter types ----------------------------------------------------
#define I2C_HW_SMBUS_AMD8111 0x0a
#define I2C_HW_SMBUS_SCX200 0x0b
#define I2C_HW_SMBUS_NFORCE2 0x0c
+#define I2C_HW_SMBUS_W9968CF 0x0d
/* --- ISA pseudo-adapter */
#define I2C_HW_ISA 0x00
#define TUN_MINOR 200
+struct device;
+
struct miscdevice
{
int minor;
const char *name;
struct file_operations *fops;
struct list_head list;
+ struct device *dev;
char devfs_name[64];
};
#include <linux/kmod.h>
#include <linux/elf.h>
#include <linux/stringify.h>
+#include <linux/kobject.h>
#include <asm/local.h>
#include <asm/module.h>
struct module
{
+ struct kobject kobj;
+
enum module_state state;
/* Member of list of modules */
{
int rc = pci_register_driver (drv);
- if (rc > 0)
- return 0;
-
- /* iff CONFIG_HOTPLUG and built into kernel, we should
- * leave the driver around for future hotplug events.
- * For the module case, a hotplug daemon of some sort
- * should load a module in response to an insert event. */
-#if defined(CONFIG_HOTPLUG) && !defined(MODULE)
- if (rc == 0)
- return 0;
-#else
- if (rc == 0)
- rc = -ENODEV;
-#endif
-
- /* if we get here, we need to clean up pci driver instance
- * and return some sort of error */
- pci_unregister_driver (drv);
-
- return rc;
+ return rc < 0 ? rc : 0;
}
/*
*/
#include <linux/fs.h>
+#include <linux/device.h>
#define SND_DEV_CTL 0 /* Control port /dev/mixer */
#define SND_DEV_SEQ 1 /* Sequencer output /dev/sequencer (FM
* Sound core interface functions
*/
-extern int register_sound_special(struct file_operations *fops, int unit);
-extern int register_sound_mixer(struct file_operations *fops, int dev);
-extern int register_sound_midi(struct file_operations *fops, int dev);
-extern int register_sound_dsp(struct file_operations *fops, int dev);
-extern int register_sound_synth(struct file_operations *fops, int dev);
+extern int register_sound_special(struct file_operations *fops, int unit, struct device *device);
+extern int register_sound_mixer(struct file_operations *fops, int dev, struct device *device);
+extern int register_sound_midi(struct file_operations *fops, int dev, struct device *device);
+extern int register_sound_dsp(struct file_operations *fops, int dev, struct device *device);
+extern int register_sound_synth(struct file_operations *fops, int dev, struct device *device);
+extern int sound_add_class_device(char *name, struct class_device *class_dev, struct device *dev);
extern void unregister_sound_special(int unit);
extern void unregister_sound_mixer(int unit);
extern void unregister_sound_midi(int unit);
extern void unregister_sound_dsp(int unit);
extern void unregister_sound_synth(int unit);
+extern void sound_remove_class_device(struct class_device *class_dev);
#define USB_DT_DEVICE_QUALIFIER 0x06
#define USB_DT_OTHER_SPEED_CONFIG 0x07
#define USB_DT_INTERFACE_POWER 0x08
+/* these are from a minor usb 2.0 revision (ECN) */
+#define USB_DT_OTG 0x09
+#define USB_DT_DEBUG 0x0a
+#define USB_DT_INTERFACE_ASSOCIATION 0x0b
+
+/* conventional codes for class-specific descriptors */
+#define USB_DT_CS_DEVICE 0x21
+#define USB_DT_CS_CONFIG 0x22
+#define USB_DT_CS_STRING 0x23
+#define USB_DT_CS_INTERFACE 0x24
+#define USB_DT_CS_ENDPOINT 0x25
/* All standard descriptors have these 2 fields at the beginning */
struct usb_descriptor_header {
#define USB_CLASS_CDC_DATA 0x0a
#define USB_CLASS_CSCID 0x0b /* chip+ smart card */
#define USB_CLASS_CONTENT_SEC 0x0d /* content security */
+#define USB_CLASS_VIDEO 0x0e
#define USB_CLASS_APP_SPEC 0xfe
#define USB_CLASS_VENDOR_SPEC 0xff
/*-------------------------------------------------------------------------*/
+/* USB_DT_OTG (from OTG 1.0a supplement) */
+struct usb_otg_descriptor {
+ __u8 bLength;
+ __u8 bDescriptorType;
+
+ __u8 bmAttributes; /* support for HNP, SRP, etc */
+} __attribute__ ((packed));
+
+/* from usb_otg_descriptor.bmAttributes */
+#define USB_OTG_SRP (1 << 0)
+#define USB_OTG_HNP (1 << 1) /* swap host/device roles */
+
+/*-------------------------------------------------------------------------*/
+
+/* USB_DT_INTERFACE_ASSOCIATION: groups interfaces */
+struct usb_interface_assoc_descriptor {
+ __u8 bLength;
+ __u8 bDescriptorType;
+
+ __u8 bFirstInterface;
+ __u8 bInterfaceCount;
+ __u8 bFunctionClass;
+ __u8 bFunctionSubClass;
+ __u8 bFunctionProtocol;
+ __u8 iFunction;
+} __attribute__ ((packed));
+
+
+/*-------------------------------------------------------------------------*/
+
/* USB 2.0 defines three speeds, here's how Linux identifies them */
enum usb_device_speed {
#define VID_HARDWARE_CPIA2 33
#define VID_HARDWARE_VICAM 34
#define VID_HARDWARE_SF16FMR2 35
+#define VID_HARDWARE_W9968CF 36 /* W996[87]CF JPEG USB Dual Mode Cam */
#endif /* __LINUX_VIDEODEV_H */
/*
int used;
unsigned int dsp_loaded;
unsigned int exclusive: 1;
+ void (*release)(snd_hwdep_t *hwdep);
+ struct class_device class_dev;
+ struct device *dev;
};
extern int snd_hwdep_new(snd_card_t * card, char *id, int device, snd_hwdep_t ** rhwdep);
spinlock_t sys_timer_lock; /* Lock for system timer access */
#endif
struct semaphore access_mutex; /* locking */
+ struct device *dev;
};
/* opl3.c */
#if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE)
snd_pcm_oss_t oss;
#endif
+ void (*release)(snd_pcm_t *pcm);
+ struct class_device class_dev[2];
+ struct device *dev;
};
typedef struct _snd_pcm_notify {
#include <linux/spinlock.h>
#include <linux/wait.h>
#include <asm/semaphore.h>
+#include <linux/device.h>
#if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE)
#include "seq_device.h"
#if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE)
snd_seq_device_t *seq_dev;
#endif
+ void (*release)(snd_rawmidi_t *rmidi);
+ struct class_device class_dev;
+ struct device *dev_ptr;
};
/* main rawmidi functions */
runs modprobe with the appropriate arguments, thereby
loading the module if it is available. If unsure, say Y.
+config MODULE_SIG
+ bool "Module signature checking"
+ depends on MODULES && SECURITY_RSA
+ help
+ If you say Y here, signed module support will be enabled.
+ If unsure, say N.
+
endmenu
+
obj-$(CONFIG_SMP) += cpu.o
obj-$(CONFIG_UID16) += uid16.o
obj-$(CONFIG_MODULES) += module.o
+obj-$(CONFIG_MODULE_SIG) += module-sig.o
obj-$(CONFIG_KALLSYMS) += kallsyms.o
obj-$(CONFIG_PM) += power/
obj-$(CONFIG_BSD_PROCESS_ACCT) += acct.o
--- /dev/null
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/vmalloc.h>
+#include <linux/elf.h>
+#include <linux/crypto.h>
+#include <asm/scatterlist.h>
+
+#define SHA1_DIGEST_SIZE 20
+
+extern int rsa_check_sig(char *sig, u8 *sha1);
+
+static u8 sha1_result[SHA1_DIGEST_SIZE];
+int module_check_sig(Elf_Ehdr *hdr, Elf_Shdr *sechdrs, const char *secstrings)
+{
+ int i;
+ unsigned int sig_index = 0;
+ static struct crypto_tfm *sha1_tfm;
+ char *sig;
+
+ /* Can't use find_sec as it only checks for Alloc bit */
+ for (i = 1; i < hdr->e_shnum; i++)
+ if (strcmp(secstrings+sechdrs[i].sh_name, "module_sig") == 0) {
+ sig_index = i;
+ break;
+ }
+ if (sig_index <= 0)
+ return 0;
+ printk(KERN_DEBUG "GREG: sig_index = %d sh_size = %d\n", sig_index, sechdrs[sig_index].sh_size);
+
+ sig = (char *)sechdrs[sig_index].sh_addr;
+ printk(KERN_DEBUG "GREG: ");
+ for (i = 0; i < sechdrs[sig_index].sh_size; ++i)
+ printk("%.2x ", (unsigned char)sig[i]);
+ printk("\n");
+
+ sha1_tfm = crypto_alloc_tfm("sha1", 0);
+ if (sha1_tfm == NULL) {
+ printk(KERN_DEBUG "GREG: failed to load transform for sha1\n");
+ return 0;
+ }
+
+ crypto_digest_init(sha1_tfm);
+
+ for (i = 1; i < hdr->e_shnum; i++) {
+ struct scatterlist sg;
+ void *temp;
+ int size;
+ const char *name = secstrings+sechdrs[i].sh_name;
+
+ /* We only care about sections with "text" or "data" in their names */
+ if ((strstr(name, "text") == NULL) &&
+ (strstr(name, "data") == NULL))
+ continue;
+ /* avoid the ".rel.*" sections too. */
+ if (strstr(name, ".rel.") != NULL)
+ continue;
+
+ temp = (void *)sechdrs[i].sh_addr;
+ size = sechdrs[i].sh_size;
+ printk(KERN_DEBUG "GREG: sha1ing the %s section, size %d\n", name, size);
+ do {
+ memset(&sg, 0x00, sizeof(struct scatterlist));
+ sg.page = virt_to_page(temp);
+ sg.offset = offset_in_page(temp);
+ sg.length = min((unsigned long)size, (PAGE_SIZE - (unsigned long)sg.offset));
+ size -= sg.length;
+ temp += sg.length;
+ printk(KERN_DEBUG "GREG: page = %p, .offset = %d, .length = %d, temp = %p, size = %d\n",
+ sg.page, sg.offset, sg.length, temp, size);
+ crypto_digest_update(sha1_tfm, &sg, 1);
+ } while (size > 0);
+ }
+ crypto_digest_final(sha1_tfm, sha1_result);
+ crypto_free_tfm(sha1_tfm);
+ printk(KERN_DEBUG "GREG: sha1 is: ");
+ for (i = 0; i < sizeof(sha1_result); ++i)
+ printk("%.2x ", sha1_result[i]);
+ printk("\n");
+
+ /* see if this sha1 is really encrypted in the section */
+ return rsa_check_sig(sig, &sha1_result[0]);
+}
+
+
--- /dev/null
+#ifndef MODULE_SIG
+#define MODULE_SIG
+
+#ifdef CONFIG_MODULE_SIG
+extern int module_check_sig(Elf_Ehdr *hdr, Elf_Shdr *sechdrs, const char *secstrings);
+#else
+static int inline module_check_sig(Elf_Ehdr *hdr, Elf_Shdr *sechdrs, const char *secstrings)
+{
+ return 0;
+}
+#endif
+
+#endif
#include <asm/semaphore.h>
#include <asm/pgalloc.h>
#include <asm/cacheflush.h>
+#include "module-sig.h"
#if 0
#define DEBUGP printk
for (i = 0; i < NR_CPUS; i++)
total += local_read(&mod->ref[i].count);
+ total += atomic_read(&mod->kobj.refcount);
return total;
}
EXPORT_SYMBOL(module_refcount);
down(&module_mutex);
}
+static void mod_kobject_remove(struct module *mod);
+
asmlinkage long
sys_delete_module(const char __user *name_user, unsigned int flags)
{
goto out;
}
}
+
+ /* unregister the kobject in this module */
+ mod_kobject_remove(mod);
+
/* Stop the machine so refcounts can't move: irqs disabled. */
DEBUGP("Stopping refcounts...\n");
ret = stop_refcounts();
struct module_use *use;
int printed_something = 0;
- seq_printf(m, " %u ", module_refcount(mod));
+ seq_printf(m, " %u ", module_refcount(mod) - atomic_read(&mod->kobj.refcount));
/* Always include a trailing , so userspace can differentiate
between this and the old multi-field proc format. */
goto free_hdr;
}
+ if (module_check_sig(hdr, sechdrs, secstrings)) {
+ err = -EPERM;
+ goto free_hdr;
+ }
+
/* Now copy in args */
arglen = strlen_user(uargs);
if (!arglen) {
else return ptr;
}
+/* sysfs stuff */
+struct module_attribute {
+ struct attribute attr;
+ ssize_t (*show)(struct module *mod, char *);
+ ssize_t (*store)(struct module *mod, const char *, size_t);
+};
+#define to_module_attr(n) container_of(n, struct module_attribute, attr);
+#define to_module(n) container_of(n, struct module, kobj)
+
+static ssize_t module_attr_show(struct kobject *kobj, struct attribute *attr, char *buf)
+{
+ struct module *slot = to_module(kobj);
+ struct module_attribute *attribute = to_module_attr(attr);
+ return attribute->show ? attribute->show(slot, buf) : 0;
+}
+
+static ssize_t module_attr_store(struct kobject *kobj, struct attribute *attr, const char *buf, size_t len)
+{
+ struct module *slot = to_module(kobj);
+ struct module_attribute *attribute = to_module_attr(attr);
+ return attribute->store ? attribute->store(slot, buf, len) : 0;
+}
+
+static struct sysfs_ops module_sysfs_ops = {
+ .show = module_attr_show,
+ .store = module_attr_store,
+};
+
+/* Huh? A release() function that doesn't do anything?
+ * This is here only because a module has another reference count that
+ * it uses to determine if it should be cleaned up or not. If the
+ * module wants to switch over to use the kobject reference instead of
+ * its own, then this release function needs to do some work.
+ */
+static void module_release(struct kobject *kobj)
+{
+}
+
+static struct kobj_type module_ktype = {
+ .sysfs_ops = &module_sysfs_ops,
+ .release = &module_release,
+};
+static decl_subsys(module, &module_ktype, NULL);
+
+static int __init module_subsys_init(void)
+{
+ return subsystem_register(&module_subsys);
+}
+core_initcall(module_subsys_init);
+
+static ssize_t show_mod_refcount(struct module *mod, char *buf)
+{
+ return sprintf(buf, "%d\n", module_refcount(mod) - atomic_read(&mod->kobj.refcount));
+}
+
+static struct module_attribute mod_refcount = {
+ .attr = {.name = "refcount", .mode = S_IRUGO},
+ .show = show_mod_refcount,
+};
+
+static int mod_kobject_init(struct module *mod)
+{
+ int retval;
+
+ memset(&mod->kobj, 0x00, sizeof(struct kobject));
+ kobject_set_name(&mod->kobj, mod->name);
+ kobj_set_kset_s(mod, module_subsys);
+ retval = kobject_register(&mod->kobj);
+ if (!retval)
+ retval = sysfs_create_file(&mod->kobj, &mod_refcount.attr);
+ return retval;
+}
+
+static void mod_kobject_remove(struct module *mod)
+{
+ sysfs_remove_file(&mod->kobj, &mod_refcount.attr);
+ kobject_unregister(&mod->kobj);
+}
+
/* This is where the real work happens */
asmlinkage long
sys_init_module(void __user *umod,
return ret;
}
+ mod_kobject_init(mod);
+
/* Now it's a first class citizen! */
down(&module_mutex);
mod->state = MODULE_STATE_LIVE;
config ZLIB_DEFLATE
tristate
+
+config DEBUG_KOBJECT
+ bool "print lots of sysfs related debug infos"
+ default n
+ help
+ This option enables DEBUG in drivers/base/*.c and lib/kobject.c
+ If unsure, say N.
+
endmenu
* about using the kobject interface.
*/
+#ifdef DEBUG_KOBJECT
+#define DEBUG 1
+#else
#undef DEBUG
+#endif
#include <linux/kobject.h>
#include <linux/string.h>
}
}
- pr_debug ("%s: %s %s %s %s %s %s\n", __FUNCTION__, argv[0], argv[1],
- envp[0], envp[1], envp[2], envp[3]);
+ pr_debug ("%s: %s %s %s %s %s %s %s\n", __FUNCTION__, argv[0], argv[1],
+ envp[0], envp[1], envp[2], envp[3], envp[4]);
retval = call_usermodehelper (argv[0], argv, envp, 0);
if (retval)
pr_debug ("%s - call_usermodehelper returned %d\n",
void kobject_init(struct kobject * kobj)
{
+ WARN_ON(atomic_read(&kobj->refcount));
atomic_set(&kobj->refcount,1);
INIT_LIST_HEAD(&kobj->entry);
kobj->kset = kset_get(kobj->kset);
list_del_init(&kobj->entry);
up_write(&kobj->kset->subsys->rwsem);
}
- if (kobj->parent)
- kobject_put(kobj->parent);
kobject_put(kobj);
}
kobj->parent = parent;
error = create_dir(kobj);
- if (error)
+ if (error) {
unlink(kobj);
- else {
+ if (parent)
+ kobject_put(parent);
+ } else {
/* If this kobj does not belong to a kset,
try to find a parent that does. */
top_kobj = kobj;
atomic_inc(&kobj->refcount);
} else
ret = NULL;
+ WARN_ON((kobj != NULL) && (ret==NULL));
return ret;
}
{
struct kobj_type * t = get_ktype(kobj);
struct kset * s = kobj->kset;
+ struct kobject * parent = kobj->parent;
pr_debug("kobject %s: cleaning up\n",kobject_name(kobj));
if (kobj->k_name != kobj->name)
kobj->k_name = NULL;
if (t && t->release)
t->release(kobj);
+ else {
+ printk(KERN_ERR "kobject '%s' does not have a release() function, "
+ "it is broken and must be fixed.\n",
+ kobj->name);
+ WARN_ON(1);
+ }
+
if (s)
kset_put(s);
+ if (parent)
+ kobject_put(parent);
}
/**
static char *get_line (void);
static void print_position (WINDOW * win, int height, int width);
-static int hscroll, fd, file_size, bytes_read;
-static int begin_reached = 1, end_reached, page_length;
+static int hscroll = 0, fd, file_size, bytes_read;
+static int begin_reached = 1, end_reached = 0, page_length;
static char *buf, *page;
/*
source security/selinux/Kconfig
+source security/rsa/Kconfig
+
endmenu
#
subdir-$(CONFIG_SECURITY_SELINUX) += selinux
+subdir-$(CONFIG_SECURITY_RSA) += rsa
+ifeq ($(CONFIG_SECURITY_RSA),y)
+ obj-$(CONFIG_SECURITY_RSA) += rsa/built-in.o
+endif
# if we don't select a security model, use the default capabilities
ifneq ($(CONFIG_SECURITY),y)
--- /dev/null
+config SECURITY_RSA
+ tristate "RSA crap"
+ help
+ bah bah bah...
+
+ If you are unsure how to answer this question, answer N.
+
--- /dev/null
+#rsacore-objs := rsa.o mpi.o mpi_generic.o
+
+rsacore-objs := \
+ gnupg_mpi_generic_mpih-lshift.o \
+ gnupg_mpi_generic_mpih-mul1.o \
+ gnupg_mpi_generic_mpih-mul2.o \
+ gnupg_mpi_generic_mpih-mul3.o \
+ gnupg_mpi_generic_mpih-rshift.o \
+ gnupg_mpi_generic_mpih-sub1.o \
+ gnupg_mpi_generic_mpih-add1.o \
+ gnupg_mpi_generic_udiv-w-sdiv.o \
+ gnupg_mpi_mpicoder.o \
+ gnupg_mpi_mpi-add.o \
+ gnupg_mpi_mpi-bit.o \
+ gnupg_mpi_mpi-div.o \
+ gnupg_mpi_mpi-cmp.o \
+ gnupg_mpi_mpi-gcd.o \
+ gnupg_mpi_mpih-cmp.o \
+ gnupg_mpi_mpih-div.o \
+ gnupg_mpi_mpih-mul.o \
+ gnupg_mpi_mpi-inline.o \
+ gnupg_mpi_mpi-inv.o \
+ gnupg_mpi_mpi-mpow.o \
+ gnupg_mpi_mpi-mul.o \
+ gnupg_mpi_mpi-pow.o \
+ gnupg_mpi_mpi-scan.o \
+ gnupg_mpi_mpiutil.o \
+ gnupg_cipher_rsa-verify.o \
+ rsa.o \
+ rsa_key.o
+
+obj-$(CONFIG_SECURITY_RSA) += rsacore.o
+
--- /dev/null
+#ifndef FOO_H
+#define FOO_H
+
+/* The size of a `unsigned int', as computed by sizeof. */
+#define SIZEOF_UNSIGNED_INT 4
+
+/* The size of a `unsigned long', as computed by sizeof. */
+#define SIZEOF_UNSIGNED_LONG 4
+
+/* The size of a `unsigned long long', as computed by sizeof. */
+#define SIZEOF_UNSIGNED_LONG_LONG 8
+
+/* The size of a `unsigned short', as computed by sizeof. */
+#define SIZEOF_UNSIGNED_SHORT 2
+
+#ifndef UMUL_TIME
+ #define UMUL_TIME 1
+#endif
+#ifndef UDIV_TIME
+ #define UDIV_TIME UMUL_TIME
+#endif
+
+/* If udiv_qrnnd was not defined for this processor, use __udiv_qrnnd_c. */
+#if !defined (udiv_qrnnd)
+#define UDIV_NEEDS_NORMALIZATION 1
+#define udiv_qrnnd __udiv_qrnnd_c
+#endif
+
+
+#define BYTES_PER_MPI_LIMB (SIZEOF_UNSIGNED_LONG)
+
+#ifndef BITS_PER_MPI_LIMB
+#if BYTES_PER_MPI_LIMB == SIZEOF_UNSIGNED_INT
+ typedef unsigned int mpi_limb_t;
+ typedef signed int mpi_limb_signed_t;
+#elif BYTES_PER_MPI_LIMB == SIZEOF_UNSIGNED_LONG
+ typedef unsigned long int mpi_limb_t;
+ typedef signed long int mpi_limb_signed_t;
+#elif BYTES_PER_MPI_LIMB == SIZEOF_UNSIGNED_LONG_LONG
+ typedef unsigned long long int mpi_limb_t;
+ typedef signed long long int mpi_limb_signed_t;
+#elif BYTES_PER_MPI_LIMB == SIZEOF_UNSIGNED_SHORT
+ typedef unsigned short int mpi_limb_t;
+ typedef signed short int mpi_limb_signed_t;
+#else
+ #error BYTES_PER_MPI_LIMB does not match any C type
+#endif
+#define BITS_PER_MPI_LIMB (8*BYTES_PER_MPI_LIMB)
+#endif /*BITS_PER_MPI_LIMB*/
+
+
+
+#define W_TYPE_SIZE BITS_PER_MPI_LIMB
+
+//typedef unsigned long mpi_limb_t;
+//typedef signed long mpi_libb_signed_t;
+typedef mpi_limb_t *mpi_ptr_t; /* pointer to a limb */
+
+typedef mpi_limb_t UWtype;
+typedef unsigned int UHWtype;
+
+typedef size_t mpi_size_t;
+
+struct gcry_mpi {
+ int alloced; /* array size (# of allocated limbs) */
+ int nlimbs; /* number of valid limbs */
+ int sign; /* indicates a negative number and is used for opaque
+ * MPIs to store the length */
+ unsigned flags; /* bit 0: array must be allocated in secure memory space */
+ /* bit 2: the limb is a pointer to some m_alloced data */
+ mpi_limb_t *d; /* array with the limbs */
+};
+
+struct karatsuba_ctx {
+ struct karatsuba_ctx *next;
+ mpi_ptr_t tspace;
+ mpi_size_t tspace_size;
+ mpi_ptr_t tp;
+ mpi_size_t tp_size;
+};
+
+typedef struct gcry_mpi *GcryMPI;
+typedef struct gcry_mpi *GCRY_MPI;
+
+#define mpi_get_nlimbs(a) ((a)->nlimbs)
+#define mpi_is_neg(a) ((a)->sign)
+#define mpi_is_secure(a) ((a) && ((a)->flags&1))
+
+
+
+extern struct gcry_mpi *gcry_mpi_new( unsigned int nbits );
+extern struct gcry_mpi *mpi_alloc_secure( unsigned nlimbs );
+extern void mpi_free(struct gcry_mpi *a );
+
+extern mpi_ptr_t mpi_alloc_limb_space( unsigned nlimbs, int sec );
+
+extern void mpi_sub_ui(struct gcry_mpi *w, struct gcry_mpi *u, unsigned long v);
+extern void mpi_add(struct gcry_mpi *w, struct gcry_mpi *u, struct gcry_mpi *v);
+extern void mpi_fdiv_r(struct gcry_mpi *rem, struct gcry_mpi *dividend, struct gcry_mpi *divisor);
+extern void mpi_powm(struct gcry_mpi *res, struct gcry_mpi *base, struct gcry_mpi *exp, struct gcry_mpi *mod);
+extern void mpi_sub(struct gcry_mpi *w, struct gcry_mpi *u, struct gcry_mpi *v);
+extern void mpi_mul(struct gcry_mpi *w, struct gcry_mpi *u,struct gcry_mpi *v);
+extern void mpi_mulm(struct gcry_mpi *w, struct gcry_mpi *u,struct gcry_mpi *v, struct gcry_mpi *m);
+
+#define assert(x)
+
+#define RESIZE_IF_NEEDED(a,b) \
+ do { \
+ if( (a)->alloced < (b) ) \
+ mpi_resize((a), (b)); \
+ } while(0)
+
+
+/* Copy N limbs from S to D. */
+#define MPN_COPY( d, s, n) \
+ do { \
+ size_t _i; \
+ for( _i = 0; _i < (n); _i++ ) \
+ (d)[_i] = (s)[_i]; \
+ } while(0)
+
+/* Zero N limbs at D */
+#define MPN_ZERO(d, n) \
+ do { \
+ int _i; \
+ for( _i = 0; _i < (n); _i++ ) \
+ (d)[_i] = 0; \
+ } while (0)
+
+
+#define MPN_NORMALIZE(d, n) \
+ do { \
+ while( (n) > 0 ) { \
+ if( (d)[(n)-1] ) \
+ break; \
+ (n)--; \
+ } \
+ } while(0)
+
+#define MPN_COPY_DECR( d, s, n ) \
+ do { \
+ mpi_size_t _i; \
+ for( _i = (n)-1; _i >= 0; _i--) \
+ (d)[_i] = (s)[_i]; \
+ } while(0)
+
+
+
+#define MPN_MUL_N_RECURSE(prodp, up, vp, size, tspace) \
+ do { \
+ if( (size) < KARATSUBA_THRESHOLD ) \
+ mul_n_basecase (prodp, up, vp, size); \
+ else \
+ mul_n (prodp, up, vp, size, tspace); \
+ } while (0);
+
+
+#define MPN_SQR_N_RECURSE(prodp, up, size, tspace) \
+ do { \
+ if ((size) < KARATSUBA_THRESHOLD) \
+ _gcry_mpih_sqr_n_basecase (prodp, up, size); \
+ else \
+ _gcry_mpih_sqr_n (prodp, up, size, tspace); \
+ } while (0);
+
+
+#ifndef KARATSUBA_THRESHOLD
+ #define KARATSUBA_THRESHOLD 16
+#endif
+
+
+#if !defined (count_leading_zeros)
+extern const unsigned char __clz_tab[];
+#define MPI_INTERNAL_NEED_CLZ_TAB 1
+#define __BITS4 (W_TYPE_SIZE / 4)
+#define __ll_B ((UWtype) 1 << (W_TYPE_SIZE / 2))
+#define __ll_lowpart(t) ((UWtype) (t) & (__ll_B - 1))
+#define __ll_highpart(t) ((UWtype) (t) >> (W_TYPE_SIZE / 2))
+
+
+#if !defined (add_ssaaaa)
+#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
+ do { \
+ UWtype __x; \
+ __x = (al) + (bl); \
+ (sh) = (ah) + (bh) + (__x < (al)); \
+ (sl) = __x; \
+ } while (0)
+#endif
+
+
+#if !defined (sub_ddmmss)
+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
+ do { \
+ UWtype __x; \
+ __x = (al) - (bl); \
+ (sh) = (ah) - (bh) - (__x > (al)); \
+ (sl) = __x; \
+ } while (0)
+#endif
+
+
+
+#if !defined (umul_ppmm)
+#define umul_ppmm(w1, w0, u, v) \
+ do { \
+ UWtype __x0, __x1, __x2, __x3; \
+ UHWtype __ul, __vl, __uh, __vh; \
+ UWtype __u = (u), __v = (v); \
+ \
+ __ul = __ll_lowpart (__u); \
+ __uh = __ll_highpart (__u); \
+ __vl = __ll_lowpart (__v); \
+ __vh = __ll_highpart (__v); \
+ \
+ __x0 = (UWtype) __ul * __vl; \
+ __x1 = (UWtype) __ul * __vh; \
+ __x2 = (UWtype) __uh * __vl; \
+ __x3 = (UWtype) __uh * __vh; \
+ \
+ __x1 += __ll_highpart (__x0);/* this can't give carry */ \
+ __x1 += __x2; /* but this indeed can */ \
+ if (__x1 < __x2) /* did we get it? */ \
+ __x3 += __ll_B; /* yes, add it in the proper pos. */ \
+ \
+ (w1) = __x3 + __ll_highpart (__x1); \
+ (w0) = (__ll_lowpart (__x1) << W_TYPE_SIZE/2) + __ll_lowpart (__x0);\
+ } while (0)
+#endif
+
+
+#define count_leading_zeros(count, x) \
+ do { \
+ UWtype __xr = (x); \
+ UWtype __a; \
+ \
+ if (W_TYPE_SIZE <= 32) \
+ { \
+ __a = __xr < ((UWtype) 1 << 2*__BITS4) \
+ ? (__xr < ((UWtype) 1 << __BITS4) ? 0 : __BITS4) \
+ : (__xr < ((UWtype) 1 << 3*__BITS4) ? 2*__BITS4 : 3*__BITS4);\
+ } \
+ else \
+ { \
+ for (__a = W_TYPE_SIZE - 8; __a > 0; __a -= 8) \
+ if (((__xr >> __a) & 0xff) != 0) \
+ break; \
+ } \
+ \
+ (count) = W_TYPE_SIZE - (__clz_tab[__xr >> __a] + __a); \
+ } while (0)
+#endif
+
+
+
+
+extern mpi_limb_t _gcry_mpih_sub_1( mpi_ptr_t res_ptr, mpi_ptr_t s1_ptr, size_t s1_size, mpi_limb_t s2_limb );
+extern mpi_limb_t _gcry_mpih_sub_n( mpi_ptr_t res_ptr, mpi_ptr_t s1_ptr, mpi_ptr_t s2_ptr, size_t size);
+extern mpi_limb_t _gcry_mpih_add_n( mpi_ptr_t res_ptr, mpi_ptr_t s1_ptr, mpi_ptr_t s2_ptr, mpi_size_t size);
+extern mpi_limb_t _gcry_mpih_lshift( mpi_ptr_t wp, mpi_ptr_t up, mpi_size_t usize, unsigned int cnt);
+extern mpi_limb_t _gcry_mpih_rshift( mpi_ptr_t wp, mpi_ptr_t up, mpi_size_t usize, unsigned cnt);
+extern mpi_limb_t _gcry_mpih_submul_1( mpi_ptr_t res_ptr, mpi_ptr_t s1_ptr, mpi_size_t s1_size, mpi_limb_t s2_limb);
+extern mpi_limb_t _gcry_mpih_mul_1( mpi_ptr_t res_ptr, mpi_ptr_t s1_ptr, mpi_size_t s1_size, mpi_limb_t s2_limb);
+extern mpi_limb_t _gcry_mpih_addmul_1( mpi_ptr_t res_ptr, mpi_ptr_t s1_ptr, mpi_size_t s1_size, mpi_limb_t s2_limb);
+
+static __inline__ mpi_limb_t
+_gcry_mpih_sub( mpi_ptr_t res_ptr, mpi_ptr_t s1_ptr, size_t s1_size,
+ mpi_ptr_t s2_ptr, size_t s2_size)
+{
+ mpi_limb_t cy = 0;
+
+ if( s2_size )
+ cy = _gcry_mpih_sub_n(res_ptr, s1_ptr, s2_ptr, s2_size);
+
+ if( s1_size - s2_size )
+ cy = _gcry_mpih_sub_1(res_ptr + s2_size, s1_ptr + s2_size,
+ s1_size - s2_size, cy);
+ return cy;
+}
+
+
+
+static __inline__ mpi_limb_t
+_gcry_mpih_add_1( mpi_ptr_t res_ptr, mpi_ptr_t s1_ptr,
+ mpi_size_t s1_size, mpi_limb_t s2_limb)
+{
+ mpi_limb_t x;
+
+ x = *s1_ptr++;
+ s2_limb += x;
+ *res_ptr++ = s2_limb;
+ if( s2_limb < x ) { /* sum is less than the left operand: handle carry */
+ while( --s1_size ) {
+ x = *s1_ptr++ + 1; /* add carry */
+ *res_ptr++ = x; /* and store */
+ if( x ) /* not 0 (no overflow): we can stop */
+ goto leave;
+ }
+ return 1; /* return carry (size of s1 to small) */
+ }
+
+ leave:
+ if( res_ptr != s1_ptr ) { /* not the same variable */
+ mpi_size_t i; /* copy the rest */
+ for( i=0; i < s1_size-1; i++ )
+ res_ptr[i] = s1_ptr[i];
+ }
+ return 0; /* no carry */
+}
+
+static __inline__ mpi_limb_t
+_gcry_mpih_add(mpi_ptr_t res_ptr, mpi_ptr_t s1_ptr, mpi_size_t s1_size,
+ mpi_ptr_t s2_ptr, mpi_size_t s2_size)
+{
+ mpi_limb_t cy = 0;
+
+ if( s2_size )
+ cy = _gcry_mpih_add_n( res_ptr, s1_ptr, s2_ptr, s2_size );
+
+ if( s1_size - s2_size )
+ cy = _gcry_mpih_add_1( res_ptr + s2_size, s1_ptr + s2_size,
+ s1_size - s2_size, cy);
+ return cy;
+}
+
+
+
+
+/****************
+ * Compare OP1_PTR/OP1_SIZE with OP2_PTR/OP2_SIZE.
+ * There are no restrictions on the relative sizes of
+ * the two arguments.
+ * Return 1 if OP1 > OP2, 0 if they are equal, and -1 if OP1 < OP2.
+ */
+static __inline__ int
+_gcry_mpih_cmp( mpi_ptr_t op1_ptr, mpi_ptr_t op2_ptr, mpi_size_t size )
+{
+ mpi_size_t i;
+ mpi_limb_t op1_word, op2_word;
+
+ for( i = size - 1; i >= 0 ; i--) {
+ op1_word = op1_ptr[i];
+ op2_word = op2_ptr[i];
+ if( op1_word != op2_word )
+ goto diff;
+ }
+ return 0;
+
+ diff:
+ /* This can *not* be simplified to
+ * op2_word - op2_word
+ * since that expression might give signed overflow. */
+ return (op1_word > op2_word) ? 1 : -1;
+}
+
+
+#endif
--- /dev/null
+/* rsa.c - RSA function
+ * Copyright (C) 1997, 1998, 1999 by Werner Koch (dd9jn)
+ * Copyright (C) 2000, 2001 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG 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.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+/* This code uses an algorithm protected by U.S. Patent #4,405,829
+ which expires on September 20, 2000. The patent holder placed that
+ patent into the public domain on Sep 6th, 2000.
+*/
+
+#include "gnupg_mpi_mpi.h"
+#include "gnupg_cipher_rsa-verify.h"
+
+#define G10ERR_PUBKEY_ALGO 4 /* Unknown pubkey algorithm */
+#define G10ERR_BAD_SIGN 8 /* Bad signature */
+#define PUBKEY_USAGE_SIG 1 /* key is good for signatures */
+#define PUBKEY_USAGE_ENC 2 /* key is good for encryption */
+
+
+typedef struct {
+ MPI n; /* modulus */
+ MPI e; /* exponent */
+} RSA_public_key;
+
+
+typedef struct {
+ MPI n; /* public modulus */
+ MPI e; /* public exponent */
+ MPI d; /* exponent */
+ MPI p; /* prime p. */
+ MPI q; /* prime q. */
+ MPI u; /* inverse of p mod q. */
+} RSA_secret_key;
+
+
+static void public(MPI output, MPI input, RSA_public_key *skey );
+
+
+/****************
+ * Public key operation. Encrypt INPUT with PKEY and put result into OUTPUT.
+ *
+ * c = m^e mod n
+ *
+ * Where c is OUTPUT, m is INPUT and e,n are elements of PKEY.
+ */
+static void
+public(MPI output, MPI input, RSA_public_key *pkey )
+{
+ if( output == input ) { /* powm doesn't like output and input the same */
+ MPI x = mpi_alloc( mpi_get_nlimbs(input)*2 );
+ mpi_powm( x, input, pkey->e, pkey->n );
+ mpi_set(output, x);
+ mpi_free(x);
+ }
+ else
+ mpi_powm( output, input, pkey->e, pkey->n );
+}
+
+
+/*********************************************
+ ************** interface ******************
+ *********************************************/
+
+int
+rsa_verify( MPI hash, MPI *data, MPI *pkey)
+{
+ RSA_public_key pk;
+ MPI result;
+ int rc;
+
+ pk.n = pkey[0];
+ pk.e = pkey[1];
+ result = mpi_alloc( (160+(BITS_PER_MPI_LIMB-1))/BITS_PER_MPI_LIMB);
+ public( result, data[0], &pk );
+ rc = mpi_cmp( result, hash )? G10ERR_BAD_SIGN:0;
+ mpi_free(result);
+
+ return rc;
+}
+
+
+int rsa_encrypt(MPI *result, MPI data, MPI *pkey)
+{
+ RSA_public_key pk;
+
+ pk.n = pkey[0];
+ pk.e = pkey[1];
+ result[0] = mpi_alloc(mpi_get_nlimbs(pk.n));
+ public(result[0], data, &pk);
+ return 0;
+}
+
+
+
+
+
--- /dev/null
+/* rsa.h
+ * Copyright (C) 1997,1998 by Werner Koch (dd9jn)
+ * Copyright (C) 2000, 2001 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG 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.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#ifndef G10_RSA_H
+#define G10_RSA_H
+
+#include "gnupg_mpi_mpi.h"
+
+int rsa_verify( MPI hash, MPI *data, MPI *pkey);
+
+#endif /*G10_RSA_H*/
--- /dev/null
+/* config.h. Generated by configure. */
+/* config.h.in. Generated from configure.ac by autoheader. */
+
+/* Defined if the host has big endian byte ordering */
+/* #undef BIG_ENDIAN_HOST */
+
+/* Define to one of `_getb67', `GETB67', `getb67' for Cray-2 and Cray-YMP
+ systems. This function is required for `alloca.c' support on those systems.
+ */
+/* #undef CRAY_STACKSEG_END */
+
+/* Define to 1 if using `alloca.c'. */
+/* #undef C_ALLOCA */
+
+/* define to disable keyserver helpers */
+/* #undef DISABLE_KEYSERVER_HELPERS */
+
+/* define to disable exec-path for keyserver helpers */
+/* #undef DISABLE_KEYSERVER_PATH */
+
+/* define to disable photo viewing */
+/* #undef DISABLE_PHOTO_VIEWER */
+
+/* Define if you don't want the default EGD socket name. For details see
+ cipher/rndegd.c */
+#define EGD_SOCKET_NAME ""
+
+/* Define to 1 if translation of program messages to the user's native
+ language is requested. */
+#define ENABLE_NLS 1
+
+/* if set, restrict photo-viewer to this */
+/* #undef FIXED_PHOTO_VIEWER */
+
+/* Define to 1 if you have `alloca', as a function or macro. */
+#define HAVE_ALLOCA 1
+
+/* Define to 1 if you have <alloca.h> and it should be used (not on Ultrix).
+ */
+#define HAVE_ALLOCA_H 1
+
+/* Define to 1 if you have the <argz.h> header file. */
+#define HAVE_ARGZ_H 1
+
+/* Define to 1 if you have the `atexit' function. */
+#define HAVE_ATEXIT 1
+
+/* Define if `gethrtime(2)' does not work correctly i.e. issues a SIGILL. */
+/* #undef HAVE_BROKEN_GETHRTIME */
+
+/* Defined if the mlock() call does not work */
+/* #undef HAVE_BROKEN_MLOCK */
+
+/* Defined if a `byte' is typedef'd */
+/* #undef HAVE_BYTE_TYPEDEF */
+
+/* Define to 1 if you have the `clock_gettime' function. */
+/* #undef HAVE_CLOCK_GETTIME */
+
+/* Define to 1 if you have the `ctermid' function. */
+#define HAVE_CTERMID 1
+
+/* Define if the GNU dcgettext() function is already present or preinstalled.
+ */
+#define HAVE_DCGETTEXT 1
+
+/* Define to 1 if you have the declaration of `sys_siglist', and to 0 if you
+ don't. */
+#define HAVE_DECL_SYS_SIGLIST 0
+
+/* defined if the system supports a random device */
+#define HAVE_DEV_RANDOM 1
+
+/* Define to 1 if you have the <direct.h> header file. */
+/* #undef HAVE_DIRECT_H */
+
+/* Define to 1 if you have the `dlopen' function. */
+/* #undef HAVE_DLOPEN */
+
+/* Defined when the dlopen function family is available */
+#define HAVE_DL_DLOPEN 1
+
+/* Define to 1 if you don't have `vprintf' but do have `_doprnt.' */
+/* #undef HAVE_DOPRNT */
+
+/* defined if we run on some of the PCDOS like systems (DOS, Windoze. OS/2)
+ with special properties like no file modes */
+/* #undef HAVE_DOSISH_SYSTEM */
+
+/* defined if we must run on a stupid file system */
+/* #undef HAVE_DRIVE_LETTERS */
+
+/* Define to 1 if you have the `feof_unlocked' function. */
+#define HAVE_FEOF_UNLOCKED 1
+
+/* Define to 1 if you have the `fgets_unlocked' function. */
+#define HAVE_FGETS_UNLOCKED 1
+
+/* Define to 1 if you have the `fork' function. */
+#define HAVE_FORK 1
+
+/* Define to 1 if fseeko (and presumably ftello) exists and is declared. */
+#define HAVE_FSEEKO 1
+
+/* Define to 1 if you have the `getcwd' function. */
+#define HAVE_GETCWD 1
+
+/* Define to 1 if you have the `getc_unlocked' function. */
+#define HAVE_GETC_UNLOCKED 1
+
+/* Define to 1 if you have the `getegid' function. */
+#define HAVE_GETEGID 1
+
+/* Define to 1 if you have the `geteuid' function. */
+#define HAVE_GETEUID 1
+
+/* Define to 1 if you have the `getgid' function. */
+#define HAVE_GETGID 1
+
+/* Define if you have the `gethrtime(2)' function. */
+/* #undef HAVE_GETHRTIME */
+
+/* Define to 1 if you have the `getpagesize' function. */
+#define HAVE_GETPAGESIZE 1
+
+/* Define to 1 if you have the `getrusage' function. */
+#define HAVE_GETRUSAGE 1
+
+/* Define if the GNU gettext() function is already present or preinstalled. */
+#define HAVE_GETTEXT 1
+
+/* Define to 1 if you have the `gettimeofday' function. */
+#define HAVE_GETTIMEOFDAY 1
+
+/* Define to 1 if you have the `getuid' function. */
+#define HAVE_GETUID 1
+
+/* Define if you have the iconv() function. */
+#define HAVE_ICONV 1
+
+/* Define if <inttypes.h> exists and doesn't clash with <sys/types.h>. */
+#define HAVE_INTTYPES_H 1
+
+/* Define if <inttypes.h> exists, doesn't clash with <sys/types.h>, and
+ declares uintmax_t. */
+#define HAVE_INTTYPES_H_WITH_UINTMAX 1
+
+/* Define if you have <langinfo.h> and nl_langinfo(CODESET). */
+#define HAVE_LANGINFO_CODESET 1
+
+/* Define to 1 if you have the <langinfo.h> header file. */
+#define HAVE_LANGINFO_H 1
+
+/* Define if your <locale.h> file defines LC_MESSAGES. */
+#define HAVE_LC_MESSAGES 1
+
+/* Define to 1 if you have the `dl' library (-ldl). */
+#define HAVE_LIBDL 1
+
+/* Define to 1 if you have the `rt' library (-lrt). */
+/* #undef HAVE_LIBRT */
+
+/* Define to 1 if you have the <limits.h> header file. */
+#define HAVE_LIMITS_H 1
+
+/* Define to 1 if you have the <locale.h> header file. */
+#define HAVE_LOCALE_H 1
+
+/* Define to 1 if you have the <malloc.h> header file. */
+#define HAVE_MALLOC_H 1
+
+/* Define to 1 if you have the `memmove' function. */
+#define HAVE_MEMMOVE 1
+
+/* Define to 1 if you have the <memory.h> header file. */
+#define HAVE_MEMORY_H 1
+
+/* Define to 1 if you have the `mempcpy' function. */
+#define HAVE_MEMPCPY 1
+
+/* Define to 1 if you have the `mkdtemp' function. */
+#define HAVE_MKDTEMP 1
+
+/* Defined if the system supports an mlock() call */
+#define HAVE_MLOCK 1
+
+/* Define to 1 if you have the `mmap' function. */
+#define HAVE_MMAP 1
+
+/* Define to 1 if you have the `munmap' function. */
+#define HAVE_MUNMAP 1
+
+/* Define to 1 if you have the `nl_langinfo' function. */
+#define HAVE_NL_LANGINFO 1
+
+/* Define to 1 if you have the <nl_types.h> header file. */
+#define HAVE_NL_TYPES_H 1
+
+/* Define to 1 if you have the `pipe' function. */
+#define HAVE_PIPE 1
+
+/* Define to 1 if you have the `plock' function. */
+/* #undef HAVE_PLOCK */
+
+/* Define to 1 if you have the `putenv' function. */
+#define HAVE_PUTENV 1
+
+/* Define to 1 if you have the `raise' function. */
+#define HAVE_RAISE 1
+
+/* Define to 1 if you have the `rand' function. */
+#define HAVE_RAND 1
+
+/* Define to 1 if you have the `setenv' function. */
+#define HAVE_SETENV 1
+
+/* Define to 1 if you have the `setlocale' function. */
+#define HAVE_SETLOCALE 1
+
+/* Define to 1 if you have the `setrlimit' function. */
+#define HAVE_SETRLIMIT 1
+
+/* Define to 1 if you have the `sigaction' function. */
+#define HAVE_SIGACTION 1
+
+/* Define to 1 if you have the `sigprocmask' function. */
+#define HAVE_SIGPROCMASK 1
+
+/* Define to 1 if you have the `stat' function. */
+#define HAVE_STAT 1
+
+/* Define to 1 if you have the <stddef.h> header file. */
+#define HAVE_STDDEF_H 1
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#define HAVE_STDINT_H 1
+
+/* Define if <stdint.h> exists, doesn't clash with <sys/types.h>, and declares
+ uintmax_t. */
+#define HAVE_STDINT_H_WITH_UINTMAX 1
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#define HAVE_STDLIB_H 1
+
+/* Define to 1 if you have the `stpcpy' function. */
+#define HAVE_STPCPY 1
+
+/* Define to 1 if you have the `strcasecmp' function. */
+#define HAVE_STRCASECMP 1
+
+/* Define to 1 if you have the `strdup' function. */
+#define HAVE_STRDUP 1
+
+/* Define to 1 if you have the `strerror' function. */
+#define HAVE_STRERROR 1
+
+/* Define to 1 if you have the `strftime' function. */
+#define HAVE_STRFTIME 1
+
+/* Define to 1 if you have the <strings.h> header file. */
+#define HAVE_STRINGS_H 1
+
+/* Define to 1 if you have the <string.h> header file. */
+#define HAVE_STRING_H 1
+
+/* Define to 1 if you have the `strlwr' function. */
+/* #undef HAVE_STRLWR */
+
+/* Define to 1 if you have the `strncasecmp' function. */
+#define HAVE_STRNCASECMP 1
+
+/* Define to 1 if you have the `strsep' function. */
+#define HAVE_STRSEP 1
+
+/* Define to 1 if you have the `strtoul' function. */
+#define HAVE_STRTOUL 1
+
+/* Define to 1 if you have the <sys/capability.h> header file. */
+/* #undef HAVE_SYS_CAPABILITY_H */
+
+/* Define to 1 if you have the <sys/ipc.h> header file. */
+#define HAVE_SYS_IPC_H 1
+
+/* Define to 1 if you have the <sys/mman.h> header file. */
+/* #undef HAVE_SYS_MMAN_H */
+
+/* Define to 1 if you have the <sys/param.h> header file. */
+#define HAVE_SYS_PARAM_H 1
+
+/* Define to 1 if you have the <sys/shm.h> header file. */
+#define HAVE_SYS_SHM_H 1
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#define HAVE_SYS_STAT_H 1
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#define HAVE_SYS_TYPES_H 1
+
+/* Define to 1 if you have the `tcgetattr' function. */
+#define HAVE_TCGETATTR 1
+
+/* Define to 1 if you have the <termio.h> header file. */
+#define HAVE_TERMIO_H 1
+
+/* Define to 1 if you have the `tsearch' function. */
+#define HAVE_TSEARCH 1
+
+/* Defined if a `u16' is typedef'd */
+/* #undef HAVE_U16_TYPEDEF */
+
+/* Defined if a `u32' is typedef'd */
+/* #undef HAVE_U32_TYPEDEF */
+
+/* Defined if a `ulong' is typedef'd */
+#define HAVE_ULONG_TYPEDEF 1
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#define HAVE_UNISTD_H 1
+
+/* Define if you have the unsigned long long type. */
+#define HAVE_UNSIGNED_LONG_LONG 1
+
+/* Defined if a `ushort' is typedef'd */
+#define HAVE_USHORT_TYPEDEF 1
+
+/* Define to 1 if you have the `vfork' function. */
+#define HAVE_VFORK 1
+
+/* Define to 1 if you have the <vfork.h> header file. */
+/* #undef HAVE_VFORK_H */
+
+/* Define to 1 if you have the `vprintf' function. */
+#define HAVE_VPRINTF 1
+
+/* Define to 1 if you have the `wait4' function. */
+#define HAVE_WAIT4 1
+
+/* Define to 1 if you have the `waitpid' function. */
+#define HAVE_WAITPID 1
+
+/* Define to 1 if `fork' works. */
+#define HAVE_WORKING_FORK 1
+
+/* Define to 1 if `vfork' works. */
+#define HAVE_WORKING_VFORK 1
+
+/* Define to 1 if you have the `__argz_count' function. */
+#define HAVE___ARGZ_COUNT 1
+
+/* Define to 1 if you have the `__argz_next' function. */
+#define HAVE___ARGZ_NEXT 1
+
+/* Define to 1 if you have the `__argz_stringify' function. */
+#define HAVE___ARGZ_STRINGIFY 1
+
+/* Define as const if the declaration of iconv() needs const. */
+#define ICONV_CONST
+
+/* Define if integer division by zero raises signal SIGFPE. */
+#define INTDIV0_RAISES_SIGFPE 1
+
+/* Defined if a SysV shared memory supports the LOCK flag */
+#define IPC_HAVE_SHM_LOCK 1
+
+/* Defined if we can do a deferred shm release */
+#define IPC_RMID_DEFERRED_RELEASE 1
+
+/* Defined if this is not a regular release */
+/* #undef IS_DEVELOPMENT_VERSION */
+
+/* Defined if the host has little endian byte ordering */
+#define LITTLE_ENDIAN_HOST 1
+
+/* Defined if mkdir() does not take permission flags */
+/* #undef MKDIR_TAKES_ONE_ARG */
+
+/* Define to use the (obsolete) malloc guarding feature */
+/* #undef M_GUARD */
+
+/* defined to the name of the strong random device */
+#define NAME_OF_DEV_RANDOM "/dev/random"
+
+/* defined to the name of the weaker random device */
+#define NAME_OF_DEV_URANDOM "/dev/urandom"
+
+/* Define if the LDAP library requires including lber.h before ldap.h */
+/* #undef NEED_LBER_H */
+
+/* Define to disable all external program execution */
+/* #undef NO_EXEC */
+
+/* Name of this package */
+#define PACKAGE "gnupg"
+
+/* Define to the address where bug reports for this package should be sent. */
+#define PACKAGE_BUGREPORT "bug-gnupg@gnu.org"
+
+/* Define to the full name of this package. */
+#define PACKAGE_NAME "gnupg"
+
+/* Define to the full name and version of this package. */
+#define PACKAGE_STRING "gnupg 1.2.2"
+
+/* Define to the one symbol short name of this package. */
+#define PACKAGE_TARNAME "gnupg"
+
+/* Define to the version of this package. */
+#define PACKAGE_VERSION "1.2.2"
+
+/* A human readable text with the name of the OS */
+#define PRINTABLE_OS_NAME "GNU/Linux"
+
+/* Define if <inttypes.h> exists and defines unusable PRI* macros. */
+/* #undef PRI_MACROS_BROKEN */
+
+/* Define as the return type of signal handlers (`int' or `void'). */
+#define RETSIGTYPE void
+
+/* The size of a `unsigned int', as computed by sizeof. */
+#define SIZEOF_UNSIGNED_INT 4
+
+/* The size of a `unsigned long', as computed by sizeof. */
+#define SIZEOF_UNSIGNED_LONG 4
+
+/* The size of a `unsigned long long', as computed by sizeof. */
+#define SIZEOF_UNSIGNED_LONG_LONG 8
+
+/* The size of a `unsigned short', as computed by sizeof. */
+#define SIZEOF_UNSIGNED_SHORT 2
+
+/* If using the C implementation of alloca, define if you know the
+ direction of stack growth for your system; otherwise it will be
+ automatically deduced at run-time.
+ STACK_DIRECTION > 0 => grows toward higher addresses
+ STACK_DIRECTION < 0 => grows toward lower addresses
+ STACK_DIRECTION = 0 => direction of growth unknown */
+/* #undef STACK_DIRECTION */
+
+/* Define to 1 if you have the ANSI C header files. */
+#define STDC_HEADERS 1
+
+/* Allow to select random modules at runtime. */
+/* #undef USE_ALL_RANDOM_MODULES */
+
+/* define if capabilities should be used */
+/* #undef USE_CAPABILITIES */
+
+/* define to enable the use of extensions */
+#define USE_DYNAMIC_LINKING 1
+
+/* define to use the experimental external HKP keyserver interface */
+/* #undef USE_EXTERNAL_HKP */
+
+/* Define to use the old fake OID for TIGER/192 digest support */
+/* #undef USE_OLD_TIGER */
+
+/* set this to limit filenames to the 8.3 format */
+/* #undef USE_ONLY_8DOT3 */
+
+/* Defined if the EGD based RNG should be used. */
+/* #undef USE_RNDEGD */
+
+/* Defined if the /dev/random based RNG should be used. */
+#define USE_RNDLINUX 1
+
+/* Defined if the default Unix RNG should be used. */
+/* #undef USE_RNDUNIX */
+
+/* Defined if the Windows specific RNG should be used. */
+/* #undef USE_RNDW32 */
+
+/* Define to include read-only SHA-384 and SHA-512 digest support */
+/* #undef USE_SHA512 */
+
+/* define if the shared memory interface should be made available */
+#define USE_SHM_COPROCESSING 1
+
+/* because the Unix gettext has too much overhead on MingW32 systems and these
+ systems lack Posix functions, we use a simplified version of gettext */
+/* #undef USE_SIMPLE_GETTEXT */
+
+/* Define to include experimental TIGER/192 digest support */
+/* #undef USE_TIGER */
+
+/* Version of this package */
+#define VERSION "1.2.2"
+
+/* Defined if compiled symbols have a leading underscore */
+/* #undef WITH_SYMBOL_UNDERSCORE */
+
+/* Number of bits in a file offset, on hosts where this is settable. */
+#define _FILE_OFFSET_BITS 64
+
+/* Some tests rely on this (stpcpy) and it should be used for new programs
+ anyway */
+#define _GNU_SOURCE 1
+
+/* Define to 1 to make fseeko visible on some hosts (e.g. glibc 2.2). */
+/* #undef _LARGEFILE_SOURCE */
+
+/* Define for large files, on AIX-style hosts. */
+/* #undef _LARGE_FILES */
+
+/* Define to empty if `const' does not conform to ANSI C. */
+/* #undef const */
+
+/* Define as `__inline' if that's what the C compiler calls it, or to nothing
+ if it is not supported. */
+/* #undef inline */
+
+/* Define to `int' if <sys/types.h> does not define. */
+/* #undef mode_t */
+
+/* Define to `long' if <sys/types.h> does not define. */
+/* #undef off_t */
+
+/* Define to `int' if <sys/types.h> does not define. */
+/* #undef pid_t */
+
+/* Define to `unsigned' if <sys/types.h> does not define. */
+/* #undef size_t */
+
+/* Define to unsigned long or unsigned long long if <inttypes.h> and
+ <stdint.h> don't define. */
+/* #undef uintmax_t */
+
+/* Define as `fork' if `vfork' does not work. */
+/* #undef vfork */
+
+/* Define to empty if the keyword `volatile' does not work. Warning: valid
+ code using `volatile' can become incorrect without. Disable with care. */
+/* #undef volatile */
+
+
+#if !(defined(HAVE_FORK) && defined(HAVE_PIPE) && defined(HAVE_WAITPID))
+#define EXEC_TEMPFILE_ONLY
+#endif
+
+#include "gnupg_mpi_g10defs.h"
+
--- /dev/null
+/* Generated automatically by configure */
+#ifdef HAVE_DRIVE_LETTERS
+ #define G10_LOCALEDIR "c:\\lib\\gnupg\\locale"
+ #define GNUPG_LIBDIR "c:\\lib\\gnupg"
+ #define GNUPG_LIBEXECDIR "c:\\lib\\gnupg"
+ #define GNUPG_DATADIR "c:\\lib\\gnupg"
+ #define GNUPG_HOMEDIR "c:\\gnupg"
+#else
+ #define G10_LOCALEDIR "/usr/local/share/locale"
+ #define GNUPG_LIBDIR "/usr/local/lib/gnupg"
+ #define GNUPG_DATADIR "/usr/local/share/gnupg"
+ #ifdef __VMS
+ #define GNUPG_HOMEDIR "/SYS$LOGIN/gnupg"
+ #else
+ #define GNUPG_HOMEDIR "~/.gnupg"
+ #endif
+#endif
+/* those are here to be redefined by handcrafted g10defs.h.
+ Please note that the string version must not contain more
+ than one character because the using code assumes strlen()==1 */
+#ifdef HAVE_DOSISH_SYSTEM
+#define DIRSEP_C '\\'
+#define EXTSEP_C '.'
+#define DIRSEP_S "\\"
+#define EXTSEP_S "."
+#else
+#define DIRSEP_C '/'
+#define EXTSEP_C '.'
+#define DIRSEP_S "/"
+#define EXTSEP_S "."
+#endif
+/* This file defines some basic constants for the MPI machinery. We
+ * need to define the types on a per-CPU basis, so it is done with
+ * this file here. */
+#define BYTES_PER_MPI_LIMB (SIZEOF_UNSIGNED_LONG)
+
+
+
+
+
+
--- /dev/null
+/* g10m.c - Wrapper for MPI
+ * Copyright (C) 1998 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG 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.
+ *
+ * GnuPG 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 "mpi.h"
+
+/* FIXME: The modules should use functions from libgcrypt */
+
+const char *g10m_revision_string(int dummy) { return "$Revision: 1.1 $"; }
+
+MPI
+g10m_new( unsigned nbits )
+{
+ return mpi_alloc( (nbits+BITS_PER_MPI_LIMB-1) / BITS_PER_MPI_LIMB );
+}
+
+MPI
+g10m_new_secure( unsigned nbits )
+{
+ return mpi_alloc_secure( (nbits+BITS_PER_MPI_LIMB-1) / BITS_PER_MPI_LIMB );
+}
+
+void
+g10m_release( MPI a )
+{
+ mpi_free(a);
+}
+
+void
+g10m_resize( MPI a, unsigned nbits )
+{
+ mpi_resize( a, (nbits+BITS_PER_MPI_LIMB-1) / BITS_PER_MPI_LIMB );
+}
+
+MPI g10m_copy( MPI a ) { return mpi_copy( a ); }
+void g10m_swap( MPI a, MPI b) { mpi_swap( a, b ); }
+void g10m_set( MPI w, MPI u) { mpi_set( w, u ); }
+void g10m_set_ui( MPI w, ulong u ) { mpi_set_ui( w, u ); }
+
+int g10m_cmp( MPI u, MPI v ) { return mpi_cmp( u, v ); }
+int g10m_cmp_ui( MPI u, ulong v ) { return mpi_cmp_ui( u, v ); }
+
+void g10m_add(MPI w, MPI u, MPI v) { mpi_add( w, u, v ); }
+void g10m_add_ui(MPI w, MPI u, ulong v ) { mpi_add_ui( w, u, v ); }
+void g10m_sub( MPI w, MPI u, MPI v) { mpi_sub( w, u, v ); }
+void g10m_sub_ui(MPI w, MPI u, ulong v ) { mpi_sub_ui( w, u, v ); }
+
+void g10m_mul( MPI w, MPI u, MPI v) { mpi_mul( w, u, v ); }
+void g10m_mulm( MPI w, MPI u, MPI v, MPI m) { mpi_mulm( w, u, v, m ); }
+void g10m_mul_2exp( MPI w, MPI u, ulong cnt) { mpi_mul_2exp( w, u, cnt ); }
+void g10m_mul_ui(MPI w, MPI u, ulong v ) { mpi_mul_ui( w, u, v ); }
+
+void g10m_fdiv_q( MPI q, MPI d, MPI r ) { mpi_fdiv_q( q, d, r ); }
+
+void g10m_powm( MPI r, MPI b, MPI e, MPI m) { mpi_powm( r, b, e, m ); }
+
+int g10m_gcd( MPI g, MPI a, MPI b ) { return mpi_gcd( g, a, b ); }
+int g10m_invm( MPI x, MPI u, MPI v ) { mpi_invm( x, u, v ); return 0; }
+
+unsigned g10m_get_nbits( MPI a ) { return mpi_get_nbits( a ); }
+
+unsigned
+g10m_get_size( MPI a )
+{
+ return mpi_get_nlimbs( a ) * BITS_PER_MPI_LIMB;
+}
+
+
+void
+g10m_set_buffer( MPI a, const char *buffer, unsigned nbytes, int sign )
+{
+ mpi_set_buffer( a, buffer, nbytes, sign );
+}
+
+
--- /dev/null
+/* This file defines some basic constants for the MPI machinery. We
+ * need to define the types on a per-CPU basis, so it is done with
+ * this file here. */
+#define BYTES_PER_MPI_LIMB (SIZEOF_UNSIGNED_LONG)
+
+
+
+
+
+
--- /dev/null
+/* mpihelp-add_1.c - MPI helper functions
+ * Copyright (C) 1994, 1996, 1997, 1998,
+ * 2000 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG 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.
+ *
+ * GnuPG 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
+ *
+ * Note: This code is heavily based on the GNU MP Library.
+ * Actually it's the same code with only minor changes in the
+ * way the data is stored; this is to support the abstraction
+ * of an optional secure memory allocation which may be used
+ * to avoid revealing of sensitive data due to paging etc.
+ * The GNU MP Library itself is published under the LGPL;
+ * however I decided to publish this code under the plain GPL.
+ */
+
+#include "gnupg_mpi_mpi-internal.h"
+#include "gnupg_mpi_longlong.h"
+
+mpi_limb_t
+mpihelp_add_n( mpi_ptr_t res_ptr, mpi_ptr_t s1_ptr,
+ mpi_ptr_t s2_ptr, mpi_size_t size)
+{
+ mpi_limb_t x, y, cy;
+ mpi_size_t j;
+
+ /* The loop counter and index J goes from -SIZE to -1. This way
+ the loop becomes faster. */
+ j = -size;
+
+ /* Offset the base pointers to compensate for the negative indices. */
+ s1_ptr -= j;
+ s2_ptr -= j;
+ res_ptr -= j;
+
+ cy = 0;
+ do {
+ y = s2_ptr[j];
+ x = s1_ptr[j];
+ y += cy; /* add previous carry to one addend */
+ cy = y < cy; /* get out carry from that addition */
+ y += x; /* add other addend */
+ cy += y < x; /* get out carry from that add, combine */
+ res_ptr[j] = y;
+ } while( ++j );
+
+ return cy;
+}
+
--- /dev/null
+/* mpihelp-lshift.c - MPI helper functions
+ * Copyright (C) 1994, 1996, 1998, 2001 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG 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.
+ *
+ * GnuPG 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
+ *
+ * Note: This code is heavily based on the GNU MP Library.
+ * Actually it's the same code with only minor changes in the
+ * way the data is stored; this is to support the abstraction
+ * of an optional secure memory allocation which may be used
+ * to avoid revealing of sensitive data due to paging etc.
+ * The GNU MP Library itself is published under the LGPL;
+ * however I decided to publish this code under the plain GPL.
+ */
+
+#include "gnupg_mpi_mpi-internal.h"
+
+/* Shift U (pointed to by UP and USIZE digits long) CNT bits to the left
+ * and store the USIZE least significant digits of the result at WP.
+ * Return the bits shifted out from the most significant digit.
+ *
+ * Argument constraints:
+ * 1. 0 < CNT < BITS_PER_MP_LIMB
+ * 2. If the result is to be written over the input, WP must be >= UP.
+ */
+
+mpi_limb_t
+mpihelp_lshift( mpi_ptr_t wp, mpi_ptr_t up, mpi_size_t usize,
+ unsigned int cnt)
+{
+ mpi_limb_t high_limb, low_limb;
+ unsigned sh_1, sh_2;
+ mpi_size_t i;
+ mpi_limb_t retval;
+
+ sh_1 = cnt;
+ wp += 1;
+ sh_2 = BITS_PER_MPI_LIMB - sh_1;
+ i = usize - 1;
+ low_limb = up[i];
+ retval = low_limb >> sh_2;
+ high_limb = low_limb;
+ while( --i >= 0 ) {
+ low_limb = up[i];
+ wp[i] = (high_limb << sh_1) | (low_limb >> sh_2);
+ high_limb = low_limb;
+ }
+ wp[i] = high_limb << sh_1;
+
+ return retval;
+}
+
+
--- /dev/null
+/* mpihelp-mul_1.c - MPI helper functions
+ * Copyright (C) 1994, 1996, 1997, 1998, 2001 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG 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.
+ *
+ * GnuPG 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
+ *
+ * Note: This code is heavily based on the GNU MP Library.
+ * Actually it's the same code with only minor changes in the
+ * way the data is stored; this is to support the abstraction
+ * of an optional secure memory allocation which may be used
+ * to avoid revealing of sensitive data due to paging etc.
+ * The GNU MP Library itself is published under the LGPL;
+ * however I decided to publish this code under the plain GPL.
+ */
+
+#include "gnupg_mpi_mpi-internal.h"
+#include "gnupg_mpi_longlong.h"
+
+mpi_limb_t
+mpihelp_mul_1( mpi_ptr_t res_ptr, mpi_ptr_t s1_ptr, mpi_size_t s1_size,
+ mpi_limb_t s2_limb)
+{
+ mpi_limb_t cy_limb;
+ mpi_size_t j;
+ mpi_limb_t prod_high, prod_low;
+
+ /* The loop counter and index J goes from -S1_SIZE to -1. This way
+ * the loop becomes faster. */
+ j = -s1_size;
+
+ /* Offset the base pointers to compensate for the negative indices. */
+ s1_ptr -= j;
+ res_ptr -= j;
+
+ cy_limb = 0;
+ do {
+ umul_ppmm( prod_high, prod_low, s1_ptr[j], s2_limb );
+ prod_low += cy_limb;
+ cy_limb = (prod_low < cy_limb?1:0) + prod_high;
+ res_ptr[j] = prod_low;
+ } while( ++j );
+
+ return cy_limb;
+}
+
--- /dev/null
+/* mpihelp-mul_2.c - MPI helper functions
+ * Copyright (C) 1994, 1996, 1997, 1998, 2001 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG 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.
+ *
+ * GnuPG 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
+ *
+ * Note: This code is heavily based on the GNU MP Library.
+ * Actually it's the same code with only minor changes in the
+ * way the data is stored; this is to support the abstraction
+ * of an optional secure memory allocation which may be used
+ * to avoid revealing of sensitive data due to paging etc.
+ * The GNU MP Library itself is published under the LGPL;
+ * however I decided to publish this code under the plain GPL.
+ */
+
+#include "gnupg_mpi_mpi-internal.h"
+#include "gnupg_mpi_longlong.h"
+
+
+mpi_limb_t
+mpihelp_addmul_1( mpi_ptr_t res_ptr, mpi_ptr_t s1_ptr,
+ mpi_size_t s1_size, mpi_limb_t s2_limb)
+{
+ mpi_limb_t cy_limb;
+ mpi_size_t j;
+ mpi_limb_t prod_high, prod_low;
+ mpi_limb_t x;
+
+ /* The loop counter and index J goes from -SIZE to -1. This way
+ * the loop becomes faster. */
+ j = -s1_size;
+ res_ptr -= j;
+ s1_ptr -= j;
+
+ cy_limb = 0;
+ do {
+ umul_ppmm( prod_high, prod_low, s1_ptr[j], s2_limb );
+
+ prod_low += cy_limb;
+ cy_limb = (prod_low < cy_limb?1:0) + prod_high;
+
+ x = res_ptr[j];
+ prod_low = x + prod_low;
+ cy_limb += prod_low < x?1:0;
+ res_ptr[j] = prod_low;
+ } while ( ++j );
+ return cy_limb;
+}
+
+
--- /dev/null
+/* mpihelp-mul_3.c - MPI helper functions
+ * Copyright (C) 1994, 1996, 1997, 1998, 2001 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG 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.
+ *
+ * GnuPG 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
+ *
+ * Note: This code is heavily based on the GNU MP Library.
+ * Actually it's the same code with only minor changes in the
+ * way the data is stored; this is to support the abstraction
+ * of an optional secure memory allocation which may be used
+ * to avoid revealing of sensitive data due to paging etc.
+ * The GNU MP Library itself is published under the LGPL;
+ * however I decided to publish this code under the plain GPL.
+ */
+
+#include "gnupg_mpi_mpi-internal.h"
+#include "gnupg_mpi_longlong.h"
+
+
+mpi_limb_t
+mpihelp_submul_1( mpi_ptr_t res_ptr, mpi_ptr_t s1_ptr,
+ mpi_size_t s1_size, mpi_limb_t s2_limb)
+{
+ mpi_limb_t cy_limb;
+ mpi_size_t j;
+ mpi_limb_t prod_high, prod_low;
+ mpi_limb_t x;
+
+ /* The loop counter and index J goes from -SIZE to -1. This way
+ * the loop becomes faster. */
+ j = -s1_size;
+ res_ptr -= j;
+ s1_ptr -= j;
+
+ cy_limb = 0;
+ do {
+ umul_ppmm( prod_high, prod_low, s1_ptr[j], s2_limb);
+
+ prod_low += cy_limb;
+ cy_limb = (prod_low < cy_limb?1:0) + prod_high;
+
+ x = res_ptr[j];
+ prod_low = x - prod_low;
+ cy_limb += prod_low > x?1:0;
+ res_ptr[j] = prod_low;
+ } while( ++j );
+
+ return cy_limb;
+}
+
+
--- /dev/null
+/* mpih-rshift.c - MPI helper functions
+ * Copyright (C) 1994, 1996, 1998, 1999,
+ * 2000, 2001 Free Software Foundation, Inc.
+ *
+ * This file is part of GNUPG
+ *
+ * GNUPG 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.
+ *
+ * GNUPG 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
+ *
+ * Note: This code is heavily based on the GNU MP Library.
+ * Actually it's the same code with only minor changes in the
+ * way the data is stored; this is to support the abstraction
+ * of an optional secure memory allocation which may be used
+ * to avoid revealing of sensitive data due to paging etc.
+ * The GNU MP Library itself is published under the LGPL;
+ * however I decided to publish this code under the plain GPL.
+ */
+
+#include "gnupg_mpi_mpi-internal.h"
+
+
+/* Shift U (pointed to by UP and USIZE limbs long) CNT bits to the right
+ * and store the USIZE least significant limbs of the result at WP.
+ * The bits shifted out to the right are returned.
+ *
+ * Argument constraints:
+ * 1. 0 < CNT < BITS_PER_MP_LIMB
+ * 2. If the result is to be written over the input, WP must be <= UP.
+ */
+
+mpi_limb_t
+mpihelp_rshift( mpi_ptr_t wp, mpi_ptr_t up, mpi_size_t usize, unsigned cnt)
+{
+ mpi_limb_t high_limb, low_limb;
+ unsigned sh_1, sh_2;
+ mpi_size_t i;
+ mpi_limb_t retval;
+
+ sh_1 = cnt;
+ wp -= 1;
+ sh_2 = BITS_PER_MPI_LIMB - sh_1;
+ high_limb = up[0];
+ retval = high_limb << sh_2;
+ low_limb = high_limb;
+ for( i=1; i < usize; i++) {
+ high_limb = up[i];
+ wp[i] = (low_limb >> sh_1) | (high_limb << sh_2);
+ low_limb = high_limb;
+ }
+ wp[i] = low_limb >> sh_1;
+
+ return retval;
+}
+
--- /dev/null
+/* mpihelp-add_2.c - MPI helper functions
+ * Copyright (C) 1994, 1996, 1997, 1998, 2001 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG 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.
+ *
+ * GnuPG 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
+ *
+ * Note: This code is heavily based on the GNU MP Library.
+ * Actually it's the same code with only minor changes in the
+ * way the data is stored; this is to support the abstraction
+ * of an optional secure memory allocation which may be used
+ * to avoid revealing of sensitive data due to paging etc.
+ * The GNU MP Library itself is published under the LGPL;
+ * however I decided to publish this code under the plain GPL.
+ */
+
+#include "gnupg_mpi_mpi-internal.h"
+#include "gnupg_mpi_longlong.h"
+
+mpi_limb_t
+mpihelp_sub_n( mpi_ptr_t res_ptr, mpi_ptr_t s1_ptr,
+ mpi_ptr_t s2_ptr, mpi_size_t size)
+{
+ mpi_limb_t x, y, cy;
+ mpi_size_t j;
+
+ /* The loop counter and index J goes from -SIZE to -1. This way
+ the loop becomes faster. */
+ j = -size;
+
+ /* Offset the base pointers to compensate for the negative indices. */
+ s1_ptr -= j;
+ s2_ptr -= j;
+ res_ptr -= j;
+
+ cy = 0;
+ do {
+ y = s2_ptr[j];
+ x = s1_ptr[j];
+ y += cy; /* add previous carry to subtrahend */
+ cy = y < cy; /* get out carry from that addition */
+ y = x - y; /* main subtract */
+ cy += y > x; /* get out carry from the subtract, combine */
+ res_ptr[j] = y;
+ } while( ++j );
+
+ return cy;
+}
+
+
--- /dev/null
+/* mpihelp_udiv_w_sdiv -- implement udiv_qrnnd on machines with only signed
+ * division.
+ * Copyright (C) 1992, 1994, 1996, 1998 Free Software Foundation, Inc.
+ * Contributed by Peter L. Montgomery.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG 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.
+ *
+ * GnuPG 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 "gnupg_mpi_mpi-internal.h"
+#include "gnupg_mpi_longlong.h"
+
+
+#if 0 /* not yet ported to MPI */
+
+mpi_limb_t
+mpihelp_udiv_w_sdiv( mpi_limp_t *rp,
+ mpi_limp_t *a1,
+ mpi_limp_t *a0,
+ mpi_limp_t *d )
+{
+ mp_limb_t q, r;
+ mp_limb_t c0, c1, b1;
+
+ if ((mpi_limb_signed_t) d >= 0)
+ {
+ if (a1 < d - a1 - (a0 >> (BITS_PER_MP_LIMB - 1)))
+ {
+ /* dividend, divisor, and quotient are nonnegative */
+ sdiv_qrnnd (q, r, a1, a0, d);
+ }
+ else
+ {
+ /* Compute c1*2^32 + c0 = a1*2^32 + a0 - 2^31*d */
+ sub_ddmmss (c1, c0, a1, a0, d >> 1, d << (BITS_PER_MP_LIMB - 1));
+ /* Divide (c1*2^32 + c0) by d */
+ sdiv_qrnnd (q, r, c1, c0, d);
+ /* Add 2^31 to quotient */
+ q += (mp_limb_t) 1 << (BITS_PER_MP_LIMB - 1);
+ }
+ }
+ else
+ {
+ b1 = d >> 1; /* d/2, between 2^30 and 2^31 - 1 */
+ c1 = a1 >> 1; /* A/2 */
+ c0 = (a1 << (BITS_PER_MP_LIMB - 1)) + (a0 >> 1);
+
+ if (a1 < b1) /* A < 2^32*b1, so A/2 < 2^31*b1 */
+ {
+ sdiv_qrnnd (q, r, c1, c0, b1); /* (A/2) / (d/2) */
+
+ r = 2*r + (a0 & 1); /* Remainder from A/(2*b1) */
+ if ((d & 1) != 0)
+ {
+ if (r >= q)
+ r = r - q;
+ else if (q - r <= d)
+ {
+ r = r - q + d;
+ q--;
+ }
+ else
+ {
+ r = r - q + 2*d;
+ q -= 2;
+ }
+ }
+ }
+ else if (c1 < b1) /* So 2^31 <= (A/2)/b1 < 2^32 */
+ {
+ c1 = (b1 - 1) - c1;
+ c0 = ~c0; /* logical NOT */
+
+ sdiv_qrnnd (q, r, c1, c0, b1); /* (A/2) / (d/2) */
+
+ q = ~q; /* (A/2)/b1 */
+ r = (b1 - 1) - r;
+
+ r = 2*r + (a0 & 1); /* A/(2*b1) */
+
+ if ((d & 1) != 0)
+ {
+ if (r >= q)
+ r = r - q;
+ else if (q - r <= d)
+ {
+ r = r - q + d;
+ q--;
+ }
+ else
+ {
+ r = r - q + 2*d;
+ q -= 2;
+ }
+ }
+ }
+ else /* Implies c1 = b1 */
+ { /* Hence a1 = d - 1 = 2*b1 - 1 */
+ if (a0 >= -d)
+ {
+ q = -1;
+ r = a0 + d;
+ }
+ else
+ {
+ q = -2;
+ r = a0 + 2*d;
+ }
+ }
+ }
+
+ *rp = r;
+ return q;
+}
+
+#endif
+
--- /dev/null
+/* longlong.h -- definitions for mixed size 32/64 bit arithmetic.
+ Note: I added some stuff for use with gnupg
+
+Copyright (C) 1991, 1992, 1993, 1994, 1996, 1998,
+ 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
+
+This file is free software; you can redistribute it and/or modify
+it under the terms of the GNU Library General Public License as published by
+the Free Software Foundation; either version 2 of the License, or (at your
+option) any later version.
+
+This file 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 Library General Public
+License for more details.
+
+You should have received a copy of the GNU Library General Public License
+along with this file; see the file COPYING.LIB. If not, write to
+the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+MA 02111-1307, USA. */
+
+/* You have to define the following before including this file:
+
+ UWtype -- An unsigned type, default type for operations (typically a "word")
+ UHWtype -- An unsigned type, at least half the size of UWtype.
+ UDWtype -- An unsigned type, at least twice as large a UWtype
+ W_TYPE_SIZE -- size in bits of UWtype
+
+ SItype, USItype -- Signed and unsigned 32 bit types.
+ DItype, UDItype -- Signed and unsigned 64 bit types.
+
+ On a 32 bit machine UWtype should typically be USItype;
+ on a 64 bit machine, UWtype should typically be UDItype.
+*/
+
+#define __BITS4 (W_TYPE_SIZE / 4)
+#define __ll_B ((UWtype) 1 << (W_TYPE_SIZE / 2))
+#define __ll_lowpart(t) ((UWtype) (t) & (__ll_B - 1))
+#define __ll_highpart(t) ((UWtype) (t) >> (W_TYPE_SIZE / 2))
+
+/* This is used to make sure no undesirable sharing between different libraries
+ that use this file takes place. */
+#ifndef __MPN
+#define __MPN(x) __##x
+#endif
+
+/* Define auxiliary asm macros.
+
+ 1) umul_ppmm(high_prod, low_prod, multipler, multiplicand) multiplies two
+ UWtype integers MULTIPLER and MULTIPLICAND, and generates a two UWtype
+ word product in HIGH_PROD and LOW_PROD.
+
+ 2) __umulsidi3(a,b) multiplies two UWtype integers A and B, and returns a
+ UDWtype product. This is just a variant of umul_ppmm.
+
+ 3) udiv_qrnnd(quotient, remainder, high_numerator, low_numerator,
+ denominator) divides a UDWtype, composed by the UWtype integers
+ HIGH_NUMERATOR and LOW_NUMERATOR, by DENOMINATOR and places the quotient
+ in QUOTIENT and the remainder in REMAINDER. HIGH_NUMERATOR must be less
+ than DENOMINATOR for correct operation. If, in addition, the most
+ significant bit of DENOMINATOR must be 1, then the pre-processor symbol
+ UDIV_NEEDS_NORMALIZATION is defined to 1.
+
+ 4) sdiv_qrnnd(quotient, remainder, high_numerator, low_numerator,
+ denominator). Like udiv_qrnnd but the numbers are signed. The quotient
+ is rounded towards 0.
+
+ 5) count_leading_zeros(count, x) counts the number of zero-bits from the
+ msb to the first non-zero bit in the UWtype X. This is the number of
+ steps X needs to be shifted left to set the msb. Undefined for X == 0,
+ unless the symbol COUNT_LEADING_ZEROS_0 is defined to some value.
+
+ 6) count_trailing_zeros(count, x) like count_leading_zeros, but counts
+ from the least significant end.
+
+ 7) add_ssaaaa(high_sum, low_sum, high_addend_1, low_addend_1,
+ high_addend_2, low_addend_2) adds two UWtype integers, composed by
+ HIGH_ADDEND_1 and LOW_ADDEND_1, and HIGH_ADDEND_2 and LOW_ADDEND_2
+ respectively. The result is placed in HIGH_SUM and LOW_SUM. Overflow
+ (i.e. carry out) is not stored anywhere, and is lost.
+
+ 8) sub_ddmmss(high_difference, low_difference, high_minuend, low_minuend,
+ high_subtrahend, low_subtrahend) subtracts two two-word UWtype integers,
+ composed by HIGH_MINUEND_1 and LOW_MINUEND_1, and HIGH_SUBTRAHEND_2 and
+ LOW_SUBTRAHEND_2 respectively. The result is placed in HIGH_DIFFERENCE
+ and LOW_DIFFERENCE. Overflow (i.e. carry out) is not stored anywhere,
+ and is lost.
+
+ If any of these macros are left undefined for a particular CPU,
+ C macros are used. */
+
+/* The CPUs come in alphabetical order below.
+
+ Please add support for more CPUs here, or improve the current support
+ for the CPUs below! */
+
+#if defined (__GNUC__) && !defined (NO_ASM)
+
+/* We sometimes need to clobber "cc" with gcc2, but that would not be
+ understood by gcc1. Use cpp to avoid major code duplication. */
+#if __GNUC__ < 2
+#define __CLOBBER_CC
+#define __AND_CLOBBER_CC
+#else /* __GNUC__ >= 2 */
+#define __CLOBBER_CC : "cc"
+#define __AND_CLOBBER_CC , "cc"
+#endif /* __GNUC__ < 2 */
+
+
+/***************************************
+ ************** A29K *****************
+ ***************************************/
+#if (defined (__a29k__) || defined (_AM29K)) && W_TYPE_SIZE == 32
+#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
+ __asm__ ("add %1,%4,%5\n" \
+ "addc %0,%2,%3" \
+ : "=r" ((USItype)(sh)), \
+ "=&r" ((USItype)(sl)) \
+ : "%r" ((USItype)(ah)), \
+ "rI" ((USItype)(bh)), \
+ "%r" ((USItype)(al)), \
+ "rI" ((USItype)(bl)))
+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
+ __asm__ ("sub %1,%4,%5\n" \
+ "subc %0,%2,%3" \
+ : "=r" ((USItype)(sh)), \
+ "=&r" ((USItype)(sl)) \
+ : "r" ((USItype)(ah)), \
+ "rI" ((USItype)(bh)), \
+ "r" ((USItype)(al)), \
+ "rI" ((USItype)(bl)))
+#define umul_ppmm(xh, xl, m0, m1) \
+ do { \
+ USItype __m0 = (m0), __m1 = (m1); \
+ __asm__ ("multiplu %0,%1,%2" \
+ : "=r" ((USItype)(xl)) \
+ : "r" (__m0), \
+ "r" (__m1)); \
+ __asm__ ("multmu %0,%1,%2" \
+ : "=r" ((USItype)(xh)) \
+ : "r" (__m0), \
+ "r" (__m1)); \
+ } while (0)
+#define udiv_qrnnd(q, r, n1, n0, d) \
+ __asm__ ("dividu %0,%3,%4" \
+ : "=r" ((USItype)(q)), \
+ "=q" ((USItype)(r)) \
+ : "1" ((USItype)(n1)), \
+ "r" ((USItype)(n0)), \
+ "r" ((USItype)(d)))
+
+#define count_leading_zeros(count, x) \
+ __asm__ ("clz %0,%1" \
+ : "=r" ((USItype)(count)) \
+ : "r" ((USItype)(x)))
+#define COUNT_LEADING_ZEROS_0 32
+#endif /* __a29k__ */
+
+
+#if defined (__alpha) && W_TYPE_SIZE == 64
+#define umul_ppmm(ph, pl, m0, m1) \
+ do { \
+ UDItype __m0 = (m0), __m1 = (m1); \
+ __asm__ ("umulh %r1,%2,%0" \
+ : "=r" ((UDItype) ph) \
+ : "%rJ" (__m0), \
+ "rI" (__m1)); \
+ (pl) = __m0 * __m1; \
+ } while (0)
+#define UMUL_TIME 46
+#ifndef LONGLONG_STANDALONE
+#define udiv_qrnnd(q, r, n1, n0, d) \
+ do { UDItype __r; \
+ (q) = __udiv_qrnnd (&__r, (n1), (n0), (d)); \
+ (r) = __r; \
+ } while (0)
+extern UDItype __udiv_qrnnd ();
+#define UDIV_TIME 220
+#endif /* LONGLONG_STANDALONE */
+#endif /* __alpha */
+
+/***************************************
+ ************** ARM ******************
+ ***************************************/
+#if defined (__arm__) && W_TYPE_SIZE == 32
+#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
+ __asm__ ("adds %1, %4, %5\n" \
+ "adc %0, %2, %3" \
+ : "=r" ((USItype)(sh)), \
+ "=&r" ((USItype)(sl)) \
+ : "%r" ((USItype)(ah)), \
+ "rI" ((USItype)(bh)), \
+ "%r" ((USItype)(al)), \
+ "rI" ((USItype)(bl)))
+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
+ __asm__ ("subs %1, %4, %5\n" \
+ "sbc %0, %2, %3" \
+ : "=r" ((USItype)(sh)), \
+ "=&r" ((USItype)(sl)) \
+ : "r" ((USItype)(ah)), \
+ "rI" ((USItype)(bh)), \
+ "r" ((USItype)(al)), \
+ "rI" ((USItype)(bl)))
+#if defined __ARM_ARCH_2__ || defined __ARM_ARCH_3__
+#define umul_ppmm(xh, xl, a, b) \
+ __asm__ ("%@ Inlined umul_ppmm\n" \
+ "mov %|r0, %2, lsr #16 @ AAAA\n" \
+ "mov %|r2, %3, lsr #16 @ BBBB\n" \
+ "bic %|r1, %2, %|r0, lsl #16 @ aaaa\n" \
+ "bic %0, %3, %|r2, lsl #16 @ bbbb\n" \
+ "mul %1, %|r1, %|r2 @ aaaa * BBBB\n" \
+ "mul %|r2, %|r0, %|r2 @ AAAA * BBBB\n" \
+ "mul %|r1, %0, %|r1 @ aaaa * bbbb\n" \
+ "mul %0, %|r0, %0 @ AAAA * bbbb\n" \
+ "adds %|r0, %1, %0 @ central sum\n" \
+ "addcs %|r2, %|r2, #65536\n" \
+ "adds %1, %|r1, %|r0, lsl #16\n" \
+ "adc %0, %|r2, %|r0, lsr #16" \
+ : "=&r" ((USItype)(xh)), \
+ "=r" ((USItype)(xl)) \
+ : "r" ((USItype)(a)), \
+ "r" ((USItype)(b)) \
+ : "r0", "r1", "r2")
+#else
+#define umul_ppmm(xh, xl, a, b) \
+ __asm__ ("%@ Inlined umul_ppmm\n" \
+ "umull %r1, %r0, %r2, %r3" \
+ : "=&r" ((USItype)(xh)), \
+ "=r" ((USItype)(xl)) \
+ : "r" ((USItype)(a)), \
+ "r" ((USItype)(b)) \
+ : "r0", "r1")
+#endif
+#define UMUL_TIME 20
+#define UDIV_TIME 100
+#endif /* __arm__ */
+
+/***************************************
+ ************** CLIPPER **************
+ ***************************************/
+#if defined (__clipper__) && W_TYPE_SIZE == 32
+#define umul_ppmm(w1, w0, u, v) \
+ ({union {UDItype __ll; \
+ struct {USItype __l, __h;} __i; \
+ } __xx; \
+ __asm__ ("mulwux %2,%0" \
+ : "=r" (__xx.__ll) \
+ : "%0" ((USItype)(u)), \
+ "r" ((USItype)(v))); \
+ (w1) = __xx.__i.__h; (w0) = __xx.__i.__l;})
+#define smul_ppmm(w1, w0, u, v) \
+ ({union {DItype __ll; \
+ struct {SItype __l, __h;} __i; \
+ } __xx; \
+ __asm__ ("mulwx %2,%0" \
+ : "=r" (__xx.__ll) \
+ : "%0" ((SItype)(u)), \
+ "r" ((SItype)(v))); \
+ (w1) = __xx.__i.__h; (w0) = __xx.__i.__l;})
+#define __umulsidi3(u, v) \
+ ({UDItype __w; \
+ __asm__ ("mulwux %2,%0" \
+ : "=r" (__w) \
+ : "%0" ((USItype)(u)), \
+ "r" ((USItype)(v))); \
+ __w; })
+#endif /* __clipper__ */
+
+
+/***************************************
+ ************** GMICRO ***************
+ ***************************************/
+#if defined (__gmicro__) && W_TYPE_SIZE == 32
+#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
+ __asm__ ("add.w %5,%1\n" \
+ "addx %3,%0" \
+ : "=g" ((USItype)(sh)), \
+ "=&g" ((USItype)(sl)) \
+ : "%0" ((USItype)(ah)), \
+ "g" ((USItype)(bh)), \
+ "%1" ((USItype)(al)), \
+ "g" ((USItype)(bl)))
+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
+ __asm__ ("sub.w %5,%1\n" \
+ "subx %3,%0" \
+ : "=g" ((USItype)(sh)), \
+ "=&g" ((USItype)(sl)) \
+ : "0" ((USItype)(ah)), \
+ "g" ((USItype)(bh)), \
+ "1" ((USItype)(al)), \
+ "g" ((USItype)(bl)))
+#define umul_ppmm(ph, pl, m0, m1) \
+ __asm__ ("mulx %3,%0,%1" \
+ : "=g" ((USItype)(ph)), \
+ "=r" ((USItype)(pl)) \
+ : "%0" ((USItype)(m0)), \
+ "g" ((USItype)(m1)))
+#define udiv_qrnnd(q, r, nh, nl, d) \
+ __asm__ ("divx %4,%0,%1" \
+ : "=g" ((USItype)(q)), \
+ "=r" ((USItype)(r)) \
+ : "1" ((USItype)(nh)), \
+ "0" ((USItype)(nl)), \
+ "g" ((USItype)(d)))
+#define count_leading_zeros(count, x) \
+ __asm__ ("bsch/1 %1,%0" \
+ : "=g" (count) \
+ : "g" ((USItype)(x)), \
+ "0" ((USItype)0))
+#endif
+
+
+/***************************************
+ ************** HPPA *****************
+ ***************************************/
+#if defined (__hppa) && W_TYPE_SIZE == 32
+#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
+ __asm__ (" add %4,%5,%1\n" \
+ " addc %2,%3,%0" \
+ : "=r" ((USItype)(sh)), \
+ "=&r" ((USItype)(sl)) \
+ : "%rM" ((USItype)(ah)), \
+ "rM" ((USItype)(bh)), \
+ "%rM" ((USItype)(al)), \
+ "rM" ((USItype)(bl)))
+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
+ __asm__ (" sub %4,%5,%1\n" \
+ " subb %2,%3,%0" \
+ : "=r" ((USItype)(sh)), \
+ "=&r" ((USItype)(sl)) \
+ : "rM" ((USItype)(ah)), \
+ "rM" ((USItype)(bh)), \
+ "rM" ((USItype)(al)), \
+ "rM" ((USItype)(bl)))
+#if defined (_PA_RISC1_1)
+#define umul_ppmm(wh, wl, u, v) \
+ do { \
+ union {UDItype __ll; \
+ struct {USItype __h, __l;} __i; \
+ } __xx; \
+ __asm__ (" xmpyu %1,%2,%0" \
+ : "=*f" (__xx.__ll) \
+ : "*f" ((USItype)(u)), \
+ "*f" ((USItype)(v))); \
+ (wh) = __xx.__i.__h; \
+ (wl) = __xx.__i.__l; \
+ } while (0)
+#define UMUL_TIME 8
+#define UDIV_TIME 60
+#else
+#define UMUL_TIME 40
+#define UDIV_TIME 80
+#endif
+#ifndef LONGLONG_STANDALONE
+#define udiv_qrnnd(q, r, n1, n0, d) \
+ do { USItype __r; \
+ (q) = __udiv_qrnnd (&__r, (n1), (n0), (d)); \
+ (r) = __r; \
+ } while (0)
+extern USItype __udiv_qrnnd ();
+#endif /* LONGLONG_STANDALONE */
+#define count_leading_zeros(count, x) \
+ do { \
+ USItype __tmp; \
+ __asm__ ( \
+ " ldi 1,%0 \n" \
+ " extru,= %1,15,16,%%r0 ; Bits 31..16 zero? \n" \
+ " extru,tr %1,15,16,%1 ; No. Shift down, skip add.\n" \
+ " ldo 16(%0),%0 ; Yes. Perform add. \n" \
+ " extru,= %1,23,8,%%r0 ; Bits 15..8 zero? \n" \
+ " extru,tr %1,23,8,%1 ; No. Shift down, skip add.\n" \
+ " ldo 8(%0),%0 ; Yes. Perform add. \n" \
+ " extru,= %1,27,4,%%r0 ; Bits 7..4 zero? \n" \
+ " extru,tr %1,27,4,%1 ; No. Shift down, skip add.\n" \
+ " ldo 4(%0),%0 ; Yes. Perform add. \n" \
+ " extru,= %1,29,2,%%r0 ; Bits 3..2 zero? \n" \
+ " extru,tr %1,29,2,%1 ; No. Shift down, skip add.\n" \
+ " ldo 2(%0),%0 ; Yes. Perform add. \n" \
+ " extru %1,30,1,%1 ; Extract bit 1. \n" \
+ " sub %0,%1,%0 ; Subtract it. " \
+ : "=r" (count), "=r" (__tmp) : "1" (x)); \
+ } while (0)
+#endif /* hppa */
+
+
+/***************************************
+ ************** I370 *****************
+ ***************************************/
+#if (defined (__i370__) || defined (__mvs__)) && W_TYPE_SIZE == 32
+#define umul_ppmm(xh, xl, m0, m1) \
+ do { \
+ union {UDItype __ll; \
+ struct {USItype __h, __l;} __i; \
+ } __xx; \
+ USItype __m0 = (m0), __m1 = (m1); \
+ __asm__ ("mr %0,%3" \
+ : "=r" (__xx.__i.__h), \
+ "=r" (__xx.__i.__l) \
+ : "%1" (__m0), \
+ "r" (__m1)); \
+ (xh) = __xx.__i.__h; (xl) = __xx.__i.__l; \
+ (xh) += ((((SItype) __m0 >> 31) & __m1) \
+ + (((SItype) __m1 >> 31) & __m0)); \
+ } while (0)
+#define smul_ppmm(xh, xl, m0, m1) \
+ do { \
+ union {DItype __ll; \
+ struct {USItype __h, __l;} __i; \
+ } __xx; \
+ __asm__ ("mr %0,%3" \
+ : "=r" (__xx.__i.__h), \
+ "=r" (__xx.__i.__l) \
+ : "%1" (m0), \
+ "r" (m1)); \
+ (xh) = __xx.__i.__h; (xl) = __xx.__i.__l; \
+ } while (0)
+#define sdiv_qrnnd(q, r, n1, n0, d) \
+ do { \
+ union {DItype __ll; \
+ struct {USItype __h, __l;} __i; \
+ } __xx; \
+ __xx.__i.__h = n1; __xx.__i.__l = n0; \
+ __asm__ ("dr %0,%2" \
+ : "=r" (__xx.__ll) \
+ : "0" (__xx.__ll), "r" (d)); \
+ (q) = __xx.__i.__l; (r) = __xx.__i.__h; \
+ } while (0)
+#endif
+
+
+/***************************************
+ ************** I386 *****************
+ ***************************************/
+#undef __i386__
+#if (defined (__i386__) || defined (__i486__)) && W_TYPE_SIZE == 32
+#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
+ __asm__ ("addl %5,%1\n" \
+ "adcl %3,%0" \
+ : "=r" ((USItype)(sh)), \
+ "=&r" ((USItype)(sl)) \
+ : "%0" ((USItype)(ah)), \
+ "g" ((USItype)(bh)), \
+ "%1" ((USItype)(al)), \
+ "g" ((USItype)(bl)))
+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
+ __asm__ ("subl %5,%1\n" \
+ "sbbl %3,%0" \
+ : "=r" ((USItype)(sh)), \
+ "=&r" ((USItype)(sl)) \
+ : "0" ((USItype)(ah)), \
+ "g" ((USItype)(bh)), \
+ "1" ((USItype)(al)), \
+ "g" ((USItype)(bl)))
+#define umul_ppmm(w1, w0, u, v) \
+ __asm__ ("mull %3" \
+ : "=a" ((USItype)(w0)), \
+ "=d" ((USItype)(w1)) \
+ : "%0" ((USItype)(u)), \
+ "rm" ((USItype)(v)))
+#define udiv_qrnnd(q, r, n1, n0, d) \
+ __asm__ ("divl %4" \
+ : "=a" ((USItype)(q)), \
+ "=d" ((USItype)(r)) \
+ : "0" ((USItype)(n0)), \
+ "1" ((USItype)(n1)), \
+ "rm" ((USItype)(d)))
+#define count_leading_zeros(count, x) \
+ do { \
+ USItype __cbtmp; \
+ __asm__ ("bsrl %1,%0" \
+ : "=r" (__cbtmp) : "rm" ((USItype)(x))); \
+ (count) = __cbtmp ^ 31; \
+ } while (0)
+#define count_trailing_zeros(count, x) \
+ __asm__ ("bsfl %1,%0" : "=r" (count) : "rm" ((USItype)(x)))
+#ifndef UMUL_TIME
+#define UMUL_TIME 40
+#endif
+#ifndef UDIV_TIME
+#define UDIV_TIME 40
+#endif
+#endif /* 80x86 */
+
+
+/***************************************
+ ************** I860 *****************
+ ***************************************/
+#if defined (__i860__) && W_TYPE_SIZE == 32
+#define rshift_rhlc(r,h,l,c) \
+ __asm__ ("shr %3,r0,r0\n" \
+ "shrd %1,%2,%0" \
+ "=r" (r) : "r" (h), "r" (l), "rn" (c))
+#endif /* i860 */
+
+/***************************************
+ ************** I960 *****************
+ ***************************************/
+#if defined (__i960__) && W_TYPE_SIZE == 32
+#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
+ __asm__ ("cmpo 1,0\n" \
+ "addc %5,%4,%1\n" \
+ "addc %3,%2,%0" \
+ : "=r" ((USItype)(sh)), \
+ "=&r" ((USItype)(sl)) \
+ : "%dI" ((USItype)(ah)), \
+ "dI" ((USItype)(bh)), \
+ "%dI" ((USItype)(al)), \
+ "dI" ((USItype)(bl)))
+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
+ __asm__ ("cmpo 0,0\n" \
+ "subc %5,%4,%1\n" \
+ "subc %3,%2,%0" \
+ : "=r" ((USItype)(sh)), \
+ "=&r" ((USItype)(sl)) \
+ : "dI" ((USItype)(ah)), \
+ "dI" ((USItype)(bh)), \
+ "dI" ((USItype)(al)), \
+ "dI" ((USItype)(bl)))
+#define umul_ppmm(w1, w0, u, v) \
+ ({union {UDItype __ll; \
+ struct {USItype __l, __h;} __i; \
+ } __xx; \
+ __asm__ ("emul %2,%1,%0" \
+ : "=d" (__xx.__ll) \
+ : "%dI" ((USItype)(u)), \
+ "dI" ((USItype)(v))); \
+ (w1) = __xx.__i.__h; (w0) = __xx.__i.__l;})
+#define __umulsidi3(u, v) \
+ ({UDItype __w; \
+ __asm__ ("emul %2,%1,%0" \
+ : "=d" (__w) \
+ : "%dI" ((USItype)(u)), \
+ "dI" ((USItype)(v))); \
+ __w; })
+#define udiv_qrnnd(q, r, nh, nl, d) \
+ do { \
+ union {UDItype __ll; \
+ struct {USItype __l, __h;} __i; \
+ } __nn; \
+ __nn.__i.__h = (nh); __nn.__i.__l = (nl); \
+ __asm__ ("ediv %d,%n,%0" \
+ : "=d" (__rq.__ll) \
+ : "dI" (__nn.__ll), \
+ "dI" ((USItype)(d))); \
+ (r) = __rq.__i.__l; (q) = __rq.__i.__h; \
+ } while (0)
+#define count_leading_zeros(count, x) \
+ do { \
+ USItype __cbtmp; \
+ __asm__ ("scanbit %1,%0" \
+ : "=r" (__cbtmp) \
+ : "r" ((USItype)(x))); \
+ (count) = __cbtmp ^ 31; \
+ } while (0)
+#define COUNT_LEADING_ZEROS_0 (-32) /* sic */
+#if defined (__i960mx) /* what is the proper symbol to test??? */
+#define rshift_rhlc(r,h,l,c) \
+ do { \
+ union {UDItype __ll; \
+ struct {USItype __l, __h;} __i; \
+ } __nn; \
+ __nn.__i.__h = (h); __nn.__i.__l = (l); \
+ __asm__ ("shre %2,%1,%0" \
+ : "=d" (r) : "dI" (__nn.__ll), "dI" (c)); \
+ }
+#endif /* i960mx */
+#endif /* i960 */
+
+
+/***************************************
+ ************** 68000 ****************
+ ***************************************/
+#if (defined (__mc68000__) || defined (__mc68020__) || defined (__NeXT__) || defined(mc68020)) && W_TYPE_SIZE == 32
+#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
+ __asm__ ("add%.l %5,%1\n" \
+ "addx%.l %3,%0" \
+ : "=d" ((USItype)(sh)), \
+ "=&d" ((USItype)(sl)) \
+ : "%0" ((USItype)(ah)), \
+ "d" ((USItype)(bh)), \
+ "%1" ((USItype)(al)), \
+ "g" ((USItype)(bl)))
+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
+ __asm__ ("sub%.l %5,%1\n" \
+ "subx%.l %3,%0" \
+ : "=d" ((USItype)(sh)), \
+ "=&d" ((USItype)(sl)) \
+ : "0" ((USItype)(ah)), \
+ "d" ((USItype)(bh)), \
+ "1" ((USItype)(al)), \
+ "g" ((USItype)(bl)))
+#if (defined (__mc68020__) || defined (__NeXT__) || defined(mc68020))
+#define umul_ppmm(w1, w0, u, v) \
+ __asm__ ("mulu%.l %3,%1:%0" \
+ : "=d" ((USItype)(w0)), \
+ "=d" ((USItype)(w1)) \
+ : "%0" ((USItype)(u)), \
+ "dmi" ((USItype)(v)))
+#define UMUL_TIME 45
+#define udiv_qrnnd(q, r, n1, n0, d) \
+ __asm__ ("divu%.l %4,%1:%0" \
+ : "=d" ((USItype)(q)), \
+ "=d" ((USItype)(r)) \
+ : "0" ((USItype)(n0)), \
+ "1" ((USItype)(n1)), \
+ "dmi" ((USItype)(d)))
+#define UDIV_TIME 90
+#define sdiv_qrnnd(q, r, n1, n0, d) \
+ __asm__ ("divs%.l %4,%1:%0" \
+ : "=d" ((USItype)(q)), \
+ "=d" ((USItype)(r)) \
+ : "0" ((USItype)(n0)), \
+ "1" ((USItype)(n1)), \
+ "dmi" ((USItype)(d)))
+#define count_leading_zeros(count, x) \
+ __asm__ ("bfffo %1{%b2:%b2},%0" \
+ : "=d" ((USItype)(count)) \
+ : "od" ((USItype)(x)), "n" (0))
+#define COUNT_LEADING_ZEROS_0 32
+#else /* not mc68020 */
+#define umul_ppmm(xh, xl, a, b) \
+ do { USItype __umul_tmp1, __umul_tmp2; \
+ __asm__ ("| Inlined umul_ppmm \n" \
+ " move%.l %5,%3 \n" \
+ " move%.l %2,%0 \n" \
+ " move%.w %3,%1 \n" \
+ " swap %3 \n" \
+ " swap %0 \n" \
+ " mulu %2,%1 \n" \
+ " mulu %3,%0 \n" \
+ " mulu %2,%3 \n" \
+ " swap %2 \n" \
+ " mulu %5,%2 \n" \
+ " add%.l %3,%2 \n" \
+ " jcc 1f \n" \
+ " add%.l %#0x10000,%0 \n" \
+ "1: move%.l %2,%3 \n" \
+ " clr%.w %2 \n" \
+ " swap %2 \n" \
+ " swap %3 \n" \
+ " clr%.w %3 \n" \
+ " add%.l %3,%1 \n" \
+ " addx%.l %2,%0 \n" \
+ " | End inlined umul_ppmm" \
+ : "=&d" ((USItype)(xh)), "=&d" ((USItype)(xl)), \
+ "=d" (__umul_tmp1), "=&d" (__umul_tmp2) \
+ : "%2" ((USItype)(a)), "d" ((USItype)(b))); \
+ } while (0)
+#define UMUL_TIME 100
+#define UDIV_TIME 400
+#endif /* not mc68020 */
+#endif /* mc68000 */
+
+
+/***************************************
+ ************** 88000 ****************
+ ***************************************/
+#if defined (__m88000__) && W_TYPE_SIZE == 32
+#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
+ __asm__ ("addu.co %1,%r4,%r5\n" \
+ "addu.ci %0,%r2,%r3" \
+ : "=r" ((USItype)(sh)), \
+ "=&r" ((USItype)(sl)) \
+ : "%rJ" ((USItype)(ah)), \
+ "rJ" ((USItype)(bh)), \
+ "%rJ" ((USItype)(al)), \
+ "rJ" ((USItype)(bl)))
+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
+ __asm__ ("subu.co %1,%r4,%r5\n" \
+ "subu.ci %0,%r2,%r3" \
+ : "=r" ((USItype)(sh)), \
+ "=&r" ((USItype)(sl)) \
+ : "rJ" ((USItype)(ah)), \
+ "rJ" ((USItype)(bh)), \
+ "rJ" ((USItype)(al)), \
+ "rJ" ((USItype)(bl)))
+#define count_leading_zeros(count, x) \
+ do { \
+ USItype __cbtmp; \
+ __asm__ ("ff1 %0,%1" \
+ : "=r" (__cbtmp) \
+ : "r" ((USItype)(x))); \
+ (count) = __cbtmp ^ 31; \
+ } while (0)
+#define COUNT_LEADING_ZEROS_0 63 /* sic */
+#if defined (__m88110__)
+#define umul_ppmm(wh, wl, u, v) \
+ do { \
+ union {UDItype __ll; \
+ struct {USItype __h, __l;} __i; \
+ } __x; \
+ __asm__ ("mulu.d %0,%1,%2" : "=r" (__x.__ll) : "r" (u), "r" (v)); \
+ (wh) = __x.__i.__h; \
+ (wl) = __x.__i.__l; \
+ } while (0)
+#define udiv_qrnnd(q, r, n1, n0, d) \
+ ({union {UDItype __ll; \
+ struct {USItype __h, __l;} __i; \
+ } __x, __q; \
+ __x.__i.__h = (n1); __x.__i.__l = (n0); \
+ __asm__ ("divu.d %0,%1,%2" \
+ : "=r" (__q.__ll) : "r" (__x.__ll), "r" (d)); \
+ (r) = (n0) - __q.__l * (d); (q) = __q.__l; })
+#define UMUL_TIME 5
+#define UDIV_TIME 25
+#else
+#define UMUL_TIME 17
+#define UDIV_TIME 150
+#endif /* __m88110__ */
+#endif /* __m88000__ */
+
+/***************************************
+ ************** MIPS *****************
+ ***************************************/
+#if defined (__mips__) && W_TYPE_SIZE == 32
+#if __GNUC__ > 2 || __GNUC_MINOR__ >= 7
+#define umul_ppmm(w1, w0, u, v) \
+ __asm__ ("multu %2,%3" \
+ : "=l" ((USItype)(w0)), \
+ "=h" ((USItype)(w1)) \
+ : "d" ((USItype)(u)), \
+ "d" ((USItype)(v)))
+#else
+#define umul_ppmm(w1, w0, u, v) \
+ __asm__ ("multu %2,%3 \n" \
+ "mflo %0 \n" \
+ "mfhi %1" \
+ : "=d" ((USItype)(w0)), \
+ "=d" ((USItype)(w1)) \
+ : "d" ((USItype)(u)), \
+ "d" ((USItype)(v)))
+#endif
+#define UMUL_TIME 10
+#define UDIV_TIME 100
+#endif /* __mips__ */
+
+/***************************************
+ ************** MIPS/64 **************
+ ***************************************/
+#if (defined (__mips) && __mips >= 3) && W_TYPE_SIZE == 64
+#if __GNUC__ > 2 || __GNUC_MINOR__ >= 7
+#define umul_ppmm(w1, w0, u, v) \
+ __asm__ ("dmultu %2,%3" \
+ : "=l" ((UDItype)(w0)), \
+ "=h" ((UDItype)(w1)) \
+ : "d" ((UDItype)(u)), \
+ "d" ((UDItype)(v)))
+#else
+#define umul_ppmm(w1, w0, u, v) \
+ __asm__ ("dmultu %2,%3 \n" \
+ "mflo %0 \n" \
+ "mfhi %1" \
+ : "=d" ((UDItype)(w0)), \
+ "=d" ((UDItype)(w1)) \
+ : "d" ((UDItype)(u)), \
+ "d" ((UDItype)(v)))
+#endif
+#define UMUL_TIME 20
+#define UDIV_TIME 140
+#endif /* __mips__ */
+
+
+/***************************************
+ ************** 32000 ****************
+ ***************************************/
+#if defined (__ns32000__) && W_TYPE_SIZE == 32
+#define umul_ppmm(w1, w0, u, v) \
+ ({union {UDItype __ll; \
+ struct {USItype __l, __h;} __i; \
+ } __xx; \
+ __asm__ ("meid %2,%0" \
+ : "=g" (__xx.__ll) \
+ : "%0" ((USItype)(u)), \
+ "g" ((USItype)(v))); \
+ (w1) = __xx.__i.__h; (w0) = __xx.__i.__l;})
+#define __umulsidi3(u, v) \
+ ({UDItype __w; \
+ __asm__ ("meid %2,%0" \
+ : "=g" (__w) \
+ : "%0" ((USItype)(u)), \
+ "g" ((USItype)(v))); \
+ __w; })
+#define udiv_qrnnd(q, r, n1, n0, d) \
+ ({union {UDItype __ll; \
+ struct {USItype __l, __h;} __i; \
+ } __xx; \
+ __xx.__i.__h = (n1); __xx.__i.__l = (n0); \
+ __asm__ ("deid %2,%0" \
+ : "=g" (__xx.__ll) \
+ : "0" (__xx.__ll), \
+ "g" ((USItype)(d))); \
+ (r) = __xx.__i.__l; (q) = __xx.__i.__h; })
+#define count_trailing_zeros(count,x) \
+ do {
+ __asm__ ("ffsd %2,%0" \
+ : "=r" ((USItype) (count)) \
+ : "0" ((USItype) 0), \
+ "r" ((USItype) (x))); \
+ } while (0)
+#endif /* __ns32000__ */
+
+
+/***************************************
+ ************** PPC ******************
+ ***************************************/
+#if (defined (_ARCH_PPC) || defined (_IBMR2)) && W_TYPE_SIZE == 32
+#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
+ do { \
+ if (__builtin_constant_p (bh) && (bh) == 0) \
+ __asm__ ("{a%I4|add%I4c} %1,%3,%4\n\t{aze|addze} %0,%2" \
+ : "=r" ((USItype)(sh)), \
+ "=&r" ((USItype)(sl)) \
+ : "%r" ((USItype)(ah)), \
+ "%r" ((USItype)(al)), \
+ "rI" ((USItype)(bl))); \
+ else if (__builtin_constant_p (bh) && (bh) ==~(USItype) 0) \
+ __asm__ ("{a%I4|add%I4c} %1,%3,%4\n\t{ame|addme} %0,%2" \
+ : "=r" ((USItype)(sh)), \
+ "=&r" ((USItype)(sl)) \
+ : "%r" ((USItype)(ah)), \
+ "%r" ((USItype)(al)), \
+ "rI" ((USItype)(bl))); \
+ else \
+ __asm__ ("{a%I5|add%I5c} %1,%4,%5\n\t{ae|adde} %0,%2,%3" \
+ : "=r" ((USItype)(sh)), \
+ "=&r" ((USItype)(sl)) \
+ : "%r" ((USItype)(ah)), \
+ "r" ((USItype)(bh)), \
+ "%r" ((USItype)(al)), \
+ "rI" ((USItype)(bl))); \
+ } while (0)
+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
+ do { \
+ if (__builtin_constant_p (ah) && (ah) == 0) \
+ __asm__ ("{sf%I3|subf%I3c} %1,%4,%3\n\t{sfze|subfze} %0,%2" \
+ : "=r" ((USItype)(sh)), \
+ "=&r" ((USItype)(sl)) \
+ : "r" ((USItype)(bh)), \
+ "rI" ((USItype)(al)), \
+ "r" ((USItype)(bl))); \
+ else if (__builtin_constant_p (ah) && (ah) ==~(USItype) 0) \
+ __asm__ ("{sf%I3|subf%I3c} %1,%4,%3\n\t{sfme|subfme} %0,%2" \
+ : "=r" ((USItype)(sh)), \
+ "=&r" ((USItype)(sl)) \
+ : "r" ((USItype)(bh)), \
+ "rI" ((USItype)(al)), \
+ "r" ((USItype)(bl))); \
+ else if (__builtin_constant_p (bh) && (bh) == 0) \
+ __asm__ ("{sf%I3|subf%I3c} %1,%4,%3\n\t{ame|addme} %0,%2" \
+ : "=r" ((USItype)(sh)), \
+ "=&r" ((USItype)(sl)) \
+ : "r" ((USItype)(ah)), \
+ "rI" ((USItype)(al)), \
+ "r" ((USItype)(bl))); \
+ else if (__builtin_constant_p (bh) && (bh) ==~(USItype) 0) \
+ __asm__ ("{sf%I3|subf%I3c} %1,%4,%3\n\t{aze|addze} %0,%2" \
+ : "=r" ((USItype)(sh)), \
+ "=&r" ((USItype)(sl)) \
+ : "r" ((USItype)(ah)), \
+ "rI" ((USItype)(al)), \
+ "r" ((USItype)(bl))); \
+ else \
+ __asm__ ("{sf%I4|subf%I4c} %1,%5,%4\n\t{sfe|subfe} %0,%3,%2" \
+ : "=r" ((USItype)(sh)), \
+ "=&r" ((USItype)(sl)) \
+ : "r" ((USItype)(ah)), \
+ "r" ((USItype)(bh)), \
+ "rI" ((USItype)(al)), \
+ "r" ((USItype)(bl))); \
+ } while (0)
+#define count_leading_zeros(count, x) \
+ __asm__ ("{cntlz|cntlzw} %0,%1" \
+ : "=r" ((USItype)(count)) \
+ : "r" ((USItype)(x)))
+#define COUNT_LEADING_ZEROS_0 32
+#if defined (_ARCH_PPC)
+#define umul_ppmm(ph, pl, m0, m1) \
+ do { \
+ USItype __m0 = (m0), __m1 = (m1); \
+ __asm__ ("mulhwu %0,%1,%2" \
+ : "=r" ((USItype) ph) \
+ : "%r" (__m0), \
+ "r" (__m1)); \
+ (pl) = __m0 * __m1; \
+ } while (0)
+#define UMUL_TIME 15
+#define smul_ppmm(ph, pl, m0, m1) \
+ do { \
+ SItype __m0 = (m0), __m1 = (m1); \
+ __asm__ ("mulhw %0,%1,%2" \
+ : "=r" ((SItype) ph) \
+ : "%r" (__m0), \
+ "r" (__m1)); \
+ (pl) = __m0 * __m1; \
+ } while (0)
+#define SMUL_TIME 14
+#define UDIV_TIME 120
+#else
+#define umul_ppmm(xh, xl, m0, m1) \
+ do { \
+ USItype __m0 = (m0), __m1 = (m1); \
+ __asm__ ("mul %0,%2,%3" \
+ : "=r" ((USItype)(xh)), \
+ "=q" ((USItype)(xl)) \
+ : "r" (__m0), \
+ "r" (__m1)); \
+ (xh) += ((((SItype) __m0 >> 31) & __m1) \
+ + (((SItype) __m1 >> 31) & __m0)); \
+ } while (0)
+#define UMUL_TIME 8
+#define smul_ppmm(xh, xl, m0, m1) \
+ __asm__ ("mul %0,%2,%3" \
+ : "=r" ((SItype)(xh)), \
+ "=q" ((SItype)(xl)) \
+ : "r" (m0), \
+ "r" (m1))
+#define SMUL_TIME 4
+#define sdiv_qrnnd(q, r, nh, nl, d) \
+ __asm__ ("div %0,%2,%4" \
+ : "=r" ((SItype)(q)), "=q" ((SItype)(r)) \
+ : "r" ((SItype)(nh)), "1" ((SItype)(nl)), "r" ((SItype)(d)))
+#define UDIV_TIME 100
+#endif
+#endif /* Power architecture variants. */
+
+
+/***************************************
+ ************** PYR ******************
+ ***************************************/
+#if defined (__pyr__) && W_TYPE_SIZE == 32
+#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
+ __asm__ ("addw %5,%1 \n" \
+ "addwc %3,%0" \
+ : "=r" ((USItype)(sh)), \
+ "=&r" ((USItype)(sl)) \
+ : "%0" ((USItype)(ah)), \
+ "g" ((USItype)(bh)), \
+ "%1" ((USItype)(al)), \
+ "g" ((USItype)(bl)))
+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
+ __asm__ ("subw %5,%1 \n" \
+ "subwb %3,%0" \
+ : "=r" ((USItype)(sh)), \
+ "=&r" ((USItype)(sl)) \
+ : "0" ((USItype)(ah)), \
+ "g" ((USItype)(bh)), \
+ "1" ((USItype)(al)), \
+ "g" ((USItype)(bl)))
+/* This insn works on Pyramids with AP, XP, or MI CPUs, but not with SP. */
+#define umul_ppmm(w1, w0, u, v) \
+ ({union {UDItype __ll; \
+ struct {USItype __h, __l;} __i; \
+ } __xx; \
+ __asm__ ("movw %1,%R0 \n" \
+ "uemul %2,%0" \
+ : "=&r" (__xx.__ll) \
+ : "g" ((USItype) (u)), \
+ "g" ((USItype)(v))); \
+ (w1) = __xx.__i.__h; (w0) = __xx.__i.__l;})
+#endif /* __pyr__ */
+
+
+/***************************************
+ ************** RT/ROMP **************
+ ***************************************/
+#if defined (__ibm032__) /* RT/ROMP */ && W_TYPE_SIZE == 32
+#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
+ __asm__ ("a %1,%5 \n" \
+ "ae %0,%3" \
+ : "=r" ((USItype)(sh)), \
+ "=&r" ((USItype)(sl)) \
+ : "%0" ((USItype)(ah)), \
+ "r" ((USItype)(bh)), \
+ "%1" ((USItype)(al)), \
+ "r" ((USItype)(bl)))
+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
+ __asm__ ("s %1,%5\n" \
+ "se %0,%3" \
+ : "=r" ((USItype)(sh)), \
+ "=&r" ((USItype)(sl)) \
+ : "0" ((USItype)(ah)), \
+ "r" ((USItype)(bh)), \
+ "1" ((USItype)(al)), \
+ "r" ((USItype)(bl)))
+#define umul_ppmm(ph, pl, m0, m1) \
+ do { \
+ USItype __m0 = (m0), __m1 = (m1); \
+ __asm__ ( \
+ "s r2,r2 \n" \
+ "mts r10,%2 \n" \
+ "m r2,%3 \n" \
+ "m r2,%3 \n" \
+ "m r2,%3 \n" \
+ "m r2,%3 \n" \
+ "m r2,%3 \n" \
+ "m r2,%3 \n" \
+ "m r2,%3 \n" \
+ "m r2,%3 \n" \
+ "m r2,%3 \n" \
+ "m r2,%3 \n" \
+ "m r2,%3 \n" \
+ "m r2,%3 \n" \
+ "m r2,%3 \n" \
+ "m r2,%3 \n" \
+ "m r2,%3 \n" \
+ "m r2,%3 \n" \
+ "cas %0,r2,r0 \n" \
+ "mfs r10,%1" \
+ : "=r" ((USItype)(ph)), \
+ "=r" ((USItype)(pl)) \
+ : "%r" (__m0), \
+ "r" (__m1) \
+ : "r2"); \
+ (ph) += ((((SItype) __m0 >> 31) & __m1) \
+ + (((SItype) __m1 >> 31) & __m0)); \
+ } while (0)
+#define UMUL_TIME 20
+#define UDIV_TIME 200
+#define count_leading_zeros(count, x) \
+ do { \
+ if ((x) >= 0x10000) \
+ __asm__ ("clz %0,%1" \
+ : "=r" ((USItype)(count)) \
+ : "r" ((USItype)(x) >> 16)); \
+ else \
+ { \
+ __asm__ ("clz %0,%1" \
+ : "=r" ((USItype)(count)) \
+ : "r" ((USItype)(x))); \
+ (count) += 16; \
+ } \
+ } while (0)
+#endif /* RT/ROMP */
+
+
+/***************************************
+ ************** SH2 ******************
+ ***************************************/
+#if (defined (__sh2__) || defined(__sh3__) || defined(__SH4__) ) \
+ && W_TYPE_SIZE == 32
+#define umul_ppmm(w1, w0, u, v) \
+ __asm__ ( \
+ "dmulu.l %2,%3\n" \
+ "sts macl,%1\n" \
+ "sts mach,%0" \
+ : "=r" ((USItype)(w1)), \
+ "=r" ((USItype)(w0)) \
+ : "r" ((USItype)(u)), \
+ "r" ((USItype)(v)) \
+ : "macl", "mach")
+#define UMUL_TIME 5
+#endif
+
+/***************************************
+ ************** SPARC ****************
+ ***************************************/
+#if defined (__sparc__) && W_TYPE_SIZE == 32
+#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
+ __asm__ ("addcc %r4,%5,%1\n" \
+ "addx %r2,%3,%0" \
+ : "=r" ((USItype)(sh)), \
+ "=&r" ((USItype)(sl)) \
+ : "%rJ" ((USItype)(ah)), \
+ "rI" ((USItype)(bh)), \
+ "%rJ" ((USItype)(al)), \
+ "rI" ((USItype)(bl)) \
+ __CLOBBER_CC)
+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
+ __asm__ ("subcc %r4,%5,%1\n" \
+ "subx %r2,%3,%0" \
+ : "=r" ((USItype)(sh)), \
+ "=&r" ((USItype)(sl)) \
+ : "rJ" ((USItype)(ah)), \
+ "rI" ((USItype)(bh)), \
+ "rJ" ((USItype)(al)), \
+ "rI" ((USItype)(bl)) \
+ __CLOBBER_CC)
+#if defined (__sparc_v8__)
+/* Don't match immediate range because, 1) it is not often useful,
+ 2) the 'I' flag thinks of the range as a 13 bit signed interval,
+ while we want to match a 13 bit interval, sign extended to 32 bits,
+ but INTERPRETED AS UNSIGNED. */
+#define umul_ppmm(w1, w0, u, v) \
+ __asm__ ("umul %2,%3,%1;rd %%y,%0" \
+ : "=r" ((USItype)(w1)), \
+ "=r" ((USItype)(w0)) \
+ : "r" ((USItype)(u)), \
+ "r" ((USItype)(v)))
+#define UMUL_TIME 5
+#ifndef SUPERSPARC /* SuperSPARC's udiv only handles 53 bit dividends */
+#define udiv_qrnnd(q, r, n1, n0, d) \
+ do { \
+ USItype __q; \
+ __asm__ ("mov %1,%%y;nop;nop;nop;udiv %2,%3,%0" \
+ : "=r" ((USItype)(__q)) \
+ : "r" ((USItype)(n1)), \
+ "r" ((USItype)(n0)), \
+ "r" ((USItype)(d))); \
+ (r) = (n0) - __q * (d); \
+ (q) = __q; \
+ } while (0)
+#define UDIV_TIME 25
+#endif /* SUPERSPARC */
+#else /* ! __sparc_v8__ */
+#if defined (__sparclite__)
+/* This has hardware multiply but not divide. It also has two additional
+ instructions scan (ffs from high bit) and divscc. */
+#define umul_ppmm(w1, w0, u, v) \
+ __asm__ ("umul %2,%3,%1;rd %%y,%0" \
+ : "=r" ((USItype)(w1)), \
+ "=r" ((USItype)(w0)) \
+ : "r" ((USItype)(u)), \
+ "r" ((USItype)(v)))
+#define UMUL_TIME 5
+#define udiv_qrnnd(q, r, n1, n0, d) \
+ __asm__ ("! Inlined udiv_qrnnd \n" \
+ " wr %%g0,%2,%%y ! Not a delayed write for sparclite \n" \
+ " tst %%g0 \n" \
+ " divscc %3,%4,%%g1 \n" \
+ " divscc %%g1,%4,%%g1 \n" \
+ " divscc %%g1,%4,%%g1 \n" \
+ " divscc %%g1,%4,%%g1 \n" \
+ " divscc %%g1,%4,%%g1 \n" \
+ " divscc %%g1,%4,%%g1 \n" \
+ " divscc %%g1,%4,%%g1 \n" \
+ " divscc %%g1,%4,%%g1 \n" \
+ " divscc %%g1,%4,%%g1 \n" \
+ " divscc %%g1,%4,%%g1 \n" \
+ " divscc %%g1,%4,%%g1 \n" \
+ " divscc %%g1,%4,%%g1 \n" \
+ " divscc %%g1,%4,%%g1 \n" \
+ " divscc %%g1,%4,%%g1 \n" \
+ " divscc %%g1,%4,%%g1 \n" \
+ " divscc %%g1,%4,%%g1 \n" \
+ " divscc %%g1,%4,%%g1 \n" \
+ " divscc %%g1,%4,%%g1 \n" \
+ " divscc %%g1,%4,%%g1 \n" \
+ " divscc %%g1,%4,%%g1 \n" \
+ " divscc %%g1,%4,%%g1 \n" \
+ " divscc %%g1,%4,%%g1 \n" \
+ " divscc %%g1,%4,%%g1 \n" \
+ " divscc %%g1,%4,%%g1 \n" \
+ " divscc %%g1,%4,%%g1 \n" \
+ " divscc %%g1,%4,%%g1 \n" \
+ " divscc %%g1,%4,%%g1 \n" \
+ " divscc %%g1,%4,%%g1 \n" \
+ " divscc %%g1,%4,%%g1 \n" \
+ " divscc %%g1,%4,%%g1 \n" \
+ " divscc %%g1,%4,%%g1 \n" \
+ " divscc %%g1,%4,%0 \n" \
+ " rd %%y,%1 \n" \
+ " bl,a 1f \n" \
+ " add %1,%4,%1 \n" \
+ "1: ! End of inline udiv_qrnnd" \
+ : "=r" ((USItype)(q)), \
+ "=r" ((USItype)(r)) \
+ : "r" ((USItype)(n1)), \
+ "r" ((USItype)(n0)), \
+ "rI" ((USItype)(d)) \
+ : "%g1" __AND_CLOBBER_CC)
+#define UDIV_TIME 37
+#define count_leading_zeros(count, x) \
+ __asm__ ("scan %1,0,%0" \
+ : "=r" ((USItype)(x)) \
+ : "r" ((USItype)(count)))
+/* Early sparclites return 63 for an argument of 0, but they warn that future
+ implementations might change this. Therefore, leave COUNT_LEADING_ZEROS_0
+ undefined. */
+#endif /* __sparclite__ */
+#endif /* __sparc_v8__ */
+/* Default to sparc v7 versions of umul_ppmm and udiv_qrnnd. */
+#ifndef umul_ppmm
+#define umul_ppmm(w1, w0, u, v) \
+ __asm__ ("! Inlined umul_ppmm \n" \
+ " wr %%g0,%2,%%y ! SPARC has 0-3 delay insn after a wr \n" \
+ " sra %3,31,%%g2 ! Don't move this insn \n" \
+ " and %2,%%g2,%%g2 ! Don't move this insn \n" \
+ " andcc %%g0,0,%%g1 ! Don't move this insn \n" \
+ " mulscc %%g1,%3,%%g1 \n" \
+ " mulscc %%g1,%3,%%g1 \n" \
+ " mulscc %%g1,%3,%%g1 \n" \
+ " mulscc %%g1,%3,%%g1 \n" \
+ " mulscc %%g1,%3,%%g1 \n" \
+ " mulscc %%g1,%3,%%g1 \n" \
+ " mulscc %%g1,%3,%%g1 \n" \
+ " mulscc %%g1,%3,%%g1 \n" \
+ " mulscc %%g1,%3,%%g1 \n" \
+ " mulscc %%g1,%3,%%g1 \n" \
+ " mulscc %%g1,%3,%%g1 \n" \
+ " mulscc %%g1,%3,%%g1 \n" \
+ " mulscc %%g1,%3,%%g1 \n" \
+ " mulscc %%g1,%3,%%g1 \n" \
+ " mulscc %%g1,%3,%%g1 \n" \
+ " mulscc %%g1,%3,%%g1 \n" \
+ " mulscc %%g1,%3,%%g1 \n" \
+ " mulscc %%g1,%3,%%g1 \n" \
+ " mulscc %%g1,%3,%%g1 \n" \
+ " mulscc %%g1,%3,%%g1 \n" \
+ " mulscc %%g1,%3,%%g1 \n" \
+ " mulscc %%g1,%3,%%g1 \n" \
+ " mulscc %%g1,%3,%%g1 \n" \
+ " mulscc %%g1,%3,%%g1 \n" \
+ " mulscc %%g1,%3,%%g1 \n" \
+ " mulscc %%g1,%3,%%g1 \n" \
+ " mulscc %%g1,%3,%%g1 \n" \
+ " mulscc %%g1,%3,%%g1 \n" \
+ " mulscc %%g1,%3,%%g1 \n" \
+ " mulscc %%g1,%3,%%g1 \n" \
+ " mulscc %%g1,%3,%%g1 \n" \
+ " mulscc %%g1,%3,%%g1 \n" \
+ " mulscc %%g1,0,%%g1 \n" \
+ " add %%g1,%%g2,%0 \n" \
+ " rd %%y,%1" \
+ : "=r" ((USItype)(w1)), \
+ "=r" ((USItype)(w0)) \
+ : "%rI" ((USItype)(u)), \
+ "r" ((USItype)(v)) \
+ : "%g1", "%g2" __AND_CLOBBER_CC)
+#define UMUL_TIME 39 /* 39 instructions */
+#endif
+#ifndef udiv_qrnnd
+#ifndef LONGLONG_STANDALONE
+#define udiv_qrnnd(q, r, n1, n0, d) \
+ do { USItype __r; \
+ (q) = __udiv_qrnnd (&__r, (n1), (n0), (d)); \
+ (r) = __r; \
+ } while (0)
+extern USItype __udiv_qrnnd ();
+#define UDIV_TIME 140
+#endif /* LONGLONG_STANDALONE */
+#endif /* udiv_qrnnd */
+#endif /* __sparc__ */
+
+
+/***************************************
+ ************** VAX ******************
+ ***************************************/
+#if defined (__vax__) && W_TYPE_SIZE == 32
+#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
+ __asm__ ("addl2 %5,%1\n" \
+ "adwc %3,%0" \
+ : "=g" ((USItype)(sh)), \
+ "=&g" ((USItype)(sl)) \
+ : "%0" ((USItype)(ah)), \
+ "g" ((USItype)(bh)), \
+ "%1" ((USItype)(al)), \
+ "g" ((USItype)(bl)))
+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
+ __asm__ ("subl2 %5,%1\n" \
+ "sbwc %3,%0" \
+ : "=g" ((USItype)(sh)), \
+ "=&g" ((USItype)(sl)) \
+ : "0" ((USItype)(ah)), \
+ "g" ((USItype)(bh)), \
+ "1" ((USItype)(al)), \
+ "g" ((USItype)(bl)))
+#define umul_ppmm(xh, xl, m0, m1) \
+ do { \
+ union {UDItype __ll; \
+ struct {USItype __l, __h;} __i; \
+ } __xx; \
+ USItype __m0 = (m0), __m1 = (m1); \
+ __asm__ ("emul %1,%2,$0,%0" \
+ : "=g" (__xx.__ll) \
+ : "g" (__m0), \
+ "g" (__m1)); \
+ (xh) = __xx.__i.__h; (xl) = __xx.__i.__l; \
+ (xh) += ((((SItype) __m0 >> 31) & __m1) \
+ + (((SItype) __m1 >> 31) & __m0)); \
+ } while (0)
+#define sdiv_qrnnd(q, r, n1, n0, d) \
+ do { \
+ union {DItype __ll; \
+ struct {SItype __l, __h;} __i; \
+ } __xx; \
+ __xx.__i.__h = n1; __xx.__i.__l = n0; \
+ __asm__ ("ediv %3,%2,%0,%1" \
+ : "=g" (q), "=g" (r) \
+ : "g" (__xx.__ll), "g" (d)); \
+ } while (0)
+#endif /* __vax__ */
+
+
+/***************************************
+ ************** Z8000 ****************
+ ***************************************/
+#if defined (__z8000__) && W_TYPE_SIZE == 16
+#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
+ __asm__ ("add %H1,%H5\n\tadc %H0,%H3" \
+ : "=r" ((unsigned int)(sh)), \
+ "=&r" ((unsigned int)(sl)) \
+ : "%0" ((unsigned int)(ah)), \
+ "r" ((unsigned int)(bh)), \
+ "%1" ((unsigned int)(al)), \
+ "rQR" ((unsigned int)(bl)))
+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
+ __asm__ ("sub %H1,%H5\n\tsbc %H0,%H3" \
+ : "=r" ((unsigned int)(sh)), \
+ "=&r" ((unsigned int)(sl)) \
+ : "0" ((unsigned int)(ah)), \
+ "r" ((unsigned int)(bh)), \
+ "1" ((unsigned int)(al)), \
+ "rQR" ((unsigned int)(bl)))
+#define umul_ppmm(xh, xl, m0, m1) \
+ do { \
+ union {long int __ll; \
+ struct {unsigned int __h, __l;} __i; \
+ } __xx; \
+ unsigned int __m0 = (m0), __m1 = (m1); \
+ __asm__ ("mult %S0,%H3" \
+ : "=r" (__xx.__i.__h), \
+ "=r" (__xx.__i.__l) \
+ : "%1" (__m0), \
+ "rQR" (__m1)); \
+ (xh) = __xx.__i.__h; (xl) = __xx.__i.__l; \
+ (xh) += ((((signed int) __m0 >> 15) & __m1) \
+ + (((signed int) __m1 >> 15) & __m0)); \
+ } while (0)
+#endif /* __z8000__ */
+
+#endif /* __GNUC__ */
+
+
+/***************************************
+ *********** Generic Versions ********
+ ***************************************/
+#if !defined (umul_ppmm) && defined (__umulsidi3)
+#define umul_ppmm(ph, pl, m0, m1) \
+ { \
+ UDWtype __ll = __umulsidi3 (m0, m1); \
+ ph = (UWtype) (__ll >> W_TYPE_SIZE); \
+ pl = (UWtype) __ll; \
+ }
+#endif
+
+#if !defined (__umulsidi3)
+#define __umulsidi3(u, v) \
+ ({UWtype __hi, __lo; \
+ umul_ppmm (__hi, __lo, u, v); \
+ ((UDWtype) __hi << W_TYPE_SIZE) | __lo; })
+#endif
+
+/* If this machine has no inline assembler, use C macros. */
+
+#if !defined (add_ssaaaa)
+#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
+ do { \
+ UWtype __x; \
+ __x = (al) + (bl); \
+ (sh) = (ah) + (bh) + (__x < (al)); \
+ (sl) = __x; \
+ } while (0)
+#endif
+
+#if !defined (sub_ddmmss)
+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
+ do { \
+ UWtype __x; \
+ __x = (al) - (bl); \
+ (sh) = (ah) - (bh) - (__x > (al)); \
+ (sl) = __x; \
+ } while (0)
+#endif
+
+#if !defined (umul_ppmm)
+#define umul_ppmm(w1, w0, u, v) \
+ do { \
+ UWtype __x0, __x1, __x2, __x3; \
+ UHWtype __ul, __vl, __uh, __vh; \
+ UWtype __u = (u), __v = (v); \
+ \
+ __ul = __ll_lowpart (__u); \
+ __uh = __ll_highpart (__u); \
+ __vl = __ll_lowpart (__v); \
+ __vh = __ll_highpart (__v); \
+ \
+ __x0 = (UWtype) __ul * __vl; \
+ __x1 = (UWtype) __ul * __vh; \
+ __x2 = (UWtype) __uh * __vl; \
+ __x3 = (UWtype) __uh * __vh; \
+ \
+ __x1 += __ll_highpart (__x0);/* this can't give carry */ \
+ __x1 += __x2; /* but this indeed can */ \
+ if (__x1 < __x2) /* did we get it? */ \
+ __x3 += __ll_B; /* yes, add it in the proper pos. */ \
+ \
+ (w1) = __x3 + __ll_highpart (__x1); \
+ (w0) = (__ll_lowpart (__x1) << W_TYPE_SIZE/2) + __ll_lowpart (__x0);\
+ } while (0)
+#endif
+
+#if !defined (umul_ppmm)
+#define smul_ppmm(w1, w0, u, v) \
+ do { \
+ UWtype __w1; \
+ UWtype __m0 = (u), __m1 = (v); \
+ umul_ppmm (__w1, w0, __m0, __m1); \
+ (w1) = __w1 - (-(__m0 >> (W_TYPE_SIZE - 1)) & __m1) \
+ - (-(__m1 >> (W_TYPE_SIZE - 1)) & __m0); \
+ } while (0)
+#endif
+
+/* Define this unconditionally, so it can be used for debugging. */
+#define __udiv_qrnnd_c(q, r, n1, n0, d) \
+ do { \
+ UWtype __d1, __d0, __q1, __q0, __r1, __r0, __m; \
+ __d1 = __ll_highpart (d); \
+ __d0 = __ll_lowpart (d); \
+ \
+ __r1 = (n1) % __d1; \
+ __q1 = (n1) / __d1; \
+ __m = (UWtype) __q1 * __d0; \
+ __r1 = __r1 * __ll_B | __ll_highpart (n0); \
+ if (__r1 < __m) \
+ { \
+ __q1--, __r1 += (d); \
+ if (__r1 >= (d)) /* i.e. we didn't get carry when adding to __r1 */\
+ if (__r1 < __m) \
+ __q1--, __r1 += (d); \
+ } \
+ __r1 -= __m; \
+ \
+ __r0 = __r1 % __d1; \
+ __q0 = __r1 / __d1; \
+ __m = (UWtype) __q0 * __d0; \
+ __r0 = __r0 * __ll_B | __ll_lowpart (n0); \
+ if (__r0 < __m) \
+ { \
+ __q0--, __r0 += (d); \
+ if (__r0 >= (d)) \
+ if (__r0 < __m) \
+ __q0--, __r0 += (d); \
+ } \
+ __r0 -= __m; \
+ \
+ (q) = (UWtype) __q1 * __ll_B | __q0; \
+ (r) = __r0; \
+ } while (0)
+
+/* If the processor has no udiv_qrnnd but sdiv_qrnnd, go through
+ __udiv_w_sdiv (defined in libgcc or elsewhere). */
+#if !defined (udiv_qrnnd) && defined (sdiv_qrnnd)
+#define udiv_qrnnd(q, r, nh, nl, d) \
+ do { \
+ UWtype __r; \
+ (q) = __MPN(udiv_w_sdiv) (&__r, nh, nl, d); \
+ (r) = __r; \
+ } while (0)
+#endif
+
+/* If udiv_qrnnd was not defined for this processor, use __udiv_qrnnd_c. */
+#if !defined (udiv_qrnnd)
+#define UDIV_NEEDS_NORMALIZATION 1
+#define udiv_qrnnd __udiv_qrnnd_c
+#endif
+
+#undef count_leading_zeros
+#if !defined (count_leading_zeros)
+extern
+#ifdef __STDC__
+const
+#endif
+unsigned char __clz_tab[];
+#define MPI_INTERNAL_NEED_CLZ_TAB 1
+#define count_leading_zeros(count, x) \
+ do { \
+ UWtype __xr = (x); \
+ UWtype __a; \
+ \
+ if (W_TYPE_SIZE <= 32) \
+ { \
+ __a = __xr < ((UWtype) 1 << 2*__BITS4) \
+ ? (__xr < ((UWtype) 1 << __BITS4) ? 0 : __BITS4) \
+ : (__xr < ((UWtype) 1 << 3*__BITS4) ? 2*__BITS4 : 3*__BITS4);\
+ } \
+ else \
+ { \
+ for (__a = W_TYPE_SIZE - 8; __a > 0; __a -= 8) \
+ if (((__xr >> __a) & 0xff) != 0) \
+ break; \
+ } \
+ \
+ (count) = W_TYPE_SIZE - (__clz_tab[__xr >> __a] + __a); \
+ } while (0)
+/* This version gives a well-defined value for zero. */
+#define COUNT_LEADING_ZEROS_0 W_TYPE_SIZE
+#endif
+
+#if !defined (count_trailing_zeros)
+/* Define count_trailing_zeros using count_leading_zeros. The latter might be
+ defined in asm, but if it is not, the C version above is good enough. */
+#define count_trailing_zeros(count, x) \
+ do { \
+ UWtype __ctz_x = (x); \
+ UWtype __ctz_c; \
+ count_leading_zeros (__ctz_c, __ctz_x & -__ctz_x); \
+ (count) = W_TYPE_SIZE - 1 - __ctz_c; \
+ } while (0)
+#endif
+
+#ifndef UDIV_NEEDS_NORMALIZATION
+#define UDIV_NEEDS_NORMALIZATION 0
+#endif
--- /dev/null
+/* memory.h - memory allocation
+ * Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc.
+ *
+ * This file is part of GNUPG.
+ *
+ * GNUPG 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.
+ *
+ * GNUPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#ifndef G10_MEMORY_H
+#define G10_MEMORY_H
+
+#include <linux/mm.h>
+
+
+#define m_alloc(n) kmalloc(n, GFP_KERNEL)
+#define m_alloc_clear(n) kmalloc(n, GFP_KERNEL) /* can't memset, no size or don't know how to get it */
+#define m_alloc_secure(n) kmalloc(n, GFP_KERNEL)
+#define m_alloc_secure_clear(n) kmalloc(n, GFP_KERNEL) /* can't memset, no size or don't know how to get it */
+#define m_free(n) kfree(n)
+#define m_check(n) /* nothing to do here */
+#define m_size(n) sizeof(n)
+#define m_is_secure(n) 1
+
+/* &&&& realloc kernel hack, should check this */
+#define m_realloc(n,m) krealloc((n),(m))
+
+static inline void *
+krealloc(void *ptr, size_t size)
+{
+ void *tmp = NULL;
+ if (size) {
+ tmp = kmalloc(size,GFP_KERNEL);
+ if (ptr) memcpy(tmp,ptr,size);
+ }
+ kfree(ptr);
+ return tmp;
+}
+
+#define DBG_MEMORY memory_debug_mode
+#define DBG_MEMSTAT memory_stat_debug_mode
+
+#define EXTERN_UNLESS_MAIN_MODULE extern
+
+EXTERN_UNLESS_MAIN_MODULE int memory_debug_mode;
+EXTERN_UNLESS_MAIN_MODULE int memory_stat_debug_mode;
+
+
+#endif /*G10_MEMORY_H*/
--- /dev/null
+/* mpi-add.c - MPI functions
+ * Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc.
+ * Copyright (C) 1994, 1996 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG 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.
+ *
+ * GnuPG 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
+ *
+ * Note: This code is heavily based on the GNU MP Library.
+ * Actually it's the same code with only minor changes in the
+ * way the data is stored; this is to support the abstraction
+ * of an optional secure memory allocation which may be used
+ * to avoid revealing of sensitive data due to paging etc.
+ * The GNU MP Library itself is published under the LGPL;
+ * however I decided to publish this code under the plain GPL.
+ */
+
+#include "gnupg_mpi_config.h"
+#include "gnupg_mpi_mpi-internal.h"
+
+/****************
+ * Add the unsigned integer V to the mpi-integer U and store the
+ * result in W. U and V may be the same.
+ */
+void
+mpi_add_ui(MPI w, MPI u, unsigned long v )
+{
+ mpi_ptr_t wp, up;
+ mpi_size_t usize, wsize;
+ int usign, wsign;
+
+ usize = u->nlimbs;
+ usign = u->sign;
+ wsign = 0;
+
+ /* If not space for W (and possible carry), increase space. */
+ wsize = usize + 1;
+ if( w->alloced < wsize )
+ mpi_resize(w, wsize);
+
+ /* These must be after realloc (U may be the same as W). */
+ up = u->d;
+ wp = w->d;
+
+ if( !usize ) { /* simple */
+ wp[0] = v;
+ wsize = v? 1:0;
+ }
+ else if( !usign ) { /* mpi is not negative */
+ mpi_limb_t cy;
+ cy = mpihelp_add_1(wp, up, usize, v);
+ wp[usize] = cy;
+ wsize = usize + cy;
+ }
+ else { /* The signs are different. Need exact comparison to determine
+ * which operand to subtract from which. */
+ if( usize == 1 && up[0] < v ) {
+ wp[0] = v - up[0];
+ wsize = 1;
+ }
+ else {
+ mpihelp_sub_1(wp, up, usize, v);
+ /* Size can decrease with at most one limb. */
+ wsize = usize - (wp[usize-1]==0);
+ wsign = 1;
+ }
+ }
+
+ w->nlimbs = wsize;
+ w->sign = wsign;
+}
+
+
+void
+mpi_add(MPI w, MPI u, MPI v)
+{
+ mpi_ptr_t wp, up, vp;
+ mpi_size_t usize, vsize, wsize;
+ int usign, vsign, wsign;
+
+ if( u->nlimbs < v->nlimbs ) { /* Swap U and V. */
+ usize = v->nlimbs;
+ usign = v->sign;
+ vsize = u->nlimbs;
+ vsign = u->sign;
+ wsize = usize + 1;
+ RESIZE_IF_NEEDED(w, wsize);
+ /* These must be after realloc (u or v may be the same as w). */
+ up = v->d;
+ vp = u->d;
+ }
+ else {
+ usize = u->nlimbs;
+ usign = u->sign;
+ vsize = v->nlimbs;
+ vsign = v->sign;
+ wsize = usize + 1;
+ RESIZE_IF_NEEDED(w, wsize);
+ /* These must be after realloc (u or v may be the same as w). */
+ up = u->d;
+ vp = v->d;
+ }
+ wp = w->d;
+ wsign = 0;
+
+ if( !vsize ) { /* simple */
+ MPN_COPY(wp, up, usize );
+ wsize = usize;
+ wsign = usign;
+ }
+ else if( usign != vsign ) { /* different sign */
+ /* This test is right since USIZE >= VSIZE */
+ if( usize != vsize ) {
+ mpihelp_sub(wp, up, usize, vp, vsize);
+ wsize = usize;
+ MPN_NORMALIZE(wp, wsize);
+ wsign = usign;
+ }
+ else if( mpihelp_cmp(up, vp, usize) < 0 ) {
+ mpihelp_sub_n(wp, vp, up, usize);
+ wsize = usize;
+ MPN_NORMALIZE(wp, wsize);
+ if( !usign )
+ wsign = 1;
+ }
+ else {
+ mpihelp_sub_n(wp, up, vp, usize);
+ wsize = usize;
+ MPN_NORMALIZE(wp, wsize);
+ if( usign )
+ wsign = 1;
+ }
+ }
+ else { /* U and V have same sign. Add them. */
+ mpi_limb_t cy = mpihelp_add(wp, up, usize, vp, vsize);
+ wp[usize] = cy;
+ wsize = usize + cy;
+ if( usign )
+ wsign = 1;
+ }
+
+ w->nlimbs = wsize;
+ w->sign = wsign;
+}
+
+
+/****************
+ * Subtract the unsigned integer V from the mpi-integer U and store the
+ * result in W.
+ */
+void
+mpi_sub_ui(MPI w, MPI u, unsigned long v )
+{
+ mpi_ptr_t wp, up;
+ mpi_size_t usize, wsize;
+ int usign, wsign;
+
+ usize = u->nlimbs;
+ usign = u->sign;
+ wsign = 0;
+
+ /* If not space for W (and possible carry), increase space. */
+ wsize = usize + 1;
+ if( w->alloced < wsize )
+ mpi_resize(w, wsize);
+
+ /* These must be after realloc (U may be the same as W). */
+ up = u->d;
+ wp = w->d;
+
+ if( !usize ) { /* simple */
+ wp[0] = v;
+ wsize = v? 1:0;
+ wsign = 1;
+ }
+ else if( usign ) { /* mpi and v are negative */
+ mpi_limb_t cy;
+ cy = mpihelp_add_1(wp, up, usize, v);
+ wp[usize] = cy;
+ wsize = usize + cy;
+ }
+ else { /* The signs are different. Need exact comparison to determine
+ * which operand to subtract from which. */
+ if( usize == 1 && up[0] < v ) {
+ wp[0] = v - up[0];
+ wsize = 1;
+ wsign = 1;
+ }
+ else {
+ mpihelp_sub_1(wp, up, usize, v);
+ /* Size can decrease with at most one limb. */
+ wsize = usize - (wp[usize-1]==0);
+ }
+ }
+
+ w->nlimbs = wsize;
+ w->sign = wsign;
+}
+
+void
+mpi_sub(MPI w, MPI u, MPI v)
+{
+ if( w == v ) {
+ MPI vv = mpi_copy(v);
+ vv->sign = !vv->sign;
+ mpi_add( w, u, vv );
+ mpi_free(vv);
+ }
+ else {
+ /* fixme: this is not thread-save (we temp. modify v) */
+ v->sign = !v->sign;
+ mpi_add( w, u, v );
+ v->sign = !v->sign;
+ }
+}
+
+
+void
+mpi_addm( MPI w, MPI u, MPI v, MPI m)
+{
+ mpi_add(w, u, v);
+ mpi_fdiv_r( w, w, m );
+}
+
+void
+mpi_subm( MPI w, MPI u, MPI v, MPI m)
+{
+ mpi_sub(w, u, v);
+ mpi_fdiv_r( w, w, m );
+}
+
--- /dev/null
+/* mpi-bit.c - MPI bit level fucntions
+ * Copyright (C) 1998, 1999 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG 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.
+ *
+ * GnuPG 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 "gnupg_mpi_mpi-internal.h"
+#include "gnupg_mpi_longlong.h"
+
+#define MPI_INTERNAL_NEED_CLZ_TAB
+#ifdef MPI_INTERNAL_NEED_CLZ_TAB
+#ifdef __STDC__
+const
+#endif
+unsigned char
+__clz_tab[] =
+{
+ 0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
+ 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
+ 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
+ 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
+ 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
+};
+#endif
+
+
+#define A_LIMB_1 ((mpi_limb_t)1)
+
+
+/****************
+ * Sometimes we have MSL (most significant limbs) which are 0;
+ * this is for some reasons not good, so this function removes them.
+ */
+void
+mpi_normalize( MPI a )
+{
+ if( mpi_is_opaque (a) )
+ return;
+
+ for( ; a->nlimbs && !a->d[a->nlimbs-1]; a->nlimbs-- )
+ ;
+}
+
+
+
+/****************
+ * Return the number of bits in A.
+ */
+unsigned
+mpi_get_nbits( MPI a )
+{
+ unsigned n;
+
+ mpi_normalize( a );
+
+ if( a->nlimbs ) {
+ mpi_limb_t alimb = a->d[a->nlimbs-1];
+ if( alimb ) {
+ count_leading_zeros( n, alimb );
+ }
+ else
+ n = BITS_PER_MPI_LIMB;
+ n = BITS_PER_MPI_LIMB - n + (a->nlimbs-1) * BITS_PER_MPI_LIMB;
+ }
+ else
+ n = 0;
+ return n;
+}
+
+
+/****************
+ * Test whether bit N is set.
+ */
+int
+mpi_test_bit( MPI a, unsigned n )
+{
+ unsigned limbno, bitno;
+ mpi_limb_t limb;
+
+ limbno = n / BITS_PER_MPI_LIMB;
+ bitno = n % BITS_PER_MPI_LIMB;
+
+ if( limbno >= a->nlimbs )
+ return 0; /* too far left: this is a 0 */
+ limb = a->d[limbno];
+ return (limb & (A_LIMB_1 << bitno))? 1: 0;
+}
+
+
+/****************
+ * Set bit N of A.
+ */
+void
+mpi_set_bit( MPI a, unsigned n )
+{
+ unsigned limbno, bitno;
+
+ limbno = n / BITS_PER_MPI_LIMB;
+ bitno = n % BITS_PER_MPI_LIMB;
+
+ if( limbno >= a->nlimbs ) { /* resize */
+ if( a->alloced >= limbno )
+ mpi_resize(a, limbno+1 );
+ a->nlimbs = limbno+1;
+ }
+ a->d[limbno] |= (A_LIMB_1<<bitno);
+}
+
+/****************
+ * Set bit N of A. and clear all bits above
+ */
+void
+mpi_set_highbit( MPI a, unsigned n )
+{
+ unsigned limbno, bitno;
+
+ limbno = n / BITS_PER_MPI_LIMB;
+ bitno = n % BITS_PER_MPI_LIMB;
+
+ if( limbno >= a->nlimbs ) { /* resize */
+ if( a->alloced >= limbno )
+ mpi_resize(a, limbno+1 );
+ a->nlimbs = limbno+1;
+ }
+ a->d[limbno] |= (A_LIMB_1<<bitno);
+ for( bitno++; bitno < BITS_PER_MPI_LIMB; bitno++ )
+ a->d[limbno] &= ~(A_LIMB_1 << bitno);
+ a->nlimbs = limbno+1;
+}
+
+/****************
+ * clear bit N of A and all bits above
+ */
+void
+mpi_clear_highbit( MPI a, unsigned n )
+{
+ unsigned limbno, bitno;
+
+ limbno = n / BITS_PER_MPI_LIMB;
+ bitno = n % BITS_PER_MPI_LIMB;
+
+ if( limbno >= a->nlimbs )
+ return; /* not allocated, so need to clear bits :-) */
+
+ for( ; bitno < BITS_PER_MPI_LIMB; bitno++ )
+ a->d[limbno] &= ~(A_LIMB_1 << bitno);
+ a->nlimbs = limbno+1;
+}
+
+/****************
+ * Clear bit N of A.
+ */
+void
+mpi_clear_bit( MPI a, unsigned n )
+{
+ unsigned limbno, bitno;
+
+ limbno = n / BITS_PER_MPI_LIMB;
+ bitno = n % BITS_PER_MPI_LIMB;
+
+ if( limbno >= a->nlimbs )
+ return; /* don't need to clear this bit, it's to far to left */
+ a->d[limbno] &= ~(A_LIMB_1 << bitno);
+}
+
+
+/****************
+ * Shift A by N bits to the right
+ * FIXME: should use alloc_limb if X and A are same.
+ */
+void
+mpi_rshift( MPI x, MPI a, unsigned n )
+{
+ mpi_ptr_t xp;
+ mpi_size_t xsize;
+
+ xsize = a->nlimbs;
+ x->sign = a->sign;
+ RESIZE_IF_NEEDED(x, xsize);
+ xp = x->d;
+
+ if( xsize ) {
+ mpihelp_rshift( xp, a->d, xsize, n);
+ MPN_NORMALIZE( xp, xsize);
+ }
+ x->nlimbs = xsize;
+}
+
+
+/****************
+ * Shift A by COUNT limbs to the left
+ * This is used only within the MPI library
+ */
+void
+mpi_lshift_limbs( MPI a, unsigned int count )
+{
+ mpi_ptr_t ap = a->d;
+ int n = a->nlimbs;
+ int i;
+
+ if( !count || !n )
+ return;
+
+ RESIZE_IF_NEEDED( a, n+count );
+
+ for( i = n-1; i >= 0; i-- )
+ ap[i+count] = ap[i];
+ for(i=0; i < count; i++ )
+ ap[i] = 0;
+ a->nlimbs += count;
+}
+
+
+/****************
+ * Shift A by COUNT limbs to the right
+ * This is used only within the MPI library
+ */
+void
+mpi_rshift_limbs( MPI a, unsigned int count )
+{
+ mpi_ptr_t ap = a->d;
+ mpi_size_t n = a->nlimbs;
+ unsigned int i;
+
+ if( count >= n ) {
+ a->nlimbs = 0;
+ return;
+ }
+
+ for( i = 0; i < n - count; i++ )
+ ap[i] = ap[i+count];
+ ap[i] = 0;
+ a->nlimbs -= count;
+}
+
+
--- /dev/null
+/* mpi-cmp.c - MPI functions
+ * Copyright (C) 1998, 1999 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG 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.
+ *
+ * GnuPG 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 "gnupg_mpi_mpi-internal.h"
+
+int
+mpi_cmp_ui( MPI u, unsigned long v )
+{
+ mpi_limb_t limb = v;
+
+ mpi_normalize( u );
+ if( !u->nlimbs && !limb )
+ return 0;
+ if( u->sign )
+ return -1;
+ if( u->nlimbs > 1 )
+ return 1;
+
+ if( u->d[0] == limb )
+ return 0;
+ else if( u->d[0] > limb )
+ return 1;
+ else
+ return -1;
+}
+
+int
+mpi_cmp( MPI u, MPI v )
+{
+ mpi_size_t usize, vsize;
+ int cmp;
+
+ mpi_normalize( u );
+ mpi_normalize( v );
+ usize = u->nlimbs;
+ vsize = v->nlimbs;
+ if( !u->sign && v->sign )
+ return 1;
+ if( u->sign && !v->sign )
+ return -1;
+ if( usize != vsize && !u->sign && !v->sign )
+ return usize - vsize;
+ if( usize != vsize && u->sign && v->sign )
+ return vsize + usize;
+ if( !usize )
+ return 0;
+ if( !(cmp=mpihelp_cmp( u->d, v->d, usize )) )
+ return 0;
+ if( (cmp < 0?1:0) == (u->sign?1:0))
+ return 1;
+ return -1;
+}
+
+
--- /dev/null
+/* mpi-div.c - MPI functions
+ * Copyright (C) 1994, 1996 Free Software Foundation, Inc.
+ * Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG 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.
+ *
+ * GnuPG 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
+ *
+ * Note: This code is heavily based on the GNU MP Library.
+ * Actually it's the same code with only minor changes in the
+ * way the data is stored; this is to support the abstraction
+ * of an optional secure memory allocation which may be used
+ * to avoid revealing of sensitive data due to paging etc.
+ * The GNU MP Library itself is published under the LGPL;
+ * however I decided to publish this code under the plain GPL.
+ */
+
+#include "gnupg_mpi_mpi-internal.h"
+#include "gnupg_mpi_longlong.h"
+
+
+void
+mpi_fdiv_r( MPI rem, MPI dividend, MPI divisor )
+{
+ int divisor_sign = divisor->sign;
+ MPI temp_divisor = NULL;
+
+ /* We need the original value of the divisor after the remainder has been
+ * preliminary calculated. We have to copy it to temporary space if it's
+ * the same variable as REM. */
+ if( rem == divisor ) {
+ temp_divisor = mpi_copy( divisor );
+ divisor = temp_divisor;
+ }
+
+ mpi_tdiv_r( rem, dividend, divisor );
+
+ if( ((divisor_sign?1:0) ^ (dividend->sign?1:0)) && rem->nlimbs )
+ mpi_add( rem, rem, divisor);
+
+ if( temp_divisor )
+ mpi_free(temp_divisor);
+}
+
+
+
+/****************
+ * Division rounding the quotient towards -infinity.
+ * The remainder gets the same sign as the denominator.
+ * rem is optional
+ */
+
+ulong
+mpi_fdiv_r_ui( MPI rem, MPI dividend, ulong divisor )
+{
+ mpi_limb_t rlimb;
+
+ rlimb = mpihelp_mod_1( dividend->d, dividend->nlimbs, divisor );
+ if( rlimb && dividend->sign )
+ rlimb = divisor - rlimb;
+
+ if( rem ) {
+ rem->d[0] = rlimb;
+ rem->nlimbs = rlimb? 1:0;
+ }
+ return rlimb;
+}
+
+
+void
+mpi_fdiv_q( MPI quot, MPI dividend, MPI divisor )
+{
+ MPI tmp = mpi_alloc( mpi_get_nlimbs(quot) );
+ mpi_fdiv_qr( quot, tmp, dividend, divisor);
+ mpi_free(tmp);
+}
+
+void
+mpi_fdiv_qr( MPI quot, MPI rem, MPI dividend, MPI divisor )
+{
+ int divisor_sign = divisor->sign;
+ MPI temp_divisor = NULL;
+
+ if( quot == divisor || rem == divisor ) {
+ temp_divisor = mpi_copy( divisor );
+ divisor = temp_divisor;
+ }
+
+ mpi_tdiv_qr( quot, rem, dividend, divisor );
+
+ if( (divisor_sign ^ dividend->sign) && rem->nlimbs ) {
+ mpi_sub_ui( quot, quot, 1 );
+ mpi_add( rem, rem, divisor);
+ }
+
+ if( temp_divisor )
+ mpi_free(temp_divisor);
+}
+
+
+/* If den == quot, den needs temporary storage.
+ * If den == rem, den needs temporary storage.
+ * If num == quot, num needs temporary storage.
+ * If den has temporary storage, it can be normalized while being copied,
+ * i.e no extra storage should be allocated.
+ */
+
+void
+mpi_tdiv_r( MPI rem, MPI num, MPI den)
+{
+ mpi_tdiv_qr(NULL, rem, num, den );
+}
+
+void
+mpi_tdiv_qr( MPI quot, MPI rem, MPI num, MPI den)
+{
+ mpi_ptr_t np, dp;
+ mpi_ptr_t qp, rp;
+ mpi_size_t nsize = num->nlimbs;
+ mpi_size_t dsize = den->nlimbs;
+ mpi_size_t qsize, rsize;
+ mpi_size_t sign_remainder = num->sign;
+ mpi_size_t sign_quotient = num->sign ^ den->sign;
+ unsigned normalization_steps;
+ mpi_limb_t q_limb;
+ mpi_ptr_t marker[5];
+ int markidx=0;
+
+ /* Ensure space is enough for quotient and remainder.
+ * We need space for an extra limb in the remainder, because it's
+ * up-shifted (normalized) below. */
+ rsize = nsize + 1;
+ mpi_resize( rem, rsize);
+
+ qsize = rsize - dsize; /* qsize cannot be bigger than this. */
+ if( qsize <= 0 ) {
+ if( num != rem ) {
+ rem->nlimbs = num->nlimbs;
+ rem->sign = num->sign;
+ MPN_COPY(rem->d, num->d, nsize);
+ }
+ if( quot ) {
+ /* This needs to follow the assignment to rem, in case the
+ * numerator and quotient are the same. */
+ quot->nlimbs = 0;
+ quot->sign = 0;
+ }
+ return;
+ }
+
+ if( quot )
+ mpi_resize( quot, qsize);
+
+ /* Read pointers here, when reallocation is finished. */
+ np = num->d;
+ dp = den->d;
+ rp = rem->d;
+
+ /* Optimize division by a single-limb divisor. */
+ if( dsize == 1 ) {
+ mpi_limb_t rlimb;
+ if( quot ) {
+ qp = quot->d;
+ rlimb = mpihelp_divmod_1( qp, np, nsize, dp[0] );
+ qsize -= qp[qsize - 1] == 0;
+ quot->nlimbs = qsize;
+ quot->sign = sign_quotient;
+ }
+ else
+ rlimb = mpihelp_mod_1( np, nsize, dp[0] );
+ rp[0] = rlimb;
+ rsize = rlimb != 0?1:0;
+ rem->nlimbs = rsize;
+ rem->sign = sign_remainder;
+ return;
+ }
+
+
+ if( quot ) {
+ qp = quot->d;
+ /* Make sure QP and NP point to different objects. Otherwise the
+ * numerator would be gradually overwritten by the quotient limbs. */
+ if(qp == np) { /* Copy NP object to temporary space. */
+ np = marker[markidx++] = mpi_alloc_limb_space(nsize,
+ mpi_is_secure(quot));
+ MPN_COPY(np, qp, nsize);
+ }
+ }
+ else /* Put quotient at top of remainder. */
+ qp = rp + dsize;
+
+ count_leading_zeros( normalization_steps, dp[dsize - 1] );
+
+ /* Normalize the denominator, i.e. make its most significant bit set by
+ * shifting it NORMALIZATION_STEPS bits to the left. Also shift the
+ * numerator the same number of steps (to keep the quotient the same!).
+ */
+ if( normalization_steps ) {
+ mpi_ptr_t tp;
+ mpi_limb_t nlimb;
+
+ /* Shift up the denominator setting the most significant bit of
+ * the most significant word. Use temporary storage not to clobber
+ * the original contents of the denominator. */
+ tp = marker[markidx++] = mpi_alloc_limb_space(dsize,mpi_is_secure(den));
+ mpihelp_lshift( tp, dp, dsize, normalization_steps );
+ dp = tp;
+
+ /* Shift up the numerator, possibly introducing a new most
+ * significant word. Move the shifted numerator in the remainder
+ * meanwhile. */
+ nlimb = mpihelp_lshift(rp, np, nsize, normalization_steps);
+ if( nlimb ) {
+ rp[nsize] = nlimb;
+ rsize = nsize + 1;
+ }
+ else
+ rsize = nsize;
+ }
+ else {
+ /* The denominator is already normalized, as required. Copy it to
+ * temporary space if it overlaps with the quotient or remainder. */
+ if( dp == rp || (quot && (dp == qp))) {
+ mpi_ptr_t tp;
+
+ tp = marker[markidx++] = mpi_alloc_limb_space(dsize, mpi_is_secure(den));
+ MPN_COPY( tp, dp, dsize );
+ dp = tp;
+ }
+
+ /* Move the numerator to the remainder. */
+ if( rp != np )
+ MPN_COPY(rp, np, nsize);
+
+ rsize = nsize;
+ }
+
+ q_limb = mpihelp_divrem( qp, 0, rp, rsize, dp, dsize );
+
+ if( quot ) {
+ qsize = rsize - dsize;
+ if(q_limb) {
+ qp[qsize] = q_limb;
+ qsize += 1;
+ }
+
+ quot->nlimbs = qsize;
+ quot->sign = sign_quotient;
+ }
+
+ rsize = dsize;
+ MPN_NORMALIZE (rp, rsize);
+
+ if( normalization_steps && rsize ) {
+ mpihelp_rshift(rp, rp, rsize, normalization_steps);
+ rsize -= rp[rsize - 1] == 0?1:0;
+ }
+
+ rem->nlimbs = rsize;
+ rem->sign = sign_remainder;
+ while( markidx )
+ mpi_free_limb_space(marker[--markidx]);
+}
+
+void
+mpi_tdiv_q_2exp( MPI w, MPI u, unsigned count )
+{
+ mpi_size_t usize, wsize;
+ mpi_size_t limb_cnt;
+
+ usize = u->nlimbs;
+ limb_cnt = count / BITS_PER_MPI_LIMB;
+ wsize = usize - limb_cnt;
+ if( limb_cnt >= usize )
+ w->nlimbs = 0;
+ else {
+ mpi_ptr_t wp;
+ mpi_ptr_t up;
+
+ RESIZE_IF_NEEDED( w, wsize );
+ wp = w->d;
+ up = u->d;
+
+ count %= BITS_PER_MPI_LIMB;
+ if( count ) {
+ mpihelp_rshift( wp, up + limb_cnt, wsize, count );
+ wsize -= !wp[wsize - 1];
+ }
+ else {
+ MPN_COPY_INCR( wp, up + limb_cnt, wsize);
+ }
+
+ w->nlimbs = wsize;
+ }
+}
+
+/****************
+ * Check whether dividend is divisible by divisor
+ * (note: divisor must fit into a limb)
+ */
+int
+mpi_divisible_ui(MPI dividend, ulong divisor )
+{
+ return !mpihelp_mod_1( dividend->d, dividend->nlimbs, divisor );
+}
+
--- /dev/null
+/* mpi-gcd.c - MPI functions
+ * Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG 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.
+ *
+ * GnuPG 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 "gnupg_mpi_mpi-internal.h"
+
+/****************
+ * Find the greatest common divisor G of A and B.
+ * Return: true if this 1, false in all other cases
+ */
+int
+mpi_gcd( MPI g, MPI xa, MPI xb )
+{
+ MPI a, b;
+
+ a = mpi_copy(xa);
+ b = mpi_copy(xb);
+
+ /* TAOCP Vol II, 4.5.2, Algorithm A */
+ a->sign = 0;
+ b->sign = 0;
+ while( mpi_cmp_ui( b, 0 ) ) {
+ mpi_fdiv_r( g, a, b ); /* g used as temorary variable */
+ mpi_set(a,b);
+ mpi_set(b,g);
+ }
+ mpi_set(g, a);
+
+ mpi_free(a);
+ mpi_free(b);
+ return !mpi_cmp_ui( g, 1);
+}
+
+
+
--- /dev/null
+/* mpi-inline.c
+ * Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG 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.
+ *
+ * GnuPG 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
+ */
+
+
+/* put the inline functions as real functions into the lib */
+#define G10_MPI_INLINE_DECL
+
+#include "gnupg_mpi_mpi-internal.h"
+
+/* always include the header becuase it is only
+ * included by mpi-internal if __GCC__ is defined but we
+ * need it here in all cases and the above definition of
+ * of the macro allows us to do so
+ */
+#include "gnupg_mpi_mpi-inline.h"
+
--- /dev/null
+/* mpi-inline.h - Internal to the Multi Precision Integers
+ * Copyright (C) 1994, 1996, 1998, 1999 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG 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.
+ *
+ * GnuPG 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
+ *
+ * Note: This code is heavily based on the GNU MP Library.
+ * Actually it's the same code with only minor changes in the
+ * way the data is stored; this is to support the abstraction
+ * of an optional secure memory allocation which may be used
+ * to avoid revealing of sensitive data due to paging etc.
+ * The GNU MP Library itself is published under the LGPL;
+ * however I decided to publish this code under the plain GPL.
+ */
+
+#ifndef G10_MPI_INLINE_H
+#define G10_MPI_INLINE_H
+
+#ifndef G10_MPI_INLINE_DECL
+ #define G10_MPI_INLINE_DECL extern __inline__
+#endif
+
+G10_MPI_INLINE_DECL mpi_limb_t
+mpihelp_add_1( mpi_ptr_t res_ptr, mpi_ptr_t s1_ptr,
+ mpi_size_t s1_size, mpi_limb_t s2_limb)
+{
+ mpi_limb_t x;
+
+ x = *s1_ptr++;
+ s2_limb += x;
+ *res_ptr++ = s2_limb;
+ if( s2_limb < x ) { /* sum is less than the left operand: handle carry */
+ while( --s1_size ) {
+ x = *s1_ptr++ + 1; /* add carry */
+ *res_ptr++ = x; /* and store */
+ if( x ) /* not 0 (no overflow): we can stop */
+ goto leave;
+ }
+ return 1; /* return carry (size of s1 to small) */
+ }
+
+ leave:
+ if( res_ptr != s1_ptr ) { /* not the same variable */
+ mpi_size_t i; /* copy the rest */
+ for( i=0; i < s1_size-1; i++ )
+ res_ptr[i] = s1_ptr[i];
+ }
+ return 0; /* no carry */
+}
+
+
+
+G10_MPI_INLINE_DECL mpi_limb_t
+mpihelp_add(mpi_ptr_t res_ptr, mpi_ptr_t s1_ptr, mpi_size_t s1_size,
+ mpi_ptr_t s2_ptr, mpi_size_t s2_size)
+{
+ mpi_limb_t cy = 0;
+
+ if( s2_size )
+ cy = mpihelp_add_n( res_ptr, s1_ptr, s2_ptr, s2_size );
+
+ if( s1_size - s2_size )
+ cy = mpihelp_add_1( res_ptr + s2_size, s1_ptr + s2_size,
+ s1_size - s2_size, cy);
+ return cy;
+}
+
+
+G10_MPI_INLINE_DECL mpi_limb_t
+mpihelp_sub_1(mpi_ptr_t res_ptr, mpi_ptr_t s1_ptr,
+ mpi_size_t s1_size, mpi_limb_t s2_limb )
+{
+ mpi_limb_t x;
+
+ x = *s1_ptr++;
+ s2_limb = x - s2_limb;
+ *res_ptr++ = s2_limb;
+ if( s2_limb > x ) {
+ while( --s1_size ) {
+ x = *s1_ptr++;
+ *res_ptr++ = x - 1;
+ if( x )
+ goto leave;
+ }
+ return 1;
+ }
+
+ leave:
+ if( res_ptr != s1_ptr ) {
+ mpi_size_t i;
+ for( i=0; i < s1_size-1; i++ )
+ res_ptr[i] = s1_ptr[i];
+ }
+ return 0;
+}
+
+
+
+G10_MPI_INLINE_DECL mpi_limb_t
+mpihelp_sub( mpi_ptr_t res_ptr, mpi_ptr_t s1_ptr, mpi_size_t s1_size,
+ mpi_ptr_t s2_ptr, mpi_size_t s2_size)
+{
+ mpi_limb_t cy = 0;
+
+ if( s2_size )
+ cy = mpihelp_sub_n(res_ptr, s1_ptr, s2_ptr, s2_size);
+
+ if( s1_size - s2_size )
+ cy = mpihelp_sub_1(res_ptr + s2_size, s1_ptr + s2_size,
+ s1_size - s2_size, cy);
+ return cy;
+}
+
+
+#endif /*G10_MPI_INLINE_H*/
--- /dev/null
+/* mpi-internal.h - Internal to the Multi Precision Integers
+ * Copyright (C) 1994, 1996 Free Software Foundation, Inc.
+ * Copyright (C) 1998, 2000 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG 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.
+ *
+ * GnuPG 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
+ *
+ * Note: This code is heavily based on the GNU MP Library.
+ * Actually it's the same code with only minor changes in the
+ * way the data is stored; this is to support the abstraction
+ * of an optional secure memory allocation which may be used
+ * to avoid revealing of sensitive data due to paging etc.
+ * The GNU MP Library itself is published under the LGPL;
+ * however I decided to publish this code under the plain GPL.
+ */
+
+#ifndef G10_MPI_INTERNAL_H
+#define G10_MPI_INTERNAL_H
+
+#include "gnupg_mpi_mpi.h"
+
+/* If KARATSUBA_THRESHOLD is not already defined, define it to a
+ * value which is good on most machines. */
+
+/* tested 4, 16, 32 and 64, where 16 gave the best performance when
+ * checking a 768 and a 1024 bit ElGamal signature.
+ * (wk 22.12.97) */
+#ifndef KARATSUBA_THRESHOLD
+ #define KARATSUBA_THRESHOLD 16
+#endif
+
+/* The code can't handle KARATSUBA_THRESHOLD smaller than 2. */
+#if KARATSUBA_THRESHOLD < 2
+ #undef KARATSUBA_THRESHOLD
+ #define KARATSUBA_THRESHOLD 2
+#endif
+
+
+typedef mpi_limb_t *mpi_ptr_t; /* pointer to a limb */
+typedef int mpi_size_t; /* (must be a signed type) */
+
+#define ABS(x) (x >= 0 ? x : -x)
+#define MIN(l,o) ((l) < (o) ? (l) : (o))
+#define MAX(h,i) ((h) > (i) ? (h) : (i))
+#define RESIZE_IF_NEEDED(a,b) \
+ do { \
+ if( (a)->alloced < (b) ) \
+ mpi_resize((a), (b)); \
+ } while(0)
+
+/* Copy N limbs from S to D. */
+#define MPN_COPY( d, s, n) \
+ do { \
+ mpi_size_t _i; \
+ for( _i = 0; _i < (n); _i++ ) \
+ (d)[_i] = (s)[_i]; \
+ } while(0)
+
+#define MPN_COPY_INCR( d, s, n) \
+ do { \
+ mpi_size_t _i; \
+ for( _i = 0; _i < (n); _i++ ) \
+ (d)[_i] = (d)[_i]; \
+ } while (0)
+
+#define MPN_COPY_DECR( d, s, n ) \
+ do { \
+ mpi_size_t _i; \
+ for( _i = (n)-1; _i >= 0; _i--) \
+ (d)[_i] = (s)[_i]; \
+ } while(0)
+
+/* Zero N limbs at D */
+#define MPN_ZERO(d, n) \
+ do { \
+ int _i; \
+ for( _i = 0; _i < (n); _i++ ) \
+ (d)[_i] = 0; \
+ } while (0)
+
+#define MPN_NORMALIZE(d, n) \
+ do { \
+ while( (n) > 0 ) { \
+ if( (d)[(n)-1] ) \
+ break; \
+ (n)--; \
+ } \
+ } while(0)
+
+#define MPN_NORMALIZE_NOT_ZERO(d, n) \
+ do { \
+ for(;;) { \
+ if( (d)[(n)-1] ) \
+ break; \
+ (n)--; \
+ } \
+ } while(0)
+
+#define MPN_MUL_N_RECURSE(prodp, up, vp, size, tspace) \
+ do { \
+ if( (size) < KARATSUBA_THRESHOLD ) \
+ mul_n_basecase (prodp, up, vp, size); \
+ else \
+ mul_n (prodp, up, vp, size, tspace); \
+ } while (0);
+
+
+/* Divide the two-limb number in (NH,,NL) by D, with DI being the largest
+ * limb not larger than (2**(2*BITS_PER_MP_LIMB))/D - (2**BITS_PER_MP_LIMB).
+ * If this would yield overflow, DI should be the largest possible number
+ * (i.e., only ones). For correct operation, the most significant bit of D
+ * has to be set. Put the quotient in Q and the remainder in R.
+ */
+#define UDIV_QRNND_PREINV(q, r, nh, nl, d, di) \
+ do { \
+ mpi_limb_t _q, _ql, _r; \
+ mpi_limb_t _xh, _xl; \
+ umul_ppmm (_q, _ql, (nh), (di)); \
+ _q += (nh); /* DI is 2**BITS_PER_MPI_LIMB too small */ \
+ umul_ppmm (_xh, _xl, _q, (d)); \
+ sub_ddmmss (_xh, _r, (nh), (nl), _xh, _xl); \
+ if( _xh ) { \
+ sub_ddmmss (_xh, _r, _xh, _r, 0, (d)); \
+ _q++; \
+ if( _xh) { \
+ sub_ddmmss (_xh, _r, _xh, _r, 0, (d)); \
+ _q++; \
+ } \
+ } \
+ if( _r >= (d) ) { \
+ _r -= (d); \
+ _q++; \
+ } \
+ (r) = _r; \
+ (q) = _q; \
+ } while (0)
+
+
+/*-- mpiutil.c --*/
+#ifdef M_DEBUG
+ #define mpi_alloc_limb_space(n,f) mpi_debug_alloc_limb_space((n),(f), M_DBGINFO( __LINE__ ) )
+ #define mpi_free_limb_space(n) mpi_debug_free_limb_space((n), M_DBGINFO( __LINE__ ) )
+ mpi_ptr_t mpi_debug_alloc_limb_space( unsigned nlimbs, int sec, const char *info );
+ void mpi_debug_free_limb_space( mpi_ptr_t a, const char *info );
+#else
+ mpi_ptr_t mpi_alloc_limb_space( unsigned nlimbs, int sec );
+ void mpi_free_limb_space( mpi_ptr_t a );
+#endif
+void mpi_assign_limb_space( MPI a, mpi_ptr_t ap, unsigned nlimbs );
+
+/*-- mpi-bit.c --*/
+void mpi_rshift_limbs( MPI a, unsigned int count );
+void mpi_lshift_limbs( MPI a, unsigned int count );
+
+
+/*-- mpihelp-add.c --*/
+mpi_limb_t mpihelp_add_1(mpi_ptr_t res_ptr, mpi_ptr_t s1_ptr,
+ mpi_size_t s1_size, mpi_limb_t s2_limb );
+mpi_limb_t mpihelp_add_n( mpi_ptr_t res_ptr, mpi_ptr_t s1_ptr,
+ mpi_ptr_t s2_ptr, mpi_size_t size);
+mpi_limb_t mpihelp_add(mpi_ptr_t res_ptr, mpi_ptr_t s1_ptr, mpi_size_t s1_size,
+ mpi_ptr_t s2_ptr, mpi_size_t s2_size);
+
+/*-- mpihelp-sub.c --*/
+mpi_limb_t mpihelp_sub_1( mpi_ptr_t res_ptr, mpi_ptr_t s1_ptr,
+ mpi_size_t s1_size, mpi_limb_t s2_limb );
+mpi_limb_t mpihelp_sub_n( mpi_ptr_t res_ptr, mpi_ptr_t s1_ptr,
+ mpi_ptr_t s2_ptr, mpi_size_t size);
+mpi_limb_t mpihelp_sub(mpi_ptr_t res_ptr, mpi_ptr_t s1_ptr, mpi_size_t s1_size,
+ mpi_ptr_t s2_ptr, mpi_size_t s2_size);
+
+/*-- mpihelp-cmp.c --*/
+int mpihelp_cmp( mpi_ptr_t op1_ptr, mpi_ptr_t op2_ptr, mpi_size_t size );
+
+/*-- mpihelp-mul.c --*/
+
+struct karatsuba_ctx {
+ struct karatsuba_ctx *next;
+ mpi_ptr_t tspace;
+ mpi_size_t tspace_size;
+ mpi_ptr_t tp;
+ mpi_size_t tp_size;
+};
+
+void mpihelp_release_karatsuba_ctx( struct karatsuba_ctx *ctx );
+
+mpi_limb_t mpihelp_addmul_1( mpi_ptr_t res_ptr, mpi_ptr_t s1_ptr,
+ mpi_size_t s1_size, mpi_limb_t s2_limb);
+mpi_limb_t mpihelp_submul_1( mpi_ptr_t res_ptr, mpi_ptr_t s1_ptr,
+ mpi_size_t s1_size, mpi_limb_t s2_limb);
+void mpihelp_mul_n( mpi_ptr_t prodp, mpi_ptr_t up, mpi_ptr_t vp,
+ mpi_size_t size);
+mpi_limb_t mpihelp_mul( mpi_ptr_t prodp, mpi_ptr_t up, mpi_size_t usize,
+ mpi_ptr_t vp, mpi_size_t vsize);
+void mpih_sqr_n_basecase( mpi_ptr_t prodp, mpi_ptr_t up, mpi_size_t size );
+void mpih_sqr_n( mpi_ptr_t prodp, mpi_ptr_t up, mpi_size_t size,
+ mpi_ptr_t tspace);
+
+void mpihelp_mul_karatsuba_case( mpi_ptr_t prodp,
+ mpi_ptr_t up, mpi_size_t usize,
+ mpi_ptr_t vp, mpi_size_t vsize,
+ struct karatsuba_ctx *ctx );
+
+
+/*-- mpihelp-mul_1.c (or xxx/cpu/ *.S) --*/
+mpi_limb_t mpihelp_mul_1( mpi_ptr_t res_ptr, mpi_ptr_t s1_ptr,
+ mpi_size_t s1_size, mpi_limb_t s2_limb);
+
+/*-- mpihelp-div.c --*/
+mpi_limb_t mpihelp_mod_1(mpi_ptr_t dividend_ptr, mpi_size_t dividend_size,
+ mpi_limb_t divisor_limb);
+mpi_limb_t mpihelp_divrem( mpi_ptr_t qp, mpi_size_t qextra_limbs,
+ mpi_ptr_t np, mpi_size_t nsize,
+ mpi_ptr_t dp, mpi_size_t dsize);
+mpi_limb_t mpihelp_divmod_1( mpi_ptr_t quot_ptr,
+ mpi_ptr_t dividend_ptr, mpi_size_t dividend_size,
+ mpi_limb_t divisor_limb);
+
+/*-- mpihelp-shift.c --*/
+mpi_limb_t mpihelp_lshift( mpi_ptr_t wp, mpi_ptr_t up, mpi_size_t usize,
+ unsigned cnt);
+mpi_limb_t mpihelp_rshift( mpi_ptr_t wp, mpi_ptr_t up, mpi_size_t usize,
+ unsigned cnt);
+
+
+/* Define stuff for longlong.h. */
+#define W_TYPE_SIZE BITS_PER_MPI_LIMB
+ typedef mpi_limb_t UWtype;
+ typedef unsigned int UHWtype;
+#if defined (__GNUC__)
+ typedef unsigned int UQItype __attribute__ ((mode (QI)));
+ typedef int SItype __attribute__ ((mode (SI)));
+ typedef unsigned int USItype __attribute__ ((mode (SI)));
+ typedef int DItype __attribute__ ((mode (DI)));
+ typedef unsigned int UDItype __attribute__ ((mode (DI)));
+#else
+ typedef unsigned char UQItype;
+ typedef long SItype;
+ typedef unsigned long USItype;
+#endif
+
+#ifdef __GNUC__
+ #include "gnupg_mpi_mpi-inline.h"
+#endif
+
+#endif /*G10_MPI_INTERNAL_H*/
--- /dev/null
+/* mpi-inv.c - MPI functions
+ * Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG 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.
+ *
+ * GnuPG 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 "gnupg_mpi_mpi-internal.h"
+
+
+/****************
+ * Calculate the multiplicative inverse X of A mod N
+ * That is: Find the solution x for
+ * 1 = (a*x) mod n
+ */
+void
+mpi_invm( MPI x, MPI a, MPI n )
+{
+ #if 0
+ MPI u, v, u1, u2, u3, v1, v2, v3, q, t1, t2, t3;
+ MPI ta, tb, tc;
+
+ u = mpi_copy(a);
+ v = mpi_copy(n);
+ u1 = mpi_alloc_set_ui(1);
+ u2 = mpi_alloc_set_ui(0);
+ u3 = mpi_copy(u);
+ v1 = mpi_alloc_set_ui(0);
+ v2 = mpi_alloc_set_ui(1);
+ v3 = mpi_copy(v);
+ q = mpi_alloc( mpi_get_nlimbs(u)+1 );
+ t1 = mpi_alloc( mpi_get_nlimbs(u)+1 );
+ t2 = mpi_alloc( mpi_get_nlimbs(u)+1 );
+ t3 = mpi_alloc( mpi_get_nlimbs(u)+1 );
+ while( mpi_cmp_ui( v3, 0 ) ) {
+ mpi_fdiv_q( q, u3, v3 );
+ mpi_mul(t1, v1, q); mpi_mul(t2, v2, q); mpi_mul(t3, v3, q);
+ mpi_sub(t1, u1, t1); mpi_sub(t2, u2, t2); mpi_sub(t3, u3, t3);
+ mpi_set(u1, v1); mpi_set(u2, v2); mpi_set(u3, v3);
+ mpi_set(v1, t1); mpi_set(v2, t2); mpi_set(v3, t3);
+ }
+ /* log_debug("result:\n");
+ log_mpidump("q =", q );
+ log_mpidump("u1=", u1);
+ log_mpidump("u2=", u2);
+ log_mpidump("u3=", u3);
+ log_mpidump("v1=", v1);
+ log_mpidump("v2=", v2); */
+ mpi_set(x, u1);
+
+ mpi_free(u1);
+ mpi_free(u2);
+ mpi_free(u3);
+ mpi_free(v1);
+ mpi_free(v2);
+ mpi_free(v3);
+ mpi_free(q);
+ mpi_free(t1);
+ mpi_free(t2);
+ mpi_free(t3);
+ mpi_free(u);
+ mpi_free(v);
+ #elif 0
+ /* Extended Euclid's algorithm (See TAOPC Vol II, 4.5.2, Alg X)
+ * modified according to Michael Penk's solution for Exercice 35 */
+
+ /* FIXME: we can simplify this in most cases (see Knuth) */
+ MPI u, v, u1, u2, u3, v1, v2, v3, t1, t2, t3;
+ unsigned k;
+ int sign;
+
+ u = mpi_copy(a);
+ v = mpi_copy(n);
+ for(k=0; !mpi_test_bit(u,0) && !mpi_test_bit(v,0); k++ ) {
+ mpi_rshift(u, u, 1);
+ mpi_rshift(v, v, 1);
+ }
+
+
+ u1 = mpi_alloc_set_ui(1);
+ u2 = mpi_alloc_set_ui(0);
+ u3 = mpi_copy(u);
+ v1 = mpi_copy(v); /* !-- used as const 1 */
+ v2 = mpi_alloc( mpi_get_nlimbs(u) ); mpi_sub( v2, u1, u );
+ v3 = mpi_copy(v);
+ if( mpi_test_bit(u, 0) ) { /* u is odd */
+ t1 = mpi_alloc_set_ui(0);
+ t2 = mpi_alloc_set_ui(1); t2->sign = 1;
+ t3 = mpi_copy(v); t3->sign = !t3->sign;
+ goto Y4;
+ }
+ else {
+ t1 = mpi_alloc_set_ui(1);
+ t2 = mpi_alloc_set_ui(0);
+ t3 = mpi_copy(u);
+ }
+ do {
+ do {
+ if( mpi_test_bit(t1, 0) || mpi_test_bit(t2, 0) ) { /* one is odd */
+ mpi_add(t1, t1, v);
+ mpi_sub(t2, t2, u);
+ }
+ mpi_rshift(t1, t1, 1);
+ mpi_rshift(t2, t2, 1);
+ mpi_rshift(t3, t3, 1);
+ Y4:
+ ;
+ } while( !mpi_test_bit( t3, 0 ) ); /* while t3 is even */
+
+ if( !t3->sign ) {
+ mpi_set(u1, t1);
+ mpi_set(u2, t2);
+ mpi_set(u3, t3);
+ }
+ else {
+ mpi_sub(v1, v, t1);
+ sign = u->sign; u->sign = !u->sign;
+ mpi_sub(v2, u, t2);
+ u->sign = sign;
+ sign = t3->sign; t3->sign = !t3->sign;
+ mpi_set(v3, t3);
+ t3->sign = sign;
+ }
+ mpi_sub(t1, u1, v1);
+ mpi_sub(t2, u2, v2);
+ mpi_sub(t3, u3, v3);
+ if( t1->sign ) {
+ mpi_add(t1, t1, v);
+ mpi_sub(t2, t2, u);
+ }
+ } while( mpi_cmp_ui( t3, 0 ) ); /* while t3 != 0 */
+ /* mpi_lshift( u3, k ); */
+ mpi_set(x, u1);
+
+ mpi_free(u1);
+ mpi_free(u2);
+ mpi_free(u3);
+ mpi_free(v1);
+ mpi_free(v2);
+ mpi_free(v3);
+ mpi_free(t1);
+ mpi_free(t2);
+ mpi_free(t3);
+ #else
+ /* Extended Euclid's algorithm (See TAOPC Vol II, 4.5.2, Alg X)
+ * modified according to Michael Penk's solution for Exercice 35
+ * with further enhancement */
+ MPI u, v, u1, u2=NULL, u3, v1, v2=NULL, v3, t1, t2=NULL, t3;
+ unsigned k;
+ int sign;
+ int odd ;
+
+ u = mpi_copy(a);
+ v = mpi_copy(n);
+
+ for(k=0; !mpi_test_bit(u,0) && !mpi_test_bit(v,0); k++ ) {
+ mpi_rshift(u, u, 1);
+ mpi_rshift(v, v, 1);
+ }
+ odd = mpi_test_bit(v,0);
+
+ u1 = mpi_alloc_set_ui(1);
+ if( !odd )
+ u2 = mpi_alloc_set_ui(0);
+ u3 = mpi_copy(u);
+ v1 = mpi_copy(v);
+ if( !odd ) {
+ v2 = mpi_alloc( mpi_get_nlimbs(u) );
+ mpi_sub( v2, u1, u ); /* U is used as const 1 */
+ }
+ v3 = mpi_copy(v);
+ if( mpi_test_bit(u, 0) ) { /* u is odd */
+ t1 = mpi_alloc_set_ui(0);
+ if( !odd ) {
+ t2 = mpi_alloc_set_ui(1); t2->sign = 1;
+ }
+ t3 = mpi_copy(v); t3->sign = !t3->sign;
+ goto Y4;
+ }
+ else {
+ t1 = mpi_alloc_set_ui(1);
+ if( !odd )
+ t2 = mpi_alloc_set_ui(0);
+ t3 = mpi_copy(u);
+ }
+ do {
+ do {
+ if( !odd ) {
+ if( mpi_test_bit(t1, 0) || mpi_test_bit(t2, 0) ) { /* one is odd */
+ mpi_add(t1, t1, v);
+ mpi_sub(t2, t2, u);
+ }
+ mpi_rshift(t1, t1, 1);
+ mpi_rshift(t2, t2, 1);
+ mpi_rshift(t3, t3, 1);
+ }
+ else {
+ if( mpi_test_bit(t1, 0) )
+ mpi_add(t1, t1, v);
+ mpi_rshift(t1, t1, 1);
+ mpi_rshift(t3, t3, 1);
+ }
+ Y4:
+ ;
+ } while( !mpi_test_bit( t3, 0 ) ); /* while t3 is even */
+
+ if( !t3->sign ) {
+ mpi_set(u1, t1);
+ if( !odd )
+ mpi_set(u2, t2);
+ mpi_set(u3, t3);
+ }
+ else {
+ mpi_sub(v1, v, t1);
+ sign = u->sign; u->sign = !u->sign;
+ if( !odd )
+ mpi_sub(v2, u, t2);
+ u->sign = sign;
+ sign = t3->sign; t3->sign = !t3->sign;
+ mpi_set(v3, t3);
+ t3->sign = sign;
+ }
+ mpi_sub(t1, u1, v1);
+ if( !odd )
+ mpi_sub(t2, u2, v2);
+ mpi_sub(t3, u3, v3);
+ if( t1->sign ) {
+ mpi_add(t1, t1, v);
+ if( !odd )
+ mpi_sub(t2, t2, u);
+ }
+ } while( mpi_cmp_ui( t3, 0 ) ); /* while t3 != 0 */
+ /* mpi_lshift( u3, k ); */
+ mpi_set(x, u1);
+
+ mpi_free(u1);
+ mpi_free(v1);
+ mpi_free(t1);
+ if( !odd ) {
+ mpi_free(u2);
+ mpi_free(v2);
+ mpi_free(t2);
+ }
+ mpi_free(u3);
+ mpi_free(v3);
+ mpi_free(t3);
+
+ mpi_free(u);
+ mpi_free(v);
+ #endif
+}
+
+
+
--- /dev/null
+/* mpi-mpow.c - MPI functions
+ * Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG 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.
+ *
+ * GnuPG 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 "gnupg_mpi_mpi-internal.h"
+#include "gnupg_mpi_longlong.h"
+
+
+static int
+build_index( MPI *exparray, int k, int i, int t )
+{
+ int j, bitno;
+ int index = 0;
+
+ bitno = t-i;
+ for(j=k-1; j >= 0; j-- ) {
+ index <<= 1;
+ if( mpi_test_bit( exparray[j], bitno ) )
+ index |= 1;
+ }
+ return index;
+}
+
+/****************
+ * RES = (BASE[0] ^ EXP[0]) * (BASE[1] ^ EXP[1]) * ... * mod M
+ */
+void
+mpi_mulpowm( MPI res, MPI *basearray, MPI *exparray, MPI m)
+{
+ int k; /* number of elements */
+ int t; /* bit size of largest exponent */
+ int i, j, idx;
+ MPI *G; /* table with precomputed values of size 2^k */
+ MPI tmp;
+
+ for(k=0; basearray[k]; k++ )
+ ;
+ assert(k);
+ for(t=0, i=0; (tmp=exparray[i]); i++ ) {
+ j = mpi_get_nbits(tmp);
+ if( j > t )
+ t = j;
+ }
+ assert(i==k);
+ assert(t);
+ assert( k < 10 );
+
+ G = m_alloc_clear( (1<<k) * sizeof *G );
+ /* and calculate */
+ tmp = mpi_alloc( mpi_get_nlimbs(m)+1 );
+ mpi_set_ui( res, 1 );
+ for(i = 1; i <= t; i++ ) {
+ mpi_mulm(tmp, res, res, m );
+ idx = build_index( exparray, k, i, t );
+ assert( idx >= 0 && idx < (1<<k) );
+ if( !G[idx] ) {
+ if( !idx )
+ G[0] = mpi_alloc_set_ui( 1 );
+ else {
+ for(j=0; j < k; j++ ) {
+ if( (idx & (1<<j) ) ) {
+ if( !G[idx] )
+ G[idx] = mpi_copy( basearray[j] );
+ else
+ mpi_mulm( G[idx], G[idx], basearray[j], m );
+ }
+ }
+ if( !G[idx] )
+ G[idx] = mpi_alloc(0);
+ }
+ }
+ mpi_mulm(res, tmp, G[idx], m );
+ }
+
+ /* cleanup */
+ mpi_free(tmp);
+ for(i=0; i < (1<<k); i++ )
+ mpi_free(G[i]);
+ m_free(G);
+}
+
+
--- /dev/null
+/* mpi-mul.c - MPI functions
+ * Copyright (C) 1994, 1996 Free Software Foundation, Inc.
+ * Copyright (C) 1998, 2001 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG 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.
+ *
+ * GnuPG 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
+ *
+ * Note: This code is heavily based on the GNU MP Library.
+ * Actually it's the same code with only minor changes in the
+ * way the data is stored; this is to support the abstraction
+ * of an optional secure memory allocation which may be used
+ * to avoid revealing of sensitive data due to paging etc.
+ * The GNU MP Library itself is published under the LGPL;
+ * however I decided to publish this code under the plain GPL.
+ */
+
+#include "gnupg_mpi_mpi-internal.h"
+
+
+void
+mpi_mul_ui( MPI prod, MPI mult, unsigned long small_mult )
+{
+ mpi_size_t size, prod_size;
+ mpi_ptr_t prod_ptr;
+ mpi_limb_t cy;
+ int sign;
+
+ size = mult->nlimbs;
+ sign = mult->sign;
+
+ if( !size || !small_mult ) {
+ prod->nlimbs = 0;
+ prod->sign = 0;
+ return;
+ }
+
+ prod_size = size + 1;
+ if( prod->alloced < prod_size )
+ mpi_resize( prod, prod_size );
+ prod_ptr = prod->d;
+
+ cy = mpihelp_mul_1( prod_ptr, mult->d, size, (mpi_limb_t)small_mult );
+ if( cy )
+ prod_ptr[size++] = cy;
+ prod->nlimbs = size;
+ prod->sign = sign;
+}
+
+
+void
+mpi_mul_2exp( MPI w, MPI u, unsigned long cnt)
+{
+ mpi_size_t usize, wsize, limb_cnt;
+ mpi_ptr_t wp;
+ mpi_limb_t wlimb;
+ int usign, wsign;
+
+ usize = u->nlimbs;
+ usign = u->sign;
+
+ if( !usize ) {
+ w->nlimbs = 0;
+ w->sign = 0;
+ return;
+ }
+
+ limb_cnt = cnt / BITS_PER_MPI_LIMB;
+ wsize = usize + limb_cnt + 1;
+ if( w->alloced < wsize )
+ mpi_resize(w, wsize );
+ wp = w->d;
+ wsize = usize + limb_cnt;
+ wsign = usign;
+
+ cnt %= BITS_PER_MPI_LIMB;
+ if( cnt ) {
+ wlimb = mpihelp_lshift( wp + limb_cnt, u->d, usize, cnt );
+ if( wlimb ) {
+ wp[wsize] = wlimb;
+ wsize++;
+ }
+ }
+ else {
+ MPN_COPY_DECR( wp + limb_cnt, u->d, usize );
+ }
+
+ /* Zero all whole limbs at low end. Do it here and not before calling
+ * mpn_lshift, not to lose for U == W. */
+ MPN_ZERO( wp, limb_cnt );
+
+ w->nlimbs = wsize;
+ w->sign = wsign;
+}
+
+
+
+void
+mpi_mul( MPI w, MPI u, MPI v)
+{
+ mpi_size_t usize, vsize, wsize;
+ mpi_ptr_t up, vp, wp;
+ mpi_limb_t cy;
+ int usign, vsign, usecure, vsecure, sign_product;
+ int assign_wp=0;
+ mpi_ptr_t tmp_limb=NULL;
+
+
+ if( u->nlimbs < v->nlimbs ) { /* Swap U and V. */
+ usize = v->nlimbs;
+ usign = v->sign;
+ usecure = mpi_is_secure(v);
+ up = v->d;
+ vsize = u->nlimbs;
+ vsign = u->sign;
+ vsecure = mpi_is_secure(u);
+ vp = u->d;
+ }
+ else {
+ usize = u->nlimbs;
+ usign = u->sign;
+ usecure = mpi_is_secure(u);
+ up = u->d;
+ vsize = v->nlimbs;
+ vsign = v->sign;
+ vsecure = mpi_is_secure(v);
+ vp = v->d;
+ }
+ sign_product = usign ^ vsign;
+ wp = w->d;
+
+ /* Ensure W has space enough to store the result. */
+ wsize = usize + vsize;
+ if ( !mpi_is_secure (w) && (mpi_is_secure (u) || mpi_is_secure (v)) ) {
+ /* w is not allocated in secure space but u or v is. To make sure
+ * that no temporray results are stored in w, we temporary use
+ * a newly allocated limb space for w */
+ wp = mpi_alloc_limb_space( wsize, 1 );
+ assign_wp = 2; /* mark it as 2 so that we can later copy it back to
+ * mormal memory */
+ }
+ else if( w->alloced < wsize ) {
+ if( wp == up || wp == vp ) {
+ wp = mpi_alloc_limb_space( wsize, mpi_is_secure(w) );
+ assign_wp = 1;
+ }
+ else {
+ mpi_resize(w, wsize );
+ wp = w->d;
+ }
+ }
+ else { /* Make U and V not overlap with W. */
+ if( wp == up ) {
+ /* W and U are identical. Allocate temporary space for U. */
+ up = tmp_limb = mpi_alloc_limb_space( usize, usecure );
+ /* Is V identical too? Keep it identical with U. */
+ if( wp == vp )
+ vp = up;
+ /* Copy to the temporary space. */
+ MPN_COPY( up, wp, usize );
+ }
+ else if( wp == vp ) {
+ /* W and V are identical. Allocate temporary space for V. */
+ vp = tmp_limb = mpi_alloc_limb_space( vsize, vsecure );
+ /* Copy to the temporary space. */
+ MPN_COPY( vp, wp, vsize );
+ }
+ }
+
+ if( !vsize )
+ wsize = 0;
+ else {
+ cy = mpihelp_mul( wp, up, usize, vp, vsize );
+ wsize -= cy? 0:1;
+ }
+
+ if( assign_wp ) {
+ if (assign_wp == 2) {
+ /* copy the temp wp from secure memory back to normal memory */
+ mpi_ptr_t tmp_wp = mpi_alloc_limb_space (wsize, 0);
+ MPN_COPY (tmp_wp, wp, wsize);
+ mpi_free_limb_space (wp);
+ wp = tmp_wp;
+ }
+ mpi_assign_limb_space( w, wp, wsize );
+ }
+ w->nlimbs = wsize;
+ w->sign = sign_product;
+ if( tmp_limb )
+ mpi_free_limb_space( tmp_limb );
+}
+
+
+void
+mpi_mulm( MPI w, MPI u, MPI v, MPI m)
+{
+ mpi_mul(w, u, v);
+ mpi_fdiv_r( w, w, m );
+}
+
--- /dev/null
+/* mpi-pow.c - MPI functions
+ * Copyright (C) 1994, 1996, 1998, 2000 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG 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.
+ *
+ * GnuPG 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
+ *
+ * Note: This code is heavily based on the GNU MP Library.
+ * Actually it's the same code with only minor changes in the
+ * way the data is stored; this is to support the abstraction
+ * of an optional secure memory allocation which may be used
+ * to avoid revealing of sensitive data due to paging etc.
+ * The GNU MP Library itself is published under the LGPL;
+ * however I decided to publish this code under the plain GPL.
+ */
+
+#include <linux/string.h>
+#include "gnupg_mpi_mpi-internal.h"
+#include "gnupg_mpi_longlong.h"
+
+
+/****************
+ * RES = BASE ^ EXP mod MOD
+ */
+void
+mpi_powm( MPI res, MPI base, MPI exp, MPI mod)
+{
+ mpi_ptr_t rp, ep, mp, bp;
+ mpi_size_t esize, msize, bsize, rsize;
+ int esign, msign, bsign, rsign;
+ int esec, msec, bsec, rsec;
+ mpi_size_t size;
+ int mod_shift_cnt;
+ int negative_result;
+ mpi_ptr_t mp_marker=NULL, bp_marker=NULL, ep_marker=NULL;
+ mpi_ptr_t xp_marker=NULL;
+ int assign_rp=0;
+ mpi_ptr_t tspace = NULL;
+ mpi_size_t tsize=0; /* to avoid compiler warning */
+ /* fixme: we should check that the warning is void*/
+
+ esize = exp->nlimbs;
+ msize = mod->nlimbs;
+ size = 2 * msize;
+ esign = exp->sign;
+ msign = mod->sign;
+
+ esec = mpi_is_secure(exp);
+ msec = mpi_is_secure(mod);
+ bsec = mpi_is_secure(base);
+ rsec = mpi_is_secure(res);
+
+ rp = res->d;
+ ep = exp->d;
+
+ if( !msize )
+ msize = 1 / msize; /* provoke a signal */
+
+ if( !esize ) {
+ /* Exponent is zero, result is 1 mod MOD, i.e., 1 or 0
+ * depending on if MOD equals 1. */
+ rp[0] = 1;
+ res->nlimbs = (msize == 1 && mod->d[0] == 1) ? 0 : 1;
+ res->sign = 0;
+ goto leave;
+ }
+
+ /* Normalize MOD (i.e. make its most significant bit set) as required by
+ * mpn_divrem. This will make the intermediate values in the calculation
+ * slightly larger, but the correct result is obtained after a final
+ * reduction using the original MOD value. */
+ mp = mp_marker = mpi_alloc_limb_space(msize, msec);
+ count_leading_zeros( mod_shift_cnt, mod->d[msize-1] );
+ if( mod_shift_cnt )
+ mpihelp_lshift( mp, mod->d, msize, mod_shift_cnt );
+ else
+ MPN_COPY( mp, mod->d, msize );
+
+ bsize = base->nlimbs;
+ bsign = base->sign;
+ if( bsize > msize ) { /* The base is larger than the module. Reduce it. */
+ /* Allocate (BSIZE + 1) with space for remainder and quotient.
+ * (The quotient is (bsize - msize + 1) limbs.) */
+ bp = bp_marker = mpi_alloc_limb_space( bsize + 1, bsec );
+ MPN_COPY( bp, base->d, bsize );
+ /* We don't care about the quotient, store it above the remainder,
+ * at BP + MSIZE. */
+ mpihelp_divrem( bp + msize, 0, bp, bsize, mp, msize );
+ bsize = msize;
+ /* Canonicalize the base, since we are going to multiply with it
+ * quite a few times. */
+ MPN_NORMALIZE( bp, bsize );
+ }
+ else
+ bp = base->d;
+
+ if( !bsize ) {
+ res->nlimbs = 0;
+ res->sign = 0;
+ goto leave;
+ }
+
+ if( res->alloced < size ) {
+ /* We have to allocate more space for RES. If any of the input
+ * parameters are identical to RES, defer deallocation of the old
+ * space. */
+ if( rp == ep || rp == mp || rp == bp ) {
+ rp = mpi_alloc_limb_space( size, rsec );
+ assign_rp = 1;
+ }
+ else {
+ mpi_resize( res, size );
+ rp = res->d;
+ }
+ }
+ else { /* Make BASE, EXP and MOD not overlap with RES. */
+ if( rp == bp ) {
+ /* RES and BASE are identical. Allocate temp. space for BASE. */
+ assert( !bp_marker );
+ bp = bp_marker = mpi_alloc_limb_space( bsize, bsec );
+ MPN_COPY(bp, rp, bsize);
+ }
+ if( rp == ep ) {
+ /* RES and EXP are identical. Allocate temp. space for EXP. */
+ ep = ep_marker = mpi_alloc_limb_space( esize, esec );
+ MPN_COPY(ep, rp, esize);
+ }
+ if( rp == mp ) {
+ /* RES and MOD are identical. Allocate temporary space for MOD.*/
+ assert( !mp_marker );
+ mp = mp_marker = mpi_alloc_limb_space( msize, msec );
+ MPN_COPY(mp, rp, msize);
+ }
+ }
+
+ MPN_COPY( rp, bp, bsize );
+ rsize = bsize;
+ rsign = bsign;
+
+ {
+ mpi_size_t i;
+ mpi_ptr_t xp = xp_marker = mpi_alloc_limb_space( 2 * (msize + 1), msec );
+ int c;
+ mpi_limb_t e;
+ mpi_limb_t carry_limb;
+ struct karatsuba_ctx karactx;
+
+ memset( &karactx, 0, sizeof karactx );
+ negative_result = (ep[0] & 1) && base->sign;
+
+ i = esize - 1;
+ e = ep[i];
+ count_leading_zeros (c, e);
+ e = (e << c) << 1; /* shift the exp bits to the left, lose msb */
+ c = BITS_PER_MPI_LIMB - 1 - c;
+
+ /* Main loop.
+ *
+ * Make the result be pointed to alternately by XP and RP. This
+ * helps us avoid block copying, which would otherwise be necessary
+ * with the overlap restrictions of mpihelp_divmod. With 50% probability
+ * the result after this loop will be in the area originally pointed
+ * by RP (==RES->d), and with 50% probability in the area originally
+ * pointed to by XP.
+ */
+
+ for(;;) {
+ while( c ) {
+ mpi_ptr_t tp;
+ mpi_size_t xsize;
+
+ /*mpihelp_mul_n(xp, rp, rp, rsize);*/
+ if( rsize < KARATSUBA_THRESHOLD )
+ mpih_sqr_n_basecase( xp, rp, rsize );
+ else {
+ if( !tspace ) {
+ tsize = 2 * rsize;
+ tspace = mpi_alloc_limb_space( tsize, 0 );
+ }
+ else if( tsize < (2*rsize) ) {
+ mpi_free_limb_space( tspace );
+ tsize = 2 * rsize;
+ tspace = mpi_alloc_limb_space( tsize, 0 );
+ }
+ mpih_sqr_n( xp, rp, rsize, tspace );
+ }
+
+ xsize = 2 * rsize;
+ if( xsize > msize ) {
+ mpihelp_divrem(xp + msize, 0, xp, xsize, mp, msize);
+ xsize = msize;
+ }
+
+ tp = rp; rp = xp; xp = tp;
+ rsize = xsize;
+
+ if( (mpi_limb_signed_t)e < 0 ) {
+ /*mpihelp_mul( xp, rp, rsize, bp, bsize );*/
+ if( bsize < KARATSUBA_THRESHOLD ) {
+ mpihelp_mul( xp, rp, rsize, bp, bsize );
+ }
+ else {
+ mpihelp_mul_karatsuba_case(
+ xp, rp, rsize, bp, bsize, &karactx );
+ }
+
+ xsize = rsize + bsize;
+ if( xsize > msize ) {
+ mpihelp_divrem(xp + msize, 0, xp, xsize, mp, msize);
+ xsize = msize;
+ }
+
+ tp = rp; rp = xp; xp = tp;
+ rsize = xsize;
+ }
+ e <<= 1;
+ c--;
+ }
+
+ i--;
+ if( i < 0 )
+ break;
+ e = ep[i];
+ c = BITS_PER_MPI_LIMB;
+ }
+
+ /* We shifted MOD, the modulo reduction argument, left MOD_SHIFT_CNT
+ * steps. Adjust the result by reducing it with the original MOD.
+ *
+ * Also make sure the result is put in RES->d (where it already
+ * might be, see above).
+ */
+ if( mod_shift_cnt ) {
+ carry_limb = mpihelp_lshift( res->d, rp, rsize, mod_shift_cnt);
+ rp = res->d;
+ if( carry_limb ) {
+ rp[rsize] = carry_limb;
+ rsize++;
+ }
+ }
+ else {
+ MPN_COPY( res->d, rp, rsize);
+ rp = res->d;
+ }
+
+ if( rsize >= msize ) {
+ mpihelp_divrem(rp + msize, 0, rp, rsize, mp, msize);
+ rsize = msize;
+ }
+
+ /* Remove any leading zero words from the result. */
+ if( mod_shift_cnt )
+ mpihelp_rshift( rp, rp, rsize, mod_shift_cnt);
+ MPN_NORMALIZE (rp, rsize);
+
+ mpihelp_release_karatsuba_ctx( &karactx );
+ }
+
+ if( negative_result && rsize ) {
+ if( mod_shift_cnt )
+ mpihelp_rshift( mp, mp, msize, mod_shift_cnt);
+ mpihelp_sub( rp, mp, msize, rp, rsize);
+ rsize = msize;
+ rsign = msign;
+ MPN_NORMALIZE(rp, rsize);
+ }
+ res->nlimbs = rsize;
+ res->sign = rsign;
+
+ leave:
+ if( assign_rp ) mpi_assign_limb_space( res, rp, size );
+ if( mp_marker ) mpi_free_limb_space( mp_marker );
+ if( bp_marker ) mpi_free_limb_space( bp_marker );
+ if( ep_marker ) mpi_free_limb_space( ep_marker );
+ if( xp_marker ) mpi_free_limb_space( xp_marker );
+ if( tspace ) mpi_free_limb_space( tspace );
+}
+
--- /dev/null
+/* mpi-scan.c - MPI functions
+ * Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG 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.
+ *
+ * GnuPG 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 "gnupg_mpi_mpi-internal.h"
+#include "gnupg_mpi_longlong.h"
+
+/****************
+ * Scan through an mpi and return byte for byte. a -1 is returned to indicate
+ * the end of the mpi. Scanning is done from the lsb to the msb, returned
+ * values are in the range of 0 .. 255.
+ *
+ * FIXME: This code is VERY ugly!
+ */
+int
+mpi_getbyte( MPI a, unsigned idx )
+{
+ int i, j;
+ unsigned n;
+ mpi_ptr_t ap;
+ mpi_limb_t limb;
+
+ ap = a->d;
+ for(n=0,i=0; i < a->nlimbs; i++ ) {
+ limb = ap[i];
+ for( j=0; j < BYTES_PER_MPI_LIMB; j++, n++ )
+ if( n == idx )
+ return (limb >> j*8) & 0xff;
+ }
+ return -1;
+}
+
+
+/****************
+ * Put a value at position IDX into A. idx counts from lsb to msb
+ */
+void
+mpi_putbyte( MPI a, unsigned idx, int xc )
+{
+ int i, j;
+ unsigned n;
+ mpi_ptr_t ap;
+ mpi_limb_t limb, c;
+
+ c = xc & 0xff;
+ ap = a->d;
+ for(n=0,i=0; i < a->alloced; i++ ) {
+ limb = ap[i];
+ for( j=0; j < BYTES_PER_MPI_LIMB; j++, n++ )
+ if( n == idx ) {
+ #if BYTES_PER_MPI_LIMB == 4
+ if( j == 0 )
+ limb = (limb & 0xffffff00) | c;
+ else if( j == 1 )
+ limb = (limb & 0xffff00ff) | (c<<8);
+ else if( j == 2 )
+ limb = (limb & 0xff00ffff) | (c<<16);
+ else
+ limb = (limb & 0x00ffffff) | (c<<24);
+ #elif BYTES_PER_MPI_LIMB == 8
+ if( j == 0 )
+ limb = (limb & 0xffffffffffffff00) | c;
+ else if( j == 1 )
+ limb = (limb & 0xffffffffffff00ff) | (c<<8);
+ else if( j == 2 )
+ limb = (limb & 0xffffffffff00ffff) | (c<<16);
+ else if( j == 3 )
+ limb = (limb & 0xffffffff00ffffff) | (c<<24);
+ else if( j == 4 )
+ limb = (limb & 0xffffff00ffffffff) | (c<<32);
+ else if( j == 5 )
+ limb = (limb & 0xffff00ffffffffff) | (c<<40);
+ else if( j == 6 )
+ limb = (limb & 0xff00ffffffffffff) | (c<<48);
+ else
+ limb = (limb & 0x00ffffffffffffff) | (c<<56);
+ #else
+ #error please enhance this function, its ugly - i know.
+ #endif
+ if( a->nlimbs <= i )
+ a->nlimbs = i+1;
+ ap[i] = limb;
+ return;
+ }
+ }
+ log_bug("index out of range\n");
+}
+
+
+/****************
+ * Count the number of zerobits at the low end of A
+ */
+unsigned
+mpi_trailing_zeros( MPI a )
+{
+ unsigned n, count = 0;
+
+ for(n=0; n < a->nlimbs; n++ ) {
+ if( a->d[n] ) {
+ unsigned nn;
+ mpi_limb_t alimb = a->d[n];
+
+ count_trailing_zeros( nn, alimb );
+ count += nn;
+ break;
+ }
+ count += BITS_PER_MPI_LIMB;
+ }
+ return count;
+
+}
+
+
--- /dev/null
+/* mpi.h - Multi Precision Integers
+ * Copyright (C) 1994, 1996, 1998, 1999,
+ * 2000, 2001 Free Software Foundation, Inc.
+ *
+ * This file is part of GNUPG.
+ *
+ * GNUPG 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.
+ *
+ * GNUPG 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
+ *
+ * Note: This code is heavily based on the GNU MP Library.
+ * Actually it's the same code with only minor changes in the
+ * way the data is stored; this is to support the abstraction
+ * of an optional secure memory allocation which may be used
+ * to avoid revealing of sensitive data due to paging etc.
+ * The GNU MP Library itself is published under the LGPL;
+ * however I decided to publish this code under the plain GPL.
+ */
+
+#ifndef G10_MPI_H
+#define G10_MPI_H
+
+#include "gnupg_mpi_config.h"
+#include "gnupg_mpi_types.h"
+#include "gnupg_mpi_memory.h"
+
+/* DSI defines */
+
+#define SHA1_DIGEST_LENGTH 20
+
+/*end of DSI defines */
+
+#define log_debug printk
+#define log_bug printk
+
+#define assert(x) do { \
+ if (!x) log_bug("failed assertion\n"); \
+ } while(0);
+
+#if BYTES_PER_MPI_LIMB == SIZEOF_UNSIGNED_INT
+ typedef unsigned int mpi_limb_t;
+ typedef signed int mpi_limb_signed_t;
+#elif BYTES_PER_MPI_LIMB == SIZEOF_UNSIGNED_LONG
+ typedef unsigned long int mpi_limb_t;
+ typedef signed long int mpi_limb_signed_t;
+#elif BYTES_PER_MPI_LIMB == SIZEOF_UNSIGNED_LONG_LONG
+ typedef unsigned long long int mpi_limb_t;
+ typedef signed long long int mpi_limb_signed_t;
+#elif BYTES_PER_MPI_LIMB == SIZEOF_UNSIGNED_SHORT
+ typedef unsigned short int mpi_limb_t;
+ typedef signed short int mpi_limb_signed_t;
+#else
+ #error BYTES_PER_MPI_LIMB does not match any C type
+#endif
+#define BITS_PER_MPI_LIMB (8*BYTES_PER_MPI_LIMB)
+
+#ifndef EXTERN_UNLESS_MAIN_MODULE
+ #if defined (__riscos__) && !defined (INCLUDED_BY_MAIN_MODULE)
+ #define EXTERN_UNLESS_MAIN_MODULE extern
+ #else
+ #define EXTERN_UNLESS_MAIN_MODULE
+ #endif
+#endif
+
+#define DBG_MPI mpi_debug_mode
+EXTERN_UNLESS_MAIN_MODULE int mpi_debug_mode;
+
+
+struct gcry_mpi {
+ int alloced; /* array size (# of allocated limbs) */
+ int nlimbs; /* number of valid limbs */
+ int nbits; /* the real number of valid bits (info only) */
+ int sign; /* indicates a negative number */
+ unsigned flags; /* bit 0: array must be allocated in secure memory space */
+ /* bit 1: not used */
+ /* bit 2: the limb is a pointer to some m_alloced data */
+ mpi_limb_t *d; /* array with the limbs */
+};
+
+typedef struct gcry_mpi *MPI;
+
+#define MPI_NULL NULL
+
+#define mpi_get_nlimbs(a) ((a)->nlimbs)
+#define mpi_is_neg(a) ((a)->sign)
+
+/*-- mpiutil.c --*/
+
+MPI mpi_alloc( unsigned nlimbs );
+MPI mpi_alloc_secure( unsigned nlimbs );
+MPI mpi_alloc_like( MPI a );
+void mpi_free( MPI a );
+void mpi_resize( MPI a, unsigned nlimbs );
+MPI mpi_copy( MPI a );
+#define mpi_is_opaque(a) ((a) && ((a)->flags&4))
+MPI mpi_set_opaque( MPI a, void *p, int len );
+void *mpi_get_opaque( MPI a, int *len );
+#define mpi_is_secure(a) ((a) && ((a)->flags&1))
+void mpi_set_secure( MPI a );
+void mpi_clear( MPI a );
+void mpi_set( MPI w, MPI u);
+void mpi_set_ui( MPI w, ulong u);
+MPI mpi_alloc_set_ui( unsigned long u);
+void mpi_m_check( MPI a );
+void mpi_swap( MPI a, MPI b);
+
+/*-- mpicoder.c --*/
+MPI do_encode_md(const byte *sha_buffer, unsigned nbits);
+MPI mpi_read_from_buffer(byte *buffer, unsigned *ret_nread, int secure);
+int mpi_fromstr(MPI val, const char *str);
+u32 mpi_get_keyid( MPI a, u32 *keyid );
+byte *mpi_get_buffer( MPI a, unsigned *nbytes, int *sign );
+byte *mpi_get_secure_buffer( MPI a, unsigned *nbytes, int *sign );
+void mpi_set_buffer( MPI a, const byte *buffer, unsigned nbytes, int sign );
+
+#define log_mpidump g10_log_mpidump
+
+/*-- mpi-add.c --*/
+void mpi_add_ui(MPI w, MPI u, ulong v );
+void mpi_add(MPI w, MPI u, MPI v);
+void mpi_addm(MPI w, MPI u, MPI v, MPI m);
+void mpi_sub_ui(MPI w, MPI u, ulong v );
+void mpi_sub( MPI w, MPI u, MPI v);
+void mpi_subm( MPI w, MPI u, MPI v, MPI m);
+
+/*-- mpi-mul.c --*/
+void mpi_mul_ui(MPI w, MPI u, ulong v );
+void mpi_mul_2exp( MPI w, MPI u, ulong cnt);
+void mpi_mul( MPI w, MPI u, MPI v);
+void mpi_mulm( MPI w, MPI u, MPI v, MPI m);
+
+/*-- mpi-div.c --*/
+ulong mpi_fdiv_r_ui( MPI rem, MPI dividend, ulong divisor );
+void mpi_fdiv_r( MPI rem, MPI dividend, MPI divisor );
+void mpi_fdiv_q( MPI quot, MPI dividend, MPI divisor );
+void mpi_fdiv_qr( MPI quot, MPI rem, MPI dividend, MPI divisor );
+void mpi_tdiv_r( MPI rem, MPI num, MPI den);
+void mpi_tdiv_qr( MPI quot, MPI rem, MPI num, MPI den);
+void mpi_tdiv_q_2exp( MPI w, MPI u, unsigned count );
+int mpi_divisible_ui(MPI dividend, ulong divisor );
+
+/*-- mpi-gcd.c --*/
+int mpi_gcd( MPI g, MPI a, MPI b );
+
+/*-- mpi-pow.c --*/
+void mpi_pow( MPI w, MPI u, MPI v);
+void mpi_powm( MPI res, MPI base, MPI exp, MPI mod);
+
+/*-- mpi-mpow.c --*/
+void mpi_mulpowm( MPI res, MPI *basearray, MPI *exparray, MPI mod);
+
+/*-- mpi-cmp.c --*/
+int mpi_cmp_ui( MPI u, ulong v );
+int mpi_cmp( MPI u, MPI v );
+
+/*-- mpi-scan.c --*/
+int mpi_getbyte( MPI a, unsigned idx );
+void mpi_putbyte( MPI a, unsigned idx, int value );
+unsigned mpi_trailing_zeros( MPI a );
+
+/*-- mpi-bit.c --*/
+void mpi_normalize( MPI a );
+unsigned mpi_get_nbits( MPI a );
+int mpi_test_bit( MPI a, unsigned n );
+void mpi_set_bit( MPI a, unsigned n );
+void mpi_set_highbit( MPI a, unsigned n );
+void mpi_clear_highbit( MPI a, unsigned n );
+void mpi_clear_bit( MPI a, unsigned n );
+void mpi_rshift( MPI x, MPI a, unsigned n );
+
+/*-- mpi-inv.c --*/
+void mpi_invm( MPI x, MPI u, MPI v );
+
+
+#endif /*G10_MPI_H*/
--- /dev/null
+/* mpicoder.c - Coder for the external representation of MPIs
+ * Copyright (C) 1998, 1999 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG 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.
+ *
+ * GnuPG 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 <linux/string.h>
+#include "gnupg_mpi_mpi.h"
+#include "gnupg_mpi_mpi-internal.h"
+
+#define DIM(v) (sizeof(v)/sizeof((v)[0]))
+#define MAX_EXTERN_MPI_BITS 16384
+
+
+static byte asn[15] = /* Object ID is 1.3.14.3.2.26 */
+ { 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03,
+ 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14 };
+
+
+
+MPI
+do_encode_md(const byte *sha_buffer, unsigned nbits)
+{
+ int nframe = (nbits+7) / 8;
+ byte *frame, *fr_pt;
+ int i = 0, n;
+ size_t asnlen = DIM(asn);
+ MPI a = MPI_NULL;
+
+ if(SHA1_DIGEST_LENGTH + asnlen + 4 > nframe )
+ log_bug("can't encode a %d bit MD into a %d bits frame\n",
+ (int)(SHA1_DIGEST_LENGTH*8), (int)nbits);
+
+ /* We encode the MD in this way:
+ *
+ * 0 A PAD(n bytes) 0 ASN(asnlen bytes) MD(len bytes)
+ *
+ * PAD consists of FF bytes.
+ */
+ frame = (byte*)m_alloc(nframe);
+ n = 0;
+ frame[n++] = 0;
+ frame[n++] = 1; /* block type */
+ i = nframe - SHA1_DIGEST_LENGTH - asnlen -3 ;
+
+ if(i <= 1) {
+ log_bug("message digest encoding failed\n");
+ m_free(frame);
+ return a;
+ }
+
+ memset( frame+n, 0xff, i ); n += i;
+ frame[n++] = 0;
+ memcpy( frame+n, &asn, asnlen ); n += asnlen;
+ memcpy( frame+n, sha_buffer, SHA1_DIGEST_LENGTH ); n += SHA1_DIGEST_LENGTH;
+
+ i = nframe;
+ fr_pt = frame;
+
+ if (n != nframe) {
+ log_bug("message digest encoding failed, frame length is wrong\n");
+ m_free(frame);
+ return a;
+ }
+
+ a = mpi_alloc( (nframe+BYTES_PER_MPI_LIMB-1) / BYTES_PER_MPI_LIMB );
+ mpi_set_buffer( a, frame, nframe, 0 );
+ m_free(frame);
+
+ return a;
+}
+
+
+MPI
+mpi_read_from_buffer(byte *buffer, unsigned *ret_nread, int secure)
+{
+ int i, j;
+ unsigned nbits, nbytes, nlimbs, nread=0;
+ mpi_limb_t a;
+ MPI val = MPI_NULL;
+
+ if( *ret_nread < 2 )
+ goto leave;
+ nbits = buffer[0] << 8 | buffer[1];
+
+ if( nbits > MAX_EXTERN_MPI_BITS ) {
+ log_bug("mpi too large (%u bits)\n", nbits);
+ goto leave;
+ }
+ buffer += 2;
+ nread = 2;
+
+ nbytes = (nbits+7) / 8;
+ nlimbs = (nbytes+BYTES_PER_MPI_LIMB-1) / BYTES_PER_MPI_LIMB;
+ val = secure? mpi_alloc_secure( nlimbs )
+ : mpi_alloc( nlimbs );
+ i = BYTES_PER_MPI_LIMB - nbytes % BYTES_PER_MPI_LIMB;
+ i %= BYTES_PER_MPI_LIMB;
+ val->nbits = nbits;
+ j= val->nlimbs = nlimbs;
+ val->sign = 0;
+ for( ; j > 0; j-- ) {
+ a = 0;
+ for(; i < BYTES_PER_MPI_LIMB; i++ ) {
+ if( ++nread > *ret_nread ) {
+ log_bug("mpi larger than buffer nread=%d ret_nread=%d\n", nread, *ret_nread);
+ goto leave;
+ }
+ a <<= 8;
+ a |= *buffer++;
+ }
+ i = 0;
+ val->d[j-1] = a;
+ }
+
+ leave:
+ *ret_nread = nread;
+ return val;
+}
+
+
+/****************
+ * Make an mpi from a character string.
+ */
+int
+mpi_fromstr(MPI val, const char *str)
+{
+ int hexmode=0, sign=0, prepend_zero=0, i, j, c, c1, c2;
+ unsigned nbits, nbytes, nlimbs;
+ mpi_limb_t a;
+
+ if( *str == '-' ) {
+ sign = 1;
+ str++;
+ }
+ if( *str == '0' && str[1] == 'x' )
+ hexmode = 1;
+ else
+ return 1; /* other bases are not yet supported */
+ str += 2;
+
+ nbits = strlen(str)*4;
+ if( nbits % 8 )
+ prepend_zero = 1;
+ nbytes = (nbits+7) / 8;
+ nlimbs = (nbytes+BYTES_PER_MPI_LIMB-1) / BYTES_PER_MPI_LIMB;
+ if( val->alloced < nlimbs )
+ mpi_resize(val, nlimbs );
+ i = BYTES_PER_MPI_LIMB - nbytes % BYTES_PER_MPI_LIMB;
+ i %= BYTES_PER_MPI_LIMB;
+ j= val->nlimbs = nlimbs;
+ val->sign = sign;
+ for( ; j > 0; j-- ) {
+ a = 0;
+ for(; i < BYTES_PER_MPI_LIMB; i++ ) {
+ if( prepend_zero ) {
+ c1 = '0';
+ prepend_zero = 0;
+ }
+ else
+ c1 = *str++;
+ assert(c1);
+ c2 = *str++;
+ assert(c2);
+ if( c1 >= '0' && c1 <= '9' )
+ c = c1 - '0';
+ else if( c1 >= 'a' && c1 <= 'f' )
+ c = c1 - 'a' + 10;
+ else if( c1 >= 'A' && c1 <= 'F' )
+ c = c1 - 'A' + 10;
+ else {
+ mpi_clear(val);
+ return 1;
+ }
+ c <<= 4;
+ if( c2 >= '0' && c2 <= '9' )
+ c |= c2 - '0';
+ else if( c2 >= 'a' && c2 <= 'f' )
+ c |= c2 - 'a' + 10;
+ else if( c2 >= 'A' && c2 <= 'F' )
+ c |= c2 - 'A' + 10;
+ else {
+ mpi_clear(val);
+ return 1;
+ }
+ a <<= 8;
+ a |= c;
+ }
+ i = 0;
+
+ val->d[j-1] = a;
+ }
+
+ return 0;
+}
+
+
+/****************
+ * Special function to get the low 8 bytes from an mpi.
+ * This can be used as a keyid; KEYID is an 2 element array.
+ * Return the low 4 bytes.
+ */
+u32
+mpi_get_keyid( MPI a, u32 *keyid )
+{
+#if BYTES_PER_MPI_LIMB == 4
+ if( keyid ) {
+ keyid[0] = a->nlimbs >= 2? a->d[1] : 0;
+ keyid[1] = a->nlimbs >= 1? a->d[0] : 0;
+ }
+ return a->nlimbs >= 1? a->d[0] : 0;
+#elif BYTES_PER_MPI_LIMB == 8
+ if( keyid ) {
+ keyid[0] = a->nlimbs? (u32)(a->d[0] >> 32) : 0;
+ keyid[1] = a->nlimbs? (u32)(a->d[0] & 0xffffffff) : 0;
+ }
+ return a->nlimbs? (u32)(a->d[0] & 0xffffffff) : 0;
+#else
+ #error Make this function work with other LIMB sizes
+#endif
+}
+
+
+/****************
+ * Return an m_alloced buffer with the MPI (msb first).
+ * NBYTES receives the length of this buffer. Caller must free the
+ * return string (This function does return a 0 byte buffer with NBYTES
+ * set to zero if the value of A is zero. If sign is not NULL, it will
+ * be set to the sign of the A.
+ */
+static byte *
+do_get_buffer( MPI a, unsigned *nbytes, int *sign, int force_secure )
+{
+ byte *p, *buffer;
+ mpi_limb_t alimb;
+ int i;
+ unsigned int n;
+
+ if( sign )
+ *sign = a->sign;
+ *nbytes = n = a->nlimbs * BYTES_PER_MPI_LIMB;
+ if (!n)
+ n++; /* avoid zero length allocation */
+ p = buffer = force_secure || mpi_is_secure(a) ? m_alloc_secure(n)
+ : m_alloc(n);
+
+ for(i=a->nlimbs-1; i >= 0; i-- ) {
+ alimb = a->d[i];
+#if BYTES_PER_MPI_LIMB == 4
+ *p++ = alimb >> 24;
+ *p++ = alimb >> 16;
+ *p++ = alimb >> 8;
+ *p++ = alimb ;
+#elif BYTES_PER_MPI_LIMB == 8
+ *p++ = alimb >> 56;
+ *p++ = alimb >> 48;
+ *p++ = alimb >> 40;
+ *p++ = alimb >> 32;
+ *p++ = alimb >> 24;
+ *p++ = alimb >> 16;
+ *p++ = alimb >> 8;
+ *p++ = alimb ;
+#else
+#error please implement for this limb size.
+#endif
+ }
+
+ /* this is sub-optimal but we need to do the shift operation
+ * because the caller has to free the returned buffer */
+ for(p=buffer; !*p && *nbytes; p++, --*nbytes )
+ ;
+ if( p != buffer )
+ memmove(buffer,p, *nbytes);
+
+ return buffer;
+}
+
+
+byte *
+mpi_get_buffer( MPI a, unsigned *nbytes, int *sign )
+{
+ return do_get_buffer( a, nbytes, sign, 0 );
+}
+
+byte *
+mpi_get_secure_buffer( MPI a, unsigned *nbytes, int *sign )
+{
+ return do_get_buffer( a, nbytes, sign, 1 );
+}
+
+/****************
+ * Use BUFFER to update MPI.
+ */
+void
+mpi_set_buffer( MPI a, const byte *buffer, unsigned nbytes, int sign )
+{
+ const byte *p;
+ mpi_limb_t alimb;
+ int nlimbs;
+ int i;
+
+ nlimbs = (nbytes + BYTES_PER_MPI_LIMB - 1) / BYTES_PER_MPI_LIMB;
+ RESIZE_IF_NEEDED(a, nlimbs);
+ a->sign = sign;
+
+ for(i=0, p = buffer+nbytes-1; p >= buffer+BYTES_PER_MPI_LIMB; ) {
+ #if BYTES_PER_MPI_LIMB == 4
+ alimb = (mpi_limb_t)*p-- ;
+ alimb |= (mpi_limb_t)*p-- << 8 ;
+ alimb |= (mpi_limb_t)*p-- << 16 ;
+ alimb |= (mpi_limb_t)*p-- << 24 ;
+ #elif BYTES_PER_MPI_LIMB == 8
+ alimb = (mpi_limb_t)*p-- ;
+ alimb |= (mpi_limb_t)*p-- << 8 ;
+ alimb |= (mpi_limb_t)*p-- << 16 ;
+ alimb |= (mpi_limb_t)*p-- << 24 ;
+ alimb |= (mpi_limb_t)*p-- << 32 ;
+ alimb |= (mpi_limb_t)*p-- << 40 ;
+ alimb |= (mpi_limb_t)*p-- << 48 ;
+ alimb |= (mpi_limb_t)*p-- << 56 ;
+ #else
+ #error please implement for this limb size.
+ #endif
+ a->d[i++] = alimb;
+ }
+ if( p >= buffer ) {
+ #if BYTES_PER_MPI_LIMB == 4
+ alimb = *p-- ;
+ if( p >= buffer ) alimb |= (mpi_limb_t)*p-- << 8 ;
+ if( p >= buffer ) alimb |= (mpi_limb_t)*p-- << 16 ;
+ if( p >= buffer ) alimb |= (mpi_limb_t)*p-- << 24 ;
+ #elif BYTES_PER_MPI_LIMB == 8
+ alimb = (mpi_limb_t)*p-- ;
+ if( p >= buffer ) alimb |= (mpi_limb_t)*p-- << 8 ;
+ if( p >= buffer ) alimb |= (mpi_limb_t)*p-- << 16 ;
+ if( p >= buffer ) alimb |= (mpi_limb_t)*p-- << 24 ;
+ if( p >= buffer ) alimb |= (mpi_limb_t)*p-- << 32 ;
+ if( p >= buffer ) alimb |= (mpi_limb_t)*p-- << 40 ;
+ if( p >= buffer ) alimb |= (mpi_limb_t)*p-- << 48 ;
+ if( p >= buffer ) alimb |= (mpi_limb_t)*p-- << 56 ;
+ #else
+ #error please implement for this limb size.
+ #endif
+ a->d[i++] = alimb;
+ }
+ a->nlimbs = i;
+ assert( i == nlimbs );
+}
+
--- /dev/null
+/* mpihelp-sub.c - MPI helper functions
+ * Copyright (C) 1994, 1996 Free Software Foundation, Inc.
+ * Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG 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.
+ *
+ * GnuPG 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
+ *
+ * Note: This code is heavily based on the GNU MP Library.
+ * Actually it's the same code with only minor changes in the
+ * way the data is stored; this is to support the abstraction
+ * of an optional secure memory allocation which may be used
+ * to avoid revealing of sensitive data due to paging etc.
+ * The GNU MP Library itself is published under the LGPL;
+ * however I decided to publish this code under the plain GPL.
+ */
+
+#include "gnupg_mpi_mpi-internal.h"
+
+/****************
+ * Compare OP1_PTR/OP1_SIZE with OP2_PTR/OP2_SIZE.
+ * There are no restrictions on the relative sizes of
+ * the two arguments.
+ * Return 1 if OP1 > OP2, 0 if they are equal, and -1 if OP1 < OP2.
+ */
+int
+mpihelp_cmp( mpi_ptr_t op1_ptr, mpi_ptr_t op2_ptr, mpi_size_t size )
+{
+ mpi_size_t i;
+ mpi_limb_t op1_word, op2_word;
+
+ for( i = size - 1; i >= 0 ; i--) {
+ op1_word = op1_ptr[i];
+ op2_word = op2_ptr[i];
+ if( op1_word != op2_word )
+ goto diff;
+ }
+ return 0;
+
+ diff:
+ /* This can *not* be simplified to
+ * op2_word - op2_word
+ * since that expression might give signed overflow. */
+ return (op1_word > op2_word) ? 1 : -1;
+}
+
--- /dev/null
+/* mpihelp-div.c - MPI helper functions
+ * Copyright (C) 1994, 1996 Free Software Foundation, Inc.
+ * Copyright (C) 1998, 1999 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG 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.
+ *
+ * GnuPG 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
+ *
+ * Note: This code is heavily based on the GNU MP Library.
+ * Actually it's the same code with only minor changes in the
+ * way the data is stored; this is to support the abstraction
+ * of an optional secure memory allocation which may be used
+ * to avoid revealing of sensitive data due to paging etc.
+ * The GNU MP Library itself is published under the LGPL;
+ * however I decided to publish this code under the plain GPL.
+ */
+
+#include "gnupg_mpi_mpi-internal.h"
+#include "gnupg_mpi_longlong.h"
+
+#ifndef UMUL_TIME
+ #define UMUL_TIME 1
+#endif
+#ifndef UDIV_TIME
+ #define UDIV_TIME UMUL_TIME
+#endif
+
+/* FIXME: We should be using invert_limb (or invert_normalized_limb)
+ * here (not udiv_qrnnd).
+ */
+
+mpi_limb_t
+mpihelp_mod_1(mpi_ptr_t dividend_ptr, mpi_size_t dividend_size,
+ mpi_limb_t divisor_limb)
+{
+ mpi_size_t i;
+ mpi_limb_t n1, n0, r;
+ int dummy;
+
+ /* Botch: Should this be handled at all? Rely on callers? */
+ if( !dividend_size )
+ return 0;
+
+ /* If multiplication is much faster than division, and the
+ * dividend is large, pre-invert the divisor, and use
+ * only multiplications in the inner loop.
+ *
+ * This test should be read:
+ * Does it ever help to use udiv_qrnnd_preinv?
+ * && Does what we save compensate for the inversion overhead?
+ */
+ if( UDIV_TIME > (2 * UMUL_TIME + 6)
+ && (UDIV_TIME - (2 * UMUL_TIME + 6)) * dividend_size > UDIV_TIME ) {
+ int normalization_steps;
+
+ count_leading_zeros( normalization_steps, divisor_limb );
+ if( normalization_steps ) {
+ mpi_limb_t divisor_limb_inverted;
+
+ divisor_limb <<= normalization_steps;
+
+ /* Compute (2**2N - 2**N * DIVISOR_LIMB) / DIVISOR_LIMB. The
+ * result is a (N+1)-bit approximation to 1/DIVISOR_LIMB, with the
+ * most significant bit (with weight 2**N) implicit.
+ *
+ * Special case for DIVISOR_LIMB == 100...000.
+ */
+ if( !(divisor_limb << 1) )
+ divisor_limb_inverted = ~(mpi_limb_t)0;
+ else
+ udiv_qrnnd(divisor_limb_inverted, dummy,
+ -divisor_limb, 0, divisor_limb);
+
+ n1 = dividend_ptr[dividend_size - 1];
+ r = n1 >> (BITS_PER_MPI_LIMB - normalization_steps);
+
+ /* Possible optimization:
+ * if (r == 0
+ * && divisor_limb > ((n1 << normalization_steps)
+ * | (dividend_ptr[dividend_size - 2] >> ...)))
+ * ...one division less...
+ */
+ for( i = dividend_size - 2; i >= 0; i--) {
+ n0 = dividend_ptr[i];
+ UDIV_QRNND_PREINV(dummy, r, r,
+ ((n1 << normalization_steps)
+ | (n0 >> (BITS_PER_MPI_LIMB - normalization_steps))),
+ divisor_limb, divisor_limb_inverted);
+ n1 = n0;
+ }
+ UDIV_QRNND_PREINV(dummy, r, r,
+ n1 << normalization_steps,
+ divisor_limb, divisor_limb_inverted);
+ return r >> normalization_steps;
+ }
+ else {
+ mpi_limb_t divisor_limb_inverted;
+
+ /* Compute (2**2N - 2**N * DIVISOR_LIMB) / DIVISOR_LIMB. The
+ * result is a (N+1)-bit approximation to 1/DIVISOR_LIMB, with the
+ * most significant bit (with weight 2**N) implicit.
+ *
+ * Special case for DIVISOR_LIMB == 100...000.
+ */
+ if( !(divisor_limb << 1) )
+ divisor_limb_inverted = ~(mpi_limb_t)0;
+ else
+ udiv_qrnnd(divisor_limb_inverted, dummy,
+ -divisor_limb, 0, divisor_limb);
+
+ i = dividend_size - 1;
+ r = dividend_ptr[i];
+
+ if( r >= divisor_limb )
+ r = 0;
+ else
+ i--;
+
+ for( ; i >= 0; i--) {
+ n0 = dividend_ptr[i];
+ UDIV_QRNND_PREINV(dummy, r, r,
+ n0, divisor_limb, divisor_limb_inverted);
+ }
+ return r;
+ }
+ }
+ else {
+ if( UDIV_NEEDS_NORMALIZATION ) {
+ int normalization_steps;
+
+ count_leading_zeros(normalization_steps, divisor_limb);
+ if( normalization_steps ) {
+ divisor_limb <<= normalization_steps;
+
+ n1 = dividend_ptr[dividend_size - 1];
+ r = n1 >> (BITS_PER_MPI_LIMB - normalization_steps);
+
+ /* Possible optimization:
+ * if (r == 0
+ * && divisor_limb > ((n1 << normalization_steps)
+ * | (dividend_ptr[dividend_size - 2] >> ...)))
+ * ...one division less...
+ */
+ for(i = dividend_size - 2; i >= 0; i--) {
+ n0 = dividend_ptr[i];
+ udiv_qrnnd (dummy, r, r,
+ ((n1 << normalization_steps)
+ | (n0 >> (BITS_PER_MPI_LIMB - normalization_steps))),
+ divisor_limb);
+ n1 = n0;
+ }
+ udiv_qrnnd (dummy, r, r,
+ n1 << normalization_steps,
+ divisor_limb);
+ return r >> normalization_steps;
+ }
+ }
+ /* No normalization needed, either because udiv_qrnnd doesn't require
+ * it, or because DIVISOR_LIMB is already normalized. */
+ i = dividend_size - 1;
+ r = dividend_ptr[i];
+
+ if(r >= divisor_limb)
+ r = 0;
+ else
+ i--;
+
+ for(; i >= 0; i--) {
+ n0 = dividend_ptr[i];
+ udiv_qrnnd (dummy, r, r, n0, divisor_limb);
+ }
+ return r;
+ }
+}
+
+/* Divide num (NP/NSIZE) by den (DP/DSIZE) and write
+ * the NSIZE-DSIZE least significant quotient limbs at QP
+ * and the DSIZE long remainder at NP. If QEXTRA_LIMBS is
+ * non-zero, generate that many fraction bits and append them after the
+ * other quotient limbs.
+ * Return the most significant limb of the quotient, this is always 0 or 1.
+ *
+ * Preconditions:
+ * 0. NSIZE >= DSIZE.
+ * 1. The most significant bit of the divisor must be set.
+ * 2. QP must either not overlap with the input operands at all, or
+ * QP + DSIZE >= NP must hold true. (This means that it's
+ * possible to put the quotient in the high part of NUM, right after the
+ * remainder in NUM.
+ * 3. NSIZE >= DSIZE, even if QEXTRA_LIMBS is non-zero.
+ */
+
+mpi_limb_t
+mpihelp_divrem( mpi_ptr_t qp, mpi_size_t qextra_limbs,
+ mpi_ptr_t np, mpi_size_t nsize,
+ mpi_ptr_t dp, mpi_size_t dsize)
+{
+ mpi_limb_t most_significant_q_limb = 0;
+
+ switch(dsize) {
+ case 0:
+ /* We are asked to divide by zero, so go ahead and do it! (To make
+ the compiler not remove this statement, return the value.) */
+ return 1 / dsize;
+
+ case 1:
+ {
+ mpi_size_t i;
+ mpi_limb_t n1;
+ mpi_limb_t d;
+
+ d = dp[0];
+ n1 = np[nsize - 1];
+
+ if( n1 >= d ) {
+ n1 -= d;
+ most_significant_q_limb = 1;
+ }
+
+ qp += qextra_limbs;
+ for( i = nsize - 2; i >= 0; i--)
+ udiv_qrnnd( qp[i], n1, n1, np[i], d );
+ qp -= qextra_limbs;
+
+ for( i = qextra_limbs - 1; i >= 0; i-- )
+ udiv_qrnnd (qp[i], n1, n1, 0, d);
+
+ np[0] = n1;
+ }
+ break;
+
+ case 2:
+ {
+ mpi_size_t i;
+ mpi_limb_t n1, n0, n2;
+ mpi_limb_t d1, d0;
+
+ np += nsize - 2;
+ d1 = dp[1];
+ d0 = dp[0];
+ n1 = np[1];
+ n0 = np[0];
+
+ if( n1 >= d1 && (n1 > d1 || n0 >= d0) ) {
+ sub_ddmmss (n1, n0, n1, n0, d1, d0);
+ most_significant_q_limb = 1;
+ }
+
+ for( i = qextra_limbs + nsize - 2 - 1; i >= 0; i-- ) {
+ mpi_limb_t q;
+ mpi_limb_t r;
+
+ if( i >= qextra_limbs )
+ np--;
+ else
+ np[0] = 0;
+
+ if( n1 == d1 ) {
+ /* Q should be either 111..111 or 111..110. Need special
+ * treatment of this rare case as normal division would
+ * give overflow. */
+ q = ~(mpi_limb_t)0;
+
+ r = n0 + d1;
+ if( r < d1 ) { /* Carry in the addition? */
+ add_ssaaaa( n1, n0, r - d0, np[0], 0, d0 );
+ qp[i] = q;
+ continue;
+ }
+ n1 = d0 - (d0 != 0?1:0);
+ n0 = -d0;
+ }
+ else {
+ udiv_qrnnd (q, r, n1, n0, d1);
+ umul_ppmm (n1, n0, d0, q);
+ }
+
+ n2 = np[0];
+ q_test:
+ if( n1 > r || (n1 == r && n0 > n2) ) {
+ /* The estimated Q was too large. */
+ q--;
+ sub_ddmmss (n1, n0, n1, n0, 0, d0);
+ r += d1;
+ if( r >= d1 ) /* If not carry, test Q again. */
+ goto q_test;
+ }
+
+ qp[i] = q;
+ sub_ddmmss (n1, n0, r, n2, n1, n0);
+ }
+ np[1] = n1;
+ np[0] = n0;
+ }
+ break;
+
+ default:
+ {
+ mpi_size_t i;
+ mpi_limb_t dX, d1, n0;
+
+ np += nsize - dsize;
+ dX = dp[dsize - 1];
+ d1 = dp[dsize - 2];
+ n0 = np[dsize - 1];
+
+ if( n0 >= dX ) {
+ if(n0 > dX || mpihelp_cmp(np, dp, dsize - 1) >= 0 ) {
+ mpihelp_sub_n(np, np, dp, dsize);
+ n0 = np[dsize - 1];
+ most_significant_q_limb = 1;
+ }
+ }
+
+ for( i = qextra_limbs + nsize - dsize - 1; i >= 0; i--) {
+ mpi_limb_t q;
+ mpi_limb_t n1, n2;
+ mpi_limb_t cy_limb;
+
+ if( i >= qextra_limbs ) {
+ np--;
+ n2 = np[dsize];
+ }
+ else {
+ n2 = np[dsize - 1];
+ MPN_COPY_DECR (np + 1, np, dsize - 1);
+ np[0] = 0;
+ }
+
+ if( n0 == dX ) {
+ /* This might over-estimate q, but it's probably not worth
+ * the extra code here to find out. */
+ q = ~(mpi_limb_t)0;
+ }
+ else {
+ mpi_limb_t r;
+
+ udiv_qrnnd(q, r, n0, np[dsize - 1], dX);
+ umul_ppmm(n1, n0, d1, q);
+
+ while( n1 > r || (n1 == r && n0 > np[dsize - 2])) {
+ q--;
+ r += dX;
+ if( r < dX ) /* I.e. "carry in previous addition?" */
+ break;
+ n1 -= n0 < d1;
+ n0 -= d1;
+ }
+ }
+
+ /* Possible optimization: We already have (q * n0) and (1 * n1)
+ * after the calculation of q. Taking advantage of that, we
+ * could make this loop make two iterations less. */
+ cy_limb = mpihelp_submul_1(np, dp, dsize, q);
+
+ if( n2 != cy_limb ) {
+ mpihelp_add_n(np, np, dp, dsize);
+ q--;
+ }
+
+ qp[i] = q;
+ n0 = np[dsize - 1];
+ }
+ }
+ }
+
+ return most_significant_q_limb;
+}
+
+
+/****************
+ * Divide (DIVIDEND_PTR,,DIVIDEND_SIZE) by DIVISOR_LIMB.
+ * Write DIVIDEND_SIZE limbs of quotient at QUOT_PTR.
+ * Return the single-limb remainder.
+ * There are no constraints on the value of the divisor.
+ *
+ * QUOT_PTR and DIVIDEND_PTR might point to the same limb.
+ */
+
+mpi_limb_t
+mpihelp_divmod_1( mpi_ptr_t quot_ptr,
+ mpi_ptr_t dividend_ptr, mpi_size_t dividend_size,
+ mpi_limb_t divisor_limb)
+{
+ mpi_size_t i;
+ mpi_limb_t n1, n0, r;
+ int dummy;
+
+ if( !dividend_size )
+ return 0;
+
+ /* If multiplication is much faster than division, and the
+ * dividend is large, pre-invert the divisor, and use
+ * only multiplications in the inner loop.
+ *
+ * This test should be read:
+ * Does it ever help to use udiv_qrnnd_preinv?
+ * && Does what we save compensate for the inversion overhead?
+ */
+ if( UDIV_TIME > (2 * UMUL_TIME + 6)
+ && (UDIV_TIME - (2 * UMUL_TIME + 6)) * dividend_size > UDIV_TIME ) {
+ int normalization_steps;
+
+ count_leading_zeros( normalization_steps, divisor_limb );
+ if( normalization_steps ) {
+ mpi_limb_t divisor_limb_inverted;
+
+ divisor_limb <<= normalization_steps;
+
+ /* Compute (2**2N - 2**N * DIVISOR_LIMB) / DIVISOR_LIMB. The
+ * result is a (N+1)-bit approximation to 1/DIVISOR_LIMB, with the
+ * most significant bit (with weight 2**N) implicit.
+ */
+ /* Special case for DIVISOR_LIMB == 100...000. */
+ if( !(divisor_limb << 1) )
+ divisor_limb_inverted = ~(mpi_limb_t)0;
+ else
+ udiv_qrnnd(divisor_limb_inverted, dummy,
+ -divisor_limb, 0, divisor_limb);
+
+ n1 = dividend_ptr[dividend_size - 1];
+ r = n1 >> (BITS_PER_MPI_LIMB - normalization_steps);
+
+ /* Possible optimization:
+ * if (r == 0
+ * && divisor_limb > ((n1 << normalization_steps)
+ * | (dividend_ptr[dividend_size - 2] >> ...)))
+ * ...one division less...
+ */
+ for( i = dividend_size - 2; i >= 0; i--) {
+ n0 = dividend_ptr[i];
+ UDIV_QRNND_PREINV( quot_ptr[i + 1], r, r,
+ ((n1 << normalization_steps)
+ | (n0 >> (BITS_PER_MPI_LIMB - normalization_steps))),
+ divisor_limb, divisor_limb_inverted);
+ n1 = n0;
+ }
+ UDIV_QRNND_PREINV( quot_ptr[0], r, r,
+ n1 << normalization_steps,
+ divisor_limb, divisor_limb_inverted);
+ return r >> normalization_steps;
+ }
+ else {
+ mpi_limb_t divisor_limb_inverted;
+
+ /* Compute (2**2N - 2**N * DIVISOR_LIMB) / DIVISOR_LIMB. The
+ * result is a (N+1)-bit approximation to 1/DIVISOR_LIMB, with the
+ * most significant bit (with weight 2**N) implicit.
+ */
+ /* Special case for DIVISOR_LIMB == 100...000. */
+ if( !(divisor_limb << 1) )
+ divisor_limb_inverted = ~(mpi_limb_t) 0;
+ else
+ udiv_qrnnd(divisor_limb_inverted, dummy,
+ -divisor_limb, 0, divisor_limb);
+
+ i = dividend_size - 1;
+ r = dividend_ptr[i];
+
+ if( r >= divisor_limb )
+ r = 0;
+ else
+ quot_ptr[i--] = 0;
+
+ for( ; i >= 0; i-- ) {
+ n0 = dividend_ptr[i];
+ UDIV_QRNND_PREINV( quot_ptr[i], r, r,
+ n0, divisor_limb, divisor_limb_inverted);
+ }
+ return r;
+ }
+ }
+ else {
+ if(UDIV_NEEDS_NORMALIZATION) {
+ int normalization_steps;
+
+ count_leading_zeros (normalization_steps, divisor_limb);
+ if( normalization_steps ) {
+ divisor_limb <<= normalization_steps;
+
+ n1 = dividend_ptr[dividend_size - 1];
+ r = n1 >> (BITS_PER_MPI_LIMB - normalization_steps);
+
+ /* Possible optimization:
+ * if (r == 0
+ * && divisor_limb > ((n1 << normalization_steps)
+ * | (dividend_ptr[dividend_size - 2] >> ...)))
+ * ...one division less...
+ */
+ for( i = dividend_size - 2; i >= 0; i--) {
+ n0 = dividend_ptr[i];
+ udiv_qrnnd (quot_ptr[i + 1], r, r,
+ ((n1 << normalization_steps)
+ | (n0 >> (BITS_PER_MPI_LIMB - normalization_steps))),
+ divisor_limb);
+ n1 = n0;
+ }
+ udiv_qrnnd (quot_ptr[0], r, r,
+ n1 << normalization_steps,
+ divisor_limb);
+ return r >> normalization_steps;
+ }
+ }
+ /* No normalization needed, either because udiv_qrnnd doesn't require
+ * it, or because DIVISOR_LIMB is already normalized. */
+ i = dividend_size - 1;
+ r = dividend_ptr[i];
+
+ if(r >= divisor_limb)
+ r = 0;
+ else
+ quot_ptr[i--] = 0;
+
+ for(; i >= 0; i--) {
+ n0 = dividend_ptr[i];
+ udiv_qrnnd( quot_ptr[i], r, r, n0, divisor_limb );
+ }
+ return r;
+ }
+}
+
+
--- /dev/null
+/* mpihelp-mul.c - MPI helper functions
+ * Copyright (C) 1994, 1996, 1998, 1999,
+ * 2000 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG 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.
+ *
+ * GnuPG 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
+ *
+ * Note: This code is heavily based on the GNU MP Library.
+ * Actually it's the same code with only minor changes in the
+ * way the data is stored; this is to support the abstraction
+ * of an optional secure memory allocation which may be used
+ * to avoid revealing of sensitive data due to paging etc.
+ * The GNU MP Library itself is published under the LGPL;
+ * however I decided to publish this code under the plain GPL.
+ */
+
+#include <linux/string.h>
+#include "gnupg_mpi_mpi-internal.h"
+#include "gnupg_mpi_longlong.h"
+
+
+
+#define MPN_MUL_N_RECURSE(prodp, up, vp, size, tspace) \
+ do { \
+ if( (size) < KARATSUBA_THRESHOLD ) \
+ mul_n_basecase (prodp, up, vp, size); \
+ else \
+ mul_n (prodp, up, vp, size, tspace); \
+ } while (0);
+
+#define MPN_SQR_N_RECURSE(prodp, up, size, tspace) \
+ do { \
+ if ((size) < KARATSUBA_THRESHOLD) \
+ mpih_sqr_n_basecase (prodp, up, size); \
+ else \
+ mpih_sqr_n (prodp, up, size, tspace); \
+ } while (0);
+
+
+
+
+/* Multiply the natural numbers u (pointed to by UP) and v (pointed to by VP),
+ * both with SIZE limbs, and store the result at PRODP. 2 * SIZE limbs are
+ * always stored. Return the most significant limb.
+ *
+ * Argument constraints:
+ * 1. PRODP != UP and PRODP != VP, i.e. the destination
+ * must be distinct from the multiplier and the multiplicand.
+ *
+ *
+ * Handle simple cases with traditional multiplication.
+ *
+ * This is the most critical code of multiplication. All multiplies rely
+ * on this, both small and huge. Small ones arrive here immediately. Huge
+ * ones arrive here as this is the base case for Karatsuba's recursive
+ * algorithm below.
+ */
+
+static mpi_limb_t
+mul_n_basecase( mpi_ptr_t prodp, mpi_ptr_t up,
+ mpi_ptr_t vp, mpi_size_t size)
+{
+ mpi_size_t i;
+ mpi_limb_t cy;
+ mpi_limb_t v_limb;
+
+ /* Multiply by the first limb in V separately, as the result can be
+ * stored (not added) to PROD. We also avoid a loop for zeroing. */
+ v_limb = vp[0];
+ if( v_limb <= 1 ) {
+ if( v_limb == 1 )
+ MPN_COPY( prodp, up, size );
+ else
+ MPN_ZERO( prodp, size );
+ cy = 0;
+ }
+ else
+ cy = mpihelp_mul_1( prodp, up, size, v_limb );
+
+ prodp[size] = cy;
+ prodp++;
+
+ /* For each iteration in the outer loop, multiply one limb from
+ * U with one limb from V, and add it to PROD. */
+ for( i = 1; i < size; i++ ) {
+ v_limb = vp[i];
+ if( v_limb <= 1 ) {
+ cy = 0;
+ if( v_limb == 1 )
+ cy = mpihelp_add_n(prodp, prodp, up, size);
+ }
+ else
+ cy = mpihelp_addmul_1(prodp, up, size, v_limb);
+
+ prodp[size] = cy;
+ prodp++;
+ }
+
+ return cy;
+}
+
+
+static void
+mul_n( mpi_ptr_t prodp, mpi_ptr_t up, mpi_ptr_t vp,
+ mpi_size_t size, mpi_ptr_t tspace )
+{
+ if( size & 1 ) {
+ /* The size is odd, and the code below doesn't handle that.
+ * Multiply the least significant (size - 1) limbs with a recursive
+ * call, and handle the most significant limb of S1 and S2
+ * separately.
+ * A slightly faster way to do this would be to make the Karatsuba
+ * code below behave as if the size were even, and let it check for
+ * odd size in the end. I.e., in essence move this code to the end.
+ * Doing so would save us a recursive call, and potentially make the
+ * stack grow a lot less.
+ */
+ mpi_size_t esize = size - 1; /* even size */
+ mpi_limb_t cy_limb;
+
+ MPN_MUL_N_RECURSE( prodp, up, vp, esize, tspace );
+ cy_limb = mpihelp_addmul_1( prodp + esize, up, esize, vp[esize] );
+ prodp[esize + esize] = cy_limb;
+ cy_limb = mpihelp_addmul_1( prodp + esize, vp, size, up[esize] );
+ prodp[esize + size] = cy_limb;
+ }
+ else {
+ /* Anatolij Alekseevich Karatsuba's divide-and-conquer algorithm.
+ *
+ * Split U in two pieces, U1 and U0, such that
+ * U = U0 + U1*(B**n),
+ * and V in V1 and V0, such that
+ * V = V0 + V1*(B**n).
+ *
+ * UV is then computed recursively using the identity
+ *
+ * 2n n n n
+ * UV = (B + B )U V + B (U -U )(V -V ) + (B + 1)U V
+ * 1 1 1 0 0 1 0 0
+ *
+ * Where B = 2**BITS_PER_MP_LIMB.
+ */
+ mpi_size_t hsize = size >> 1;
+ mpi_limb_t cy;
+ int negflg;
+
+ /* Product H. ________________ ________________
+ * |_____U1 x V1____||____U0 x V0_____|
+ * Put result in upper part of PROD and pass low part of TSPACE
+ * as new TSPACE.
+ */
+ MPN_MUL_N_RECURSE(prodp + size, up + hsize, vp + hsize, hsize, tspace);
+
+ /* Product M. ________________
+ * |_(U1-U0)(V0-V1)_|
+ */
+ if( mpihelp_cmp(up + hsize, up, hsize) >= 0 ) {
+ mpihelp_sub_n(prodp, up + hsize, up, hsize);
+ negflg = 0;
+ }
+ else {
+ mpihelp_sub_n(prodp, up, up + hsize, hsize);
+ negflg = 1;
+ }
+ if( mpihelp_cmp(vp + hsize, vp, hsize) >= 0 ) {
+ mpihelp_sub_n(prodp + hsize, vp + hsize, vp, hsize);
+ negflg ^= 1;
+ }
+ else {
+ mpihelp_sub_n(prodp + hsize, vp, vp + hsize, hsize);
+ /* No change of NEGFLG. */
+ }
+ /* Read temporary operands from low part of PROD.
+ * Put result in low part of TSPACE using upper part of TSPACE
+ * as new TSPACE.
+ */
+ MPN_MUL_N_RECURSE(tspace, prodp, prodp + hsize, hsize, tspace + size);
+
+ /* Add/copy product H. */
+ MPN_COPY (prodp + hsize, prodp + size, hsize);
+ cy = mpihelp_add_n( prodp + size, prodp + size,
+ prodp + size + hsize, hsize);
+
+ /* Add product M (if NEGFLG M is a negative number) */
+ if(negflg)
+ cy -= mpihelp_sub_n(prodp + hsize, prodp + hsize, tspace, size);
+ else
+ cy += mpihelp_add_n(prodp + hsize, prodp + hsize, tspace, size);
+
+ /* Product L. ________________ ________________
+ * |________________||____U0 x V0_____|
+ * Read temporary operands from low part of PROD.
+ * Put result in low part of TSPACE using upper part of TSPACE
+ * as new TSPACE.
+ */
+ MPN_MUL_N_RECURSE(tspace, up, vp, hsize, tspace + size);
+
+ /* Add/copy Product L (twice) */
+
+ cy += mpihelp_add_n(prodp + hsize, prodp + hsize, tspace, size);
+ if( cy )
+ mpihelp_add_1(prodp + hsize + size, prodp + hsize + size, hsize, cy);
+
+ MPN_COPY(prodp, tspace, hsize);
+ cy = mpihelp_add_n(prodp + hsize, prodp + hsize, tspace + hsize, hsize);
+ if( cy )
+ mpihelp_add_1(prodp + size, prodp + size, size, 1);
+ }
+}
+
+
+void
+mpih_sqr_n_basecase( mpi_ptr_t prodp, mpi_ptr_t up, mpi_size_t size )
+{
+ mpi_size_t i;
+ mpi_limb_t cy_limb;
+ mpi_limb_t v_limb;
+
+ /* Multiply by the first limb in V separately, as the result can be
+ * stored (not added) to PROD. We also avoid a loop for zeroing. */
+ v_limb = up[0];
+ if( v_limb <= 1 ) {
+ if( v_limb == 1 )
+ MPN_COPY( prodp, up, size );
+ else
+ MPN_ZERO(prodp, size);
+ cy_limb = 0;
+ }
+ else
+ cy_limb = mpihelp_mul_1( prodp, up, size, v_limb );
+
+ prodp[size] = cy_limb;
+ prodp++;
+
+ /* For each iteration in the outer loop, multiply one limb from
+ * U with one limb from V, and add it to PROD. */
+ for( i=1; i < size; i++) {
+ v_limb = up[i];
+ if( v_limb <= 1 ) {
+ cy_limb = 0;
+ if( v_limb == 1 )
+ cy_limb = mpihelp_add_n(prodp, prodp, up, size);
+ }
+ else
+ cy_limb = mpihelp_addmul_1(prodp, up, size, v_limb);
+
+ prodp[size] = cy_limb;
+ prodp++;
+ }
+}
+
+
+void
+mpih_sqr_n( mpi_ptr_t prodp, mpi_ptr_t up, mpi_size_t size, mpi_ptr_t tspace)
+{
+ if( size & 1 ) {
+ /* The size is odd, and the code below doesn't handle that.
+ * Multiply the least significant (size - 1) limbs with a recursive
+ * call, and handle the most significant limb of S1 and S2
+ * separately.
+ * A slightly faster way to do this would be to make the Karatsuba
+ * code below behave as if the size were even, and let it check for
+ * odd size in the end. I.e., in essence move this code to the end.
+ * Doing so would save us a recursive call, and potentially make the
+ * stack grow a lot less.
+ */
+ mpi_size_t esize = size - 1; /* even size */
+ mpi_limb_t cy_limb;
+
+ MPN_SQR_N_RECURSE( prodp, up, esize, tspace );
+ cy_limb = mpihelp_addmul_1( prodp + esize, up, esize, up[esize] );
+ prodp[esize + esize] = cy_limb;
+ cy_limb = mpihelp_addmul_1( prodp + esize, up, size, up[esize] );
+
+ prodp[esize + size] = cy_limb;
+ }
+ else {
+ mpi_size_t hsize = size >> 1;
+ mpi_limb_t cy;
+
+ /* Product H. ________________ ________________
+ * |_____U1 x U1____||____U0 x U0_____|
+ * Put result in upper part of PROD and pass low part of TSPACE
+ * as new TSPACE.
+ */
+ MPN_SQR_N_RECURSE(prodp + size, up + hsize, hsize, tspace);
+
+ /* Product M. ________________
+ * |_(U1-U0)(U0-U1)_|
+ */
+ if( mpihelp_cmp( up + hsize, up, hsize) >= 0 )
+ mpihelp_sub_n( prodp, up + hsize, up, hsize);
+ else
+ mpihelp_sub_n (prodp, up, up + hsize, hsize);
+
+ /* Read temporary operands from low part of PROD.
+ * Put result in low part of TSPACE using upper part of TSPACE
+ * as new TSPACE. */
+ MPN_SQR_N_RECURSE(tspace, prodp, hsize, tspace + size);
+
+ /* Add/copy product H */
+ MPN_COPY(prodp + hsize, prodp + size, hsize);
+ cy = mpihelp_add_n(prodp + size, prodp + size,
+ prodp + size + hsize, hsize);
+
+ /* Add product M (if NEGFLG M is a negative number). */
+ cy -= mpihelp_sub_n (prodp + hsize, prodp + hsize, tspace, size);
+
+ /* Product L. ________________ ________________
+ * |________________||____U0 x U0_____|
+ * Read temporary operands from low part of PROD.
+ * Put result in low part of TSPACE using upper part of TSPACE
+ * as new TSPACE. */
+ MPN_SQR_N_RECURSE (tspace, up, hsize, tspace + size);
+
+ /* Add/copy Product L (twice). */
+ cy += mpihelp_add_n (prodp + hsize, prodp + hsize, tspace, size);
+ if( cy )
+ mpihelp_add_1(prodp + hsize + size, prodp + hsize + size,
+ hsize, cy);
+
+ MPN_COPY(prodp, tspace, hsize);
+ cy = mpihelp_add_n (prodp + hsize, prodp + hsize, tspace + hsize, hsize);
+ if( cy )
+ mpihelp_add_1 (prodp + size, prodp + size, size, 1);
+ }
+}
+
+
+/* This should be made into an inline function in gmp.h. */
+void
+mpihelp_mul_n( mpi_ptr_t prodp, mpi_ptr_t up, mpi_ptr_t vp, mpi_size_t size)
+{
+ int secure;
+
+ if( up == vp ) {
+ if( size < KARATSUBA_THRESHOLD )
+ mpih_sqr_n_basecase( prodp, up, size );
+ else {
+ mpi_ptr_t tspace;
+ secure = m_is_secure( up );
+ tspace = mpi_alloc_limb_space( 2 * size, secure );
+ mpih_sqr_n( prodp, up, size, tspace );
+ mpi_free_limb_space( tspace );
+ }
+ }
+ else {
+ if( size < KARATSUBA_THRESHOLD )
+ mul_n_basecase( prodp, up, vp, size );
+ else {
+ mpi_ptr_t tspace;
+ secure = m_is_secure( up ) || m_is_secure( vp );
+ tspace = mpi_alloc_limb_space( 2 * size, secure );
+ mul_n (prodp, up, vp, size, tspace);
+ mpi_free_limb_space( tspace );
+ }
+ }
+}
+
+
+
+void
+mpihelp_mul_karatsuba_case( mpi_ptr_t prodp,
+ mpi_ptr_t up, mpi_size_t usize,
+ mpi_ptr_t vp, mpi_size_t vsize,
+ struct karatsuba_ctx *ctx )
+{
+ mpi_limb_t cy;
+
+ if( !ctx->tspace || ctx->tspace_size < vsize ) {
+ if( ctx->tspace )
+ mpi_free_limb_space( ctx->tspace );
+ ctx->tspace = mpi_alloc_limb_space( 2 * vsize,
+ m_is_secure( up ) || m_is_secure( vp ) );
+ ctx->tspace_size = vsize;
+ }
+
+ MPN_MUL_N_RECURSE( prodp, up, vp, vsize, ctx->tspace );
+
+ prodp += vsize;
+ up += vsize;
+ usize -= vsize;
+ if( usize >= vsize ) {
+ if( !ctx->tp || ctx->tp_size < vsize ) {
+ if( ctx->tp )
+ mpi_free_limb_space( ctx->tp );
+ ctx->tp = mpi_alloc_limb_space( 2 * vsize, m_is_secure( up )
+ || m_is_secure( vp ) );
+ ctx->tp_size = vsize;
+ }
+
+ do {
+ MPN_MUL_N_RECURSE( ctx->tp, up, vp, vsize, ctx->tspace );
+ cy = mpihelp_add_n( prodp, prodp, ctx->tp, vsize );
+ mpihelp_add_1( prodp + vsize, ctx->tp + vsize, vsize, cy );
+ prodp += vsize;
+ up += vsize;
+ usize -= vsize;
+ } while( usize >= vsize );
+ }
+
+ if( usize ) {
+ if( usize < KARATSUBA_THRESHOLD ) {
+ mpihelp_mul( ctx->tspace, vp, vsize, up, usize );
+ }
+ else {
+ if( !ctx->next ) {
+ ctx->next = m_alloc_clear( sizeof *ctx );
+ }
+ mpihelp_mul_karatsuba_case( ctx->tspace,
+ vp, vsize,
+ up, usize,
+ ctx->next );
+ }
+
+ cy = mpihelp_add_n( prodp, prodp, ctx->tspace, vsize);
+ mpihelp_add_1( prodp + vsize, ctx->tspace + vsize, usize, cy );
+ }
+}
+
+
+void
+mpihelp_release_karatsuba_ctx( struct karatsuba_ctx *ctx )
+{
+ struct karatsuba_ctx *ctx2;
+
+ if( ctx->tp )
+ mpi_free_limb_space( ctx->tp );
+ if( ctx->tspace )
+ mpi_free_limb_space( ctx->tspace );
+ for( ctx=ctx->next; ctx; ctx = ctx2 ) {
+ ctx2 = ctx->next;
+ if( ctx->tp )
+ mpi_free_limb_space( ctx->tp );
+ if( ctx->tspace )
+ mpi_free_limb_space( ctx->tspace );
+ m_free( ctx );
+ }
+}
+
+/* Multiply the natural numbers u (pointed to by UP, with USIZE limbs)
+ * and v (pointed to by VP, with VSIZE limbs), and store the result at
+ * PRODP. USIZE + VSIZE limbs are always stored, but if the input
+ * operands are normalized. Return the most significant limb of the
+ * result.
+ *
+ * NOTE: The space pointed to by PRODP is overwritten before finished
+ * with U and V, so overlap is an error.
+ *
+ * Argument constraints:
+ * 1. USIZE >= VSIZE.
+ * 2. PRODP != UP and PRODP != VP, i.e. the destination
+ * must be distinct from the multiplier and the multiplicand.
+ */
+
+mpi_limb_t
+mpihelp_mul( mpi_ptr_t prodp, mpi_ptr_t up, mpi_size_t usize,
+ mpi_ptr_t vp, mpi_size_t vsize)
+{
+ mpi_ptr_t prod_endp = prodp + usize + vsize - 1;
+ mpi_limb_t cy;
+ struct karatsuba_ctx ctx;
+
+ if( vsize < KARATSUBA_THRESHOLD ) {
+ mpi_size_t i;
+ mpi_limb_t v_limb;
+
+ if( !vsize )
+ return 0;
+
+ /* Multiply by the first limb in V separately, as the result can be
+ * stored (not added) to PROD. We also avoid a loop for zeroing. */
+ v_limb = vp[0];
+ if( v_limb <= 1 ) {
+ if( v_limb == 1 )
+ MPN_COPY( prodp, up, usize );
+ else
+ MPN_ZERO( prodp, usize );
+ cy = 0;
+ }
+ else
+ cy = mpihelp_mul_1( prodp, up, usize, v_limb );
+
+ prodp[usize] = cy;
+ prodp++;
+
+ /* For each iteration in the outer loop, multiply one limb from
+ * U with one limb from V, and add it to PROD. */
+ for( i = 1; i < vsize; i++ ) {
+ v_limb = vp[i];
+ if( v_limb <= 1 ) {
+ cy = 0;
+ if( v_limb == 1 )
+ cy = mpihelp_add_n(prodp, prodp, up, usize);
+ }
+ else
+ cy = mpihelp_addmul_1(prodp, up, usize, v_limb);
+
+ prodp[usize] = cy;
+ prodp++;
+ }
+
+ return cy;
+ }
+
+ memset( &ctx, 0, sizeof ctx );
+ mpihelp_mul_karatsuba_case( prodp, up, usize, vp, vsize, &ctx );
+ mpihelp_release_karatsuba_ctx( &ctx );
+ return *prod_endp;
+}
+
+
--- /dev/null
+/* mpiutil.ac - Utility functions for MPI
+ * Copyright (C) 1998, 1999 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG 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.
+ *
+ * GnuPG 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 <linux/string.h>
+
+#include "gnupg_mpi_mpi.h"
+#include "gnupg_mpi_mpi-internal.h"
+#include "gnupg_mpi_memory.h"
+
+
+int DBG_MEMORY = 0;
+
+#ifdef M_DEBUG
+ #undef mpi_alloc
+ #undef mpi_alloc_secure
+ #undef mpi_free
+#endif
+
+/****************
+ * Note: It was a bad idea to use the number of limbs to allocate
+ * because on a alpha the limbs are large but we normally need
+ * integers of n bits - So we should chnage this to bits (or bytes).
+ *
+ * But mpi_alloc is used in a lot of places :-)
+ */
+MPI
+#ifdef M_DEBUG
+mpi_debug_alloc( unsigned nlimbs, const char *info )
+#else
+mpi_alloc( unsigned nlimbs )
+#endif
+{
+ MPI a;
+
+ if( DBG_MEMORY )
+ log_debug("mpi_alloc(%u)\n", nlimbs*BITS_PER_MPI_LIMB );
+ #ifdef M_DEBUG
+ a = m_debug_alloc( sizeof *a, info );
+ a->d = nlimbs? mpi_debug_alloc_limb_space( nlimbs, 0, info ) : NULL;
+ #else
+ a = m_alloc( sizeof *a );
+ a->d = nlimbs? mpi_alloc_limb_space( nlimbs, 0 ) : NULL;
+ #endif
+ a->alloced = nlimbs;
+ a->nlimbs = 0;
+ a->sign = 0;
+ a->flags = 0;
+ a->nbits = 0;
+ return a;
+}
+
+void
+mpi_m_check( MPI a )
+{
+ m_check(a);
+ m_check(a->d);
+}
+
+MPI
+#ifdef M_DEBUG
+mpi_debug_alloc_secure( unsigned nlimbs, const char *info )
+#else
+mpi_alloc_secure( unsigned nlimbs )
+#endif
+{
+ MPI a;
+
+ if( DBG_MEMORY )
+ log_debug("mpi_alloc_secure(%u)\n", nlimbs*BITS_PER_MPI_LIMB );
+ #ifdef M_DEBUG
+ a = m_debug_alloc( sizeof *a, info );
+ a->d = nlimbs? mpi_debug_alloc_limb_space( nlimbs, 1, info ) : NULL;
+ #else
+ a = m_alloc( sizeof *a );
+ a->d = nlimbs? mpi_alloc_limb_space( nlimbs, 1 ) : NULL;
+ #endif
+ a->alloced = nlimbs;
+ a->flags = 1;
+ a->nlimbs = 0;
+ a->sign = 0;
+ a->nbits = 0;
+ return a;
+}
+
+
+#if 0
+static void *unused_limbs_5;
+static void *unused_limbs_32;
+static void *unused_limbs_64;
+#endif
+
+mpi_ptr_t
+#ifdef M_DEBUG
+mpi_debug_alloc_limb_space( unsigned nlimbs, int secure, const char *info )
+#else
+mpi_alloc_limb_space( unsigned nlimbs, int secure )
+#endif
+{
+ size_t len = nlimbs * sizeof(mpi_limb_t);
+ mpi_ptr_t p;
+
+ if( DBG_MEMORY )
+ log_debug("mpi_alloc_limb_space(%u)\n", (unsigned)len*8 );
+ #if 0
+ if( !secure ) {
+ if( nlimbs == 5 && unused_limbs_5 ) { /* DSA 160 bits */
+ p = unused_limbs_5;
+ unused_limbs_5 = *p;
+ return p;
+ }
+ else if( nlimbs == 32 && unused_limbs_32 ) { /* DSA 1024 bits */
+ p = unused_limbs_32;
+ unused_limbs_32 = *p;
+ return p;
+ }
+ else if( nlimbs == 64 && unused_limbs_64 ) { /* DSA 2*1024 bits */
+ p = unused_limbs_64;
+ unused_limbs_64 = *p;
+ return p;
+ }
+ }
+ #endif
+
+ #ifdef M_DEBUG
+ p = secure? m_debug_alloc_secure(len, info):m_debug_alloc( len, info );
+ #else
+ p = secure? m_alloc_secure( len ):m_alloc( len );
+ #endif
+
+ return p;
+}
+
+void
+#ifdef M_DEBUG
+mpi_debug_free_limb_space( mpi_ptr_t a, const char *info )
+#else
+mpi_free_limb_space( mpi_ptr_t a )
+#endif
+{
+ if( !a )
+ return;
+ if( DBG_MEMORY )
+ log_debug("mpi_free_limb_space of size %lu\n", (ulong)m_size(a)*8 );
+
+ #if 0
+ if( !m_is_secure(a) ) {
+ size_t nlimbs = m_size(a) / 4 ;
+ void *p = a;
+
+ if( nlimbs == 5 ) { /* DSA 160 bits */
+ *a = unused_limbs_5;
+ unused_limbs_5 = a;
+ return;
+ }
+ else if( nlimbs == 32 ) { /* DSA 1024 bits */
+ *a = unused_limbs_32;
+ unused_limbs_32 = a;
+ return;
+ }
+ else if( nlimbs == 64 ) { /* DSA 2*1024 bits */
+ *a = unused_limbs_64;
+ unused_limbs_64 = a;
+ return;
+ }
+ }
+ #endif
+
+
+ m_free(a);
+}
+
+
+void
+mpi_assign_limb_space( MPI a, mpi_ptr_t ap, unsigned nlimbs )
+{
+ mpi_free_limb_space(a->d);
+ a->d = ap;
+ a->alloced = nlimbs;
+}
+
+
+
+/****************
+ * Resize the array of A to NLIMBS. the additional space is cleared
+ * (set to 0) [done by m_realloc()]
+ */
+void
+#ifdef M_DEBUG
+mpi_debug_resize( MPI a, unsigned nlimbs, const char *info )
+#else
+mpi_resize( MPI a, unsigned nlimbs )
+#endif
+{
+ if( nlimbs <= a->alloced )
+ return; /* no need to do it */
+ /* Note: a->secure is not used - instead the realloc functions
+ * take care of it. Maybe we should drop a->secure completely
+ * and rely on a mpi_is_secure function, which would be
+ * a wrapper around m_is_secure
+ */
+ #ifdef M_DEBUG
+ if( a->d )
+ a->d = m_debug_realloc(a->d, nlimbs * sizeof(mpi_limb_t), info );
+ else
+ a->d = m_debug_alloc_clear( nlimbs * sizeof(mpi_limb_t), info );
+ #else
+ if( a->d )
+ a->d = m_realloc(a->d, nlimbs * sizeof(mpi_limb_t) );
+ else
+ a->d = m_alloc_clear( nlimbs * sizeof(mpi_limb_t) );
+ #endif
+ a->alloced = nlimbs;
+}
+
+void
+mpi_clear( MPI a )
+{
+ a->nlimbs = 0;
+ a->nbits = 0;
+ a->flags = 0;
+}
+
+
+void
+#ifdef M_DEBUG
+mpi_debug_free( MPI a, const char *info )
+#else
+mpi_free( MPI a )
+#endif
+{
+ if( !a )
+ return;
+ if( DBG_MEMORY )
+ log_debug("mpi_free\n" );
+ if( a->flags & 4 )
+ m_free( a->d );
+ else {
+ #ifdef M_DEBUG
+ mpi_debug_free_limb_space(a->d, info);
+ #else
+ mpi_free_limb_space(a->d);
+ #endif
+ }
+ if( a->flags & ~7 )
+ log_bug("invalid flag value in mpi\n");
+ m_free(a);
+}
+
+
+void
+mpi_set_secure( MPI a )
+{
+ mpi_ptr_t ap, bp;
+
+ if( (a->flags & 1) )
+ return;
+ a->flags |= 1;
+ ap = a->d;
+ if( !a->nlimbs ) {
+ assert(!ap);
+ return;
+ }
+ #ifdef M_DEBUG
+ bp = mpi_debug_alloc_limb_space( a->nlimbs, 1, "set_secure" );
+ #else
+ bp = mpi_alloc_limb_space( a->nlimbs, 1 );
+ #endif
+ MPN_COPY( bp, ap, a->nlimbs );
+ a->d = bp;
+ #ifdef M_DEBUG
+ mpi_debug_free_limb_space(ap, "set_secure");
+ #else
+ mpi_free_limb_space(ap);
+ #endif
+}
+
+
+MPI
+mpi_set_opaque( MPI a, void *p, int len )
+{
+ if( !a ) {
+ #ifdef M_DEBUG
+ a = mpi_debug_alloc(0,"alloc_opaque");
+ #else
+ a = mpi_alloc(0);
+ #endif
+ }
+
+ if( a->flags & 4 )
+ m_free( a->d );
+ else {
+ #ifdef M_DEBUG
+ mpi_debug_free_limb_space(a->d, "alloc_opaque");
+ #else
+ mpi_free_limb_space(a->d);
+ #endif
+ }
+
+ a->d = p;
+ a->alloced = 0;
+ a->nlimbs = 0;
+ a->nbits = len;
+ a->flags = 4;
+ return a;
+}
+
+
+void *
+mpi_get_opaque( MPI a, int *len )
+{
+ if( !(a->flags & 4) )
+ log_bug("mpi_get_opaque on normal mpi\n");
+ if( len )
+ *len = a->nbits;
+ return a->d;
+}
+
+
+/****************
+ * Note: This copy function should not interpret the MPI
+ * but copy it transparently.
+ */
+MPI
+#ifdef M_DEBUG
+mpi_debug_copy( MPI a, const char *info )
+#else
+mpi_copy( MPI a )
+#endif
+{
+ int i;
+ MPI b;
+
+ if( a && (a->flags & 4) ) {
+ void *p = m_is_secure(a->d)? m_alloc_secure( a->nbits )
+ : m_alloc( a->nbits );
+ memcpy( p, a->d, a->nbits );
+ b = mpi_set_opaque( NULL, p, a->nbits );
+ }
+ else if( a ) {
+ #ifdef M_DEBUG
+ b = mpi_is_secure(a)? mpi_debug_alloc_secure( a->nlimbs, info )
+ : mpi_debug_alloc( a->nlimbs, info );
+ #else
+ b = mpi_is_secure(a)? mpi_alloc_secure( a->nlimbs )
+ : mpi_alloc( a->nlimbs );
+ #endif
+ b->nlimbs = a->nlimbs;
+ b->sign = a->sign;
+ b->flags = a->flags;
+ b->nbits = a->nbits;
+ for(i=0; i < b->nlimbs; i++ )
+ b->d[i] = a->d[i];
+ }
+ else
+ b = NULL;
+ return b;
+}
+
+
+/****************
+ * This function allocates an MPI which is optimized to hold
+ * a value as large as the one given in the arhgument and allocates it
+ * with the same flags as A.
+ */
+MPI
+#ifdef M_DEBUG
+mpi_debug_alloc_like( MPI a, const char *info )
+#else
+mpi_alloc_like( MPI a )
+#endif
+{
+ MPI b;
+
+ if( a && (a->flags & 4) ) {
+ void *p = m_is_secure(a->d)? m_alloc_secure( a->nbits )
+ : m_alloc( a->nbits );
+ memcpy( p, a->d, a->nbits );
+ b = mpi_set_opaque( NULL, p, a->nbits );
+ }
+ else if( a ) {
+ #ifdef M_DEBUG
+ b = mpi_is_secure(a)? mpi_debug_alloc_secure( a->nlimbs, info )
+ : mpi_debug_alloc( a->nlimbs, info );
+ #else
+ b = mpi_is_secure(a)? mpi_alloc_secure( a->nlimbs )
+ : mpi_alloc( a->nlimbs );
+ #endif
+ b->nlimbs = 0;
+ b->sign = 0;
+ b->flags = a->flags;
+ b->nbits = 0;
+ }
+ else
+ b = NULL;
+ return b;
+}
+
+
+void
+mpi_set( MPI w, MPI u)
+{
+ mpi_ptr_t wp, up;
+ mpi_size_t usize = u->nlimbs;
+ int usign = u->sign;
+
+ RESIZE_IF_NEEDED(w, usize);
+ wp = w->d;
+ up = u->d;
+ MPN_COPY( wp, up, usize );
+ w->nlimbs = usize;
+ w->nbits = u->nbits;
+ w->flags = u->flags;
+ w->sign = usign;
+}
+
+
+void
+mpi_set_ui( MPI w, unsigned long u)
+{
+ RESIZE_IF_NEEDED(w, 1);
+ w->d[0] = u;
+ w->nlimbs = u? 1:0;
+ w->sign = 0;
+ w->nbits = 0;
+ w->flags = 0;
+}
+
+
+MPI
+mpi_alloc_set_ui( unsigned long u)
+{
+ #ifdef M_DEBUG
+ MPI w = mpi_debug_alloc(1,"alloc_set_ui");
+ #else
+ MPI w = mpi_alloc(1);
+ #endif
+ w->d[0] = u;
+ w->nlimbs = u? 1:0;
+ w->sign = 0;
+ return w;
+}
+
+
+void
+mpi_swap( MPI a, MPI b)
+{
+ struct gcry_mpi tmp;
+
+ tmp = *a; *a = *b; *b = tmp;
+}
+
--- /dev/null
+/* types.h - some common typedefs
+ * Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc.
+ *
+ * This file is part of GNUPG.
+ *
+ * GNUPG 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.
+ *
+ * GNUPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#ifndef G10_TYPES_H
+#define G10_TYPES_H
+
+
+/* The AC_CHECK_SIZEOF() in configure fails for some machines.
+ * we provide some fallback values here */
+#if !SIZEOF_UNSIGNED_SHORT
+ #undef SIZEOF_UNSIGNED_SHORT
+ #define SIZEOF_UNSIGNED_SHORT 2
+#endif
+#if !SIZEOF_UNSIGNED_INT
+ #undef SIZEOF_UNSIGNED_INT
+ #define SIZEOF_UNSIGNED_INT 4
+#endif
+#if !SIZEOF_UNSIGNED_LONG
+ #undef SIZEOF_UNSIGNED_LONG
+ #define SIZEOF_UNSIGNED_LONG 4
+#endif
+
+
+#include <linux/types.h>
+
+
+#ifndef HAVE_BYTE_TYPEDEF
+ #undef byte /* maybe there is a macro with this name */
+ #ifndef __riscos__
+ typedef unsigned char byte;
+ #else
+ /* Norcroft treats char = unsigned char as legal assignment
+ but char* = unsigned char* as illegal assignment
+ and the same applies to the signed variants as well */
+ typedef char byte;
+ #endif
+ #define HAVE_BYTE_TYPEDEF
+#endif
+
+#ifndef HAVE_USHORT_TYPEDEF
+ #undef ushort /* maybe there is a macro with this name */
+ typedef unsigned short ushort;
+ #define HAVE_USHORT_TYPEDEF
+#endif
+
+#ifndef HAVE_ULONG_TYPEDEF
+ #undef ulong /* maybe there is a macro with this name */
+ typedef unsigned long ulong;
+ #define HAVE_ULONG_TYPEDEF
+#endif
+
+typedef union {
+ int a;
+ short b;
+ char c[1];
+ long d;
+ #ifdef HAVE_U64_TYPEDEF
+ u64 e;
+ #endif
+ float f;
+ double g;
+} PROPERLY_ALIGNED_TYPE;
+
+typedef struct string_list {
+ struct string_list *next;
+ unsigned int flags;
+ char d[1];
+} *STRLIST;
+
+
+#endif /*G10_TYPES_H*/
--- /dev/null
+//#include <stdlib.h>
+//#include <string.h>
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include "foo.h"
+
+mpi_limb_t _gcry_mpih_mul( mpi_ptr_t prodp, mpi_ptr_t up, mpi_size_t usize, mpi_ptr_t vp, mpi_size_t vsize);
+
+const unsigned char __clz_tab[] = {
+ 0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
+ 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
+ 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
+ 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
+ 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
+};
+
+//#define gcry_xmalloc(X) malloc(X)
+//#define gcry_xrealloc(X, Y) realloc(X, Y)
+//#define gcry_xcalloc(X, Y) calloc(X, Y)
+//#define gcry_free(X) free(X)
+
+static __inline__ void * kcalloc(size_t nmemb, size_t size)
+{
+ void *foo;
+ foo = kmalloc(nmemb*size, GFP_KERNEL);
+ if (!foo)
+ return NULL;
+ memset(foo, 0x00, nmemb*size);
+ return foo;
+}
+
+// yeah, it's a hack, sue me...
+static __inline__ void * krealloc(void *old, size_t size)
+{
+ void *new;
+
+ new = kmalloc(size, GFP_KERNEL);
+ if (old == NULL)
+ return new;
+ if (!new)
+ return NULL;
+ memcpy(new, old, size);
+ return new;
+}
+
+
+#define gcry_xmalloc(X) kmalloc(X, GFP_KERNEL)
+#define gcry_xrealloc(X, Y) krealloc(X, Y)
+#define gcry_xcalloc(X, Y) kcalloc(X, Y)
+#define gcry_free(X) kfree(X)
+
+
+#define gcry_is_secure(X) (0)
+#define mpi_alloc(X) mpi_alloc_secure(X)
+
+#define mpi_free_limb_space(X) gcry_free(X)
+
+/* Divide the two-limb number in (NH,,NL) by D, with DI being the largest
+ * limb not larger than (2**(2*BITS_PER_MP_LIMB))/D - (2**BITS_PER_MP_LIMB).
+ * If this would yield overflow, DI should be the largest possible number
+ * (i.e., only ones). For correct operation, the most significant bit of D
+ * has to be set. Put the quotient in Q and the remainder in R.
+ */
+#define UDIV_QRNND_PREINV(q, r, nh, nl, d, di) \
+ do { \
+ mpi_limb_t _q, _ql, _r; \
+ mpi_limb_t _xh, _xl; \
+ umul_ppmm (_q, _ql, (nh), (di)); \
+ _q += (nh); /* DI is 2**BITS_PER_MPI_LIMB too small */ \
+ umul_ppmm (_xh, _xl, _q, (d)); \
+ sub_ddmmss (_xh, _r, (nh), (nl), _xh, _xl); \
+ if( _xh ) { \
+ sub_ddmmss (_xh, _r, _xh, _r, 0, (d)); \
+ _q++; \
+ if( _xh) { \
+ sub_ddmmss (_xh, _r, _xh, _r, 0, (d)); \
+ _q++; \
+ } \
+ } \
+ if( _r >= (d) ) { \
+ _r -= (d); \
+ _q++; \
+ } \
+ (r) = _r; \
+ (q) = _q; \
+ } while (0)
+
+
+/* Define this unconditionally, so it can be used for debugging. */
+#define __udiv_qrnnd_c(q, r, n1, n0, d) \
+ do { \
+ UWtype __d1, __d0, __q1, __q0, __r1, __r0, __m; \
+ __d1 = __ll_highpart (d); \
+ __d0 = __ll_lowpart (d); \
+ \
+ __r1 = (n1) % __d1; \
+ __q1 = (n1) / __d1; \
+ __m = (UWtype) __q1 * __d0; \
+ __r1 = __r1 * __ll_B | __ll_highpart (n0); \
+ if (__r1 < __m) \
+ { \
+ __q1--, __r1 += (d); \
+ if (__r1 >= (d)) /* i.e. we didn't get carry when adding to __r1 */\
+ if (__r1 < __m) \
+ __q1--, __r1 += (d); \
+ } \
+ __r1 -= __m; \
+ \
+ __r0 = __r1 % __d1; \
+ __q0 = __r1 / __d1; \
+ __m = (UWtype) __q0 * __d0; \
+ __r0 = __r0 * __ll_B | __ll_lowpart (n0); \
+ if (__r0 < __m) \
+ { \
+ __q0--, __r0 += (d); \
+ if (__r0 >= (d)) \
+ if (__r0 < __m) \
+ __q0--, __r0 += (d); \
+ } \
+ __r0 -= __m; \
+ \
+ (q) = (UWtype) __q1 * __ll_B | __q0; \
+ (r) = __r0; \
+ } while (0)
+
+
+struct gcry_mpi *mpi_alloc_secure( unsigned nlimbs )
+{
+ struct gcry_mpi *a;
+
+ a = gcry_xmalloc( sizeof *a );
+ a->d = nlimbs? mpi_alloc_limb_space( nlimbs, 1 ) : NULL;
+ a->alloced = nlimbs;
+ a->flags = 1;
+ a->nlimbs = 0;
+ a->sign = 0;
+ return a;
+}
+
+
+mpi_ptr_t mpi_alloc_limb_space( unsigned nlimbs, int secure )
+{
+ size_t len = nlimbs * sizeof(mpi_limb_t);
+ mpi_ptr_t p;
+
+ p = gcry_xmalloc( len );
+
+ return p;
+}
+
+void mpi_assign_limb_space( struct gcry_mpi *a, mpi_ptr_t ap, unsigned nlimbs )
+{
+ mpi_free_limb_space(a->d);
+ a->d = ap;
+ a->alloced = nlimbs;
+}
+
+
+
+struct gcry_mpi * gcry_mpi_set_opaque( struct gcry_mpi * a, void *p, unsigned int nbits )
+{
+ if( !a ) {
+ a = mpi_alloc(0);
+ }
+
+ if( a->flags & 4 )
+ gcry_free( a->d );
+ else {
+ //mpi_free_limb_space(a->d);
+ gcry_free(a->d);
+ }
+
+ a->d = p;
+ a->alloced = 0;
+ a->nlimbs = 0;
+ a->sign = nbits;
+ a->flags = 4;
+ return a;
+}
+
+
+/****************
+ * Note: This copy function should not interpret the MPI
+ * but copy it transparently.
+ */
+struct gcry_mpi *mpi_copy( struct gcry_mpi * a )
+{
+ int i;
+ struct gcry_mpi *b;
+
+ if( a && (a->flags & 4) ) {
+ void *p = gcry_xmalloc( (a->sign+7)/8 );
+ memcpy( p, a->d, (a->sign+7)/8 );
+ b = gcry_mpi_set_opaque( NULL, p, a->sign );
+ }
+ else if( a ) {
+ b = mpi_is_secure(a)? mpi_alloc_secure( a->nlimbs )
+ : mpi_alloc( a->nlimbs );
+ b->nlimbs = a->nlimbs;
+ b->sign = a->sign;
+ b->flags = a->flags;
+ for(i=0; i < b->nlimbs; i++ )
+ b->d[i] = a->d[i];
+ }
+ else
+ b = NULL;
+ return b;
+}
+
+
+
+
+mpi_limb_t _gcry_mpih_sub_1(mpi_ptr_t res_ptr, mpi_ptr_t s1_ptr, size_t s1_size, mpi_limb_t s2_limb )
+{
+ mpi_limb_t x;
+
+ x = *s1_ptr++;
+ s2_limb = x - s2_limb;
+ *res_ptr++ = s2_limb;
+ if( s2_limb > x ) {
+ while( --s1_size ) {
+ x = *s1_ptr++;
+ *res_ptr++ = x - 1;
+ if( x )
+ goto leave;
+ }
+ return 1;
+ }
+
+ leave:
+ if( res_ptr != s1_ptr ) {
+ size_t i;
+ for( i=0; i < s1_size-1; i++ )
+ res_ptr[i] = s1_ptr[i];
+ }
+ return 0;
+}
+
+
+/****************
+ * Resize the array of A to NLIMBS. the additional space is cleared
+ * (set to 0) [done by gcry_realloc()]
+ */
+static void mpi_resize(struct gcry_mpi *a, unsigned nlimbs )
+{
+ if( nlimbs <= a->alloced )
+ return; /* no need to do it */
+ /* Note: a->secure is not used - instead the realloc functions
+ * take care of it. Maybe we should drop a->secure completely
+ * and rely on a mpi_is_secure function, which would be
+ * a wrapper around gcry_is_secure
+ */
+ if( a->d )
+ a->d = gcry_xrealloc(a->d, nlimbs * sizeof(mpi_limb_t) );
+ else /* FIXME: It may not be allocted in secure memory */
+ a->d = gcry_xcalloc( nlimbs , sizeof(mpi_limb_t) );
+ a->alloced = nlimbs;
+}
+
+
+/****************
+ * Subtract the unsigned integer V from the mpi-integer U and store the
+ * result in W.
+ */
+void mpi_sub_ui(struct gcry_mpi *w, struct gcry_mpi *u, unsigned long v )
+{
+ mpi_ptr_t wp, up;
+ size_t usize, wsize;
+ int usign, wsign;
+
+ usize = u->nlimbs;
+ usign = u->sign;
+ wsign = 0;
+
+ /* If not space for W (and possible carry), increase space. */
+ wsize = usize + 1;
+ if( w->alloced < wsize )
+ mpi_resize(w, wsize);
+
+ /* These must be after realloc (U may be the same as W). */
+ up = u->d;
+ wp = w->d;
+
+ if( !usize ) { /* simple */
+ wp[0] = v;
+ wsize = v? 1:0;
+ wsign = 1;
+ }
+ else if( usign ) { /* mpi and v are negative */
+ mpi_limb_t cy;
+ cy = _gcry_mpih_add_1(wp, up, usize, v);
+ wp[usize] = cy;
+ wsize = usize + cy;
+ }
+ else { /* The signs are different. Need exact comparison to determine
+ * which operand to subtract from which. */
+ if( usize == 1 && up[0] < v ) {
+ wp[0] = v - up[0];
+ wsize = 1;
+ wsign = 1;
+ }
+ else {
+ _gcry_mpih_sub_1(wp, up, usize, v);
+ /* Size can decrease with at most one limb. */
+ wsize = usize - (wp[usize-1]==0);
+ }
+ }
+
+ w->nlimbs = wsize;
+ w->sign = wsign;
+}
+
+void mpi_free(struct gcry_mpi *a )
+{
+ if( !a )
+ return;
+ if( a->flags & 4 )
+ gcry_free( a->d );
+ else {
+ //mpi_free_limb_space(a->d);
+ gcry_free(a->d);
+ }
+// if( a->flags & ~7 )
+// log_bug("invalid flag value in mpi\n");
+ gcry_free(a);
+}
+
+void mpi_add(struct gcry_mpi *w, struct gcry_mpi *u, struct gcry_mpi *v)
+{
+ mpi_ptr_t wp, up, vp;
+ size_t usize, vsize, wsize;
+ int usign, vsign, wsign;
+
+ if( u->nlimbs < v->nlimbs ) { /* Swap U and V. */
+ usize = v->nlimbs;
+ usign = v->sign;
+ vsize = u->nlimbs;
+ vsign = u->sign;
+ wsize = usize + 1;
+ RESIZE_IF_NEEDED(w, wsize);
+ /* These must be after realloc (u or v may be the same as w). */
+ up = v->d;
+ vp = u->d;
+ }
+ else {
+ usize = u->nlimbs;
+ usign = u->sign;
+ vsize = v->nlimbs;
+ vsign = v->sign;
+ wsize = usize + 1;
+ RESIZE_IF_NEEDED(w, wsize);
+ /* These must be after realloc (u or v may be the same as w). */
+ up = u->d;
+ vp = v->d;
+ }
+ wp = w->d;
+ wsign = 0;
+
+ if( !vsize ) { /* simple */
+ MPN_COPY(wp, up, usize );
+ wsize = usize;
+ wsign = usign;
+ }
+ else if( usign != vsign ) { /* different sign */
+ /* This test is right since USIZE >= VSIZE */
+ if( usize != vsize ) {
+ _gcry_mpih_sub(wp, up, usize, vp, vsize);
+ wsize = usize;
+ MPN_NORMALIZE(wp, wsize);
+ wsign = usign;
+ }
+ else if( _gcry_mpih_cmp(up, vp, usize) < 0 ) {
+ _gcry_mpih_sub_n(wp, vp, up, usize);
+ wsize = usize;
+ MPN_NORMALIZE(wp, wsize);
+ if( !usign )
+ wsign = 1;
+ }
+ else {
+ _gcry_mpih_sub_n(wp, up, vp, usize);
+ wsize = usize;
+ MPN_NORMALIZE(wp, wsize);
+ if( usign )
+ wsign = 1;
+ }
+ }
+ else { /* U and V have same sign. Add them. */
+ mpi_limb_t cy = _gcry_mpih_add(wp, up, usize, vp, vsize);
+ wp[usize] = cy;
+ wsize = usize + cy;
+ if( usign )
+ wsign = 1;
+ }
+
+ w->nlimbs = wsize;
+ w->sign = wsign;
+}
+
+
+/****************
+ * Divide (DIVIDEND_PTR,,DIVIDEND_SIZE) by DIVISOR_LIMB.
+ * Write DIVIDEND_SIZE limbs of quotient at QUOT_PTR.
+ * Return the single-limb remainder.
+ * There are no constraints on the value of the divisor.
+ *
+ * QUOT_PTR and DIVIDEND_PTR might point to the same limb.
+ */
+
+mpi_limb_t
+_gcry_mpih_divmod_1( mpi_ptr_t quot_ptr,
+ mpi_ptr_t dividend_ptr, mpi_size_t dividend_size,
+ mpi_limb_t divisor_limb)
+{
+ mpi_size_t i;
+ mpi_limb_t n1, n0, r;
+ int dummy;
+
+ if( !dividend_size )
+ return 0;
+
+ /* If multiplication is much faster than division, and the
+ * dividend is large, pre-invert the divisor, and use
+ * only multiplications in the inner loop.
+ *
+ * This test should be read:
+ * Does it ever help to use udiv_qrnnd_preinv?
+ * && Does what we save compensate for the inversion overhead?
+ */
+ if( UDIV_TIME > (2 * UMUL_TIME + 6)
+ && (UDIV_TIME - (2 * UMUL_TIME + 6)) * dividend_size > UDIV_TIME ) {
+ int normalization_steps;
+
+ count_leading_zeros( normalization_steps, divisor_limb );
+ if( normalization_steps ) {
+ mpi_limb_t divisor_limb_inverted;
+
+ divisor_limb <<= normalization_steps;
+
+ /* Compute (2**2N - 2**N * DIVISOR_LIMB) / DIVISOR_LIMB. The
+ * result is a (N+1)-bit approximation to 1/DIVISOR_LIMB, with the
+ * most significant bit (with weight 2**N) implicit.
+ */
+ /* Special case for DIVISOR_LIMB == 100...000. */
+ if( !(divisor_limb << 1) )
+ divisor_limb_inverted = ~(mpi_limb_t)0;
+ else
+ udiv_qrnnd(divisor_limb_inverted, dummy,
+ -divisor_limb, 0, divisor_limb);
+
+ n1 = dividend_ptr[dividend_size - 1];
+ r = n1 >> (BITS_PER_MPI_LIMB - normalization_steps);
+
+ /* Possible optimization:
+ * if (r == 0
+ * && divisor_limb > ((n1 << normalization_steps)
+ * | (dividend_ptr[dividend_size - 2] >> ...)))
+ * ...one division less...
+ */
+ for( i = dividend_size - 2; i >= 0; i--) {
+ n0 = dividend_ptr[i];
+ UDIV_QRNND_PREINV( quot_ptr[i + 1], r, r,
+ ((n1 << normalization_steps)
+ | (n0 >> (BITS_PER_MPI_LIMB - normalization_steps))),
+ divisor_limb, divisor_limb_inverted);
+ n1 = n0;
+ }
+ UDIV_QRNND_PREINV( quot_ptr[0], r, r,
+ n1 << normalization_steps,
+ divisor_limb, divisor_limb_inverted);
+ return r >> normalization_steps;
+ }
+ else {
+ mpi_limb_t divisor_limb_inverted;
+
+ /* Compute (2**2N - 2**N * DIVISOR_LIMB) / DIVISOR_LIMB. The
+ * result is a (N+1)-bit approximation to 1/DIVISOR_LIMB, with the
+ * most significant bit (with weight 2**N) implicit.
+ */
+ /* Special case for DIVISOR_LIMB == 100...000. */
+ if( !(divisor_limb << 1) )
+ divisor_limb_inverted = ~(mpi_limb_t) 0;
+ else
+ udiv_qrnnd(divisor_limb_inverted, dummy,
+ -divisor_limb, 0, divisor_limb);
+
+ i = dividend_size - 1;
+ r = dividend_ptr[i];
+
+ if( r >= divisor_limb )
+ r = 0;
+ else
+ quot_ptr[i--] = 0;
+
+ for( ; i >= 0; i-- ) {
+ n0 = dividend_ptr[i];
+ UDIV_QRNND_PREINV( quot_ptr[i], r, r,
+ n0, divisor_limb, divisor_limb_inverted);
+ }
+ return r;
+ }
+ }
+ else {
+ if(UDIV_NEEDS_NORMALIZATION) {
+ int normalization_steps;
+
+ count_leading_zeros (normalization_steps, divisor_limb);
+ if( normalization_steps ) {
+ divisor_limb <<= normalization_steps;
+
+ n1 = dividend_ptr[dividend_size - 1];
+ r = n1 >> (BITS_PER_MPI_LIMB - normalization_steps);
+
+ /* Possible optimization:
+ * if (r == 0
+ * && divisor_limb > ((n1 << normalization_steps)
+ * | (dividend_ptr[dividend_size - 2] >> ...)))
+ * ...one division less...
+ */
+ for( i = dividend_size - 2; i >= 0; i--) {
+ n0 = dividend_ptr[i];
+ udiv_qrnnd (quot_ptr[i + 1], r, r,
+ ((n1 << normalization_steps)
+ | (n0 >> (BITS_PER_MPI_LIMB - normalization_steps))),
+ divisor_limb);
+ n1 = n0;
+ }
+ udiv_qrnnd (quot_ptr[0], r, r,
+ n1 << normalization_steps,
+ divisor_limb);
+ return r >> normalization_steps;
+ }
+ }
+ /* No normalization needed, either because udiv_qrnnd doesn't require
+ * it, or because DIVISOR_LIMB is already normalized. */
+ i = dividend_size - 1;
+ r = dividend_ptr[i];
+
+ if(r >= divisor_limb)
+ r = 0;
+ else
+ quot_ptr[i--] = 0;
+
+ for(; i >= 0; i--) {
+ n0 = dividend_ptr[i];
+ udiv_qrnnd( quot_ptr[i], r, r, n0, divisor_limb );
+ }
+ return r;
+ }
+}
+
+
+mpi_limb_t
+_gcry_mpih_mod_1(mpi_ptr_t dividend_ptr, mpi_size_t dividend_size,
+ mpi_limb_t divisor_limb)
+{
+ mpi_size_t i;
+ mpi_limb_t n1, n0, r;
+ int dummy;
+
+ /* Botch: Should this be handled at all? Rely on callers? */
+ if( !dividend_size )
+ return 0;
+
+ /* If multiplication is much faster than division, and the
+ * dividend is large, pre-invert the divisor, and use
+ * only multiplications in the inner loop.
+ *
+ * This test should be read:
+ * Does it ever help to use udiv_qrnnd_preinv?
+ * && Does what we save compensate for the inversion overhead?
+ */
+ if( UDIV_TIME > (2 * UMUL_TIME + 6)
+ && (UDIV_TIME - (2 * UMUL_TIME + 6)) * dividend_size > UDIV_TIME ) {
+ int normalization_steps;
+
+ count_leading_zeros( normalization_steps, divisor_limb );
+ if( normalization_steps ) {
+ mpi_limb_t divisor_limb_inverted;
+
+ divisor_limb <<= normalization_steps;
+
+ /* Compute (2**2N - 2**N * DIVISOR_LIMB) / DIVISOR_LIMB. The
+ * result is a (N+1)-bit approximation to 1/DIVISOR_LIMB, with the
+ * most significant bit (with weight 2**N) implicit.
+ *
+ * Special case for DIVISOR_LIMB == 100...000.
+ */
+ if( !(divisor_limb << 1) )
+ divisor_limb_inverted = ~(mpi_limb_t)0;
+ else
+ udiv_qrnnd(divisor_limb_inverted, dummy,
+ -divisor_limb, 0, divisor_limb);
+
+ n1 = dividend_ptr[dividend_size - 1];
+ r = n1 >> (BITS_PER_MPI_LIMB - normalization_steps);
+
+ /* Possible optimization:
+ * if (r == 0
+ * && divisor_limb > ((n1 << normalization_steps)
+ * | (dividend_ptr[dividend_size - 2] >> ...)))
+ * ...one division less...
+ */
+ for( i = dividend_size - 2; i >= 0; i--) {
+ n0 = dividend_ptr[i];
+ UDIV_QRNND_PREINV(dummy, r, r,
+ ((n1 << normalization_steps)
+ | (n0 >> (BITS_PER_MPI_LIMB - normalization_steps))),
+ divisor_limb, divisor_limb_inverted);
+ n1 = n0;
+ }
+ UDIV_QRNND_PREINV(dummy, r, r,
+ n1 << normalization_steps,
+ divisor_limb, divisor_limb_inverted);
+ return r >> normalization_steps;
+ }
+ else {
+ mpi_limb_t divisor_limb_inverted;
+
+ /* Compute (2**2N - 2**N * DIVISOR_LIMB) / DIVISOR_LIMB. The
+ * result is a (N+1)-bit approximation to 1/DIVISOR_LIMB, with the
+ * most significant bit (with weight 2**N) implicit.
+ *
+ * Special case for DIVISOR_LIMB == 100...000.
+ */
+ if( !(divisor_limb << 1) )
+ divisor_limb_inverted = ~(mpi_limb_t)0;
+ else
+ udiv_qrnnd(divisor_limb_inverted, dummy,
+ -divisor_limb, 0, divisor_limb);
+
+ i = dividend_size - 1;
+ r = dividend_ptr[i];
+
+ if( r >= divisor_limb )
+ r = 0;
+ else
+ i--;
+
+ for( ; i >= 0; i--) {
+ n0 = dividend_ptr[i];
+ UDIV_QRNND_PREINV(dummy, r, r,
+ n0, divisor_limb, divisor_limb_inverted);
+ }
+ return r;
+ }
+ }
+ else {
+ if( UDIV_NEEDS_NORMALIZATION ) {
+ int normalization_steps;
+
+ count_leading_zeros(normalization_steps, divisor_limb);
+ if( normalization_steps ) {
+ divisor_limb <<= normalization_steps;
+
+ n1 = dividend_ptr[dividend_size - 1];
+ r = n1 >> (BITS_PER_MPI_LIMB - normalization_steps);
+
+ /* Possible optimization:
+ * if (r == 0
+ * && divisor_limb > ((n1 << normalization_steps)
+ * | (dividend_ptr[dividend_size - 2] >> ...)))
+ * ...one division less...
+ */
+ for(i = dividend_size - 2; i >= 0; i--) {
+ n0 = dividend_ptr[i];
+ udiv_qrnnd (dummy, r, r,
+ ((n1 << normalization_steps)
+ | (n0 >> (BITS_PER_MPI_LIMB - normalization_steps))),
+ divisor_limb);
+ n1 = n0;
+ }
+ udiv_qrnnd (dummy, r, r,
+ n1 << normalization_steps,
+ divisor_limb);
+ return r >> normalization_steps;
+ }
+ }
+ /* No normalization needed, either because udiv_qrnnd doesn't require
+ * it, or because DIVISOR_LIMB is already normalized. */
+ i = dividend_size - 1;
+ r = dividend_ptr[i];
+
+ if(r >= divisor_limb)
+ r = 0;
+ else
+ i--;
+
+ for(; i >= 0; i--) {
+ n0 = dividend_ptr[i];
+ udiv_qrnnd (dummy, r, r, n0, divisor_limb);
+ }
+ return r;
+ }
+}
+
+
+/* Divide num (NP/NSIZE) by den (DP/DSIZE) and write
+ * the NSIZE-DSIZE least significant quotient limbs at QP
+ * and the DSIZE long remainder at NP. If QEXTRA_LIMBS is
+ * non-zero, generate that many fraction bits and append them after the
+ * other quotient limbs.
+ * Return the most significant limb of the quotient, this is always 0 or 1.
+ *
+ * Preconditions:
+ * 0. NSIZE >= DSIZE.
+ * 1. The most significant bit of the divisor must be set.
+ * 2. QP must either not overlap with the input operands at all, or
+ * QP + DSIZE >= NP must hold true. (This means that it's
+ * possible to put the quotient in the high part of NUM, right after the
+ * remainder in NUM.
+ * 3. NSIZE >= DSIZE, even if QEXTRA_LIMBS is non-zero.
+ */
+
+mpi_limb_t
+_gcry_mpih_divrem( mpi_ptr_t qp, mpi_size_t qextra_limbs,
+ mpi_ptr_t np, mpi_size_t nsize,
+ mpi_ptr_t dp, mpi_size_t dsize)
+{
+ mpi_limb_t most_significant_q_limb = 0;
+
+ switch(dsize) {
+ case 0:
+ /* We are asked to divide by zero, so go ahead and do it! (To make
+ the compiler not remove this statement, return the value.) */
+ return 1 / dsize;
+
+ case 1:
+ {
+ mpi_size_t i;
+ mpi_limb_t n1;
+ mpi_limb_t d;
+
+ d = dp[0];
+ n1 = np[nsize - 1];
+
+ if( n1 >= d ) {
+ n1 -= d;
+ most_significant_q_limb = 1;
+ }
+
+ qp += qextra_limbs;
+ for( i = nsize - 2; i >= 0; i--)
+ udiv_qrnnd( qp[i], n1, n1, np[i], d );
+ qp -= qextra_limbs;
+
+ for( i = qextra_limbs - 1; i >= 0; i-- )
+ udiv_qrnnd (qp[i], n1, n1, 0, d);
+
+ np[0] = n1;
+ }
+ break;
+
+ case 2:
+ {
+ mpi_size_t i;
+ mpi_limb_t n1, n0, n2;
+ mpi_limb_t d1, d0;
+
+ np += nsize - 2;
+ d1 = dp[1];
+ d0 = dp[0];
+ n1 = np[1];
+ n0 = np[0];
+
+ if( n1 >= d1 && (n1 > d1 || n0 >= d0) ) {
+ sub_ddmmss (n1, n0, n1, n0, d1, d0);
+ most_significant_q_limb = 1;
+ }
+
+ for( i = qextra_limbs + nsize - 2 - 1; i >= 0; i-- ) {
+ mpi_limb_t q;
+ mpi_limb_t r;
+
+ if( i >= qextra_limbs )
+ np--;
+ else
+ np[0] = 0;
+
+ if( n1 == d1 ) {
+ /* Q should be either 111..111 or 111..110. Need special
+ * treatment of this rare case as normal division would
+ * give overflow. */
+ q = ~(mpi_limb_t)0;
+
+ r = n0 + d1;
+ if( r < d1 ) { /* Carry in the addition? */
+ add_ssaaaa( n1, n0, r - d0, np[0], 0, d0 );
+ qp[i] = q;
+ continue;
+ }
+ n1 = d0 - (d0 != 0?1:0);
+ n0 = -d0;
+ }
+ else {
+ udiv_qrnnd (q, r, n1, n0, d1);
+ umul_ppmm (n1, n0, d0, q);
+ }
+
+ n2 = np[0];
+ q_test:
+ if( n1 > r || (n1 == r && n0 > n2) ) {
+ /* The estimated Q was too large. */
+ q--;
+ sub_ddmmss (n1, n0, n1, n0, 0, d0);
+ r += d1;
+ if( r >= d1 ) /* If not carry, test Q again. */
+ goto q_test;
+ }
+
+ qp[i] = q;
+ sub_ddmmss (n1, n0, r, n2, n1, n0);
+ }
+ np[1] = n1;
+ np[0] = n0;
+ }
+ break;
+
+ default:
+ {
+ mpi_size_t i;
+ mpi_limb_t dX, d1, n0;
+
+ np += nsize - dsize;
+ dX = dp[dsize - 1];
+ d1 = dp[dsize - 2];
+ n0 = np[dsize - 1];
+
+ if( n0 >= dX ) {
+ if(n0 > dX || _gcry_mpih_cmp(np, dp, dsize - 1) >= 0 ) {
+ _gcry_mpih_sub_n(np, np, dp, dsize);
+ n0 = np[dsize - 1];
+ most_significant_q_limb = 1;
+ }
+ }
+
+ for( i = qextra_limbs + nsize - dsize - 1; i >= 0; i--) {
+ mpi_limb_t q;
+ mpi_limb_t n1, n2;
+ mpi_limb_t cy_limb;
+
+ if( i >= qextra_limbs ) {
+ np--;
+ n2 = np[dsize];
+ }
+ else {
+ n2 = np[dsize - 1];
+ MPN_COPY_DECR (np + 1, np, dsize - 1);
+ np[0] = 0;
+ }
+
+ if( n0 == dX ) {
+ /* This might over-estimate q, but it's probably not worth
+ * the extra code here to find out. */
+ q = ~(mpi_limb_t)0;
+ }
+ else {
+ mpi_limb_t r;
+
+ udiv_qrnnd(q, r, n0, np[dsize - 1], dX);
+ umul_ppmm(n1, n0, d1, q);
+
+ while( n1 > r || (n1 == r && n0 > np[dsize - 2])) {
+ q--;
+ r += dX;
+ if( r < dX ) /* I.e. "carry in previous addition?" */
+ break;
+ n1 -= n0 < d1;
+ n0 -= d1;
+ }
+ }
+
+ /* Possible optimization: We already have (q * n0) and (1 * n1)
+ * after the calculation of q. Taking advantage of that, we
+ * could make this loop make two iterations less. */
+ cy_limb = _gcry_mpih_submul_1(np, dp, dsize, q);
+
+ if( n2 != cy_limb ) {
+ _gcry_mpih_add_n(np, np, dp, dsize);
+ q--;
+ }
+
+ qp[i] = q;
+ n0 = np[dsize - 1];
+ }
+ }
+ }
+
+ return most_significant_q_limb;
+}
+
+
+
+
+static void _gcry_mpi_tdiv_qr(struct gcry_mpi *quot, struct gcry_mpi *rem, struct gcry_mpi *num, struct gcry_mpi *den)
+{
+ mpi_ptr_t np, dp;
+ mpi_ptr_t qp, rp;
+ mpi_size_t nsize = num->nlimbs;
+ mpi_size_t dsize = den->nlimbs;
+ mpi_size_t qsize, rsize;
+ mpi_size_t sign_remainder = num->sign;
+ mpi_size_t sign_quotient = num->sign ^ den->sign;
+ unsigned normalization_steps;
+ mpi_limb_t q_limb;
+ mpi_ptr_t marker[5];
+ int markidx=0;
+
+ /* Ensure space is enough for quotient and remainder.
+ * We need space for an extra limb in the remainder, because it's
+ * up-shifted (normalized) below. */
+ rsize = nsize + 1;
+ mpi_resize( rem, rsize);
+
+ qsize = rsize - dsize; /* qsize cannot be bigger than this. */
+ if( qsize <= 0 ) {
+ if( num != rem ) {
+ rem->nlimbs = num->nlimbs;
+ rem->sign = num->sign;
+ MPN_COPY(rem->d, num->d, nsize);
+ }
+ if( quot ) {
+ /* This needs to follow the assignment to rem, in case the
+ * numerator and quotient are the same. */
+ quot->nlimbs = 0;
+ quot->sign = 0;
+ }
+ return;
+ }
+
+ if( quot )
+ mpi_resize( quot, qsize);
+
+ /* Read pointers here, when reallocation is finished. */
+ np = num->d;
+ dp = den->d;
+ rp = rem->d;
+
+ /* Optimize division by a single-limb divisor. */
+ if( dsize == 1 ) {
+ mpi_limb_t rlimb;
+ if( quot ) {
+ qp = quot->d;
+ rlimb = _gcry_mpih_divmod_1( qp, np, nsize, dp[0] );
+ qsize -= qp[qsize - 1] == 0;
+ quot->nlimbs = qsize;
+ quot->sign = sign_quotient;
+ }
+ else
+ rlimb = _gcry_mpih_mod_1( np, nsize, dp[0] );
+ rp[0] = rlimb;
+ rsize = rlimb != 0?1:0;
+ rem->nlimbs = rsize;
+ rem->sign = sign_remainder;
+ return;
+ }
+
+
+ if( quot ) {
+ qp = quot->d;
+ /* Make sure QP and NP point to different objects. Otherwise the
+ * numerator would be gradually overwritten by the quotient limbs. */
+ if(qp == np) { /* Copy NP object to temporary space. */
+ np = marker[markidx++] = mpi_alloc_limb_space(nsize,
+ mpi_is_secure(quot));
+ MPN_COPY(np, qp, nsize);
+ }
+ }
+ else /* Put quotient at top of remainder. */
+ qp = rp + dsize;
+
+ count_leading_zeros( normalization_steps, dp[dsize - 1] );
+
+ /* Normalize the denominator, i.e. make its most significant bit set by
+ * shifting it NORMALIZATION_STEPS bits to the left. Also shift the
+ * numerator the same number of steps (to keep the quotient the same!).
+ */
+ if( normalization_steps ) {
+ mpi_ptr_t tp;
+ mpi_limb_t nlimb;
+
+ /* Shift up the denominator setting the most significant bit of
+ * the most significant word. Use temporary storage not to clobber
+ * the original contents of the denominator. */
+ tp = marker[markidx++] = mpi_alloc_limb_space(dsize,mpi_is_secure(den));
+ _gcry_mpih_lshift( tp, dp, dsize, normalization_steps );
+ dp = tp;
+
+ /* Shift up the numerator, possibly introducing a new most
+ * significant word. Move the shifted numerator in the remainder
+ * meanwhile. */
+ nlimb = _gcry_mpih_lshift(rp, np, nsize, normalization_steps);
+ if( nlimb ) {
+ rp[nsize] = nlimb;
+ rsize = nsize + 1;
+ }
+ else
+ rsize = nsize;
+ }
+ else {
+ /* The denominator is already normalized, as required. Copy it to
+ * temporary space if it overlaps with the quotient or remainder. */
+ if( dp == rp || (quot && (dp == qp))) {
+ mpi_ptr_t tp;
+
+ tp = marker[markidx++] = mpi_alloc_limb_space(dsize, mpi_is_secure(den));
+ MPN_COPY( tp, dp, dsize );
+ dp = tp;
+ }
+
+ /* Move the numerator to the remainder. */
+ if( rp != np )
+ MPN_COPY(rp, np, nsize);
+
+ rsize = nsize;
+ }
+
+ q_limb = _gcry_mpih_divrem( qp, 0, rp, rsize, dp, dsize );
+
+ if( quot ) {
+ qsize = rsize - dsize;
+ if(q_limb) {
+ qp[qsize] = q_limb;
+ qsize += 1;
+ }
+
+ quot->nlimbs = qsize;
+ quot->sign = sign_quotient;
+ }
+
+ rsize = dsize;
+ MPN_NORMALIZE (rp, rsize);
+
+ if( normalization_steps && rsize ) {
+ _gcry_mpih_rshift(rp, rp, rsize, normalization_steps);
+ rsize -= rp[rsize - 1] == 0?1:0;
+ }
+
+ rem->nlimbs = rsize;
+ rem->sign = sign_remainder;
+ while( markidx )
+// mpi_free_limb_space(marker[--markidx]);
+ gcry_free(marker[--markidx]);
+}
+
+/* If den == quot, den needs temporary storage.
+ * If den == rem, den needs temporary storage.
+ * If num == quot, num needs temporary storage.
+ * If den has temporary storage, it can be normalized while being copied,
+ * i.e no extra storage should be allocated.
+ */
+
+static void _gcry_mpi_tdiv_r(struct gcry_mpi *rem, struct gcry_mpi *num, struct gcry_mpi *den)
+{
+ _gcry_mpi_tdiv_qr(NULL, rem, num, den );
+}
+
+
+
+void mpi_fdiv_r(struct gcry_mpi *rem, struct gcry_mpi *dividend, struct gcry_mpi *divisor )
+{
+ int divisor_sign = divisor->sign;
+ struct gcry_mpi *temp_divisor = NULL;
+
+ /* We need the original value of the divisor after the remainder has been
+ * preliminary calculated. We have to copy it to temporary space if it's
+ * the same variable as REM. */
+ if( rem == divisor ) {
+ temp_divisor = mpi_copy( divisor );
+ divisor = temp_divisor;
+ }
+
+ _gcry_mpi_tdiv_r( rem, dividend, divisor );
+
+ if( ((divisor_sign?1:0) ^ (dividend->sign?1:0)) && rem->nlimbs )
+ mpi_add( rem, rem, divisor);
+
+ if( temp_divisor )
+ mpi_free(temp_divisor);
+}
+
+
+
+void
+_gcry_mpih_sqr_n_basecase( mpi_ptr_t prodp, mpi_ptr_t up, mpi_size_t size )
+{
+ mpi_size_t i;
+ mpi_limb_t cy_limb;
+ mpi_limb_t v_limb;
+
+ /* Multiply by the first limb in V separately, as the result can be
+ * stored (not added) to PROD. We also avoid a loop for zeroing. */
+ v_limb = up[0];
+ if( v_limb <= 1 ) {
+ if( v_limb == 1 )
+ MPN_COPY( prodp, up, size );
+ else
+ MPN_ZERO(prodp, size);
+ cy_limb = 0;
+ }
+ else
+ cy_limb = _gcry_mpih_mul_1( prodp, up, size, v_limb );
+
+ prodp[size] = cy_limb;
+ prodp++;
+
+ /* For each iteration in the outer loop, multiply one limb from
+ * U with one limb from V, and add it to PROD. */
+ for( i=1; i < size; i++) {
+ v_limb = up[i];
+ if( v_limb <= 1 ) {
+ cy_limb = 0;
+ if( v_limb == 1 )
+ cy_limb = _gcry_mpih_add_n(prodp, prodp, up, size);
+ }
+ else
+ cy_limb = _gcry_mpih_addmul_1(prodp, up, size, v_limb);
+
+ prodp[size] = cy_limb;
+ prodp++;
+ }
+}
+
+
+/* Multiply the natural numbers u (pointed to by UP) and v (pointed to by VP),
+ * both with SIZE limbs, and store the result at PRODP. 2 * SIZE limbs are
+ * always stored. Return the most significant limb.
+ *
+ * Argument constraints:
+ * 1. PRODP != UP and PRODP != VP, i.e. the destination
+ * must be distinct from the multiplier and the multiplicand.
+ *
+ *
+ * Handle simple cases with traditional multiplication.
+ *
+ * This is the most critical code of multiplication. All multiplies rely
+ * on this, both small and huge. Small ones arrive here immediately. Huge
+ * ones arrive here as this is the base case for Karatsuba's recursive
+ * algorithm below.
+ */
+
+static mpi_limb_t
+mul_n_basecase( mpi_ptr_t prodp, mpi_ptr_t up,
+ mpi_ptr_t vp, mpi_size_t size)
+{
+ mpi_size_t i;
+ mpi_limb_t cy;
+ mpi_limb_t v_limb;
+
+ /* Multiply by the first limb in V separately, as the result can be
+ * stored (not added) to PROD. We also avoid a loop for zeroing. */
+ v_limb = vp[0];
+ if( v_limb <= 1 ) {
+ if( v_limb == 1 )
+ MPN_COPY( prodp, up, size );
+ else
+ MPN_ZERO( prodp, size );
+ cy = 0;
+ }
+ else
+ cy = _gcry_mpih_mul_1( prodp, up, size, v_limb );
+
+ prodp[size] = cy;
+ prodp++;
+
+ /* For each iteration in the outer loop, multiply one limb from
+ * U with one limb from V, and add it to PROD. */
+ for( i = 1; i < size; i++ ) {
+ v_limb = vp[i];
+ if( v_limb <= 1 ) {
+ cy = 0;
+ if( v_limb == 1 )
+ cy = _gcry_mpih_add_n(prodp, prodp, up, size);
+ }
+ else
+ cy = _gcry_mpih_addmul_1(prodp, up, size, v_limb);
+
+ prodp[size] = cy;
+ prodp++;
+ }
+
+ return cy;
+}
+
+
+
+void
+_gcry_mpih_sqr_n( mpi_ptr_t prodp,
+ mpi_ptr_t up, mpi_size_t size, mpi_ptr_t tspace)
+{
+ if( size & 1 ) {
+ /* The size is odd, and the code below doesn't handle that.
+ * Multiply the least significant (size - 1) limbs with a recursive
+ * call, and handle the most significant limb of S1 and S2
+ * separately.
+ * A slightly faster way to do this would be to make the Karatsuba
+ * code below behave as if the size were even, and let it check for
+ * odd size in the end. I.e., in essence move this code to the end.
+ * Doing so would save us a recursive call, and potentially make the
+ * stack grow a lot less.
+ */
+ mpi_size_t esize = size - 1; /* even size */
+ mpi_limb_t cy_limb;
+
+ MPN_SQR_N_RECURSE( prodp, up, esize, tspace );
+ cy_limb = _gcry_mpih_addmul_1( prodp + esize, up, esize, up[esize] );
+ prodp[esize + esize] = cy_limb;
+ cy_limb = _gcry_mpih_addmul_1( prodp + esize, up, size, up[esize] );
+
+ prodp[esize + size] = cy_limb;
+ }
+ else {
+ mpi_size_t hsize = size >> 1;
+ mpi_limb_t cy;
+
+ /* Product H. ________________ ________________
+ * |_____U1 x U1____||____U0 x U0_____|
+ * Put result in upper part of PROD and pass low part of TSPACE
+ * as new TSPACE.
+ */
+ MPN_SQR_N_RECURSE(prodp + size, up + hsize, hsize, tspace);
+
+ /* Product M. ________________
+ * |_(U1-U0)(U0-U1)_|
+ */
+ if( _gcry_mpih_cmp( up + hsize, up, hsize) >= 0 )
+ _gcry_mpih_sub_n( prodp, up + hsize, up, hsize);
+ else
+ _gcry_mpih_sub_n (prodp, up, up + hsize, hsize);
+
+ /* Read temporary operands from low part of PROD.
+ * Put result in low part of TSPACE using upper part of TSPACE
+ * as new TSPACE. */
+ MPN_SQR_N_RECURSE(tspace, prodp, hsize, tspace + size);
+
+ /* Add/copy product H */
+ MPN_COPY(prodp + hsize, prodp + size, hsize);
+ cy = _gcry_mpih_add_n(prodp + size, prodp + size,
+ prodp + size + hsize, hsize);
+
+ /* Add product M (if NEGFLG M is a negative number). */
+ cy -= _gcry_mpih_sub_n (prodp + hsize, prodp + hsize, tspace, size);
+
+ /* Product L. ________________ ________________
+ * |________________||____U0 x U0_____|
+ * Read temporary operands from low part of PROD.
+ * Put result in low part of TSPACE using upper part of TSPACE
+ * as new TSPACE. */
+ MPN_SQR_N_RECURSE (tspace, up, hsize, tspace + size);
+
+ /* Add/copy Product L (twice). */
+ cy += _gcry_mpih_add_n (prodp + hsize, prodp + hsize, tspace, size);
+ if( cy )
+ _gcry_mpih_add_1(prodp + hsize + size, prodp + hsize + size,
+ hsize, cy);
+
+ MPN_COPY(prodp, tspace, hsize);
+ cy = _gcry_mpih_add_n (prodp + hsize, prodp + hsize, tspace + hsize, hsize);
+ if( cy )
+ _gcry_mpih_add_1 (prodp + size, prodp + size, size, 1);
+ }
+}
+
+
+static void
+mul_n( mpi_ptr_t prodp, mpi_ptr_t up, mpi_ptr_t vp,
+ mpi_size_t size, mpi_ptr_t tspace )
+{
+ if( size & 1 ) {
+ /* The size is odd, and the code below doesn't handle that.
+ * Multiply the least significant (size - 1) limbs with a recursive
+ * call, and handle the most significant limb of S1 and S2
+ * separately.
+ * A slightly faster way to do this would be to make the Karatsuba
+ * code below behave as if the size were even, and let it check for
+ * odd size in the end. I.e., in essence move this code to the end.
+ * Doing so would save us a recursive call, and potentially make the
+ * stack grow a lot less.
+ */
+ mpi_size_t esize = size - 1; /* even size */
+ mpi_limb_t cy_limb;
+
+ MPN_MUL_N_RECURSE( prodp, up, vp, esize, tspace );
+ cy_limb = _gcry_mpih_addmul_1( prodp + esize, up, esize, vp[esize] );
+ prodp[esize + esize] = cy_limb;
+ cy_limb = _gcry_mpih_addmul_1( prodp + esize, vp, size, up[esize] );
+ prodp[esize + size] = cy_limb;
+ }
+ else {
+ /* Anatolij Alekseevich Karatsuba's divide-and-conquer algorithm.
+ *
+ * Split U in two pieces, U1 and U0, such that
+ * U = U0 + U1*(B**n),
+ * and V in V1 and V0, such that
+ * V = V0 + V1*(B**n).
+ *
+ * UV is then computed recursively using the identity
+ *
+ * 2n n n n
+ * UV = (B + B )U V + B (U -U )(V -V ) + (B + 1)U V
+ * 1 1 1 0 0 1 0 0
+ *
+ * Where B = 2**BITS_PER_MP_LIMB.
+ */
+ mpi_size_t hsize = size >> 1;
+ mpi_limb_t cy;
+ int negflg;
+
+ /* Product H. ________________ ________________
+ * |_____U1 x V1____||____U0 x V0_____|
+ * Put result in upper part of PROD and pass low part of TSPACE
+ * as new TSPACE.
+ */
+ MPN_MUL_N_RECURSE(prodp + size, up + hsize, vp + hsize, hsize, tspace);
+
+ /* Product M. ________________
+ * |_(U1-U0)(V0-V1)_|
+ */
+ if( _gcry_mpih_cmp(up + hsize, up, hsize) >= 0 ) {
+ _gcry_mpih_sub_n(prodp, up + hsize, up, hsize);
+ negflg = 0;
+ }
+ else {
+ _gcry_mpih_sub_n(prodp, up, up + hsize, hsize);
+ negflg = 1;
+ }
+ if( _gcry_mpih_cmp(vp + hsize, vp, hsize) >= 0 ) {
+ _gcry_mpih_sub_n(prodp + hsize, vp + hsize, vp, hsize);
+ negflg ^= 1;
+ }
+ else {
+ _gcry_mpih_sub_n(prodp + hsize, vp, vp + hsize, hsize);
+ /* No change of NEGFLG. */
+ }
+ /* Read temporary operands from low part of PROD.
+ * Put result in low part of TSPACE using upper part of TSPACE
+ * as new TSPACE.
+ */
+ MPN_MUL_N_RECURSE(tspace, prodp, prodp + hsize, hsize, tspace + size);
+
+ /* Add/copy product H. */
+ MPN_COPY (prodp + hsize, prodp + size, hsize);
+ cy = _gcry_mpih_add_n( prodp + size, prodp + size,
+ prodp + size + hsize, hsize);
+
+ /* Add product M (if NEGFLG M is a negative number) */
+ if(negflg)
+ cy -= _gcry_mpih_sub_n(prodp + hsize, prodp + hsize, tspace, size);
+ else
+ cy += _gcry_mpih_add_n(prodp + hsize, prodp + hsize, tspace, size);
+
+ /* Product L. ________________ ________________
+ * |________________||____U0 x V0_____|
+ * Read temporary operands from low part of PROD.
+ * Put result in low part of TSPACE using upper part of TSPACE
+ * as new TSPACE.
+ */
+ MPN_MUL_N_RECURSE(tspace, up, vp, hsize, tspace + size);
+
+ /* Add/copy Product L (twice) */
+
+ cy += _gcry_mpih_add_n(prodp + hsize, prodp + hsize, tspace, size);
+ if( cy )
+ _gcry_mpih_add_1(prodp + hsize + size, prodp + hsize + size, hsize, cy);
+
+ MPN_COPY(prodp, tspace, hsize);
+ cy = _gcry_mpih_add_n(prodp + hsize, prodp + hsize, tspace + hsize, hsize);
+ if( cy )
+ _gcry_mpih_add_1(prodp + size, prodp + size, size, 1);
+ }
+}
+
+
+void
+_gcry_mpih_mul_karatsuba_case( mpi_ptr_t prodp,
+ mpi_ptr_t up, mpi_size_t usize,
+ mpi_ptr_t vp, mpi_size_t vsize,
+ struct karatsuba_ctx *ctx )
+{
+ mpi_limb_t cy;
+
+ if( !ctx->tspace || ctx->tspace_size < vsize ) {
+ if( ctx->tspace )
+ mpi_free_limb_space( ctx->tspace );
+ ctx->tspace = mpi_alloc_limb_space( 2 * vsize,
+ gcry_is_secure( up ) || gcry_is_secure( vp ) );
+ ctx->tspace_size = vsize;
+ }
+
+ MPN_MUL_N_RECURSE( prodp, up, vp, vsize, ctx->tspace );
+
+ prodp += vsize;
+ up += vsize;
+ usize -= vsize;
+ if( usize >= vsize ) {
+ if( !ctx->tp || ctx->tp_size < vsize ) {
+ if( ctx->tp )
+ mpi_free_limb_space( ctx->tp );
+ ctx->tp = mpi_alloc_limb_space( 2 * vsize, gcry_is_secure( up )
+ || gcry_is_secure( vp ) );
+ ctx->tp_size = vsize;
+ }
+
+ do {
+ MPN_MUL_N_RECURSE( ctx->tp, up, vp, vsize, ctx->tspace );
+ cy = _gcry_mpih_add_n( prodp, prodp, ctx->tp, vsize );
+ _gcry_mpih_add_1( prodp + vsize, ctx->tp + vsize, vsize, cy );
+ prodp += vsize;
+ up += vsize;
+ usize -= vsize;
+ } while( usize >= vsize );
+ }
+
+ if( usize ) {
+ if( usize < KARATSUBA_THRESHOLD ) {
+ _gcry_mpih_mul( ctx->tspace, vp, vsize, up, usize );
+ }
+ else {
+ if( !ctx->next ) {
+ ctx->next = gcry_xcalloc( 1, sizeof *ctx );
+ }
+ _gcry_mpih_mul_karatsuba_case( ctx->tspace,
+ vp, vsize,
+ up, usize,
+ ctx->next );
+ }
+
+ cy = _gcry_mpih_add_n( prodp, prodp, ctx->tspace, vsize);
+ _gcry_mpih_add_1( prodp + vsize, ctx->tspace + vsize, usize, cy );
+ }
+}
+
+
+void
+_gcry_mpih_release_karatsuba_ctx( struct karatsuba_ctx *ctx )
+{
+ struct karatsuba_ctx *ctx2;
+
+ if( ctx->tp )
+ mpi_free_limb_space( ctx->tp );
+ if( ctx->tspace )
+ mpi_free_limb_space( ctx->tspace );
+ for( ctx=ctx->next; ctx; ctx = ctx2 ) {
+ ctx2 = ctx->next;
+ if( ctx->tp )
+ mpi_free_limb_space( ctx->tp );
+ if( ctx->tspace )
+ mpi_free_limb_space( ctx->tspace );
+ gcry_free( ctx );
+ }
+}
+
+/* Multiply the natural numbers u (pointed to by UP, with USIZE limbs)
+ * and v (pointed to by VP, with VSIZE limbs), and store the result at
+ * PRODP. USIZE + VSIZE limbs are always stored, but if the input
+ * operands are normalized. Return the most significant limb of the
+ * result.
+ *
+ * NOTE: The space pointed to by PRODP is overwritten before finished
+ * with U and V, so overlap is an error.
+ *
+ * Argument constraints:
+ * 1. USIZE >= VSIZE.
+ * 2. PRODP != UP and PRODP != VP, i.e. the destination
+ * must be distinct from the multiplier and the multiplicand.
+ */
+
+mpi_limb_t
+_gcry_mpih_mul( mpi_ptr_t prodp, mpi_ptr_t up, mpi_size_t usize,
+ mpi_ptr_t vp, mpi_size_t vsize)
+{
+ mpi_ptr_t prod_endp = prodp + usize + vsize - 1;
+ mpi_limb_t cy;
+ struct karatsuba_ctx ctx;
+
+ if( vsize < KARATSUBA_THRESHOLD ) {
+ mpi_size_t i;
+ mpi_limb_t v_limb;
+
+ if( !vsize )
+ return 0;
+
+ /* Multiply by the first limb in V separately, as the result can be
+ * stored (not added) to PROD. We also avoid a loop for zeroing. */
+ v_limb = vp[0];
+ if( v_limb <= 1 ) {
+ if( v_limb == 1 )
+ MPN_COPY( prodp, up, usize );
+ else
+ MPN_ZERO( prodp, usize );
+ cy = 0;
+ }
+ else
+ cy = _gcry_mpih_mul_1( prodp, up, usize, v_limb );
+
+ prodp[usize] = cy;
+ prodp++;
+
+ /* For each iteration in the outer loop, multiply one limb from
+ * U with one limb from V, and add it to PROD. */
+ for( i = 1; i < vsize; i++ ) {
+ v_limb = vp[i];
+ if( v_limb <= 1 ) {
+ cy = 0;
+ if( v_limb == 1 )
+ cy = _gcry_mpih_add_n(prodp, prodp, up, usize);
+ }
+ else
+ cy = _gcry_mpih_addmul_1(prodp, up, usize, v_limb);
+
+ prodp[usize] = cy;
+ prodp++;
+ }
+
+ return cy;
+ }
+
+ memset( &ctx, 0, sizeof ctx );
+ _gcry_mpih_mul_karatsuba_case( prodp, up, usize, vp, vsize, &ctx );
+ _gcry_mpih_release_karatsuba_ctx( &ctx );
+ return *prod_endp;
+}
+
+
+
+
+
+
+
+
+/****************
+ * RES = BASE ^ EXP mod MOD
+ */
+void mpi_powm(struct gcry_mpi *res, struct gcry_mpi *base, struct gcry_mpi *exp, struct gcry_mpi *mod)
+{
+ mpi_ptr_t rp, ep, mp, bp;
+ mpi_size_t esize, msize, bsize, rsize;
+ int esign, msign, bsign, rsign;
+ int esec, msec, bsec, rsec;
+ mpi_size_t size;
+ int mod_shift_cnt;
+ int negative_result;
+ mpi_ptr_t mp_marker=NULL, bp_marker=NULL, ep_marker=NULL;
+ mpi_ptr_t xp_marker=NULL;
+ int assign_rp=0;
+ mpi_ptr_t tspace = NULL;
+ mpi_size_t tsize=0; /* to avoid compiler warning */
+ /* fixme: we should check that the warning is void*/
+
+ esize = exp->nlimbs;
+ msize = mod->nlimbs;
+ size = 2 * msize;
+ esign = exp->sign;
+ msign = mod->sign;
+
+ esec = mpi_is_secure(exp);
+ msec = mpi_is_secure(mod);
+ bsec = mpi_is_secure(base);
+ rsec = mpi_is_secure(res);
+
+ rp = res->d;
+ ep = exp->d;
+
+ if( !msize )
+ msize = 1 / msize; /* provoke a signal */
+
+ if( !esize ) {
+ /* Exponent is zero, result is 1 mod MOD, i.e., 1 or 0
+ * depending on if MOD equals 1. */
+ rp[0] = 1;
+ res->nlimbs = (msize == 1 && mod->d[0] == 1) ? 0 : 1;
+ res->sign = 0;
+ goto leave;
+ }
+
+ /* Normalize MOD (i.e. make its most significant bit set) as required by
+ * mpn_divrem. This will make the intermediate values in the calculation
+ * slightly larger, but the correct result is obtained after a final
+ * reduction using the original MOD value. */
+ mp = mp_marker = mpi_alloc_limb_space(msize, msec);
+ count_leading_zeros( mod_shift_cnt, mod->d[msize-1] );
+ if( mod_shift_cnt )
+ _gcry_mpih_lshift( mp, mod->d, msize, mod_shift_cnt );
+ else
+ MPN_COPY( mp, mod->d, msize );
+
+ bsize = base->nlimbs;
+ bsign = base->sign;
+ if( bsize > msize ) { /* The base is larger than the module. Reduce it. */
+ /* Allocate (BSIZE + 1) with space for remainder and quotient.
+ * (The quotient is (bsize - msize + 1) limbs.) */
+ bp = bp_marker = mpi_alloc_limb_space( bsize + 1, bsec );
+ MPN_COPY( bp, base->d, bsize );
+ /* We don't care about the quotient, store it above the remainder,
+ * at BP + MSIZE. */
+ _gcry_mpih_divrem( bp + msize, 0, bp, bsize, mp, msize );
+ bsize = msize;
+ /* Canonicalize the base, since we are going to multiply with it
+ * quite a few times. */
+ MPN_NORMALIZE( bp, bsize );
+ }
+ else
+ bp = base->d;
+
+ if( !bsize ) {
+ res->nlimbs = 0;
+ res->sign = 0;
+ goto leave;
+ }
+
+ if( res->alloced < size ) {
+ /* We have to allocate more space for RES. If any of the input
+ * parameters are identical to RES, defer deallocation of the old
+ * space. */
+ if( rp == ep || rp == mp || rp == bp ) {
+ rp = mpi_alloc_limb_space( size, rsec );
+ assign_rp = 1;
+ }
+ else {
+ mpi_resize( res, size );
+ rp = res->d;
+ }
+ }
+ else { /* Make BASE, EXP and MOD not overlap with RES. */
+ if( rp == bp ) {
+ /* RES and BASE are identical. Allocate temp. space for BASE. */
+ assert( !bp_marker );
+ bp = bp_marker = mpi_alloc_limb_space( bsize, bsec );
+ MPN_COPY(bp, rp, bsize);
+ }
+ if( rp == ep ) {
+ /* RES and EXP are identical. Allocate temp. space for EXP. */
+ ep = ep_marker = mpi_alloc_limb_space( esize, esec );
+ MPN_COPY(ep, rp, esize);
+ }
+ if( rp == mp ) {
+ /* RES and MOD are identical. Allocate temporary space for MOD.*/
+ assert( !mp_marker );
+ mp = mp_marker = mpi_alloc_limb_space( msize, msec );
+ MPN_COPY(mp, rp, msize);
+ }
+ }
+
+ MPN_COPY( rp, bp, bsize );
+ rsize = bsize;
+ rsign = bsign;
+
+ {
+ mpi_size_t i;
+ mpi_ptr_t xp = xp_marker = mpi_alloc_limb_space( 2 * (msize + 1), msec );
+ int c;
+ mpi_limb_t e;
+ mpi_limb_t carry_limb;
+ struct karatsuba_ctx karactx;
+
+ memset( &karactx, 0, sizeof karactx );
+ negative_result = (ep[0] & 1) && base->sign;
+
+ i = esize - 1;
+ e = ep[i];
+ count_leading_zeros (c, e);
+ e = (e << c) << 1; /* shift the exp bits to the left, lose msb */
+ c = BITS_PER_MPI_LIMB - 1 - c;
+
+ /* Main loop.
+ *
+ * Make the result be pointed to alternately by XP and RP. This
+ * helps us avoid block copying, which would otherwise be necessary
+ * with the overlap restrictions of _gcry_mpih_divmod. With 50% probability
+ * the result after this loop will be in the area originally pointed
+ * by RP (==RES->d), and with 50% probability in the area originally
+ * pointed to by XP.
+ */
+
+ for(;;) {
+ while( c ) {
+ mpi_ptr_t tp;
+ mpi_size_t xsize;
+
+ /*mpih_mul_n(xp, rp, rp, rsize);*/
+ if( rsize < KARATSUBA_THRESHOLD )
+ _gcry_mpih_sqr_n_basecase( xp, rp, rsize );
+ else {
+ if( !tspace ) {
+ tsize = 2 * rsize;
+ tspace = mpi_alloc_limb_space( tsize, 0 );
+ }
+ else if( tsize < (2*rsize) ) {
+ mpi_free_limb_space( tspace );
+ tsize = 2 * rsize;
+ tspace = mpi_alloc_limb_space( tsize, 0 );
+ }
+ _gcry_mpih_sqr_n( xp, rp, rsize, tspace );
+ }
+
+ xsize = 2 * rsize;
+ if( xsize > msize ) {
+ _gcry_mpih_divrem(xp + msize, 0, xp, xsize, mp, msize);
+ xsize = msize;
+ }
+
+ tp = rp; rp = xp; xp = tp;
+ rsize = xsize;
+
+ if( (mpi_limb_signed_t)e < 0 ) {
+ /*mpih_mul( xp, rp, rsize, bp, bsize );*/
+ if( bsize < KARATSUBA_THRESHOLD ) {
+ _gcry_mpih_mul( xp, rp, rsize, bp, bsize );
+ }
+ else {
+ _gcry_mpih_mul_karatsuba_case(
+ xp, rp, rsize, bp, bsize, &karactx );
+ }
+
+ xsize = rsize + bsize;
+ if( xsize > msize ) {
+ _gcry_mpih_divrem(xp + msize, 0, xp, xsize, mp, msize);
+ xsize = msize;
+ }
+
+ tp = rp; rp = xp; xp = tp;
+ rsize = xsize;
+ }
+ e <<= 1;
+ c--;
+ }
+
+ i--;
+ if( i < 0 )
+ break;
+ e = ep[i];
+ c = BITS_PER_MPI_LIMB;
+ }
+
+ /* We shifted MOD, the modulo reduction argument, left MOD_SHIFT_CNT
+ * steps. Adjust the result by reducing it with the original MOD.
+ *
+ * Also make sure the result is put in RES->d (where it already
+ * might be, see above).
+ */
+ if( mod_shift_cnt ) {
+ carry_limb = _gcry_mpih_lshift( res->d, rp, rsize, mod_shift_cnt);
+ rp = res->d;
+ if( carry_limb ) {
+ rp[rsize] = carry_limb;
+ rsize++;
+ }
+ }
+ else {
+ MPN_COPY( res->d, rp, rsize);
+ rp = res->d;
+ }
+
+ if( rsize >= msize ) {
+ _gcry_mpih_divrem(rp + msize, 0, rp, rsize, mp, msize);
+ rsize = msize;
+ }
+
+ /* Remove any leading zero words from the result. */
+ if( mod_shift_cnt )
+ _gcry_mpih_rshift( rp, rp, rsize, mod_shift_cnt);
+ MPN_NORMALIZE (rp, rsize);
+
+ _gcry_mpih_release_karatsuba_ctx( &karactx );
+ }
+
+ if( negative_result && rsize ) {
+ if( mod_shift_cnt )
+ _gcry_mpih_rshift( mp, mp, msize, mod_shift_cnt);
+ _gcry_mpih_sub( rp, mp, msize, rp, rsize);
+ rsize = msize;
+ rsign = msign;
+ MPN_NORMALIZE(rp, rsize);
+ }
+ res->nlimbs = rsize;
+ res->sign = rsign;
+
+ leave:
+ if( assign_rp ) mpi_assign_limb_space( res, rp, size );
+ if( mp_marker ) mpi_free_limb_space( mp_marker );
+ if( bp_marker ) mpi_free_limb_space( bp_marker );
+ if( ep_marker ) mpi_free_limb_space( ep_marker );
+ if( xp_marker ) mpi_free_limb_space( xp_marker );
+ if( tspace ) mpi_free_limb_space( tspace );
+}
+
+
+void mpi_sub(struct gcry_mpi *w, struct gcry_mpi *u, struct gcry_mpi *v)
+{
+ if( w == v ) {
+ struct gcry_mpi *vv = mpi_copy(v);
+ vv->sign = !vv->sign;
+ mpi_add( w, u, vv );
+ mpi_free(vv);
+ }
+ else {
+ /* fixme: this is not thread-save (we temp. modify v) */
+ v->sign = !v->sign;
+ mpi_add( w, u, v );
+ v->sign = !v->sign;
+ }
+}
+
+void _gcry_mpi_fdiv_r(struct gcry_mpi *rem, struct gcry_mpi *dividend, struct gcry_mpi *divisor )
+{
+ int divisor_sign = divisor->sign;
+ struct gcry_mpi * temp_divisor = NULL;
+
+ /* We need the original value of the divisor after the remainder has been
+ * preliminary calculated. We have to copy it to temporary space if it's
+ * the same variable as REM. */
+ if( rem == divisor ) {
+ temp_divisor = mpi_copy( divisor );
+ divisor = temp_divisor;
+ }
+
+ _gcry_mpi_tdiv_r( rem, dividend, divisor );
+
+ if( ((divisor_sign?1:0) ^ (dividend->sign?1:0)) && rem->nlimbs )
+ mpi_add( rem, rem, divisor);
+
+ if( temp_divisor )
+ mpi_free(temp_divisor);
+}
+
+
+
+
+void mpi_mul(struct gcry_mpi *w, struct gcry_mpi *u,struct gcry_mpi *v)
+{
+ mpi_size_t usize, vsize, wsize;
+ mpi_ptr_t up, vp, wp;
+ mpi_limb_t cy;
+ int usign, vsign, usecure, vsecure, sign_product;
+ int assign_wp=0;
+ mpi_ptr_t tmp_limb=NULL;
+
+
+ if( u->nlimbs < v->nlimbs ) { /* Swap U and V. */
+ usize = v->nlimbs;
+ usign = v->sign;
+ usecure = mpi_is_secure(v);
+ up = v->d;
+ vsize = u->nlimbs;
+ vsign = u->sign;
+ vsecure = mpi_is_secure(u);
+ vp = u->d;
+ }
+ else {
+ usize = u->nlimbs;
+ usign = u->sign;
+ usecure = mpi_is_secure(u);
+ up = u->d;
+ vsize = v->nlimbs;
+ vsign = v->sign;
+ vsecure = mpi_is_secure(v);
+ vp = v->d;
+ }
+ sign_product = usign ^ vsign;
+ wp = w->d;
+
+ /* Ensure W has space enough to store the result. */
+ wsize = usize + vsize;
+ if ( !mpi_is_secure (w) && (mpi_is_secure (u) || mpi_is_secure (v)) ) {
+ /* w is not allocated in secure space but u or v is. To make sure
+ * that no temporray results are stored in w, we temporary use
+ * a newly allocated limb space for w */
+ wp = mpi_alloc_limb_space( wsize, 1 );
+ assign_wp = 2; /* mark it as 2 so that we can later copy it back to
+ * mormal memory */
+ }
+ else if( w->alloced < wsize ) {
+ if( wp == up || wp == vp ) {
+ wp = mpi_alloc_limb_space( wsize, mpi_is_secure(w) );
+ assign_wp = 1;
+ }
+ else {
+ mpi_resize(w, wsize );
+ wp = w->d;
+ }
+ }
+ else { /* Make U and V not overlap with W. */
+ if( wp == up ) {
+ /* W and U are identical. Allocate temporary space for U. */
+ up = tmp_limb = mpi_alloc_limb_space( usize, usecure );
+ /* Is V identical too? Keep it identical with U. */
+ if( wp == vp )
+ vp = up;
+ /* Copy to the temporary space. */
+ MPN_COPY( up, wp, usize );
+ }
+ else if( wp == vp ) {
+ /* W and V are identical. Allocate temporary space for V. */
+ vp = tmp_limb = mpi_alloc_limb_space( vsize, vsecure );
+ /* Copy to the temporary space. */
+ MPN_COPY( vp, wp, vsize );
+ }
+ }
+
+ if( !vsize )
+ wsize = 0;
+ else {
+ cy = _gcry_mpih_mul( wp, up, usize, vp, vsize );
+ wsize -= cy? 0:1;
+ }
+
+ if( assign_wp ) {
+ if (assign_wp == 2) {
+ /* copy the temp wp from secure memory back to normal memory */
+ mpi_ptr_t tmp_wp = mpi_alloc_limb_space (wsize, 0);
+ MPN_COPY (tmp_wp, wp, wsize);
+ mpi_free_limb_space (wp);
+ wp = tmp_wp;
+ }
+ mpi_assign_limb_space( w, wp, wsize );
+ }
+ w->nlimbs = wsize;
+ w->sign = sign_product;
+ if( tmp_limb )
+ mpi_free_limb_space( tmp_limb );
+}
+
+
+void mpi_mulm(struct gcry_mpi *w, struct gcry_mpi *u,struct gcry_mpi *v, struct gcry_mpi *m)
+{
+ mpi_mul(w, u, v);
+ _gcry_mpi_fdiv_r( w, w, m );
+}
+
--- /dev/null
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include "foo.h"
+
+
+mpi_limb_t
+_gcry_mpih_sub_n( mpi_ptr_t res_ptr, mpi_ptr_t s1_ptr,
+ mpi_ptr_t s2_ptr, mpi_size_t size)
+{
+ mpi_limb_t x, y, cy;
+ mpi_size_t j;
+
+ /* The loop counter and index J goes from -SIZE to -1. This way
+ the loop becomes faster. */
+ j = -size;
+
+ /* Offset the base pointers to compensate for the negative indices. */
+ s1_ptr -= j;
+ s2_ptr -= j;
+ res_ptr -= j;
+
+ cy = 0;
+ do {
+ y = s2_ptr[j];
+ x = s1_ptr[j];
+ y += cy; /* add previous carry to subtrahend */
+ cy = y < cy; /* get out carry from that addition */
+ y = x - y; /* main subtract */
+ cy += y > x; /* get out carry from the subtract, combine */
+ res_ptr[j] = y;
+ } while( ++j );
+
+ return cy;
+}
+
+
+mpi_limb_t
+_gcry_mpih_add_n( mpi_ptr_t res_ptr, mpi_ptr_t s1_ptr,
+ mpi_ptr_t s2_ptr, mpi_size_t size)
+{
+ mpi_limb_t x, y, cy;
+ mpi_size_t j;
+
+ /* The loop counter and index J goes from -SIZE to -1. This way
+ the loop becomes faster. */
+ j = -size;
+
+ /* Offset the base pointers to compensate for the negative indices. */
+ s1_ptr -= j;
+ s2_ptr -= j;
+ res_ptr -= j;
+
+ cy = 0;
+ do {
+ y = s2_ptr[j];
+ x = s1_ptr[j];
+ y += cy; /* add previous carry to one addend */
+ cy = y < cy; /* get out carry from that addition */
+ y += x; /* add other addend */
+ cy += y < x; /* get out carry from that add, combine */
+ res_ptr[j] = y;
+ } while( ++j );
+
+ return cy;
+}
+
+/* Shift U (pointed to by UP and USIZE digits long) CNT bits to the left
+ * and store the USIZE least significant digits of the result at WP.
+ * Return the bits shifted out from the most significant digit.
+ *
+ * Argument constraints:
+ * 1. 0 < CNT < BITS_PER_MP_LIMB
+ * 2. If the result is to be written over the input, WP must be >= UP.
+ */
+
+mpi_limb_t
+_gcry_mpih_lshift( mpi_ptr_t wp, mpi_ptr_t up, mpi_size_t usize,
+ unsigned int cnt)
+{
+ mpi_limb_t high_limb, low_limb;
+ unsigned sh_1, sh_2;
+ mpi_size_t i;
+ mpi_limb_t retval;
+
+ sh_1 = cnt;
+ wp += 1;
+ sh_2 = BITS_PER_MPI_LIMB - sh_1;
+ i = usize - 1;
+ low_limb = up[i];
+ retval = low_limb >> sh_2;
+ high_limb = low_limb;
+ while( --i >= 0 ) {
+ low_limb = up[i];
+ wp[i] = (high_limb << sh_1) | (low_limb >> sh_2);
+ high_limb = low_limb;
+ }
+ wp[i] = high_limb << sh_1;
+
+ return retval;
+}
+
+
+/* Shift U (pointed to by UP and USIZE limbs long) CNT bits to the right
+ * and store the USIZE least significant limbs of the result at WP.
+ * The bits shifted out to the right are returned.
+ *
+ * Argument constraints:
+ * 1. 0 < CNT < BITS_PER_MP_LIMB
+ * 2. If the result is to be written over the input, WP must be <= UP.
+ */
+mpi_limb_t
+_gcry_mpih_rshift( mpi_ptr_t wp, mpi_ptr_t up, mpi_size_t usize, unsigned cnt)
+{
+ mpi_limb_t high_limb, low_limb;
+ unsigned sh_1, sh_2;
+ mpi_size_t i;
+ mpi_limb_t retval;
+
+ sh_1 = cnt;
+ wp -= 1;
+ sh_2 = BITS_PER_MPI_LIMB - sh_1;
+ high_limb = up[0];
+ retval = high_limb << sh_2;
+ low_limb = high_limb;
+ for( i=1; i < usize; i++) {
+ high_limb = up[i];
+ wp[i] = (low_limb >> sh_1) | (high_limb << sh_2);
+ low_limb = high_limb;
+ }
+ wp[i] = low_limb >> sh_1;
+
+ return retval;
+}
+
+mpi_limb_t
+_gcry_mpih_submul_1( mpi_ptr_t res_ptr, mpi_ptr_t s1_ptr,
+ mpi_size_t s1_size, mpi_limb_t s2_limb)
+{
+ mpi_limb_t cy_limb;
+ mpi_size_t j;
+ mpi_limb_t prod_high, prod_low;
+ mpi_limb_t x;
+
+ /* The loop counter and index J goes from -SIZE to -1. This way
+ * the loop becomes faster. */
+ j = -s1_size;
+ res_ptr -= j;
+ s1_ptr -= j;
+
+ cy_limb = 0;
+ do {
+ umul_ppmm( prod_high, prod_low, s1_ptr[j], s2_limb);
+
+ prod_low += cy_limb;
+ cy_limb = (prod_low < cy_limb?1:0) + prod_high;
+
+ x = res_ptr[j];
+ prod_low = x - prod_low;
+ cy_limb += prod_low > x?1:0;
+ res_ptr[j] = prod_low;
+ } while( ++j );
+
+ return cy_limb;
+}
+
+
+mpi_limb_t
+_gcry_mpih_mul_1( mpi_ptr_t res_ptr, mpi_ptr_t s1_ptr, mpi_size_t s1_size,
+ mpi_limb_t s2_limb)
+{
+ mpi_limb_t cy_limb;
+ mpi_size_t j;
+ mpi_limb_t prod_high, prod_low;
+
+ /* The loop counter and index J goes from -S1_SIZE to -1. This way
+ * the loop becomes faster. */
+ j = -s1_size;
+
+ /* Offset the base pointers to compensate for the negative indices. */
+ s1_ptr -= j;
+ res_ptr -= j;
+
+ cy_limb = 0;
+ do {
+ umul_ppmm( prod_high, prod_low, s1_ptr[j], s2_limb );
+ prod_low += cy_limb;
+ cy_limb = (prod_low < cy_limb?1:0) + prod_high;
+ res_ptr[j] = prod_low;
+ } while( ++j );
+
+ return cy_limb;
+}
+
+
+mpi_limb_t
+_gcry_mpih_addmul_1( mpi_ptr_t res_ptr, mpi_ptr_t s1_ptr,
+ mpi_size_t s1_size, mpi_limb_t s2_limb)
+{
+ mpi_limb_t cy_limb;
+ mpi_size_t j;
+ mpi_limb_t prod_high, prod_low;
+ mpi_limb_t x;
+
+ /* The loop counter and index J goes from -SIZE to -1. This way
+ * the loop becomes faster. */
+ j = -s1_size;
+ res_ptr -= j;
+ s1_ptr -= j;
+
+ cy_limb = 0;
+ do {
+ umul_ppmm( prod_high, prod_low, s1_ptr[j], s2_limb );
+
+ prod_low += cy_limb;
+ cy_limb = (prod_low < cy_limb?1:0) + prod_high;
+
+ x = res_ptr[j];
+ prod_low = x + prod_low;
+ cy_limb += prod_low < x?1:0;
+ res_ptr[j] = prod_low;
+ } while ( ++j );
+ return cy_limb;
+}
+
+
--- /dev/null
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/string.h>
+
+#include "gnupg_mpi_mpi.h"
+
+extern char rsa_key_n[];
+extern char rsa_key_e[];
+extern const int rsa_key_n_size;
+extern const int rsa_key_e_size;
+
+static MPI public_key[2];
+
+int rsa_check_sig(char *sig, u8 *sha1)
+{
+ int nread;
+
+ /* initialize our keys */
+ nread = rsa_key_n_size;
+ public_key[0] = mpi_read_from_buffer(&rsa_key_n[0], &nread, 0);
+ nread = rsa_key_e_size;
+ public_key[1] = mpi_read_from_buffer(&rsa_key_e[0], &nread, 0);
+
+
+ return 0;
+}
+EXPORT_SYMBOL(rsa_check_sig);
+
+
--- /dev/null
+/* rsa.h
+ * Copyright (C) 1997,1998 by Werner Koch (dd9jn)
+ * Copyright (C) 2000, 2001, 2002 Free Software Foundation, Inc.
+ *
+ * This file is part of Libgcrypt.
+ *
+ * Libgcrypt is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * Libgcrypt 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+#ifndef G10_RSA_H
+#define G10_RSA_H
+
+int _gcry_rsa_generate( int algo, unsigned nbits, struct gcry_mpi * *skey, struct gcry_mpi * **retfactors );
+int _gcry_rsa_check_secret_key( int algo, struct gcry_mpi * *skey );
+int _gcry_rsa_encrypt( int algo, struct gcry_mpi * *resarr, struct gcry_mpi * data, struct gcry_mpi * *pkey );
+int _gcry_rsa_decrypt( int algo, struct gcry_mpi * *result, struct gcry_mpi * *data, struct gcry_mpi * *skey );
+int _gcry_rsa_sign( int algo, struct gcry_mpi * *resarr, struct gcry_mpi * data, struct gcry_mpi * *skey );
+int _gcry_rsa_verify( int algo, struct gcry_mpi * hash, struct gcry_mpi * *data, struct gcry_mpi * *pkey,
+ int (*cmp)(void *, struct gcry_mpi *), void *opaquev );
+unsigned _gcry_rsa_get_nbits( int algo, struct gcry_mpi * *pkey );
+const char *_gcry_rsa_get_info( int algo, int *npkey, int *nskey,
+ int *nenc, int *nsig, int *use );
+
+
+#endif /*G10_RSA_H*/
--- /dev/null
+const char rsa_key_n[] = {
+0x04, 0x00, 0xb6, 0x3a, 0x9f, 0x93, 0x4c, 0x3e, 0xec, 0xb9, 0x0a, 0xd7, 0x87, 0x4b, 0x37, 0xd9, 0x2e, 0xcc, 0xfb, 0x2a, 0xad, 0x51, 0x6b, 0x67, 0x61, 0x23, 0x96, 0x5c, 0x3d, 0xdb, 0x86, 0x9b, 0x5a, 0x67, 0xfe, 0x44, 0x3d, 0x38, 0x50, 0xe5, 0x8d, 0x10, 0xe3, 0x8c, 0x1e, 0x4a, 0xfb, 0xd4, 0x37, 0x4a, 0x81, 0x36, 0xc7, 0x12, 0x0f, 0x22, 0x3d, 0x7d, 0x9e, 0x35, 0x99, 0xb1, 0xbd, 0x24, 0x97, 0x98, 0x4c, 0xb5, 0xc4, 0x5c, 0x7b, 0x44, 0xe7, 0x96, 0x4d, 0x43, 0xb3, 0x7b, 0x2d, 0xb7, 0x13, 0x7c, 0xb7, 0x9a, 0x54, 0xe8, 0xc4, 0x3e, 0xc7, 0x72, 0xba, 0x2e, 0xa3, 0xa6, 0x98, 0xc8, 0x25, 0xd6, 0x9e, 0x45, 0x1b, 0xea, 0xc3, 0x73, 0xc6, 0xad, 0x02, 0xb7, 0x6a, 0x3f, 0xb5, 0x83, 0x38, 0x49, 0x2a, 0x29, 0x1c, 0x1b, 0x52, 0x48, 0x7b, 0x49, 0x40, 0xee, 0x95, 0x3f, 0x2d, 0x78, 0x02, 0xed, };
+
+const int rsa_key_n_size = 130;
+const char rsa_key_e[] = {
+0x00, 0x06, 0x29, };
+const int rsa_key_e_size = 3;
+const char rsa_key[] =
+ "\x98\x8b\x04\x3f\x84\x97\x99\x01\x04\x00\xb6\x3a\x9f\x93\x4c\x3e"
+ "\xec\xb9\x0a\xd7\x87\x4b\x37\xd9\x2e\xcc\xfb\x2a\xad\x51\x6b\x67"
+ "\x61\x23\x96\x5c\x3d\xdb\x86\x9b\x5a\x67\xfe\x44\x3d\x38\x50\xe5"
+ "\x8d\x10\xe3\x8c\x1e\x4a\xfb\xd4\x37\x4a\x81\x36\xc7\x12\x0f\x22"
+ "\x3d\x7d\x9e\x35\x99\xb1\xbd\x24\x97\x98\x4c\xb5\xc4\x5c\x7b\x44"
+ "\xe7\x96\x4d\x43\xb3\x7b\x2d\xb7\x13\x7c\xb7\x9a\x54\xe8\xc4\x3e"
+ "\xc7\x72\xba\x2e\xa3\xa6\x98\xc8\x25\xd6\x9e\x45\x1b\xea\xc3\x73"
+ "\xc6\xad\x02\xb7\x6a\x3f\xb5\x83\x38\x49\x2a\x29\x1c\x1b\x52\x48"
+ "\x7b\x49\x40\xee\x95\x3f\x2d\x78\x02\xed\x00\x06\x29\xb4\x07\x74"
+ "\x65\x73\x74\x6b\x65\x79\x88\xb2\x04\x13\x01\x02\x00\x1c\x05\x02"
+ "\x3f\x84\x97\x99\x02\x1b\x03\x04\x0b\x07\x03\x02\x03\x15\x02\x03"
+ "\x03\x16\x02\x01\x02\x1e\x01\x02\x17\x80\x00\x0a\x09\x10\x82\xbd"
+ "\x9b\x6d\xe6\x83\xcc\x5a\x77\x23\x03\xfe\x2e\x67\x4d\x03\x87\x36"
+ "\x05\x56\xdc\x31\x5f\xa7\x0a\x93\x3b\x74\x1e\x87\xfd\x95\x98\x1f"
+ "\x75\x1d\x20\x77\x1a\x07\xa8\x85\x95\x94\x6c\x9e\x5b\xab\x96\xeb"
+ "\x4d\x46\xbe\xea\xcb\xff\xd8\xac\xa7\xdd\x32\x0e\xb5\xfb\x1d\x10"
+ "\xf7\xb6\xf7\x54\x4c\x4e\xfc\x12\x28\x47\x1c\xd9\x1d\x06\x66\xdb"
+ "\x94\xe3\xde\x62\xfd\xe7\xeb\x2d\xc5\xfb\x11\xa1\x3d\xf1\x4e\xf4"
+ "\x46\x42\x8f\x8b\x86\x78\x08\xb7\x9e\xef\xbf\x88\x72\x7e\x73\x73"
+ "\x30\x92\xa6\xa1\x59\x30\x5e\x94\x51\x39\x92\xab\xd5\xce\xf8\x67"
+ "\x8d\x9c\x37\x85\xdc\x6e\x25\x3c\xc1\x81\xb8\x8b\x04\x3f\x84\x97"
+ "\xb8\x01\x04\x00\xe7\x16\x95\xed\x53\xcc\x3d\x03\xc3\x49\xa9\xf2"
+ "\xa6\x4e\x6c\x74\x5f\xc3\xe1\x53\x14\x4e\x29\xa2\x54\x36\xd6\xc9"
+ "\x47\x66\x40\xb9\xd9\x71\xae\x53\x15\xab\x97\x37\x60\x68\x58\x1c"
+ "\x7b\xe9\xc8\xe1\xf1\x81\xa4\xb0\x06\xd8\x71\x9e\x75\x82\x5e\xe6"
+ "\x1b\x7e\xbd\xb6\xa1\x4f\x4c\x9d\xee\xaf\x83\x32\x8a\xa9\x87\xf6"
+ "\x40\x99\x2c\xe5\x80\x42\x10\x29\xe6\xaf\x72\xcc\xd5\x2b\xf1\x83"
+ "\xe9\xa7\xee\xd1\x07\xc7\xd6\x05\xdd\xdd\x37\x84\x06\xf9\x18\x50"
+ "\x0c\xd1\x83\x76\xe0\xe4\x7d\x31\xdf\x8e\x4a\x2b\xed\x56\x4e\x82"
+ "\xb3\x0f\xf1\x0b\x00\x06\x29\x88\x9f\x04\x18\x01\x02\x00\x09\x05"
+ "\x02\x3f\x84\x97\xb8\x02\x1b\x0c\x00\x0a\x09\x10\x82\xbd\x9b\x6d"
+ "\xe6\x83\xcc\x5a\x61\xcb\x03\xff\x54\x94\xa1\x6f\x90\x56\x32\x12"
+ "\xcf\x2a\xd9\xf6\x3f\xec\xbe\xcc\x47\x6c\xac\x09\x42\xef\x14\xec"
+ "\xe6\xf7\xed\x8c\xf6\x98\x20\xd2\xe1\xb9\x47\xc1\x47\xd4\xcb\xd0"
+ "\x1b\xb1\x6b\x38\xf1\x35\xc1\xfd\x36\xc8\x3f\xfc\xbc\x81\xc6\xa5"
+ "\x14\xcc\x41\x2b\x7c\x69\x78\x9a\x7e\x08\x8c\x3b\x5d\x2b\x86\x50"
+ "\xea\x4c\x7b\x4c\x34\x01\x87\xae\xe6\x04\x77\x93\x7d\xe5\x54\xcc"
+ "\x70\x30\x56\x11\x1b\xaf\x8c\xcf\x4b\x99\x2c\x5a\xa8\x70\x32\xfc"
+ "\xd9\xef\x6f\x3e\x76\x4c\xd5\x97\xdf\x1c\x49\x85\xcf\x24\x01\xed"
+ "\x3c\x8a\x56\x03\x8f\xad\x50\x35"
+ ;
+
+const int rsa_key_size = 632;
--- /dev/null
+# Set the following to `true' to make a debuggable build.
+# Leave this set to `false' for production use.
+DEBUG = true
+
+
+ROOT = mod
+VERSION = 0.1
+INSTALL_DIR = /usr/local/bin
+RELEASE_NAME = $(ROOT)-$(VERSION)
+
+CC = gcc
+
+INCLUDES =
+CFLAGS = -g -O -Wall
+
+OBJS = mod.o
+
+all: extract_pkey $(ROOT)
+
+$(ROOT): $(OBJS)
+ $(CC) $(LDFLAGS) -o $(ROOT) $(OBJS) -lbfd -liberty $(LIB_OBJS) $(ARCH_LIB_OBJS)
+
+.c.o:
+ $(CC) $(INCLUDES) $(CFLAGS) -c $< -o $@
+
+extract_pkey: extract_pkey.c
+ gcc -g extract_pkey.c -o extract_pkey
+
+clean:
+ -rm $(OBJS) $(ROOT) extract_pkey
--- /dev/null
+/*
+ * Distributed Security Module (DSM)
+ *
+ * Simple extractor of MPIs for e and n in gpg exported keys (or pubrings
+ * with only one key apparently)
+ * Exported keys come from gpg --export.
+ * Output is meant to be copy pasted into kernel code until better mechanism.
+ *
+ * Copyright (C) 2002-2003 Ericsson, 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.
+ *
+ * Author: David Gordon Aug 2003
+ * Modifs: Vincent Roy Sept 2003
+ */
+
+#include <errno.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#define DSI_ELF_SIG_SIZE 512 /* this is a redefinition */
+#define DSI_PKEY_N_OFFSET 8 /* offset for pkey->n */
+#define DSI_MPI_MAX_SIZE_N 1024
+/* pkey->e MPI follows pkey->n MPI */
+
+
+void usage();
+
+
+int main(int argc, char **argv)
+{
+ int pkey_file, module_file, nread, count, i;
+ unsigned int num;
+ unsigned int len;
+ unsigned char c;
+ unsigned char *key;
+ unsigned char *temp;
+ int key_offset = 0;
+
+ if (argc <= 1)
+ usage();
+
+ /* Get pkey MPIs, one for 'n' and the other for 'e' */
+ pkey_file = open(argv[1], O_RDONLY);
+ if (!pkey_file) {
+ printf ("Unable to open pkey_file %s %s\n", argv[1], strerror(errno));
+ return -1;
+ }
+// module_file = open ("/dev/Digsig", O_WRONLY);
+// if (!module_file) {
+// printf ("Unable to open module char device %s\n", strerror(errno));
+// return -1;
+// }
+
+ key = (unsigned char *)malloc (DSI_MPI_MAX_SIZE_N+1);
+// key[key_offset++] = 'n';
+ /*
+ * Format of an MPI:
+ * - 2 bytes: length of MPI in BITS
+ * - MPI
+ */
+
+ lseek(pkey_file, DSI_PKEY_N_OFFSET, SEEK_SET);
+ read(pkey_file, &c, 1);
+ key[key_offset++] = c;
+ len = c << 8;
+
+ read(pkey_file, &c, 1);
+ key[key_offset++] = c;
+ len |= c;
+ len = (len + 7) / 8; /* round up */
+
+ if (len > DSI_ELF_SIG_SIZE) {
+ printf("\nLength of 'n' MPI is too large %#x\n", len);
+ return -1;
+ }
+
+ for (i = 0; i < len; i++) {
+ read(pkey_file, &c, 1);
+ key[key_offset++] = c;
+ if (key_offset == DSI_MPI_MAX_SIZE_N+2) {
+ temp = (unsigned char *)malloc (DSI_MPI_MAX_SIZE_N*2+1);
+ memcpy (temp, key, key_offset-1);
+ free (key);
+ key = temp;
+ }
+ }
+ printf("const char rsa_key_n[] = {\n");
+ for (i = 0; i < key_offset; i++ ) {
+ printf("0x%02x, ", key[i] & 0xff);
+ }
+ printf("};\n\n");
+ printf("const int rsa_key_n_size = %d;\n", key_offset);
+// write (module_file, key, key_offset);
+
+ key_offset = 0;
+// key[key_offset++] = 'e';
+ read(pkey_file, &c, 1);
+ key[key_offset++] = c;
+ len = c << 8;
+
+ read(pkey_file, &c, 1);
+ key[key_offset++] = c;
+ len |= c;
+ len = (len + 7) / 8; /* round up */
+
+ if (len > DSI_ELF_SIG_SIZE) {
+ printf("\nLength of 'e' MPI is too large %#x\n", len);
+ return -1;
+ }
+
+ for (i = 0; i < len; i++) {
+ read(pkey_file, &c, 1);
+ key[key_offset++] = c;
+ }
+
+ printf("const char rsa_key_e[] = {\n");
+ for (i = 0; i < key_offset; i++ ) {
+ printf("0x%02x, ", key[i] & 0xff);
+ }
+ printf("};\n");
+ printf("const int rsa_key_e_size = %d;\n", key_offset);
+// write (module_file, key, key_offset);
+
+ return 0;
+}
+
+
+void usage()
+{
+ printf("Usage: extract_pkey gpgkey\nYou can export a key with gpg --export\n");
+}
+
+
--- /dev/null
+
+
+
+#include <errno.h>
+#include <error.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <bfd.h>
+
+/* This should be set in a Makefile somehow */
+#define TARGET "i686-pc-linux-gnu"
+
+static FILE *out;
+
+static void dump_data(bfd *abfd)
+{
+ asection *section;
+ bfd_byte *data = 0;
+ bfd_size_type addr_offset;
+ bfd_size_type stop_offset;
+ unsigned int opb = bfd_octets_per_byte(abfd);
+
+ for (section = abfd->sections; section != NULL; section = section->next) {
+ if (section->flags & SEC_HAS_CONTENTS) {
+ if (bfd_section_size(abfd, section) == 0)
+ continue;
+
+ /* We only care about sections with "text" or "data" in their names */
+ if ((strstr(section->name, "text") == NULL) &&
+ (strstr(section->name, "data") == NULL))
+ continue;
+
+ printf ("Contents of section %s size %d:\n", section->name, (int)bfd_section_size(abfd, section));
+
+ data = (bfd_byte *) malloc((size_t) bfd_section_size (abfd, section));
+
+ bfd_get_section_contents(abfd, section, (PTR) data, 0, bfd_section_size (abfd, section));
+
+ stop_offset = bfd_section_size(abfd, section) / opb;
+
+ for (addr_offset = 0; addr_offset < stop_offset; ++addr_offset) {
+ fprintf (out, "%c", (data[addr_offset]));
+// fprintf (out, "%02x", (unsigned)(data[addr_offset]));
+ }
+ free (data);
+// printf('\n');
+ }
+ }
+}
+
+void set_default_bfd_target(void)
+{
+ const char *target = TARGET;
+
+ if (!bfd_set_default_target(target))
+ fprintf(stderr, "can't set BFD default target to `%s': %s", target, bfd_errmsg (bfd_get_error ()));
+}
+
+int main (int argc, char *argv[])
+{
+ char *in_file;
+ char *out_file;
+ bfd *file;
+ char **matching;
+
+ if (argc != 3) {
+ fprintf(stderr, "%s [infile] [outfile]\n", argv[0]);
+ exit(1);
+ }
+
+ in_file = argv[1];
+ out_file = argv[2];
+
+ bfd_init();
+ set_default_bfd_target();
+
+
+// file = bfd_openr(in_file, "elf32-i386");
+ file = bfd_openr(in_file, NULL);
+ if (file == NULL) {
+ fprintf(stderr, "error \"%s\" trying to open %s\n", strerror(errno), in_file);
+ exit(1);
+ }
+
+ out = fopen(out_file, "w");
+ if (out == NULL) {
+ fprintf(stderr, "error \"%s\" trying to create %s\n", strerror(errno), out_file);
+ exit(1);
+ }
+
+ if (bfd_check_format_matches(file, bfd_object, &matching)) {
+ dump_data (file);
+ }
+
+ fclose(out);
+
+ return 0;
+}
+
--- /dev/null
+#include <argp.h>
+#include <assert.h>
+#include <dwarf.h>
+#include <errno.h>
+#include <error.h>
+#include <fcntl.h>
+#include <gelf.h>
+#include <inttypes.h>
+#include <langinfo.h>
+//#include <libdwarf.h>
+#include <libebl.h>
+//#include <libintl.h>
+#include <locale.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/param.h>
+
+
+size_t section_num;
+
+/* Print the section headers. */
+static void print_shdr (Elf *elf, GElf_Ehdr *ehdr)
+{
+ size_t cnt;
+ size_t shstrndx;
+
+ /* Get the section header string table index. */
+ if (elf_getshstrndx(elf, &shstrndx) < 0) {
+ fprintf(stderr, "cannot get section header string table index");
+ return;
+ }
+
+ for (cnt = 0; cnt < section_num; ++cnt) {
+ char flagbuf[20];
+ char *cp;
+ Elf_Scn *scn = elf_getscn(elf, cnt);
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr;
+ char *name;
+ char *buf;
+ int i;
+
+ if (scn == NULL)
+ error (EXIT_FAILURE, 0, "cannot get section: %s", elf_errmsg (-1));
+
+ /* Get the section header. */
+ shdr = gelf_getshdr (scn, &shdr_mem);
+ if (shdr == NULL)
+ error (EXIT_FAILURE, 0, "cannot get section header: %s", elf_errmsg (-1));
+
+ name = elf_strptr(elf, shstrndx, shdr->sh_name);
+ buf = elf_strptr(elf, shstrndx, shdr->sh_addr);
+ printf("%s - ", name);
+ for (i = 0; i < 200; ++i) {
+ printf("%c", buf[i]);
+ }
+ printf("\n");
+
+ //if (shdr->sh_flags & SHF_ALLOC)
+ printf ("[%2zu] %-20s %-12s %0*" PRIx64 " %0*" PRIx64 " %0*" PRIx64
+ " %2" PRId64 " %-5s %2" PRId32 " %3" PRId32
+ " %2" PRId64 "\n",
+ cnt,
+ elf_strptr(elf, shstrndx, shdr->sh_name)
+ ?: "<corrupt>",
+ //ebl_section_type_name (ebl, shdr->sh_type, buf, sizeof (buf)),
+ "foobar",
+ ehdr->e_ident[EI_CLASS] == ELFCLASS32 ? 8 : 16, shdr->sh_addr,
+ ehdr->e_ident[EI_CLASS] == ELFCLASS32 ? 6 : 8, shdr->sh_offset,
+ ehdr->e_ident[EI_CLASS] == ELFCLASS32 ? 6 : 8, shdr->sh_size,
+ shdr->sh_entsize, flagbuf, shdr->sh_link, shdr->sh_info,
+ shdr->sh_addralign);
+ }
+
+ fputc_unlocked ('\n', stdout);
+}
+
+
+
+
+
+int main (int argc, char *argv[])
+{
+ char *filename = "foo";
+ int fd;
+ Elf *elf;
+
+ elf_version(EV_CURRENT);
+
+ fd = open(filename, O_RDONLY);
+ if (fd < 0) {
+ fprintf(stderr, "error \"%s\" trying to open %s\n", strerror(errno), filename);
+ exit(1);
+ }
+
+ elf = elf_begin(fd, ELF_C_READ_MMAP, NULL);
+ if (elf == NULL) {
+ fprintf(stderr, "Can't get elf descriptor for %s\n", filename);
+ goto exit;
+ }
+
+ Elf_Kind kind = elf_kind(elf);
+ switch(kind) {
+ case ELF_K_ELF:
+ printf("ELF_K_ELF\n");
+ break;
+ default:
+ fprintf(stderr, "Not a proper elf file for us to process.\n");
+ goto end;
+ }
+
+ GElf_Ehdr ehdr_mem;
+ GElf_Ehdr *ehdr = gelf_getehdr(elf, &ehdr_mem);
+ if (ehdr == NULL) {
+ fprintf(stderr, "Can not read elf header.\n");
+ goto end;
+ }
+
+ if (elf_getshnum(elf, §ion_num) < 0) {
+ fprintf(stderr, "Can not determine number of sections.\n");
+ goto end;
+ }
+
+ printf("section_num = %d\n", section_num);
+
+ print_shdr(elf, ehdr);
+
+
+end:
+
+ elf_end(elf);
+exit:
+ close(fd);
+
+ return 0;
+}
+
--- /dev/null
+#!/bin/bash
+
+
+if [ $# = "0" ] ; then
+ echo
+# echo "usage: $0 <module_to_sign> <key_name>"
+ echo "usage: $0 <module_to_sign>"
+ echo
+ exit 1
+fi
+
+module=$1
+#key=$2
+
+# strip out only the sections that we care about
+./mod $module $module.out
+
+# sign the sections
+gpg --no-greeting -sb $module.out
+## sha1 the sections
+#sha1sum $module.out | awk "{print \$1}" > $module.sha1
+#
+## encrypt the sections
+#gpg --no-greeting -e -o - -r $key $module.sha1 > $module.crypt
+
+# add the encrypted data to the module
+#objcopy --add-section module_sig=$module.crypt $module $module.sign
+objcopy --add-section module_sig=$module.out.sig $module $module.sign
+
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/time.h>
+#include <linux/sound.h>
#include <sound/core.h>
#include <sound/control.h>
#include <sound/minors.h>
return snd_hwdep_free(hwdep);
}
+static void snd_hwdep_dev_release(snd_hwdep_t *hwdep)
+{
+ return;
+}
+
+#define to_hwdep_dev(hwdep) container_of(hwdep, snd_hwdep_t, class_dev)
+
+static ssize_t show_dev(struct class_device *class_dev, char *buf)
+{
+ int minor = -1;
+ snd_hwdep_t *hwdep = to_hwdep_dev(class_dev);
+ minor = SNDRV_MINOR(hwdep->card->number, SNDRV_DEVICE_TYPE_HWDEP + hwdep->device);
+
+ return sprintf(buf, "%d:%d\n", CONFIG_SND_MAJOR, minor);
+}
+
+static CLASS_DEVICE_ATTR(dev, S_IRUGO, show_dev, NULL);
+
static int snd_hwdep_dev_register(snd_device_t *device)
{
snd_hwdep_t *hwdep = snd_magic_cast(snd_hwdep_t, device->device_data, return -ENXIO);
up(®ister_mutex);
return -EBUSY;
}
+ hwdep->release = snd_hwdep_dev_release;
snd_hwdep_devices[idx] = hwdep;
sprintf(name, "hwC%iD%i", hwdep->card->number, hwdep->device);
if ((err = snd_register_device(SNDRV_DEVICE_TYPE_HWDEP,
up(®ister_mutex);
return err;
}
+ sound_add_class_device(name, &hwdep->class_dev, hwdep->dev);
+ class_device_create_file(&hwdep->class_dev, &class_device_attr_dev);
#ifdef CONFIG_SND_OSSEMUL
hwdep->ossreg = 0;
if (hwdep->oss_type >= 0) {
if (hwdep->ossreg)
snd_unregister_oss_device(hwdep->oss_type, hwdep->card, hwdep->device);
#endif
+ sound_remove_class_device(&hwdep->class_dev);
snd_unregister_device(SNDRV_DEVICE_TYPE_HWDEP, hwdep->card, hwdep->device);
snd_hwdep_devices[idx] = NULL;
up(®ister_mutex);
#include <sound/driver.h>
#include <linux/init.h>
#include <linux/slab.h>
+#include <linux/sound.h>
#include <linux/time.h>
#include <sound/core.h>
#include <sound/minors.h>
substream->pstr->substream_opened--;
}
+static void snd_pcm_dev_release(snd_pcm_t *pcm)
+{
+ return;
+}
+
+#define to_pcm_dev0(pcm) container_of(pcm, snd_pcm_t, class_dev[0])
+#define to_pcm_dev1(pcm) container_of(pcm, snd_pcm_t, class_dev[1])
+
+static ssize_t show_dev(struct class_device *class_dev, char *buf)
+{
+ int minor = -1;
+ snd_pcm_t *pcm;
+ if (!strcmp((class_dev->class_id + strlen(class_dev->class_id) - 1), "p")) {
+ pcm = to_pcm_dev0(class_dev);
+ minor = SNDRV_MINOR(pcm->card->number, SNDRV_DEVICE_TYPE_PCM_PLAYBACK + pcm->device);
+ }
+ else
+ {
+ pcm = to_pcm_dev1(class_dev);
+ minor = SNDRV_MINOR(pcm->card->number, SNDRV_DEVICE_TYPE_PCM_CAPTURE + pcm->device);
+ }
+ return sprintf(buf, "%d:%d\n", CONFIG_SND_MAJOR, minor);
+}
+
+static CLASS_DEVICE_ATTR(dev, S_IRUGO, show_dev, NULL);
+
static int snd_pcm_dev_register(snd_device_t *device)
{
int idx, cidx, err;
snd_pcm_devices[idx] = pcm;
for (cidx = 0; cidx < 2; cidx++) {
int devtype = -1;
+ pcm->release = snd_pcm_dev_release;
if (pcm->streams[cidx].substream == NULL)
continue;
switch (cidx) {
snd_pcm_lock(1);
return err;
}
+ sound_add_class_device(str, &pcm->class_dev[cidx], pcm->dev);
+ class_device_create_file(&pcm->class_dev[cidx], &class_device_attr_dev);
for (substream = pcm->streams[cidx].substream; substream; substream = substream->next)
snd_pcm_timer_init(substream);
}
break;
case SNDRV_PCM_STREAM_CAPTURE:
devtype = SNDRV_DEVICE_TYPE_PCM_CAPTURE;
+ pcm->class_dev[0] = pcm->class_dev[cidx];
break;
}
+ sound_remove_class_device(&pcm->class_dev[0]);
snd_unregister_device(devtype, pcm->card, pcm->device);
for (substream = pcm->streams[cidx].substream; substream; substream = substream->next)
snd_pcm_timer_done(substream);
#include <linux/slab.h>
#include <linux/time.h>
#include <linux/wait.h>
+#include <linux/sound.h>
#include <sound/rawmidi.h>
#include <sound/info.h>
#include <sound/control.h>
}
#endif
+static void snd_rawmidi_dev_release(snd_rawmidi_t *rmidi)
+{
+ return;
+}
+
+#define to_rawmidi_dev(rmidi) container_of(rmidi, snd_rawmidi_t, class_dev)
+
+static ssize_t show_dev(struct class_device *class_dev, char *buf)
+{
+ snd_rawmidi_t *rmidi = to_rawmidi_dev(class_dev);
+ int minor = SNDRV_MINOR(rmidi->card->number, SNDRV_DEVICE_TYPE_RAWMIDI + rmidi->device);
+ return sprintf(buf, "%d:%d\n", CONFIG_SND_MAJOR, minor);
+}
+
+static CLASS_DEVICE_ATTR(dev, S_IRUGO, show_dev, NULL);
+
static int snd_rawmidi_dev_register(snd_device_t *device)
{
int idx, err;
return -EBUSY;
}
snd_rawmidi_devices[idx] = rmidi;
+ rmidi->release = snd_rawmidi_dev_release;
sprintf(name, "midiC%iD%i", rmidi->card->number, rmidi->device);
if ((err = snd_register_device(SNDRV_DEVICE_TYPE_RAWMIDI,
rmidi->card, rmidi->device,
up(®ister_mutex);
return err;
}
+ sound_add_class_device(name, &rmidi->class_dev, rmidi->dev_ptr);
+ class_device_create_file(&rmidi->class_dev, &class_device_attr_dev);
if (rmidi->ops && rmidi->ops->dev_register &&
(err = rmidi->ops->dev_register(rmidi)) < 0) {
snd_unregister_device(SNDRV_DEVICE_TYPE_RAWMIDI, rmidi->card, rmidi->device);
snd_rawmidi_devices[idx] = NULL;
+ sound_remove_class_device(&rmidi->class_dev);
up(®ister_mutex);
return err;
}
#endif /* CONFIG_SND_OSSEMUL */
if (rmidi->ops && rmidi->ops->dev_unregister)
rmidi->ops->dev_unregister(rmidi);
+ sound_remove_class_device(&rmidi->class_dev);
snd_unregister_device(SNDRV_DEVICE_TYPE_RAWMIDI, rmidi->card, rmidi->device);
up(®ister_mutex);
#if defined(CONFIG_SND_SEQUENCER) || (defined(MODULE) && defined(CONFIG_SND_SEQUENCER_MODULE))
track2 = SNDRV_MINOR_OSS(cidx, SNDRV_MINOR_OSS_DMMIDI1);
break;
}
- register1 = register_sound_special(reg->f_ops, minor);
+ register1 = register_sound_special(reg->f_ops, minor, NULL);
if (register1 != minor)
goto __end;
if (track2 >= 0) {
- register2 = register_sound_special(reg->f_ops, track2);
+ register2 = register_sound_special(reg->f_ops, track2, NULL);
if (register2 != track2)
goto __end;
}
return err;
}
hw->private_data = opl3;
+ hw->dev = opl3->dev;
#ifdef CONFIG_SND_OSSEMUL
if (device == 0) {
hw->oss_type = SNDRV_OSS_DEVICE_TYPE_DMFM;
dev->ac97_features = eid;
- if ((ac97->dev_mixer = register_sound_mixer(&ad1889_mixer_fops, -1)) < 0) {
+ if ((ac97->dev_mixer = register_sound_mixer(&ad1889_mixer_fops, -1, &dev->pci->dev)) < 0) {
printk(KERN_ERR DEVNAME ": cannot register mixer\n");
goto out_free;
}
goto err_free_mem;
/* register /dev/dsp */
- if ((dev->dev_audio = register_sound_dsp(&ad1889_fops, -1)) < 0) {
+ if ((dev->dev_audio = register_sound_dsp(&ad1889_fops, -1, &pcidev->dev)) < 0) {
printk(KERN_ERR DEVNAME ": cannot register /dev/dsp\n");
goto err_free_irq;
}
if ((eid & 0xc000) == 0) /* primary codec */
total_channels += 2;
- if ((codec->dev_mixer = register_sound_mixer(&ali_mixer_fops, -1)) < 0) {
+ if ((codec->dev_mixer = register_sound_mixer(&ali_mixer_fops, -1, &card->pci_dev->dev)) < 0) {
printk(KERN_ERR "ali_audio: couldn't register mixer!\n");
kfree(codec);
break;
}
/* register /dev/dsp */
- if ((card->dev_audio = register_sound_dsp(&ali_audio_fops, -1)) < 0) {
+ if ((card->dev_audio = register_sound_dsp(&ali_audio_fops, -1, &pci_dev->dev)) < 0) {
int i;
printk(KERN_ERR"ali_audio: couldn't register DSP device!\n");
release_region(card->iobase, 256);
/* register devices */
if (digital) {
rc = bta->dsp_digital =
- register_sound_dsp(&btaudio_digital_dsp_fops,dsp1);
+ register_sound_dsp(&btaudio_digital_dsp_fops,dsp1, &pci_dev->dev);
if (rc < 0) {
printk(KERN_WARNING
"btaudio: can't register digital dsp (rc=%d)\n",rc);
}
if (analog) {
rc = bta->dsp_analog =
- register_sound_dsp(&btaudio_analog_dsp_fops,dsp2);
+ register_sound_dsp(&btaudio_analog_dsp_fops,dsp2, &pci_dev->dev);
if (rc < 0) {
printk(KERN_WARNING
"btaudio: can't register analog dsp (rc=%d)\n",rc);
}
printk(KERN_INFO "btaudio: registered device dsp%d [analog]\n",
bta->dsp_analog >> 4);
- rc = bta->mixer_dev = register_sound_mixer(&btaudio_mixer_fops,mixer);
+ rc = bta->mixer_dev = register_sound_mixer(&btaudio_mixer_fops,mixer, &pci_dev->dev);
if (rc < 0) {
printk(KERN_WARNING
"btaudio: can't register mixer (rc=%d)\n",rc);
printk(KERN_INFO "cmpci: found %s adapter at io %#06x irq %u\n",
devicename, s->iobase, s->irq);
/* register devices */
- if ((s->dev_audio = register_sound_dsp(&cm_audio_fops, -1)) < 0)
+ if ((s->dev_audio = register_sound_dsp(&cm_audio_fops, -1, &pcidev->dev)) < 0)
goto err_dev1;
- if ((s->dev_mixer = register_sound_mixer(&cm_mixer_fops, -1)) < 0)
+ if ((s->dev_mixer = register_sound_mixer(&cm_mixer_fops, -1, &pcidev->dev)) < 0)
goto err_dev2;
#ifdef CONFIG_SOUND_CMPCI_MIDI
- if ((s->dev_midi = register_sound_midi(&cm_midi_fops, -1)) < 0)
+ if ((s->dev_midi = register_sound_midi(&cm_midi_fops, -1, &pcidev->dev)) < 0)
goto err_dev3;
#endif
#ifdef CONFIG_SOUND_CMPCI_FM
- if ((s->dev_dmfm = register_sound_special(&cm_dmfm_fops, 15 /* ?? */)) < 0)
+ if ((s->dev_dmfm = register_sound_special(&cm_dmfm_fops, 15, &pcidev->dev /* ?? */)) < 0)
goto err_dev4;
#endif
pci_set_master(pcidev); /* enable bus mastering */
printk(KERN_ERR "cs4281: irq %u in use\n", s->irq));
goto err_irq;
}
- if ((s->dev_audio = register_sound_dsp(&cs4281_audio_fops, -1)) <
+ if ((s->dev_audio = register_sound_dsp(&cs4281_audio_fops, -1, &pcidev->dev)) <
0) {
CS_DBGOUT(CS_INIT | CS_ERROR, 1, printk(KERN_ERR
"cs4281: probe() register_sound_dsp() failed.\n"));
goto err_dev1;
}
- if ((s->dev_mixer = register_sound_mixer(&cs4281_mixer_fops, -1)) <
+ if ((s->dev_mixer = register_sound_mixer(&cs4281_mixer_fops, -1, &pcidev->dev)) <
0) {
CS_DBGOUT(CS_INIT | CS_ERROR, 1, printk(KERN_ERR
"cs4281: probe() register_sound_mixer() failed.\n"));
goto err_dev2;
}
- if ((s->dev_midi = register_sound_midi(&cs4281_midi_fops, -1)) < 0) {
+ if ((s->dev_midi = register_sound_midi(&cs4281_midi_fops, -1, &pcidev->dev)) < 0) {
CS_DBGOUT(CS_INIT | CS_ERROR, 1, printk(KERN_ERR
"cs4281: probe() register_sound_midi() failed.\n"));
goto err_dev3;
card->ac97_features = eid;
- if ((codec->dev_mixer = register_sound_mixer(&cs_mixer_fops, -1)) < 0) {
+ if ((codec->dev_mixer = register_sound_mixer(&cs_mixer_fops, -1, &card->pci_dev->dev)) < 0) {
printk(KERN_ERR "cs46xx: couldn't register mixer!\n");
ac97_release_codec(codec);
break;
goto fail2;
}
/* register /dev/dsp */
- if ((card->dev_audio = register_sound_dsp(&cs461x_fops, -1)) < 0) {
+ if ((card->dev_audio = register_sound_dsp(&cs461x_fops, -1, &card->pci_dev->dev)) < 0) {
printk(KERN_ERR "cs46xx: unable to register dsp\n");
goto fail;
}
/* register /dev/midi */
- if((card->dev_midi = register_sound_midi(&cs_midi_fops, -1)) < 0)
+ if((card->dev_midi = register_sound_midi(&cs_midi_fops, -1, &card->pci_dev->dev)) < 0)
printk(KERN_ERR "cs46xx: unable to register midi\n");
card->pm.flags |= CS46XX_PM_IDLE;
int sound_alloc_audiodev(void)
{
- int i = register_sound_dsp(&oss_sound_fops, -1);
+ int i = register_sound_dsp(&oss_sound_fops, -1, NULL);
if(i==-1)
return i;
i>>=4;
int sound_alloc_mididev(void)
{
- int i = register_sound_midi(&oss_sound_fops, -1);
+ int i = register_sound_midi(&oss_sound_fops, -1, NULL);
if(i==-1)
return i;
i>>=4;
int sound_alloc_mixerdev(void)
{
- int i = register_sound_mixer(&oss_sound_fops, -1);
+ int i = register_sound_mixer(&oss_sound_fops, -1, NULL);
if(i==-1)
return -1;
i>>=4;
static int __devinit emu10k1_register_devices(struct emu10k1_card *card)
{
- card->audio_dev = register_sound_dsp(&emu10k1_audio_fops, -1);
+ card->audio_dev = register_sound_dsp(&emu10k1_audio_fops, -1, &card->pci_dev->dev);
if (card->audio_dev < 0) {
printk(KERN_ERR "emu10k1: cannot register first audio device!\n");
goto err_dev;
}
- card->audio_dev1 = register_sound_dsp(&emu10k1_audio_fops, -1);
+ card->audio_dev1 = register_sound_dsp(&emu10k1_audio_fops, -1, &card->pci_dev->dev);
if (card->audio_dev1 < 0) {
printk(KERN_ERR "emu10k1: cannot register second audio device!\n");
goto err_dev1;
}
- card->ac97->dev_mixer = register_sound_mixer(&emu10k1_mixer_fops, -1);
+ card->ac97->dev_mixer = register_sound_mixer(&emu10k1_mixer_fops, -1, &card->pci_dev->dev);
if (card->ac97->dev_mixer < 0) {
printk(KERN_ERR "emu10k1: cannot register mixer device\n");
goto err_mixer;
}
- card->midi_dev = register_sound_midi(&emu10k1_midi_fops, -1);
+ card->midi_dev = register_sound_midi(&emu10k1_midi_fops, -1, &card->pci_dev->dev);
if (card->midi_dev < 0) {
printk(KERN_ERR "emu10k1: cannot register midi device!\n");
goto err_midi;
(s->ctrl & CTRL_XCTL0) ? "out" : "in",
(s->ctrl & CTRL_XCTL1) ? "1" : "0");
/* register devices */
- if ((s->dev_audio = register_sound_dsp(&es1370_audio_fops, -1)) < 0) {
+ if ((s->dev_audio = register_sound_dsp(&es1370_audio_fops, -1, &pcidev->dev)) < 0) {
ret = s->dev_audio;
goto err_dev1;
}
- if ((s->dev_mixer = register_sound_mixer(&es1370_mixer_fops, -1)) < 0) {
+ if ((s->dev_mixer = register_sound_mixer(&es1370_mixer_fops, -1, &pcidev->dev)) < 0) {
ret = s->dev_mixer;
goto err_dev2;
}
- if ((s->dev_dac = register_sound_dsp(&es1370_dac_fops, -1)) < 0) {
+ if ((s->dev_dac = register_sound_dsp(&es1370_dac_fops, -1, &pcidev->dev)) < 0) {
ret = s->dev_dac;
goto err_dev3;
}
- if ((s->dev_midi = register_sound_midi(&es1370_midi_fops, -1)) < 0) {
+ if ((s->dev_midi = register_sound_midi(&es1370_midi_fops, -1, &pcidev->dev)) < 0) {
ret = s->dev_midi;
goto err_dev4;
}
printk(KERN_INFO PFX "found es1371 rev %d at io %#lx irq %u joystick %#x\n",
s->rev, s->io, s->irq, s->gameport.io);
/* register devices */
- if ((res=(s->dev_audio = register_sound_dsp(&es1371_audio_fops,-1)))<0)
+ if ((res=(s->dev_audio = register_sound_dsp(&es1371_audio_fops,-1, &pcidev->dev)))<0)
goto err_dev1;
- if ((res=(s->codec->dev_mixer = register_sound_mixer(&es1371_mixer_fops, -1))) < 0)
+ if ((res=(s->codec->dev_mixer = register_sound_mixer(&es1371_mixer_fops, -1, &pcidev->dev))) < 0)
goto err_dev2;
- if ((res=(s->dev_dac = register_sound_dsp(&es1371_dac_fops, -1))) < 0)
+ if ((res=(s->dev_dac = register_sound_dsp(&es1371_dac_fops, -1, &pcidev->dev))) < 0)
goto err_dev3;
- if ((res=(s->dev_midi = register_sound_midi(&es1371_midi_fops, -1)))<0 )
+ if ((res=(s->dev_midi = register_sound_midi(&es1371_midi_fops, -1, &pcidev->dev)))<0 )
goto err_dev4;
#ifdef ES1371_DEBUG
/* initialize the debug proc device */
}
printk(KERN_INFO "solo1: joystick port at %#x\n", s->gameport.io+1);
/* register devices */
- if ((s->dev_audio = register_sound_dsp(&solo1_audio_fops, -1)) < 0) {
+ if ((s->dev_audio = register_sound_dsp(&solo1_audio_fops, -1, &pcidev->dev)) < 0) {
ret = s->dev_audio;
goto err_dev1;
}
- if ((s->dev_mixer = register_sound_mixer(&solo1_mixer_fops, -1)) < 0) {
+ if ((s->dev_mixer = register_sound_mixer(&solo1_mixer_fops, -1, &pcidev->dev)) < 0) {
ret = s->dev_mixer;
goto err_dev2;
}
- if ((s->dev_midi = register_sound_midi(&solo1_midi_fops, -1)) < 0) {
+ if ((s->dev_midi = register_sound_midi(&solo1_midi_fops, -1, &pcidev->dev)) < 0) {
ret = s->dev_midi;
goto err_dev3;
}
- if ((s->dev_dmfm = register_sound_special(&solo1_dmfm_fops, 15 /* ?? */)) < 0) {
+ if ((s->dev_dmfm = register_sound_special(&solo1_dmfm_fops, 15, &pcidev->dev /* ?? */)) < 0) {
ret = s->dev_dmfm;
goto err_dev4;
}
/* Register mixer */
if ((codec->dev_mixer =
- register_sound_mixer (&forte_mixer_fops, -1)) < 0) {
+ register_sound_mixer (&forte_mixer_fops, -1, &chip->pci_dev->dev)) < 0) {
printk (KERN_ERR PFX "couldn't register mixer!\n");
ac97_release_codec(codec);
return -1;
chip->ac97 = codec;
/* Register DSP */
- if ((chip->dsp = register_sound_dsp (&forte_dsp_fops, -1) ) < 0) {
+ if ((chip->dsp = register_sound_dsp (&forte_dsp_fops, -1, &chip->pci_dev->dev) ) < 0) {
printk (KERN_ERR PFX "couldn't register dsp!\n");
return -1;
}
printk("i810_audio: AC'97 codec %d Unable to map surround DAC's (or DAC's not present), total channels = %d\n", ac97_id, total_channels);
}
- if ((codec->dev_mixer = register_sound_mixer(&i810_mixer_fops, -1)) < 0) {
+ if ((codec->dev_mixer = register_sound_mixer(&i810_mixer_fops, -1, &card->pci_dev->dev)) < 0) {
printk(KERN_ERR "i810_audio: couldn't register mixer!\n");
ac97_release_codec(codec);
break;
}
/* register /dev/dsp */
- if ((card->dev_audio = register_sound_dsp(&i810_audio_fops, -1)) < 0) {
+ if ((card->dev_audio = register_sound_dsp(&i810_audio_fops, -1, &pci_dev->dev)) < 0) {
int i;
printk(KERN_ERR "i810_audio: couldn't register DSP device!\n");
free_irq(card->irq, card);
if(s->dma_adc.ready || s->dma_dac.ready || s->dma_adc.rawbuf)
printk("maestro: BOTCH!\n");
/* register devices */
- if ((s->dev_audio = register_sound_dsp(&ess_audio_fops, -1)) < 0)
+ if ((s->dev_audio = register_sound_dsp(&ess_audio_fops, -1, NULL)) < 0)
break;
}
maestro_ac97_init(card);
}
- if ((card->dev_mixer = register_sound_mixer(&ess_mixer_fops, -1)) < 0) {
+ if ((card->dev_mixer = register_sound_mixer(&ess_mixer_fops, -1, NULL)) < 0) {
printk("maestro: couldn't register mixer!\n");
} else {
memcpy(card->mix.mixer_state,mixer_defaults,sizeof(card->mix.mixer_state));
return -1;
}
- if ((codec->dev_mixer = register_sound_mixer(&m3_mixer_fops, -1)) < 0) {
+ if ((codec->dev_mixer = register_sound_mixer(&m3_mixer_fops, -1, &card->pcidev->dev)) < 0) {
printk(KERN_ERR PFX "couldn't register mixer!\n");
ac97_release_codec(codec);
return -1;
if(s->dma_adc.ready || s->dma_dac.ready || s->dma_adc.rawbuf)
printk(KERN_WARNING PFX "initing a dsp device that is already in use?\n");
/* register devices */
- if ((s->dev_audio = register_sound_dsp(&m3_audio_fops, -1)) < 0) {
+ if ((s->dev_audio = register_sound_dsp(&m3_audio_fops, -1, &pci_dev->dev)) < 0) {
break;
}
return err;
}
- if ((dev.dsp_minor = register_sound_dsp(&dev_fileops, -1)) < 0) {
+ if ((dev.dsp_minor = register_sound_dsp(&dev_fileops, -1, NULL)) < 0) {
printk(KERN_ERR LOGNAME ": Unable to register DSP operations\n");
msnd_unregister(&dev);
release_region(dev.io, dev.numio);
return dev.dsp_minor;
}
- if ((dev.mixer_minor = register_sound_mixer(&dev_fileops, -1)) < 0) {
+ if ((dev.mixer_minor = register_sound_mixer(&dev_fileops, -1, NULL)) < 0) {
printk(KERN_ERR LOGNAME ": Unable to register mixer operations\n");
unregister_sound_mixer(dev.mixer_minor);
msnd_unregister(&dev);
goto err_devices;
}
for (i=0;i<devices;i++) {
- if ((s->dspnum[i] = register_sound_dsp(&rme96xx_audio_fops, -1)) < 0)
+ if ((s->dspnum[i] = register_sound_dsp(&rme96xx_audio_fops, -1, &pcidev->dev)) < 0)
goto err_devices;
}
- if ((s->mixer = register_sound_mixer(&rme96xx_mixer_fops, -1)) < 0)
+ if ((s->mixer = register_sound_mixer(&rme96xx_mixer_fops, -1, &pcidev->dev)) < 0)
goto err_devices;
pci_set_drvdata(pcidev, s);
printk(KERN_INFO "sv: found adapter at io %#lx irq %u dmaa %#06x dmac %#06x revision %u\n",
s->ioenh, s->irq, s->iodmaa, s->iodmac, rdindir(s, SV_CIREVISION));
/* register devices */
- if ((s->dev_audio = register_sound_dsp(&sv_audio_fops, -1)) < 0) {
+ if ((s->dev_audio = register_sound_dsp(&sv_audio_fops, -1, &pcidev->dev)) < 0) {
ret = s->dev_audio;
goto err_dev1;
}
- if ((s->dev_mixer = register_sound_mixer(&sv_mixer_fops, -1)) < 0) {
+ if ((s->dev_mixer = register_sound_mixer(&sv_mixer_fops, -1, &pcidev->dev)) < 0) {
ret = s->dev_mixer;
goto err_dev2;
}
- if ((s->dev_midi = register_sound_midi(&sv_midi_fops, -1)) < 0) {
+ if ((s->dev_midi = register_sound_midi(&sv_midi_fops, -1, &pcidev->dev)) < 0) {
ret = s->dev_midi;
goto err_dev3;
}
- if ((s->dev_dmfm = register_sound_special(&sv_dmfm_fops, 15 /* ?? */)) < 0) {
+ if ((s->dev_dmfm = register_sound_special(&sv_dmfm_fops, 15, &pcidev->dev/* ?? */)) < 0) {
ret = s->dev_dmfm;
goto err_dev4;
}
static int create_special_devices(void)
{
int seq1,seq2;
- seq1=register_sound_special(&oss_sound_fops, 1);
+ seq1=register_sound_special(&oss_sound_fops, 1, NULL);
if(seq1==-1)
goto bad;
- seq2=register_sound_special(&oss_sound_fops, 8);
+ seq2=register_sound_special(&oss_sound_fops, 8, NULL);
if(seq2!=-1)
return 0;
unregister_sound_special(1);
if (ac97_probe_codec(codec) == 0)
break;
- if ((codec->dev_mixer = register_sound_mixer(&trident_mixer_fops, -1)) < 0) {
+ if ((codec->dev_mixer = register_sound_mixer(&trident_mixer_fops, -1, &card->pci_dev->dev)) < 0) {
printk(KERN_ERR "trident: couldn't register mixer!\n");
ac97_release_codec(codec);
break;
goto out_proc_fs;
}
/* register /dev/dsp */
- if ((card->dev_audio = register_sound_dsp(&trident_audio_fops, -1)) < 0) {
+ if ((card->dev_audio = register_sound_dsp(&trident_audio_fops, -1, &pci_dev->dev)) < 0) {
printk(KERN_ERR "trident: couldn't register DSP device!\n");
goto out_free_irq;
}
card->ac97->codec_write = via_ac97_write_reg;
card->ac97->codec_wait = via_ac97_codec_wait;
- card->ac97->dev_mixer = register_sound_mixer (&via_mixer_fops, -1);
+ card->ac97->dev_mixer = register_sound_mixer (&via_mixer_fops, -1, &card->pdev->dev);
if (card->ac97->dev_mixer < 0) {
printk (KERN_ERR PFX "unable to register AC97 mixer, aborting\n");
DPRINTK ("EXIT, returning -EIO\n");
via_stop_everything (card);
- card->dev_dsp = register_sound_dsp (&via_dsp_fops, -1);
+ card->dev_dsp = register_sound_dsp (&via_dsp_fops, -1, &card->pdev->dev);
if (card->dev_dsp < 0) {
DPRINTK ("EXIT, returning -ENODEV\n");
return -ENODEV;
static int __init install_wavefront (void)
{
- if ((dev.synth_dev = register_sound_synth (&wavefront_fops, -1)) < 0) {
+ if ((dev.synth_dev = register_sound_synth (&wavefront_fops, -1, NULL)) < 0) {
printk (KERN_ERR LOGNAME "cannot register raw synth\n");
return -1;
}
unit->ac97_features = eid;
- if ((codec->dev_mixer = register_sound_mixer(&ymf_mixer_fops, -1)) < 0) {
+ if ((codec->dev_mixer = register_sound_mixer(&ymf_mixer_fops, -1, &unit->pci->dev)) < 0) {
printk(KERN_ERR "ymfpci: couldn't register mixer!\n");
goto out_kfree;
}
}
/* register /dev/dsp */
- if ((codec->dev_audio = register_sound_dsp(&ymf_fops, -1)) < 0) {
+ if ((codec->dev_audio = register_sound_dsp(&ymf_fops, -1, &pcidev->dev)) < 0) {
printk(KERN_ERR "ymfpci: unable to register dsp\n");
goto out_free_irq;
}
pcm->info_flags = 0;
pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX;
strcpy(pcm->name, "ALI 5451");
+ pcm->dev = &codec->pci->dev;
codec->pcm = pcm;
if (rpcm) *rpcm = pcm;
return 0;
return err;
pcm->private_free = snd_als4000_pcm_free;
pcm->private_data = chip;
+ pcm->dev = &chip->pci->dev;
pcm->info_flags = SNDRV_PCM_INFO_JOINT_DUPLEX;
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_als4000_playback_ops);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_als4000_capture_ops);
snd_card_free(card);
printk(KERN_ERR "als4000: no MPU-401device at 0x%lx ?\n", gcr+0x30);
return err;
+ } else {
+ chip->rmidi->dev_ptr = &pci->dev;
}
if ((err = snd_als4000_pcm(chip, 0)) < 0) {
printk(KERN_ERR "als4000: no OPL device at 0x%lx-0x%lx ?\n",
gcr+0x10, gcr+0x12 );
} else {
+ opl3->dev = &pci->dev;
if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) {
snd_card_free(card);
return err;
pcm->private_free = snd_azf3328_pcm_free;
pcm->info_flags = 0;
strcpy(pcm->name, chip->card->shortname);
+ pcm->dev = &chip->pci->dev;
chip->pcm = pcm;
snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 64*1024, 64*1024);
snd_printk("azf3328: no MPU-401 device at 0x%lx?\n", chip->mpu_port);
snd_card_free(card);
return err;
+ } else {
+ chip->rmidi->dev_ptr = &pci->dev;
}
if ((err = snd_azf3328_pcm(chip, 0)) < 0) {
snd_printk("azf3328: no OPL3 device at 0x%lx-0x%lx?\n",
chip->synth_port, chip->synth_port+2 );
} else {
+ opl3->dev = &pci->dev;
if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) {
snd_card_free(card);
return err;
pcm->private_free = snd_cmipci_pcm_free;
pcm->info_flags = 0;
strcpy(pcm->name, "C-Media PCI DAC/ADC");
+ pcm->dev = &cm->pci->dev;
cm->pcm = pcm;
snd_pcm_lib_preallocate_pci_pages_for_all(cm->pci, pcm, 64*1024, 128*1024);
pcm->private_free = snd_cmipci_pcm_free;
pcm->info_flags = 0;
strcpy(pcm->name, "C-Media PCI 2nd DAC");
+ pcm->dev = &cm->pci->dev;
cm->pcm2 = pcm;
snd_pcm_lib_preallocate_pci_pages_for_all(cm->pci, pcm, 64*1024, 128*1024);
pcm->private_free = snd_cmipci_pcm_free;
pcm->info_flags = 0;
strcpy(pcm->name, "C-Media PCI IEC958");
+ pcm->dev = &cm->pci->dev;
cm->pcm_spdif = pcm;
snd_pcm_lib_preallocate_pci_pages_for_all(cm->pci, pcm, 64*1024, 128*1024);
printk(KERN_ERR "cmipci: no OPL device at 0x%lx, skipping...\n", iosynth);
iosynth = 0;
} else {
+ cm->opl3->dev = &pci->dev;
if ((err = snd_opl3_hwdep_new(cm->opl3, 0, 1, &cm->opl3hwdep)) < 0) {
printk(KERN_ERR "cmipci: cannot create OPL3 hwdep\n");
return err;
iomidi, 0,
cm->irq, 0, &cm->rmidi)) < 0) {
printk(KERN_ERR "cmipci: no UART401 device at 0x%lx\n", iomidi);
+ } else {
+ cm->rmidi->dev_ptr = &pci->dev;
}
}
pcm->private_free = snd_cs4281_pcm_free;
pcm->info_flags = 0;
strcpy(pcm->name, "CS4281");
+ pcm->dev = &chip->pci->dev;
chip->pcm = pcm;
snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 64*1024, 512*1024);
rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX;
rmidi->private_data = chip;
chip->rmidi = rmidi;
+ chip->rmidi->dev_ptr = &chip->pci->dev;
if (rrawmidi)
*rrawmidi = rmidi;
return 0;
snd_card_free(card);
return err;
}
+ opl3->dev = &pci->dev;
if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) {
snd_card_free(card);
return err;
/* global setup */
pcm->info_flags = 0;
strcpy(pcm->name, "CS46xx");
+ pcm->dev = &chip->pci->dev;
chip->pcm = pcm;
snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 64*1024, 256*1024);
/* global setup */
pcm->info_flags = 0;
strcpy(pcm->name, "CS46xx - Rear");
+ pcm->dev = &chip->pci->dev;
chip->pcm_rear = pcm;
snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 64*1024, 256*1024);
/* global setup */
pcm->info_flags = 0;
strcpy(pcm->name, "CS46xx - Center LFE");
+ pcm->dev = &chip->pci->dev;
chip->pcm_center_lfe = pcm;
snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 64*1024, 256*1024);
/* global setup */
pcm->info_flags = 0;
strcpy(pcm->name, "CS46xx - IEC958");
+ pcm->dev = &chip->pci->dev;
chip->pcm_rear = pcm;
snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 64*1024, 256*1024);
snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_cs46xx_midi_input);
rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX;
rmidi->private_data = chip;
+ rmidi->dev_ptr = &chip->pci->dev;
chip->rmidi = rmidi;
if (rrawmidi)
*rrawmidi = NULL;
pcm->info_flags = 0;
strcpy(pcm->name, "EMU10K1 FX8010");
+ pcm->dev = &emu->pci->dev;
emu->pcm_fx8010 = pcm;
snd_pcm_lib_preallocate_pci_pages_for_all(emu->pci, pcm, 64*1024, 0);
hw->ops.ioctl = snd_emu10k1_fx8010_ioctl;
hw->ops.release = snd_emu10k1_fx8010_release;
hw->private_data = emu;
+ hw->dev = &emu->pci->dev;
if (rhwdep)
*rhwdep = hw;
return 0;
SNDRV_RAWMIDI_INFO_DUPLEX;
rmidi->private_data = midi;
rmidi->private_free = snd_emu10k1_midi_free;
+ rmidi->dev_ptr = &emu->pci->dev;
midi->rmidi = rmidi;
return 0;
}
pcm->info_flags = 0;
pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX;
strcpy(pcm->name, "EMU10K1");
+ pcm->dev = &emu->pci->dev;
emu->pcm = pcm;
for (substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; substream; substream = substream->next)
pcm->info_flags = 0;
strcpy(pcm->name, "EMU10K1 MIC");
+ pcm->dev = &emu->pci->dev;
emu->pcm_mic = pcm;
snd_pcm_lib_preallocate_pci_pages_for_all(emu->pci, pcm, 64*1024, 64*1024);
pcm->info_flags = 0;
strcpy(pcm->name, "EMU10K1 EFX");
+ pcm->dev = &emu->pci->dev;
emu->pcm_efx = pcm;
if (rpcm)
*rpcm = pcm;
pcm->private_data = ensoniq;
pcm->private_free = snd_ensoniq_pcm_free;
+ pcm->dev = &ensoniq->pci->dev;
pcm->info_flags = 0;
#ifdef CHIP1370
strcpy(pcm->name, "ES1370 DAC2/ADC");
#endif
pcm->private_data = ensoniq;
pcm->private_free = snd_ensoniq_pcm_free2;
+ pcm->dev = &ensoniq->pci->dev;
pcm->info_flags = 0;
#ifdef CHIP1370
strcpy(pcm->name, "ES1370 DAC1");
snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_ensoniq_midi_input);
rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX;
rmidi->private_data = ensoniq;
+ rmidi->dev_ptr = &ensoniq->pci->dev;
ensoniq->rmidi = rmidi;
if (rrawmidi)
*rrawmidi = rmidi;
pcm->private_free = snd_es1938_free_pcm;
pcm->info_flags = 0;
strcpy(pcm->name, "ESS Solo-1");
+ pcm->dev = &chip->pci->dev;
snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 64*1024, 64*1024);
snd_card_free(card);
return err;
}
+ opl3->dev = &pci->dev;
if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) {
snd_card_free(card);
return err;
if (snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401,
chip->mpu_port, 1, chip->irq, 0, &chip->rmidi) < 0) {
printk(KERN_ERR "es1938: unable to initialize MPU-401\n");
+ } else {
+ chip->rmidi->dev_ptr = &pci->dev;
} /*else
snd_es1938_mixer_bits(chip, ESSSB_IREG_MPU401CONTROL, 0x40, 0x40);*/
strcpy(pcm->name, "ESS Maestro");
+ pcm->dev = &chip->pci->dev;
chip->pcm = pcm;
return 0;
printk(KERN_WARNING "es1968: skipping MPU-401 MIDI support..\n");
}
}
+ chip->rmidi->dev_ptr = &pci->dev;
/* card switches */
for (i = 0; i < num_controls(snd_es1968_control_switches); i++) {
pcm->private_free = snd_fm801_pcm_free;
pcm->info_flags = 0;
strcpy(pcm->name, "FM801");
+ pcm->dev = &chip->pci->dev;
chip->pcm = pcm;
snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, chip->multichannel ? 128*1024 : 64*1024, 128*1024);
chip->irq, 0, &chip->rmidi)) < 0) {
snd_card_free(card);
return err;
+ } else {
+ chip->rmidi->dev_ptr = &pci->dev;
}
if ((err = snd_opl3_create(card, FM801_REG(chip, OPL3_BANK0),
FM801_REG(chip, OPL3_BANK1),
snd_card_free(card);
return err;
}
+ opl3->dev = &pci->dev;
if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) {
snd_card_free(card);
return err;
pcm->private_free = snd_ice1712_pcm_free;
pcm->info_flags = 0;
strcpy(pcm->name, "ICE1712 consumer");
+ pcm->dev = &ice->pci->dev;
ice->pcm = pcm;
snd_pcm_lib_preallocate_pci_pages_for_all(ice->pci, pcm, 64*1024, 64*1024);
pcm->private_free = snd_ice1712_pcm_free_ds;
pcm->info_flags = 0;
strcpy(pcm->name, "ICE1712 consumer (DS)");
+ pcm->dev = &ice->pci->dev;
ice->pcm_ds = pcm;
snd_pcm_lib_preallocate_pci_pages_for_all(ice->pci, pcm, 64*1024, 128*1024);
pcm->private_free = snd_ice1712_pcm_profi_free;
pcm->info_flags = 0;
strcpy(pcm->name, "ICE1712 multi");
+ pcm->dev = &ice->pci->dev;
snd_pcm_lib_preallocate_pci_pages_for_all(ice->pci, pcm, 256*1024, 256*1024);
&ice->rmidi[0])) < 0) {
snd_card_free(card);
return err;
+ } else {
+ ice->rmidi[0]->dev_ptr = &pci->dev;
}
- if (ice->eeprom.data[ICE_EEP1_CODEC] & ICE1712_CFG_2xMPU401)
+ if (ice->eeprom.data[ICE_EEP1_CODEC] & ICE1712_CFG_2xMPU401) {
if ((err = snd_mpu401_uart_new(card, 1, MPU401_HW_ICE1712,
ICEREG(ice, MPU2_CTRL), 1,
ice->irq, 0,
&ice->rmidi[1])) < 0) {
snd_card_free(card);
return err;
+ } else {
+ ice->rmidi[1]->dev_ptr = &pci->dev;
}
+ }
}
sprintf(card->longname, "%s at 0x%lx, irq %i",
pcm->private_data = ice;
pcm->info_flags = 0;
strcpy(pcm->name, "ICE1724");
+ pcm->dev = &ice->pci->dev;
snd_pcm_lib_preallocate_pci_pages_for_all(ice->pci, pcm, 256*1024, 256*1024);
pcm->private_data = ice;
pcm->info_flags = 0;
strcpy(pcm->name, "ICE1724 IEC958");
+ pcm->dev = &ice->pci->dev;
snd_pcm_lib_preallocate_pci_pages_for_all(ice->pci, pcm, 64*1024, 64*1024);
&ice->rmidi[0])) < 0) {
snd_card_free(card);
return err;
+ } else {
+ ice->rmidi[0]->dev_ptr = &pci->dev;
}
}
}
pcm->private_data = chip;
pcm->info_flags = 0;
+ pcm->dev = &chip->pci->dev;
if (rec->suffix)
sprintf(pcm->name, "%s - %s", chip->card->shortname, rec->suffix);
else
-1, 0, &chip->rmidi)) < 0) {
printk(KERN_ERR "intel8x0: no UART401 device at 0x%x, skipping.\n", mpu_port[dev]);
mpu_port[dev] = 0;
+ } else {
+ chip->rmidi->dev_ptr = &pci->dev;
}
} else
mpu_port[dev] = 0;
korg1212->pcm->private_data = korg1212;
korg1212->pcm->private_free = snd_korg1212_free_pcm;
strcpy(korg1212->pcm->name, "korg1212");
+ korg1212->pcm->dev = &pci->dev;
snd_pcm_set_ops(korg1212->pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_korg1212_playback_ops);
pcm->private_data = chip;
pcm->info_flags = 0;
strcpy(pcm->name, chip->card->driver);
+ pcm->dev = &chip->pci->dev;
chip->pcm = pcm;
snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 64*1024, 64*1024);
pcm->private_data = chip;
pcm->info_flags = 0;
+ pcm->dev = &chip->pci->dev;
chip->pcm = pcm;
return 0;
rme32->spdif_pcm->private_data = rme32;
rme32->spdif_pcm->private_free = snd_rme32_free_spdif_pcm;
strcpy(rme32->spdif_pcm->name, "Digi32 IEC958");
+ rme32->spdif_pcm->dev = &rme32->pci->dev;
snd_pcm_set_ops(rme32->spdif_pcm, SNDRV_PCM_STREAM_PLAYBACK,
&snd_rme32_playback_spdif_ops);
snd_pcm_set_ops(rme32->spdif_pcm, SNDRV_PCM_STREAM_CAPTURE,
rme32->adat_pcm->private_data = rme32;
rme32->adat_pcm->private_free = snd_rme32_free_adat_pcm;
strcpy(rme32->adat_pcm->name, "Digi32 ADAT");
+ rme32->adat_pcm->dev = &rme32->pci->dev;
snd_pcm_set_ops(rme32->adat_pcm, SNDRV_PCM_STREAM_PLAYBACK,
&snd_rme32_playback_adat_ops);
snd_pcm_set_ops(rme32->adat_pcm, SNDRV_PCM_STREAM_CAPTURE,
rme96->spdif_pcm->private_data = rme96;
rme96->spdif_pcm->private_free = snd_rme96_free_spdif_pcm;
strcpy(rme96->spdif_pcm->name, "Digi96 IEC958");
+ rme96->spdif_pcm->dev = &rme96->pci->dev;
snd_pcm_set_ops(rme96->spdif_pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_rme96_playback_spdif_ops);
snd_pcm_set_ops(rme96->spdif_pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_rme96_capture_spdif_ops);
rme96->adat_pcm->private_data = rme96;
rme96->adat_pcm->private_free = snd_rme96_free_adat_pcm;
strcpy(rme96->adat_pcm->name, "Digi96 ADAT");
+ rme96->adat_pcm->dev = &rme96->pci->dev;
snd_pcm_set_ops(rme96->adat_pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_rme96_playback_adat_ops);
snd_pcm_set_ops(rme96->adat_pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_rme96_capture_adat_ops);
sprintf (hdsp->midi[id].rmidi->name, "%s MIDI %d", card->id, id+1);
hdsp->midi[id].rmidi->private_data = &hdsp->midi[id];
+ hdsp->midi[id].rmidi->dev_ptr = &hdsp->pci->dev;
snd_rawmidi_set_ops (hdsp->midi[id].rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_hdsp_midi_output);
snd_rawmidi_set_ops (hdsp->midi[id].rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_hdsp_midi_input);
hdsp->hwdep = hw;
hw->private_data = hdsp;
strcpy(hw->name, "HDSP hwdep interface");
+ hw->dev = &hdsp->pci->dev;
hw->ops.open = snd_hdsp_hwdep_dummy_op;
hw->ops.ioctl = snd_hdsp_hwdep_ioctl;
hdsp->pcm = pcm;
pcm->private_data = hdsp;
strcpy(pcm->name, hdsp->card_name);
+ pcm->dev = &hdsp->pci->dev;
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_hdsp_playback_ops);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_hdsp_capture_ops);
rme9652->pcm = pcm;
pcm->private_data = rme9652;
+ pcm->dev = &rme9652->pci->dev;
strcpy(pcm->name, rme9652->card_name);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_rme9652_playback_ops);
pcm->private_free = snd_sonicvibes_pcm_free;
pcm->info_flags = 0;
strcpy(pcm->name, "S3 SonicVibes");
+ pcm->dev = &sonic->pci->dev;
sonic->pcm = pcm;
snd_pcm_lib_preallocate_pci_pages_for_all(sonic->pci, pcm, 64*1024, 128*1024);
&midi_uart)) < 0) {
snd_card_free(card);
return err;
+ } else {
+ midi_uart->dev_ptr = &pci->dev;
}
snd_sonicvibes_midi(sonic, midi_uart);
if ((err = snd_opl3_create(card, sonic->synth_port,
snd_card_free(card);
return err;
}
+ opl3->dev = &pci->dev;
if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) {
snd_card_free(card);
return err;
trident->irq, 0, &trident->rmidi)) < 0) {
snd_card_free(card);
return err;
+ } else {
+ trident->rmidi->dev_ptr = &pci->dev;
}
#if defined(CONFIG_SND_SEQUENCER) || (defined(MODULE) && defined(CONFIG_SND_SEQUENCER_MODULE))
pcm->info_flags = 0;
pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX;
strcpy(pcm->name, "Trident 4DWave");
+ pcm->dev = &trident->pci->dev;
trident->pcm = pcm;
if (trident->tlb.entries) {
foldback->private_data = trident;
foldback->private_free = snd_trident_foldback_pcm_free;
+ foldback->dev = &trident->pci->dev;
if (trident->tlb.entries)
snd_pcm_set_ops(foldback, SNDRV_PCM_STREAM_CAPTURE, &snd_trident_nx_foldback_ops);
else
spdif->private_data = trident;
spdif->private_free = snd_trident_spdif_pcm_free;
+ spdif->dev = &trident->pci->dev;
if (trident->device != TRIDENT_DEVICE_ID_SI7018) {
snd_pcm_set_ops(spdif, SNDRV_PCM_STREAM_PLAYBACK, &snd_trident_spdif_ops);
} else {
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_via8233_capture_ops);
pcm->private_data = chip;
strcpy(pcm->name, chip->card->shortname);
+ pcm->dev = &chip->pci->dev;
/* set up playbacks */
for (i = 0; i < 4; i++)
init_viadev(chip, i, 0x10 * i, 0);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_via8233_capture_ops);
pcm->private_data = chip;
strcpy(pcm->name, chip->card->shortname);
+ pcm->dev = &chip->pci->dev;
/* set up playback */
init_viadev(chip, chip->multi_devno, VIA_REG_MULTPLAY_STATUS, 0);
/* set up capture */
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_via8233_capture_ops);
pcm->private_data = chip;
strcpy(pcm->name, chip->card->shortname);
+ pcm->dev = &chip->pci->dev;
/* set up playback */
init_viadev(chip, chip->multi_devno, VIA_REG_MULTPLAY_STATUS, 0);
/* capture */
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_via8233_playback_ops);
pcm->private_data = chip;
strcpy(pcm->name, chip->card->shortname);
+ pcm->dev = &chip->pci->dev;
/* set up playback */
init_viadev(chip, chip->playback_devno, 0x30, 0);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_via686_capture_ops);
pcm->private_data = chip;
strcpy(pcm->name, chip->card->shortname);
+ pcm->dev = &chip->pci->dev;
init_viadev(chip, 0, VIA_REG_PLAYBACK_STATUS, 0);
init_viadev(chip, 1, VIA_REG_CAPTURE_STATUS, 1);
if ((err = snd_vx_hwdep_new(&vx->core)) < 0) {
snd_card_free(card);
return err;
+ } else {
+ (vx->core).hwdep->dev = &pci->dev;
}
if ((err = snd_card_register(card)) < 0) {
printk(KERN_WARNING "ymfpci: cannot initialize MPU401 at 0x%lx, skipping...\n", mpu_port[dev]);
legacy_ctrl &= ~YMFPCI_LEGACY_MIEN; /* disable MPU401 irq */
pci_write_config_word(pci, PCIR_DSXG_LEGACY, legacy_ctrl);
+ } else {
+ chip->rawmidi->dev_ptr = &pci->dev;
}
}
if (chip->fm_res) {
printk(KERN_WARNING "ymfpci: cannot initialize FM OPL3 at 0x%lx, skipping...\n", fm_port[dev]);
legacy_ctrl &= ~YMFPCI_LEGACY_FMEN;
pci_write_config_word(pci, PCIR_DSXG_LEGACY, legacy_ctrl);
- } else if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) {
+ } else {
+ opl3->dev = &pci->dev;
+ if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) {
snd_card_free(card);
snd_printk("cannot create opl3 hwdep\n");
return err;
+ }
}
}
#if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE))
/* global setup */
pcm->info_flags = 0;
strcpy(pcm->name, "YMFPCI");
+ pcm->dev = &chip->pci->dev;
chip->pcm = pcm;
snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 64*1024, 256*1024);
/* global setup */
pcm->info_flags = 0;
strcpy(pcm->name, "YMFPCI - AC'97");
+ pcm->dev = &chip->pci->dev;
chip->pcm2 = pcm;
snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 64*1024, 256*1024);
/* global setup */
pcm->info_flags = 0;
strcpy(pcm->name, "YMFPCI - IEC958");
+ pcm->dev = &chip->pci->dev;
chip->pcm_spdif = pcm;
snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 64*1024, 256*1024);
/* global setup */
pcm->info_flags = 0;
strcpy(pcm->name, "YMFPCI - Rear PCM");
+ pcm->dev = &chip->pci->dev;
chip->pcm_4ch = pcm;
snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 64*1024, 256*1024);
#include <linux/major.h>
#include <linux/kmod.h>
#include <linux/devfs_fs_kernel.h>
+#include <linux/device.h>
#define SOUND_STEP 16
struct file_operations *unit_fops;
struct sound_unit *next;
char name[32];
+ void (*release)(struct sound_unit *su);
+ struct class_device class_dev;
};
+#define to_sound_unit(su) container_of(su, struct sound_unit, class_dev)
+
+static void release_sound_unit(struct sound_unit *su)
+{
+ kfree(su);
+}
+
+static void release_sound(struct class_device *class_dev)
+{
+ struct sound_unit *su = to_sound_unit(class_dev);
+ if (!su->release){
+ printk (KERN_INFO "Please create your release method.\n");
+ return;
+ }
+ su->release(su);
+}
+static ssize_t show_dev(struct class_device *class_dev, char *buf)
+{
+ struct sound_unit *su = to_sound_unit(class_dev);
+ return sprintf(buf, "%d:%d\n", SOUND_MAJOR, su->unit_minor);
+}
+
+static CLASS_DEVICE_ATTR(dev, S_IRUGO, show_dev, NULL);
+
+struct class sound_class = {
+ .name = "sound",
+ .release = &release_sound,
+};
+
+EXPORT_SYMBOL(sound_class);
+
#ifdef CONFIG_SOUND_MSNDCLAS
extern int msnd_classic_init(void);
#endif
* list. Acquires locks as needed
*/
-static int sound_insert_unit(struct sound_unit **list, struct file_operations *fops, int index, int low, int top, const char *name, umode_t mode)
+static int sound_insert_unit(struct sound_unit **list, struct file_operations *fops, int index, int low, int top, const char *name, umode_t mode, struct device *dev)
{
struct sound_unit *s = kmalloc(sizeof(*s), GFP_KERNEL);
- int r;
+ int r, rc;
if (!s)
return -ENOMEM;
else
sprintf(s->name, "sound/%s%d", name, r / SOUND_STEP);
- devfs_mk_cdev(MKDEV(SOUND_MAJOR, s->unit_minor),
- S_IFCHR | mode, s->name);
+ devfs_mk_cdev(MKDEV(SOUND_MAJOR, s->unit_minor), S_IFCHR | mode, s->name);
+ s->release = release_sound_unit;
+ rc = sound_add_class_device(s->name+6, &s->class_dev, dev);
+ if (rc) {
+ r = rc;
+ goto fail;
+ }
+ class_device_create_file(&s->class_dev, &class_device_attr_dev);
return r;
fail:
p = __sound_remove_unit(list, unit);
spin_unlock(&sound_loader_lock);
if (p) {
+ sound_remove_class_device(&p->class_dev);
devfs_remove(p->name);
- kfree(p);
+ /* kfree(p) now handled in release_sound_unit()*/
+ //kfree(p);
}
}
+int sound_add_class_device(char *name, struct class_device *class_dev, struct device *dev)
+{
+ memset(class_dev, 0x00, sizeof(struct class_device));
+ class_dev->dev = dev;
+ class_dev->class = &sound_class;
+ sprintf(class_dev->class_id, "%s", name);
+ return class_device_register(class_dev);
+}
+EXPORT_SYMBOL(sound_add_class_device);
+
+void sound_remove_class_device(struct class_device *class_dev)
+{
+ class_device_unregister(class_dev);
+}
+EXPORT_SYMBOL(sound_remove_class_device);
/*
* Allocations
*
* a negative error code is returned.
*/
-int register_sound_special(struct file_operations *fops, int unit)
+int register_sound_special(struct file_operations *fops, int unit, struct device *device)
{
const int chain = unit % SOUND_STEP;
int max_unit = 128 + chain;
}
break;
}
- return sound_insert_unit(&chains[chain], fops, -1, unit, max_unit,
- name, S_IRUSR | S_IWUSR);
+ return sound_insert_unit(&chains[chain], fops, -1, unit, max_unit, name, S_IRUSR | S_IWUSR, device);
}
EXPORT_SYMBOL(register_sound_special);
* number is returned, on failure a negative error code is returned.
*/
-int register_sound_mixer(struct file_operations *fops, int dev)
+int register_sound_mixer(struct file_operations *fops, int dev, struct device *device)
{
return sound_insert_unit(&chains[0], fops, dev, 0, 128,
- "mixer", S_IRUSR | S_IWUSR);
+ "mixer", S_IRUSR | S_IWUSR, device);
}
EXPORT_SYMBOL(register_sound_mixer);
* number is returned, on failure a negative error code is returned.
*/
-int register_sound_midi(struct file_operations *fops, int dev)
+int register_sound_midi(struct file_operations *fops, int dev, struct device *device)
{
return sound_insert_unit(&chains[2], fops, dev, 2, 130,
- "midi", S_IRUSR | S_IWUSR);
+ "midi", S_IRUSR | S_IWUSR, device);
}
EXPORT_SYMBOL(register_sound_midi);
* and will always allocate them as a matching pair - eg dsp3/audio3
*/
-int register_sound_dsp(struct file_operations *fops, int dev)
+int register_sound_dsp(struct file_operations *fops, int dev, struct device *device)
{
return sound_insert_unit(&chains[3], fops, dev, 3, 131,
- "dsp", S_IWUSR | S_IRUSR);
+ "dsp", S_IWUSR | S_IRUSR, device);
}
EXPORT_SYMBOL(register_sound_dsp);
*/
-int register_sound_synth(struct file_operations *fops, int dev)
+int register_sound_synth(struct file_operations *fops, int dev, struct device *device)
{
return sound_insert_unit(&chains[9], fops, dev, 9, 137,
- "synth", S_IRUSR | S_IWUSR);
+ "synth", S_IRUSR | S_IWUSR, device);
}
EXPORT_SYMBOL(register_sound_synth);
{
/* We have nothing to really do here - we know the lists must be
empty */
+ class_unregister(&sound_class);
unregister_chrdev(SOUND_MAJOR, "sound");
devfs_remove("sound");
}
}
devfs_mk_dir ("sound");
+ class_register(&sound_class);
return 0;
}
#define USB_SUBCLASS_MIDI_STREAMING 0x03
#define USB_SUBCLASS_VENDOR_SPEC 0xff
-#define USB_DT_CS_DEVICE 0x21
-#define USB_DT_CS_CONFIG 0x22
-#define USB_DT_CS_STRING 0x23
-#define USB_DT_CS_INTERFACE 0x24
-#define USB_DT_CS_ENDPOINT 0x25
-
#define CS_AUDIO_UNDEFINED 0x20
#define CS_AUDIO_DEVICE 0x21
#define CS_AUDIO_CONFIGURATION 0x22
snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_usbmidi_output_ops);
snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_usbmidi_input_ops);
+ rmidi->dev_ptr = &umidi->iface->dev;
umidi->rmidi = rmidi;
return 0;
}