--- /dev/null
+This directory attempts to document the ABI between the Linux kernel and
+userspace, and the relative stability of these interfaces. Due to the
+everchanging nature of Linux, and the differing maturity levels, these
+interfaces should be used by userspace programs in different ways.
+
+We have four different levels of ABI stability, as shown by the four
+different subdirectories in this location. Interfaces may change levels
+of stability according to the rules described below.
+
+The different levels of stability are:
+
+ stable/
+ This directory documents the interfaces that the developer has
+ defined to be stable. Userspace programs are free to use these
+ interfaces with no restrictions, and backward compatibility for
+ them will be guaranteed for at least 2 years. Most interfaces
+ (like syscalls) are expected to never change and always be
+ available.
+
+ testing/
+ This directory documents interfaces that are felt to be stable,
+ as the main development of this interface has been completed.
+ The interface can be changed to add new features, but the
+ current interface will not break by doing this, unless grave
+ errors or security problems are found in them. Userspace
+ programs can start to rely on these interfaces, but they must be
+ aware of changes that can occur before these interfaces move to
+ be marked stable. Programs that use these interfaces are
+ strongly encouraged to add their name to the description of
+ these interfaces, so that the kernel developers can easily
+ notify them if any changes occur (see the description of the
+ layout of the files below for details on how to do this.)
+
+ obsolete/
+ This directory documents interfaces that are still remaining in
+ the kernel, but are marked to be removed at some later point in
+ time. The description of the interface will document the reason
+ why it is obsolete and when it can be expected to be removed.
+ The file Documentation/feature-removal-schedule.txt may describe
+ some of these interfaces, giving a schedule for when they will
+ be removed.
+
+ removed/
+ This directory contains a list of the old interfaces that have
+ been removed from the kernel.
+
+Every file in these directories will contain the following information:
+
+What: Short description of the interface
+Date: Date created
+KernelVersion: Kernel version this feature first showed up in.
+Contact: Primary contact for this interface (may be a mailing list)
+Description: Long description of the interface and how to use it.
+Users: All users of this interface who wish to be notified when
+ it changes. This is very important for interfaces in
+ the "testing" stage, so that kernel developers can work
+ with userspace developers to ensure that things do not
+ break in ways that are unacceptable. It is also
+ important to get feedback for these interfaces to make
+ sure they are working in a proper way and do not need to
+ be changed further.
+
+
+How things move between levels:
+
+Interfaces in stable may move to obsolete, as long as the proper
+notification is given.
+
+Interfaces may be removed from obsolete and the kernel as long as the
+documented amount of time has gone by.
+
+Interfaces in the testing state can move to the stable state when the
+developers feel they are finished. They cannot be removed from the
+kernel tree without going through the obsolete state first.
+
+It's up to the developer to place their interfaces in the category they
+wish for it to start out in.
--- /dev/null
+What: devfs
+Date: July 2005
+Contact: Greg Kroah-Hartman <gregkh@suse.de>
+Description:
+ devfs has been unmaintained for a number of years, has unfixable
+ races, contains a naming policy within the kernel that is
+ against the LSB, and can be replaced by using udev.
+ The files fs/devfs/*, include/linux/devfs_fs*.h will be removed,
+ along with the the assorted devfs function calls throughout the
+ kernel tree.
+
+Users:
+
--- /dev/null
+What: The kernel syscall interface
+Description:
+ This interface matches much of the POSIX interface and is based
+ on it and other Unix based interfaces. It will only be added to
+ over time, and not have things removed from it.
+
+ Note that this interface is different for every architecture
+ that Linux supports. Please see the architecture-specific
+ documentation for details on the syscall numbers that are to be
+ mapped to each syscall.
--- /dev/null
+What: /sys/module
+Description:
+ The /sys/module tree consists of the following structure:
+
+ /sys/module/MODULENAME
+ The name of the module that is in the kernel. This
+ module name will show up either if the module is built
+ directly into the kernel, or if it is loaded as a
+ dyanmic module.
+
+ /sys/module/MODULENAME/parameters
+ This directory contains individual files that are each
+ individual parameters of the module that are able to be
+ changed at runtime. See the individual module
+ documentation as to the contents of these parameters and
+ what they accomplish.
+
+ Note: The individual parameter names and values are not
+ considered stable, only the fact that they will be
+ placed in this location within sysfs. See the
+ individual driver documentation for details as to the
+ stability of the different parameters.
+
+ /sys/module/MODULENAME/refcnt
+ If the module is able to be unloaded from the kernel, this file
+ will contain the current reference count of the module.
+
+ Note: If the module is built into the kernel, or if the
+ CONFIG_MODULE_UNLOAD kernel configuration value is not enabled,
+ this file will not be present.
--- /dev/null
+What: /sys/class/
+Date: Febuary 2006
+Contact: Greg Kroah-Hartman <gregkh@suse.de>
+Description:
+ The /sys/class directory will consist of a group of
+ subdirectories describing individual classes of devices
+ in the kernel. The individual directories will consist
+ of either subdirectories, or symlinks to other
+ directories.
+
+ All programs that use this directory tree must be able
+ to handle both subdirectories or symlinks in order to
+ work properly.
+
+Users:
+ udev <linux-hotplug-devel@lists.sourceforge.net>
--- /dev/null
+What: /sys/devices
+Date: February 2006
+Contact: Greg Kroah-Hartman <gregkh@suse.de>
+Description:
+ The /sys/devices tree contains a snapshot of the
+ internal state of the kernel device tree. Devices will
+ be added and removed dynamically as the machine runs,
+ and between different kernel versions, the layout of the
+ devices within this tree will change.
+
+ Please do not rely on the format of this tree because of
+ this. If a program wishes to find different things in
+ the tree, please use the /sys/class structure and rely
+ on the symlinks there to point to the proper location
+ within the /sys/devices tree of the individual devices.
+ Or rely on the uevent messages to notify programs of
+ devices being added and removed from this tree to find
+ the location of those devices.
+
+ Note that sometimes not all devices along the directory
+ chain will have emitted uevent messages, so userspace
+ programs must be able to handle such occurrences.
+
+Users:
+ udev <linux-hotplug-devel@lists.sourceforge.net>
You can use some configuration tool of your distribution to configure this
"modem" or configure pppd/wvdial manually. There are some example ppp
- configuration files and chat scripts in the gigaset-VERSION/ppp directory.
+ configuration files and chat scripts in the gigaset-VERSION/ppp directory
+ in the driver packages from http://sourceforge.net/projects/gigaset307x/.
Please note that the USB drivers are not able to change the state of the
control lines (the M105 driver can be configured to use some undocumented
control requests, if you really need the control lines, though). This means
If you want both of these at once, you are out of luck.
- You can also use /sys/module/<name>/parameters/cidmode for changing
- the CID mode setting (<name> is usb_gigaset or bas_gigaset).
+ You can also use /sys/class/tty/ttyGxy/cidmode for changing the CID mode
+ setting (ttyGxy is ttyGU0 or ttyGB0).
3. Troubleshooting
- Key overview
- Key service overview
- Key access permissions
+ - SELinux support
- New procfs files
- Userspace system call interface
- Kernel services
the key or having the sysadmin capability is sufficient.
+===============
+SELINUX SUPPORT
+===============
+
+The security class "key" has been added to SELinux so that mandatory access
+controls can be applied to keys created within various contexts. This support
+is preliminary, and is likely to change quite significantly in the near future.
+Currently, all of the basic permissions explained above are provided in SELinux
+as well; SE Linux is simply invoked after all basic permission checks have been
+performed.
+
+Each key is labeled with the same context as the task to which it belongs.
+Typically, this is the same task that was running when the key was created.
+The default keyrings are handled differently, but in a way that is very
+intuitive:
+
+ (*) The user and user session keyrings that are created when the user logs in
+ are currently labeled with the context of the login manager.
+
+ (*) The keyrings associated with new threads are each labeled with the context
+ of their associated thread, and both session and process keyrings are
+ handled similarly.
+
+Note, however, that the default keyrings associated with the root user are
+labeled with the default kernel context, since they are created early in the
+boot process, before root has a chance to log in.
+
+
================
NEW PROCFS FILES
================
It is not safe to sleep in this method; the caller may hold spinlocks.
+ (*) void (*revoke)(struct key *key);
+
+ This method is optional. It is called to discard part of the payload
+ data upon a key being revoked. The caller will have the key semaphore
+ write-locked.
+
+ It is safe to sleep in this method, though care should be taken to avoid
+ a deadlock against the key semaphore.
+
+
(*) void (*destroy)(struct key *key);
This method is optional. It is called to discard the payload data on a key
There is currently no way to know what states a device or driver
supports a priori. This will change in the future.
-pm_message_t meaning
-
-pm_message_t has two fields. event ("major"), and flags. If driver
-does not know event code, it aborts the request, returning error. Some
-drivers may need to deal with special cases based on the actual type
-of suspend operation being done at the system level. This is why
-there are flags.
-
-Event codes are:
-
-ON -- no need to do anything except special cases like broken
-HW.
-
-# NOTIFICATION -- pretty much same as ON?
-
-FREEZE -- stop DMA and interrupts, and be prepared to reinit HW from
-scratch. That probably means stop accepting upstream requests, the
-actual policy of what to do with them beeing specific to a given
-driver. It's acceptable for a network driver to just drop packets
-while a block driver is expected to block the queue so no request is
-lost. (Use IDE as an example on how to do that). FREEZE requires no
-power state change, and it's expected for drivers to be able to
-quickly transition back to operating state.
-
-SUSPEND -- like FREEZE, but also put hardware into low-power state. If
-there's need to distinguish several levels of sleep, additional flag
-is probably best way to do that.
-
-Transitions are only from a resumed state to a suspended state, never
-between 2 suspended states. (ON -> FREEZE or ON -> SUSPEND can happen,
-FREEZE -> SUSPEND or SUSPEND -> FREEZE can not).
-
-All events are:
-
-[NOTE NOTE NOTE: If you are driver author, you should not care; you
-should only look at event, and ignore flags.]
-
-#Prepare for suspend -- userland is still running but we are going to
-#enter suspend state. This gives drivers chance to load firmware from
-#disk and store it in memory, or do other activities taht require
-#operating userland, ability to kmalloc GFP_KERNEL, etc... All of these
-#are forbiden once the suspend dance is started.. event = ON, flags =
-#PREPARE_TO_SUSPEND
-
-Apm standby -- prepare for APM event. Quiesce devices to make life
-easier for APM BIOS. event = FREEZE, flags = APM_STANDBY
-
-Apm suspend -- same as APM_STANDBY, but it we should probably avoid
-spinning down disks. event = FREEZE, flags = APM_SUSPEND
-
-System halt, reboot -- quiesce devices to make life easier for BIOS. event
-= FREEZE, flags = SYSTEM_HALT or SYSTEM_REBOOT
-
-System shutdown -- at least disks need to be spun down, or data may be
-lost. Quiesce devices, just to make life easier for BIOS. event =
-FREEZE, flags = SYSTEM_SHUTDOWN
-
-Kexec -- turn off DMAs and put hardware into some state where new
-kernel can take over. event = FREEZE, flags = KEXEC
-
-Powerdown at end of swsusp -- very similar to SYSTEM_SHUTDOWN, except wake
-may need to be enabled on some devices. This actually has at least 3
-subtypes, system can reboot, enter S4 and enter S5 at the end of
-swsusp. event = FREEZE, flags = SWSUSP and one of SYSTEM_REBOOT,
-SYSTEM_SHUTDOWN, SYSTEM_S4
-
-Suspend to ram -- put devices into low power state. event = SUSPEND,
-flags = SUSPEND_TO_RAM
-
-Freeze for swsusp snapshot -- stop DMA and interrupts. No need to put
-devices into low power mode, but you must be able to reinitialize
-device from scratch in resume method. This has two flavors, its done
-once on suspending kernel, once on resuming kernel. event = FREEZE,
-flags = DURING_SUSPEND or DURING_RESUME
-
-Device detach requested from /sys -- deinitialize device; proably same as
-SYSTEM_SHUTDOWN, I do not understand this one too much. probably event
-= FREEZE, flags = DEV_DETACH.
-
-#These are not really events sent:
-#
-#System fully on -- device is working normally; this is probably never
-#passed to suspend() method... event = ON, flags = 0
-#
-#Ready after resume -- userland is now running, again. Time to free any
-#memory you ate during prepare to suspend... event = ON, flags =
-#READY_AFTER_RESUME
-#
-
-
pm_message_t meaning
pm_message_t has two fields. event ("major"), and flags. If driver
*
* (*) suspend/resume support is needed to make it safe.
*
- * If you have any filesystems on USB devices mounted before suspend,
+ * If you have any filesystems on USB devices mounted before software suspend,
* they won't be accessible after resume and you may lose data, as though
- * you have unplugged the USB devices with mounted filesystems on them
- * (see the FAQ below for details).
+ * you have unplugged the USB devices with mounted filesystems on them;
+ * see the FAQ below for details. (This is not true for more traditional
+ * power states like "standby", which normally don't turn USB off.)
You need to append resume=/dev/your_swap_partition to kernel command
line. Then you suspend by
distinctions between SUSPEND and FREEZE.
A: Doing SUSPEND when you are asked to do FREEZE is always correct,
-but it may be unneccessarily slow. If you want USB to stay simple,
+but it may be unneccessarily slow. If you want your driver to stay simple,
slowness may not matter to you. It can always be fixed later.
For devices like disk it does matter, you do not want to spindown for
I suspend to disk, I can lose data unless the filesystem has been mounted
with "sync"?
-A: That's right. It depends on your hardware, and it could be true even for
-suspend-to-RAM. In fact, even with "-o sync" you can lose data if your
-programs have information in buffers they haven't written out to disk.
+A: That's right ... if you disconnect that device, you may lose data.
+In fact, even with "-o sync" you can lose data if your programs have
+information in buffers they haven't written out to a disk you disconnect,
+or if you disconnect before the device finished saving data you wrote.
-If you're lucky, your hardware will support low-power modes for USB
-controllers while the system is asleep. Lots of hardware doesn't,
-however. Shutting off the power to a USB controller is equivalent to
-unplugging all the attached devices.
+Software suspend normally powers down USB controllers, which is equivalent
+to disconnecting all USB devices attached to your system.
+
+Your system might well support low-power modes for its USB controllers
+while the system is asleep, maintaining the connection, using true sleep
+modes like "suspend-to-RAM" or "standby". (Don't write "disk" to the
+/sys/power/state file; write "standby" or "mem".) We've not seen any
+hardware that can use these modes through software suspend, although in
+theory some systems might support "platform" or "firmware" modes that
+won't break the USB connections.
Remember that it's always a bad idea to unplug a disk drive containing a
-mounted filesystem. With USB that's true even when your system is asleep!
-The safest thing is to unmount all USB-based filesystems before suspending
-and remount them after resuming.
+mounted filesystem. That's true even when your system is asleep! The
+safest thing is to unmount all filesystems on removable media (such USB,
+Firewire, CompactFlash, MMC, external SATA, or even IDE hotplug bays)
+before suspending; then remount them after resuming.
Module for C-Media CMI8338 and 8738 PCI sound cards.
- mpu_port - 0x300,0x310,0x320,0x330, 0 = disable (default)
+ mpu_port - 0x300,0x310,0x320,0x330 = legacy port,
+ 1 = integrated PCI port,
+ 0 = disable (default)
fm_port - 0x388 (default), 0 = disable (default)
soft_ac3 - Software-conversion of raw SPDIF packets (model 033 only)
(default = 1)
Module for multifunction CS5535 companion PCI device
- This module supports multiple cards.
+ The power-management is supported.
Module snd-dt019x
-----------------
Module snd-hda-intel
--------------------
- Module for Intel HD Audio (ICH6, ICH6M, ICH7), ATI SB450,
- VIA VT8251/VT8237A
+ Module for Intel HD Audio (ICH6, ICH6M, ESB2, ICH7, ICH8),
+ ATI SB450, SB600, RS600,
+ VIA VT8251/VT8237A,
+ SIS966, ULI M5461
model - force the model name
position_fix - Fix DMA pointer (0 = auto, 1 = none, 2 = POSBUF, 3 = FIFO size)
AD1981
basic 3-jack (default)
hp HP nx6320
+ thinkpad Lenovo Thinkpad T60/X60/Z60
AD1986A
6stack 6-jack, separate surrounds (default)
About capture IBL, see the description of snd-vx222 module.
- Note: the driver is build only when CONFIG_ISA is set.
-
- Note2: snd-vxp440 driver is merged to snd-vxpocket driver since
+ Note: snd-vxp440 driver is merged to snd-vxpocket driver since
ALSA 1.0.10.
The power-management is supported.
Module for Sound Core PDAudioCF sound card.
- Note: the driver is build only when CONFIG_ISA is set.
-
The power-management is supported.
<programlisting>
<![CDATA[
struct snd_rawmidi *rmidi;
- snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401, port, integrated,
+ snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401, port, info_flags,
irq, irq_flags, &rmidi);
]]>
</programlisting>
</para>
<para>
+ The 5th argument is bitflags for additional information.
When the i/o port address above is a part of the PCI i/o
region, the MPU401 i/o port might have been already allocated
- (reserved) by the driver itself. In such a case, pass non-zero
- to the 5th argument
- (<parameter>integrated</parameter>). Otherwise, pass 0 to it,
+ (reserved) by the driver itself. In such a case, pass a bit flag
+ <constant>MPU401_INFO_INTEGRATED</constant>,
and
the mpu401-uart layer will allocate the i/o ports by itself.
</para>
+ <para>
+ When the controller supports only the input or output MIDI stream,
+ pass <constant>MPU401_INFO_INPUT</constant> or
+ <constant>MPU401_INFO_OUTPUT</constant> bitflag, respectively.
+ Then the rawmidi instance is created as a single stream.
+ </para>
+
+ <para>
+ <constant>MPU401_INFO_MMIO</constant> bitflag is used to change
+ the access method to MMIO (via readb and writeb) instead of
+ iob and outb. In this case, you have to pass the iomapped address
+ to <function>snd_mpu401_uart_new()</function>.
+ </para>
+
+ <para>
+ When <constant>MPU401_INFO_TX_IRQ</constant> is set, the output
+ stream isn't checked in the default interrupt handler. The driver
+ needs to call <function>snd_mpu401_uart_interrupt_tx()</function>
+ by itself to start processing the output stream in irq handler.
+ </para>
+
<para>
Usually, the port address corresponds to the command port and
port + 1 corresponds to the data port. If not, you may change
<informalexample>
<programlisting>
<![CDATA[
- snd_info_set_text_ops(entry, chip, read_size, my_proc_read);
+ snd_info_set_text_ops(entry, chip, my_proc_read);
]]>
</programlisting>
</informalexample>
<informalexample>
<programlisting>
<![CDATA[
- entry->c.text.write_size = 256;
entry->c.text.write = my_proc_write;
]]>
</programlisting>
</informalexample>
</para>
- <para>
- The buffer size for read is set to 1024 implicitly by
- <function>snd_info_set_text_ops()</function>. It should suffice
- in most cases (the size will be aligned to
- <constant>PAGE_SIZE</constant> anyway), but if you need to handle
- very large text files, you can set it explicitly, too.
-
- <informalexample>
- <programlisting>
-<![CDATA[
- entry->c.text.read_size = 65536;
-]]>
- </programlisting>
- </informalexample>
- </para>
-
<para>
For the write callback, you can use
<function>snd_info_get_line()</function> to get a text line, and
power status.</para></listitem>
<listitem><para>Call <function>snd_pcm_suspend_all()</function> to suspend the running PCM streams.</para></listitem>
<listitem><para>If AC97 codecs are used, call
- <function>snd_ac97_resume()</function> for each codec.</para></listitem>
+ <function>snd_ac97_suspend()</function> for each codec.</para></listitem>
<listitem><para>Save the register values if necessary.</para></listitem>
<listitem><para>Stop the hardware if necessary.</para></listitem>
<listitem><para>Disable the PCI device by calling
# mount -t debugfs none_debugs /sys/kernel/debug
# modprobe usbmon
+#
Verify that bus sockets are present.
-[root@lembas zaitcev]# ls /sys/kernel/debug/usbmon
+# ls /sys/kernel/debug/usbmon
1s 1t 2s 2t 3s 3t 4s 4t
-[root@lembas zaitcev]#
-
-# ls /sys/kernel
+#
2. Find which bus connects to the desired device
* Raw text data format
-The '0t' type data consists of a stream of events, such as URB submission,
+The '1t' type data consists of a stream of events, such as URB submission,
URB callback, submission error. Every event is a text line, which consists
of whitespace separated words. The number of position of words may depend
on the event type, but there is a set of words, common for all types.
Zi Zo Isochronous input and output
Ii Io Interrupt input and output
Bi Bo Bulk input and output
- Device address and Endpoint number are decimal numbers with leading zeroes
- or 3 and 2 positions, correspondingly.
-- URB Status. This field makes no sense for submissions, but is present
- to help scripts with parsing. In error case, it contains the error code.
- In case of a setup packet, it contains a Setup Tag. If scripts read a number
- in this field, they proceed to read Data Length. Otherwise, they read
- the setup packet before reading the Data Length.
+ Device address and Endpoint number are 3-digit and 2-digit (respectively)
+ decimal numbers, with leading zeroes.
+- URB Status. In most cases, this field contains a number, sometimes negative,
+ which represents a "status" field of the URB. This field makes no sense for
+ submissions, but is present anyway to help scripts with parsing. When an
+ error occurs, the field contains the error code. In case of a submission of
+ a Control packet, this field contains a Setup Tag instead of an error code.
+ It is easy to tell whether the Setup Tag is present because it is never a
+ number. Thus if scripts find a number in this field, they proceed to read
+ Data Length. If they find something else, like a letter, they read the setup
+ packet before reading the Data Length.
- Setup packet, if present, consists of 5 words: one of each for bmRequestType,
bRequest, wValue, wIndex, wLength, as specified by the USB Specification 2.0.
These words are safe to decode if Setup Tag was 's'. Otherwise, the setup
packet was present, but not captured, and the fields contain filler.
-- Data Length. This is the actual length in the URB.
+- Data Length. For submissions, this is the requested length. For callbacks,
+ this is the actual length.
- Data tag. The usbmon may not always capture data, even if length is nonzero.
- Only if tag is '=', the data words are present.
+ The data words are present only if this tag is '='.
- Data words follow, in big endian hexadecimal format. Notice that they are
not machine words, but really just a byte stream split into words to make
it easier to read. Thus, the last word may contain from one to four bytes.
P: Silicon Graphics Inc
M: xfs-masters@oss.sgi.com
M: nathans@sgi.com
-L: linux-xfs@oss.sgi.com
+L: xfs@oss.sgi.com
W: http://oss.sgi.com/projects/xfs
S: Supported
OBJCOPY_COFF_ARGS := -O aixcoff-rs6000 --set-start 0x500000
OBJCOPY_MIB_ARGS := -O aixcoff-rs6000 -R .stab -R .stabstr -R .comment
-zlib := infblock.c infcodes.c inffast.c inflate.c inftrees.c infutil.c
-zlibheader := infblock.h infcodes.h inffast.h inftrees.h infutil.h
+zlib := inffast.c inflate.c inftrees.c
+zlibheader := inffast.h inffixed.h inflate.h inftrees.h infutil.h
zliblinuxheader := zlib.h zconf.h zutil.h
$(addprefix $(obj)/,$(zlib) main.o): $(addprefix $(obj)/,$(zliblinuxheader)) $(addprefix $(obj)/,$(zlibheader))
{
struct device_node *np;
unsigned int i;
- struct platform_device *usb_dev;
+ struct platform_device *usb_dev_mph = NULL, *usb_dev_dr = NULL;
int ret;
for (np = NULL, i = 0;
r[1].end = np->intrs[0].line;
r[1].flags = IORESOURCE_IRQ;
- usb_dev =
- platform_device_register_simple("fsl-usb2-mph", i, r, 2);
- if (IS_ERR(usb_dev)) {
- ret = PTR_ERR(usb_dev);
+ usb_dev_mph =
+ platform_device_register_simple("fsl-ehci", i, r, 2);
+ if (IS_ERR(usb_dev_mph)) {
+ ret = PTR_ERR(usb_dev_mph);
goto err;
}
- usb_dev->dev.coherent_dma_mask = 0xffffffffUL;
- usb_dev->dev.dma_mask = &usb_dev->dev.coherent_dma_mask;
+ usb_dev_mph->dev.coherent_dma_mask = 0xffffffffUL;
+ usb_dev_mph->dev.dma_mask = &usb_dev_mph->dev.coherent_dma_mask;
usb_data.operating_mode = FSL_USB2_MPH_HOST;
usb_data.phy_mode = determine_usb_phy(prop);
ret =
- platform_device_add_data(usb_dev, &usb_data,
+ platform_device_add_data(usb_dev_mph, &usb_data,
sizeof(struct
fsl_usb2_platform_data));
if (ret)
- goto unreg;
+ goto unreg_mph;
}
- return 0;
-
-unreg:
- platform_device_unregister(usb_dev);
-err:
- return ret;
-}
-
-arch_initcall(fsl_usb_of_init);
-
-static int __init fsl_usb_dr_of_init(void)
-{
- struct device_node *np;
- unsigned int i;
- struct platform_device *usb_dev;
- int ret;
-
- for (np = NULL, i = 0;
+ for (np = NULL;
(np = of_find_compatible_node(np, "usb", "fsl-usb2-dr")) != NULL;
i++) {
struct resource r[2];
ret = of_address_to_resource(np, 0, &r[0]);
if (ret)
- goto err;
+ goto unreg_mph;
r[1].start = np->intrs[0].line;
r[1].end = np->intrs[0].line;
r[1].flags = IORESOURCE_IRQ;
- usb_dev =
- platform_device_register_simple("fsl-usb2-dr", i, r, 2);
- if (IS_ERR(usb_dev)) {
- ret = PTR_ERR(usb_dev);
+ usb_dev_dr =
+ platform_device_register_simple("fsl-ehci", i, r, 2);
+ if (IS_ERR(usb_dev_dr)) {
+ ret = PTR_ERR(usb_dev_dr);
goto err;
}
- usb_dev->dev.coherent_dma_mask = 0xffffffffUL;
- usb_dev->dev.dma_mask = &usb_dev->dev.coherent_dma_mask;
+ usb_dev_dr->dev.coherent_dma_mask = 0xffffffffUL;
+ usb_dev_dr->dev.dma_mask = &usb_dev_dr->dev.coherent_dma_mask;
usb_data.operating_mode = FSL_USB2_DR_HOST;
usb_data.phy_mode = determine_usb_phy(prop);
ret =
- platform_device_add_data(usb_dev, &usb_data,
+ platform_device_add_data(usb_dev_dr, &usb_data,
sizeof(struct
fsl_usb2_platform_data));
if (ret)
- goto unreg;
+ goto unreg_dr;
}
-
return 0;
-unreg:
- platform_device_unregister(usb_dev);
+unreg_dr:
+ if (usb_dev_dr)
+ platform_device_unregister(usb_dev_dr);
+unreg_mph:
+ if (usb_dev_mph)
+ platform_device_unregister(usb_dev_mph);
err:
return ret;
}
-arch_initcall(fsl_usb_dr_of_init);
+arch_initcall(fsl_usb_of_init);
CFLAGS_kbd.o := -Idrivers/char
CFLAGS_vreset.o := -Iarch/ppc/boot/include
-zlib := infblock.c infcodes.c inffast.c inflate.c inftrees.c infutil.c
+zlib := inffast.c inflate.c inftrees.c
lib-y += $(zlib:.c=.o) div64.o
lib-$(CONFIG_VGA_CONSOLE) += vreset.o kbd.o
},
},
[MPC83xx_USB2_DR] = {
- .name = "fsl-usb2-dr",
+ .name = "fsl-ehci",
.id = 1,
.num_resources = 2,
.resource = (struct resource[]) {
},
},
[MPC83xx_USB2_MPH] = {
- .name = "fsl-usb2-mph",
- .id = 1,
+ .name = "fsl-ehci",
+ .id = 2,
.num_resources = 2,
.resource = (struct resource[]) {
{
if (!root_flags)
root_mountflags &= ~MS_RDONLY;
ROOT_DEV = old_decode_dev(root_dev);
-#ifdef CONFIG_BLK_DEV_INITRD
+#ifdef CONFIG_BLK_DEV_RAM
rd_image_start = ram_flags & RAMDISK_IMAGE_START_MASK;
rd_prompt = ((ram_flags & RAMDISK_PROMPT_FLAG) != 0);
rd_doload = ((ram_flags & RAMDISK_LOAD_FLAG) != 0);
void time_init_kern(void)
{
- unsigned long long nsecs;
+ long long nsecs;
nsecs = os_nsecs();
set_normalized_timespec(&wall_to_monotonic, -nsecs / BILLION,
bool "K8 GART IOMMU support"
default y
select SWIOTLB
+ select AGP
depends on PCI
help
Support for hardware IOMMU in AMD's Opteron/Athlon64 Processors
northbridge and a software emulation used on other systems without
hardware IOMMU. If unsure, say Y.
-# need this always enabled with GART_IOMMU for the VIA workaround
+# need this always selected by GART_IOMMU for the VIA workaround
config SWIOTLB
bool
- default y
- depends on GART_IOMMU
config X86_MCE
bool "Machine check support" if EMBEDDED
# Makefile for some libs needed by zImage.
#
-zlib := infblock.c infcodes.c inffast.c inflate.c inftrees.c infutil.c
+zlib := inffast.c inflate.c inftrees.c
lib-y += $(zlib:.c=.o) zmem.o
#include <linux/buffer_head.h>
#include <linux/mutex.h>
-static struct subsystem block_subsys;
-
+struct subsystem block_subsys;
static DEFINE_MUTEX(block_subsys_lock);
/*
.uevent = block_uevent,
};
-/* declare block_subsys. */
-static decl_subsys(block, &ktype_block, &block_uevent_ops);
-
+decl_subsys(block, &ktype_block, &block_uevent_ops);
/*
* aggregate disk stat collector. Uses the same stats that the sysfs
If you are unsure about this, say N here.
endmenu
+
+config SYS_HYPERVISOR
+ bool
+ default n
cpu.o firmware.o init.o map.o dmapool.o \
attribute_container.o transport_class.o
obj-y += power/
+obj-$(CONFIG_ISA) += isa.o
obj-$(CONFIG_FW_LOADER) += firmware_class.o
obj-$(CONFIG_NUMA) += node.o
obj-$(CONFIG_MEMORY_HOTPLUG) += memory.o
obj-$(CONFIG_SMP) += topology.o
+obj-$(CONFIG_SYS_HYPERVISOR) += hypervisor.o
ifeq ($(CONFIG_DEBUG_DRIVER),y)
EXTRA_CFLAGS += -DDEBUG
}
up(&attribute_container_mutex);
}
-EXPORT_SYMBOL_GPL(attribute_container_remove_device);
/**
* attribute_container_device_trigger - execute a trigger for each matching classdev
}
up(&attribute_container_mutex);
}
-EXPORT_SYMBOL_GPL(attribute_container_device_trigger);
/**
* attribute_container_trigger - trigger a function for each matching container
}
up(&attribute_container_mutex);
}
-EXPORT_SYMBOL_GPL(attribute_container_trigger);
/**
* attribute_container_add_attrs - add attributes
return 0;
}
-EXPORT_SYMBOL_GPL(attribute_container_add_attrs);
/**
* attribute_container_add_class_device - same function as class_device_add
return error;
return attribute_container_add_attrs(classdev);
}
-EXPORT_SYMBOL_GPL(attribute_container_add_class_device);
/**
* attribute_container_add_class_device_adapter - simple adapter for triggers
{
return attribute_container_add_class_device(classdev);
}
-EXPORT_SYMBOL_GPL(attribute_container_add_class_device_adapter);
/**
* attribute_container_remove_attrs - remove any attribute files
for (i = 0; attrs[i]; i++)
class_device_remove_file(classdev, attrs[i]);
}
-EXPORT_SYMBOL_GPL(attribute_container_remove_attrs);
/**
* attribute_container_class_device_del - equivalent of class_device_del
attribute_container_remove_attrs(classdev);
class_device_del(classdev);
}
-EXPORT_SYMBOL_GPL(attribute_container_class_device_del);
/**
* attribute_container_find_class_device - find the corresponding class_device
extern int buses_init(void);
extern int classes_init(void);
extern int firmware_init(void);
+#ifdef CONFIG_SYS_HYPERVISOR
+extern int hypervisor_init(void);
+#else
+static inline int hypervisor_init(void) { return 0; }
+#endif
extern int platform_bus_init(void);
extern int system_bus_init(void);
extern int cpu_dev_init(void);
extern int attribute_container_init(void);
extern int bus_add_device(struct device * dev);
+extern void bus_attach_device(struct device * dev);
extern void bus_remove_device(struct device * dev);
+extern struct bus_type *get_bus(struct bus_type * bus);
+extern void put_bus(struct bus_type * bus);
extern int bus_add_driver(struct device_driver *);
extern void bus_remove_driver(struct device_driver *);
return container_of(_attr, struct class_device_attribute, attr);
}
+extern char *make_class_name(const char *name, struct kobject *kobj);
* @dev: device being added
*
* - Add the device to its bus's list of devices.
- * - Try to attach to driver.
- * - Create link to device's physical location.
+ * - Create link to device's bus.
*/
int bus_add_device(struct device * dev)
{
if (bus) {
pr_debug("bus %s: add device %s\n", bus->name, dev->bus_id);
- device_attach(dev);
- klist_add_tail(&dev->knode_bus, &bus->klist_devices);
error = device_add_attrs(bus, dev);
if (!error) {
sysfs_create_link(&bus->devices.kobj, &dev->kobj, dev->bus_id);
+ sysfs_create_link(&dev->kobj, &dev->bus->subsys.kset.kobj, "subsystem");
sysfs_create_link(&dev->kobj, &dev->bus->subsys.kset.kobj, "bus");
}
}
return error;
}
+/**
+ * bus_attach_device - add device to bus
+ * @dev: device tried to attach to a driver
+ *
+ * - Try to attach to driver.
+ */
+void bus_attach_device(struct device * dev)
+{
+ struct bus_type * bus = dev->bus;
+
+ if (bus) {
+ device_attach(dev);
+ klist_add_tail(&dev->knode_bus, &bus->klist_devices);
+ }
+}
+
/**
* bus_remove_device - remove device from bus
* @dev: device to be removed
void bus_remove_device(struct device * dev)
{
if (dev->bus) {
+ sysfs_remove_link(&dev->kobj, "subsystem");
sysfs_remove_link(&dev->kobj, "bus");
sysfs_remove_link(&dev->bus->devices.kobj, dev->bus_id);
device_remove_attrs(dev->bus, dev);
EXPORT_SYMBOL_GPL(bus_find_device);
EXPORT_SYMBOL_GPL(bus_for_each_drv);
-EXPORT_SYMBOL_GPL(bus_add_device);
-EXPORT_SYMBOL_GPL(bus_remove_device);
EXPORT_SYMBOL_GPL(bus_register);
EXPORT_SYMBOL_GPL(bus_unregister);
EXPORT_SYMBOL_GPL(bus_rescan_devices);
-EXPORT_SYMBOL_GPL(get_bus);
-EXPORT_SYMBOL_GPL(put_bus);
-EXPORT_SYMBOL_GPL(find_bus);
EXPORT_SYMBOL_GPL(bus_create_file);
EXPORT_SYMBOL_GPL(bus_remove_file);
sysfs_remove_file(&cls->subsys.kset.kobj, &attr->attr);
}
-struct class * class_get(struct class * cls)
+static struct class *class_get(struct class *cls)
{
if (cls)
return container_of(subsys_get(&cls->subsys), struct class, subsys);
return NULL;
}
-void class_put(struct class * cls)
+static void class_put(struct class * cls)
{
if (cls)
subsys_put(&cls->subsys);
pr_debug("device class '%s': registering\n", cls->name);
INIT_LIST_HEAD(&cls->children);
+ INIT_LIST_HEAD(&cls->devices);
INIT_LIST_HEAD(&cls->interfaces);
init_MUTEX(&cls->sem);
error = kobject_set_name(&cls->subsys.kset.kobj, "%s", cls->name);
INIT_LIST_HEAD(&class_dev->node);
}
-static char *make_class_name(struct class_device *class_dev)
+char *make_class_name(const char *name, struct kobject *kobj)
{
- char *name;
+ char *class_name;
int size;
- size = strlen(class_dev->class->name) +
- strlen(kobject_name(&class_dev->kobj)) + 2;
+ size = strlen(name) + strlen(kobject_name(kobj)) + 2;
- name = kmalloc(size, GFP_KERNEL);
- if (!name)
+ class_name = kmalloc(size, GFP_KERNEL);
+ if (!class_name)
return ERR_PTR(-ENOMEM);
- strcpy(name, class_dev->class->name);
- strcat(name, ":");
- strcat(name, kobject_name(&class_dev->kobj));
- return name;
+ strcpy(class_name, name);
+ strcat(class_name, ":");
+ strcat(class_name, kobject_name(kobj));
+ return class_name;
}
int class_device_add(struct class_device *class_dev)
return -EINVAL;
if (!strlen(class_dev->class_id))
- goto register_done;
+ goto out1;
parent_class = class_get(class_dev->class);
if (!parent_class)
- goto register_done;
+ goto out1;
+
parent_class_dev = class_device_get(class_dev->parent);
pr_debug("CLASS: registering class device: ID = '%s'\n",
class_dev->class_id);
/* first, register with generic layer. */
- kobject_set_name(&class_dev->kobj, "%s", class_dev->class_id);
+ error = kobject_set_name(&class_dev->kobj, "%s", class_dev->class_id);
+ if (error)
+ goto out2;
+
if (parent_class_dev)
class_dev->kobj.parent = &parent_class_dev->kobj;
else
error = kobject_add(&class_dev->kobj);
if (error)
- goto register_done;
+ goto out2;
/* add the needed attributes to this device */
+ sysfs_create_link(&class_dev->kobj, &parent_class->subsys.kset.kobj, "subsystem");
class_dev->uevent_attr.attr.name = "uevent";
class_dev->uevent_attr.attr.mode = S_IWUSR;
class_dev->uevent_attr.attr.owner = parent_class->owner;
class_dev->uevent_attr.store = store_uevent;
- class_device_create_file(class_dev, &class_dev->uevent_attr);
+ error = class_device_create_file(class_dev, &class_dev->uevent_attr);
+ if (error)
+ goto out3;
if (MAJOR(class_dev->devt)) {
struct class_device_attribute *attr;
attr = kzalloc(sizeof(*attr), GFP_KERNEL);
if (!attr) {
error = -ENOMEM;
- kobject_del(&class_dev->kobj);
- goto register_done;
+ goto out4;
}
attr->attr.name = "dev";
attr->attr.mode = S_IRUGO;
attr->attr.owner = parent_class->owner;
attr->show = show_dev;
- class_device_create_file(class_dev, attr);
+ error = class_device_create_file(class_dev, attr);
+ if (error) {
+ kfree(attr);
+ goto out4;
+ }
+
class_dev->devt_attr = attr;
}
- class_device_add_attrs(class_dev);
+ error = class_device_add_attrs(class_dev);
+ if (error)
+ goto out5;
+
if (class_dev->dev) {
- class_name = make_class_name(class_dev);
- sysfs_create_link(&class_dev->kobj,
- &class_dev->dev->kobj, "device");
- sysfs_create_link(&class_dev->dev->kobj, &class_dev->kobj,
- class_name);
+ class_name = make_class_name(class_dev->class->name,
+ &class_dev->kobj);
+ error = sysfs_create_link(&class_dev->kobj,
+ &class_dev->dev->kobj, "device");
+ if (error)
+ goto out6;
+ error = sysfs_create_link(&class_dev->dev->kobj, &class_dev->kobj,
+ class_name);
+ if (error)
+ goto out7;
}
- class_device_add_groups(class_dev);
+ error = class_device_add_groups(class_dev);
+ if (error)
+ goto out8;
kobject_uevent(&class_dev->kobj, KOBJ_ADD);
}
up(&parent_class->sem);
- register_done:
- if (error) {
- class_put(parent_class);
+ goto out1;
+
+ out8:
+ if (class_dev->dev)
+ sysfs_remove_link(&class_dev->kobj, class_name);
+ out7:
+ if (class_dev->dev)
+ sysfs_remove_link(&class_dev->kobj, "device");
+ out6:
+ class_device_remove_attrs(class_dev);
+ out5:
+ if (class_dev->devt_attr)
+ class_device_remove_file(class_dev, class_dev->devt_attr);
+ out4:
+ class_device_remove_file(class_dev, &class_dev->uevent_attr);
+ out3:
+ kobject_del(&class_dev->kobj);
+ out2:
+ if(parent_class_dev)
class_device_put(parent_class_dev);
- }
+ class_put(parent_class);
+ out1:
class_device_put(class_dev);
kfree(class_name);
return error;
}
if (class_dev->dev) {
- class_name = make_class_name(class_dev);
+ class_name = make_class_name(class_dev->class->name,
+ &class_dev->kobj);
sysfs_remove_link(&class_dev->kobj, "device");
sysfs_remove_link(&class_dev->dev->kobj, class_name);
}
+ sysfs_remove_link(&class_dev->kobj, "subsystem");
class_device_remove_file(class_dev, &class_dev->uevent_attr);
if (class_dev->devt_attr)
class_device_remove_file(class_dev, class_dev->devt_attr);
new_name);
if (class_dev->dev)
- old_class_name = make_class_name(class_dev);
+ old_class_name = make_class_name(class_dev->class->name,
+ &class_dev->kobj);
strlcpy(class_dev->class_id, new_name, KOBJ_NAME_LEN);
error = kobject_rename(&class_dev->kobj, new_name);
if (class_dev->dev) {
- new_class_name = make_class_name(class_dev);
+ new_class_name = make_class_name(class_dev->class->name,
+ &class_dev->kobj);
sysfs_create_link(&class_dev->dev->kobj, &class_dev->kobj,
new_class_name);
sysfs_remove_link(&class_dev->dev->kobj, old_class_name);
EXPORT_SYMBOL_GPL(class_remove_file);
EXPORT_SYMBOL_GPL(class_register);
EXPORT_SYMBOL_GPL(class_unregister);
-EXPORT_SYMBOL_GPL(class_get);
-EXPORT_SYMBOL_GPL(class_put);
EXPORT_SYMBOL_GPL(class_create);
EXPORT_SYMBOL_GPL(class_destroy);
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/string.h>
+#include <linux/kdev_t.h>
#include <asm/semaphore.h>
* sysfs bindings for devices.
*/
+/**
+ * dev_driver_string - Return a device's driver name, if at all possible
+ * @dev: struct device to get the name of
+ *
+ * Will return the device's driver's name if it is bound to a device. If
+ * the device is not bound to a device, it will return the name of the bus
+ * it is attached to. If it is not attached to a bus either, an empty
+ * string will be returned.
+ */
+const char *dev_driver_string(struct device *dev)
+{
+ return dev->driver ? dev->driver->name :
+ (dev->bus ? dev->bus->name : "");
+}
+EXPORT_SYMBOL_GPL(dev_driver_string);
+
#define to_dev(obj) container_of(obj, struct device, kobj)
#define to_dev_attr(_attr) container_of(_attr, struct device_attribute, attr)
struct device *dev = to_dev(kobj);
if (dev->bus)
return 1;
+ if (dev->class)
+ return 1;
}
return 0;
}
{
struct device *dev = to_dev(kobj);
- return dev->bus->name;
+ if (dev->bus)
+ return dev->bus->name;
+ if (dev->class)
+ return dev->class->name;
+ return NULL;
}
static int dev_uevent(struct kset *kset, struct kobject *kobj, char **envp,
int length = 0;
int retval = 0;
+ /* add the major/minor if present */
+ if (MAJOR(dev->devt)) {
+ add_uevent_var(envp, num_envp, &i,
+ buffer, buffer_size, &length,
+ "MAJOR=%u", MAJOR(dev->devt));
+ add_uevent_var(envp, num_envp, &i,
+ buffer, buffer_size, &length,
+ "MINOR=%u", MINOR(dev->devt));
+ }
+
/* add bus name of physical device */
if (dev->bus)
add_uevent_var(envp, num_envp, &i,
return count;
}
+static ssize_t show_dev(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ return print_dev_t(buf, dev->devt);
+}
+
/*
* devices_subsys - structure to be registered with kobject core.
*/
klist_init(&dev->klist_children, klist_children_get,
klist_children_put);
INIT_LIST_HEAD(&dev->dma_pools);
+ INIT_LIST_HEAD(&dev->node);
init_MUTEX(&dev->sem);
device_init_wakeup(dev, 0);
}
int device_add(struct device *dev)
{
struct device *parent = NULL;
+ char *class_name = NULL;
int error = -EINVAL;
dev = get_device(dev);
dev->uevent_attr.store = store_uevent;
device_create_file(dev, &dev->uevent_attr);
- kobject_uevent(&dev->kobj, KOBJ_ADD);
+ if (MAJOR(dev->devt)) {
+ struct device_attribute *attr;
+ attr = kzalloc(sizeof(*attr), GFP_KERNEL);
+ if (!attr) {
+ error = -ENOMEM;
+ goto PMError;
+ }
+ attr->attr.name = "dev";
+ attr->attr.mode = S_IRUGO;
+ if (dev->driver)
+ attr->attr.owner = dev->driver->owner;
+ attr->show = show_dev;
+ error = device_create_file(dev, attr);
+ if (error) {
+ kfree(attr);
+ goto attrError;
+ }
+
+ dev->devt_attr = attr;
+ }
+
+ if (dev->class) {
+ sysfs_create_link(&dev->kobj, &dev->class->subsys.kset.kobj,
+ "subsystem");
+ sysfs_create_link(&dev->class->subsys.kset.kobj, &dev->kobj,
+ dev->bus_id);
+
+ sysfs_create_link(&dev->kobj, &dev->parent->kobj, "device");
+ class_name = make_class_name(dev->class->name, &dev->kobj);
+ sysfs_create_link(&dev->parent->kobj, &dev->kobj, class_name);
+ }
+
if ((error = device_pm_add(dev)))
goto PMError;
if ((error = bus_add_device(dev)))
goto BusError;
+ kobject_uevent(&dev->kobj, KOBJ_ADD);
+ bus_attach_device(dev);
if (parent)
klist_add_tail(&dev->knode_parent, &parent->klist_children);
if (platform_notify)
platform_notify(dev);
Done:
+ kfree(class_name);
put_device(dev);
return error;
BusError:
device_pm_remove(dev);
PMError:
+ if (dev->devt_attr) {
+ device_remove_file(dev, dev->devt_attr);
+ kfree(dev->devt_attr);
+ }
+ attrError:
kobject_uevent(&dev->kobj, KOBJ_REMOVE);
kobject_del(&dev->kobj);
Error:
void device_del(struct device * dev)
{
struct device * parent = dev->parent;
+ char *class_name = NULL;
if (parent)
klist_del(&dev->knode_parent);
+ if (dev->devt_attr)
+ device_remove_file(dev, dev->devt_attr);
+ if (dev->class) {
+ sysfs_remove_link(&dev->kobj, "subsystem");
+ sysfs_remove_link(&dev->class->subsys.kset.kobj, dev->bus_id);
+ class_name = make_class_name(dev->class->name, &dev->kobj);
+ sysfs_remove_link(&dev->kobj, "device");
+ sysfs_remove_link(&dev->parent->kobj, class_name);
+ kfree(class_name);
+ }
device_remove_file(dev, &dev->uevent_attr);
/* Notify the platform of the removal, in case they
EXPORT_SYMBOL_GPL(device_create_file);
EXPORT_SYMBOL_GPL(device_remove_file);
+
+
+static void device_create_release(struct device *dev)
+{
+ pr_debug("%s called for %s\n", __FUNCTION__, dev->bus_id);
+ kfree(dev);
+}
+
+/**
+ * device_create - creates a device and registers it with sysfs
+ * @cs: pointer to the struct class that this device should be registered to.
+ * @parent: pointer to the parent struct device of this new device, if any.
+ * @dev: the dev_t for the char device to be added.
+ * @fmt: string for the class device's name
+ *
+ * This function can be used by char device classes. A struct
+ * device will be created in sysfs, registered to the specified
+ * class.
+ * A "dev" file will be created, showing the dev_t for the device, if
+ * the dev_t is not 0,0.
+ * If a pointer to a parent struct device is passed in, the newly
+ * created struct device will be a child of that device in sysfs. The
+ * pointer to the struct device will be returned from the call. Any
+ * further sysfs files that might be required can be created using this
+ * pointer.
+ *
+ * Note: the struct class passed to this function must have previously
+ * been created with a call to class_create().
+ */
+struct device *device_create(struct class *class, struct device *parent,
+ dev_t devt, char *fmt, ...)
+{
+ va_list args;
+ struct device *dev = NULL;
+ int retval = -ENODEV;
+
+ if (class == NULL || IS_ERR(class))
+ goto error;
+ if (parent == NULL) {
+ printk(KERN_WARNING "%s does not work yet for NULL parents\n", __FUNCTION__);
+ goto error;
+ }
+
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (!dev) {
+ retval = -ENOMEM;
+ goto error;
+ }
+
+ dev->devt = devt;
+ dev->class = class;
+ dev->parent = parent;
+ dev->release = device_create_release;
+
+ va_start(args, fmt);
+ vsnprintf(dev->bus_id, BUS_ID_SIZE, fmt, args);
+ va_end(args);
+ retval = device_register(dev);
+ if (retval)
+ goto error;
+
+ /* tie the class to the device */
+ down(&class->sem);
+ list_add_tail(&dev->node, &class->devices);
+ up(&class->sem);
+
+ return dev;
+
+error:
+ kfree(dev);
+ return ERR_PTR(retval);
+}
+EXPORT_SYMBOL_GPL(device_create);
+
+/**
+ * device_destroy - removes a device that was created with device_create()
+ * @class: the pointer to the struct class that this device was registered * with.
+ * @dev: the dev_t of the device that was previously registered.
+ *
+ * This call unregisters and cleans up a class device that was created with a
+ * call to class_device_create()
+ */
+void device_destroy(struct class *class, dev_t devt)
+{
+ struct device *dev = NULL;
+ struct device *dev_tmp;
+
+ down(&class->sem);
+ list_for_each_entry(dev_tmp, &class->devices, node) {
+ if (dev_tmp->devt == devt) {
+ dev = dev_tmp;
+ break;
+ }
+ }
+ up(&class->sem);
+
+ if (dev) {
+ list_del_init(&dev->node);
+ device_unregister(dev);
+ }
+}
+EXPORT_SYMBOL_GPL(device_destroy);
#include <linux/vmalloc.h>
#include <linux/interrupt.h>
#include <linux/bitops.h>
-#include <asm/semaphore.h>
+#include <linux/mutex.h>
#include <linux/firmware.h>
#include "base.h"
/* fw_lock could be moved to 'struct firmware_priv' but since it is just
* guarding for corner cases a global lock should be OK */
-static DECLARE_MUTEX(fw_lock);
+static DEFINE_MUTEX(fw_lock);
struct firmware_priv {
char fw_id[FIRMWARE_NAME_MAX];
switch (loading) {
case 1:
- down(&fw_lock);
+ mutex_lock(&fw_lock);
if (!fw_priv->fw) {
- up(&fw_lock);
+ mutex_unlock(&fw_lock);
break;
}
vfree(fw_priv->fw->data);
fw_priv->fw->size = 0;
fw_priv->alloc_size = 0;
set_bit(FW_STATUS_LOADING, &fw_priv->status);
- up(&fw_lock);
+ mutex_unlock(&fw_lock);
break;
case 0:
if (test_bit(FW_STATUS_LOADING, &fw_priv->status)) {
struct firmware *fw;
ssize_t ret_count = count;
- down(&fw_lock);
+ mutex_lock(&fw_lock);
fw = fw_priv->fw;
if (!fw || test_bit(FW_STATUS_DONE, &fw_priv->status)) {
ret_count = -ENODEV;
memcpy(buffer, fw->data + offset, ret_count);
out:
- up(&fw_lock);
+ mutex_unlock(&fw_lock);
return ret_count;
}
if (!capable(CAP_SYS_RAWIO))
return -EPERM;
- down(&fw_lock);
+ mutex_lock(&fw_lock);
fw = fw_priv->fw;
if (!fw || test_bit(FW_STATUS_DONE, &fw_priv->status)) {
retval = -ENODEV;
fw->size = max_t(size_t, offset + count, fw->size);
retval = count;
out:
- up(&fw_lock);
+ mutex_unlock(&fw_lock);
return retval;
}
} else
wait_for_completion(&fw_priv->completion);
- down(&fw_lock);
+ mutex_lock(&fw_lock);
if (!fw_priv->fw->size || test_bit(FW_STATUS_ABORT, &fw_priv->status)) {
retval = -ENOENT;
release_firmware(fw_priv->fw);
*firmware_p = NULL;
}
fw_priv->fw = NULL;
- up(&fw_lock);
+ mutex_unlock(&fw_lock);
class_device_unregister(class_dev);
goto out;
--- /dev/null
+/*
+ * hypervisor.c - /sys/hypervisor subsystem.
+ *
+ * This file is released under the GPLv2
+ *
+ */
+
+#include <linux/kobject.h>
+#include <linux/device.h>
+
+#include "base.h"
+
+decl_subsys(hypervisor, NULL, NULL);
+EXPORT_SYMBOL_GPL(hypervisor_subsys);
+
+int __init hypervisor_init(void)
+{
+ return subsystem_register(&hypervisor_subsys);
+}
buses_init();
classes_init();
firmware_init();
+ hypervisor_init();
/* These are also core pieces, but must come after the
* core core pieces.
--- /dev/null
+/*
+ * ISA bus.
+ */
+
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/isa.h>
+
+static struct device isa_bus = {
+ .bus_id = "isa"
+};
+
+struct isa_dev {
+ struct device dev;
+ struct device *next;
+ unsigned int id;
+};
+
+#define to_isa_dev(x) container_of((x), struct isa_dev, dev)
+
+static int isa_bus_match(struct device *dev, struct device_driver *driver)
+{
+ struct isa_driver *isa_driver = to_isa_driver(driver);
+
+ if (dev->platform_data == isa_driver) {
+ if (!isa_driver->match ||
+ isa_driver->match(dev, to_isa_dev(dev)->id))
+ return 1;
+ dev->platform_data = NULL;
+ }
+ return 0;
+}
+
+static int isa_bus_probe(struct device *dev)
+{
+ struct isa_driver *isa_driver = dev->platform_data;
+
+ if (isa_driver->probe)
+ return isa_driver->probe(dev, to_isa_dev(dev)->id);
+
+ return 0;
+}
+
+static int isa_bus_remove(struct device *dev)
+{
+ struct isa_driver *isa_driver = dev->platform_data;
+
+ if (isa_driver->remove)
+ return isa_driver->remove(dev, to_isa_dev(dev)->id);
+
+ return 0;
+}
+
+static void isa_bus_shutdown(struct device *dev)
+{
+ struct isa_driver *isa_driver = dev->platform_data;
+
+ if (isa_driver->shutdown)
+ isa_driver->shutdown(dev, to_isa_dev(dev)->id);
+}
+
+static int isa_bus_suspend(struct device *dev, pm_message_t state)
+{
+ struct isa_driver *isa_driver = dev->platform_data;
+
+ if (isa_driver->suspend)
+ return isa_driver->suspend(dev, to_isa_dev(dev)->id, state);
+
+ return 0;
+}
+
+static int isa_bus_resume(struct device *dev)
+{
+ struct isa_driver *isa_driver = dev->platform_data;
+
+ if (isa_driver->resume)
+ return isa_driver->resume(dev, to_isa_dev(dev)->id);
+
+ return 0;
+}
+
+static struct bus_type isa_bus_type = {
+ .name = "isa",
+ .match = isa_bus_match,
+ .probe = isa_bus_probe,
+ .remove = isa_bus_remove,
+ .shutdown = isa_bus_shutdown,
+ .suspend = isa_bus_suspend,
+ .resume = isa_bus_resume
+};
+
+static void isa_dev_release(struct device *dev)
+{
+ kfree(to_isa_dev(dev));
+}
+
+void isa_unregister_driver(struct isa_driver *isa_driver)
+{
+ struct device *dev = isa_driver->devices;
+
+ while (dev) {
+ struct device *tmp = to_isa_dev(dev)->next;
+ device_unregister(dev);
+ dev = tmp;
+ }
+ driver_unregister(&isa_driver->driver);
+}
+EXPORT_SYMBOL_GPL(isa_unregister_driver);
+
+int isa_register_driver(struct isa_driver *isa_driver, unsigned int ndev)
+{
+ int error;
+ unsigned int id;
+
+ isa_driver->driver.bus = &isa_bus_type;
+ isa_driver->devices = NULL;
+
+ error = driver_register(&isa_driver->driver);
+ if (error)
+ return error;
+
+ for (id = 0; id < ndev; id++) {
+ struct isa_dev *isa_dev;
+
+ isa_dev = kzalloc(sizeof *isa_dev, GFP_KERNEL);
+ if (!isa_dev) {
+ error = -ENOMEM;
+ break;
+ }
+
+ isa_dev->dev.parent = &isa_bus;
+ isa_dev->dev.bus = &isa_bus_type;
+
+ snprintf(isa_dev->dev.bus_id, BUS_ID_SIZE, "%s.%u",
+ isa_driver->driver.name, id);
+
+ isa_dev->dev.platform_data = isa_driver;
+ isa_dev->dev.release = isa_dev_release;
+ isa_dev->id = id;
+
+ error = device_register(&isa_dev->dev);
+ if (error) {
+ put_device(&isa_dev->dev);
+ break;
+ }
+
+ if (isa_dev->dev.platform_data) {
+ isa_dev->next = isa_driver->devices;
+ isa_driver->devices = &isa_dev->dev;
+ } else
+ device_unregister(&isa_dev->dev);
+ }
+
+ if (!error && !isa_driver->devices)
+ error = -ENODEV;
+
+ if (error)
+ isa_unregister_driver(isa_driver);
+
+ return error;
+}
+EXPORT_SYMBOL_GPL(isa_register_driver);
+
+static int __init isa_bus_init(void)
+{
+ int error;
+
+ error = bus_register(&isa_bus_type);
+ if (!error) {
+ error = device_register(&isa_bus);
+ if (error)
+ bus_unregister(&isa_bus_type);
+ }
+ return error;
+}
+
+device_initcall(isa_bus_init);
pr_debug("Registering platform device '%s'. Parent at %s\n",
pdev->dev.bus_id, pdev->dev.parent->bus_id);
- ret = device_register(&pdev->dev);
+ ret = device_add(&pdev->dev);
if (ret == 0)
return ret;
EXPORT_SYMBOL_GPL(platform_driver_unregister);
+/* modalias support enables more hands-off userspace setup:
+ * (a) environment variable lets new-style hotplug events work once system is
+ * fully running: "modprobe $MODALIAS"
+ * (b) sysfs attribute lets new-style coldplug recover from hotplug events
+ * mishandled before system is fully running: "modprobe $(cat modalias)"
+ */
+static ssize_t
+modalias_show(struct device *dev, struct device_attribute *a, char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ int len = snprintf(buf, PAGE_SIZE, "%s\n", pdev->name);
+
+ return (len >= PAGE_SIZE) ? (PAGE_SIZE - 1) : len;
+}
+
+static struct device_attribute platform_dev_attrs[] = {
+ __ATTR_RO(modalias),
+ __ATTR_NULL,
+};
+
+static int platform_uevent(struct device *dev, char **envp, int num_envp,
+ char *buffer, int buffer_size)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+
+ envp[0] = buffer;
+ snprintf(buffer, buffer_size, "MODALIAS=%s", pdev->name);
+ return 0;
+}
+
+
/**
* platform_match - bind platform device to platform driver.
* @dev: device.
struct bus_type platform_bus_type = {
.name = "platform",
+ .dev_attrs = platform_dev_attrs,
.match = platform_match,
+ .uevent = platform_uevent,
.suspend = platform_suspend,
.resume = platform_resume,
};
ifeq ($(CONFIG_DEBUG_DRIVER),y)
EXTRA_CFLAGS += -DDEBUG
endif
+ifeq ($(CONFIG_PM_DEBUG),y)
+EXTRA_CFLAGS += -DDEBUG
+endif
* lists. This way, the ancestors will be accessed before their descendents.
*/
+static inline char *suspend_verb(u32 event)
+{
+ switch (event) {
+ case PM_EVENT_SUSPEND: return "suspend";
+ case PM_EVENT_FREEZE: return "freeze";
+ default: return "(unknown suspend event)";
+ }
+}
+
/**
* suspend_device - Save state of one device.
dev->power.prev_state = dev->power.power_state;
if (dev->bus && dev->bus->suspend && !dev->power.power_state.event) {
- dev_dbg(dev, "suspending\n");
+ dev_dbg(dev, "%s%s\n",
+ suspend_verb(state.event),
+ ((state.event == PM_EVENT_SUSPEND)
+ && device_may_wakeup(dev))
+ ? ", may wakeup"
+ : ""
+ );
error = dev->bus->suspend(dev, state);
suspend_report_result(dev->bus->suspend, error);
}
EXPORT_SYMBOL_GPL(sysdev_create_file);
EXPORT_SYMBOL_GPL(sysdev_remove_file);
+#define to_sysdev_class(k) container_of(k, struct sysdev_class, kset.kobj)
+#define to_sysdev_class_attr(a) container_of(a, \
+ struct sysdev_class_attribute, attr)
+
+static ssize_t sysdev_class_show(struct kobject *kobj, struct attribute *attr,
+ char *buffer)
+{
+ struct sysdev_class * class = to_sysdev_class(kobj);
+ struct sysdev_class_attribute *class_attr = to_sysdev_class_attr(attr);
+
+ if (class_attr->show)
+ return class_attr->show(class, buffer);
+ return -EIO;
+}
+
+static ssize_t sysdev_class_store(struct kobject *kobj, struct attribute *attr,
+ const char *buffer, size_t count)
+{
+ struct sysdev_class * class = to_sysdev_class(kobj);
+ struct sysdev_class_attribute * class_attr = to_sysdev_class_attr(attr);
+
+ if (class_attr->store)
+ return class_attr->store(class, buffer, count);
+ return -EIO;
+}
+
+static struct sysfs_ops sysfs_class_ops = {
+ .show = sysdev_class_show,
+ .store = sysdev_class_store,
+};
+
+static struct kobj_type ktype_sysdev_class = {
+ .sysfs_ops = &sysfs_class_ops,
+};
+
+int sysdev_class_create_file(struct sysdev_class *c,
+ struct sysdev_class_attribute *a)
+{
+ return sysfs_create_file(&c->kset.kobj, &a->attr);
+}
+EXPORT_SYMBOL_GPL(sysdev_class_create_file);
+
+void sysdev_class_remove_file(struct sysdev_class *c,
+ struct sysdev_class_attribute *a)
+{
+ sysfs_remove_file(&c->kset.kobj, &a->attr);
+}
+EXPORT_SYMBOL_GPL(sysdev_class_remove_file);
+
/*
* declare system_subsys
*/
-static decl_subsys(system, &ktype_sysdev, NULL);
+static decl_subsys(system, &ktype_sysdev_class, NULL);
int sysdev_class_register(struct sysdev_class * cls)
{
disk->fops = &cciss_fops;
disk->queue = q;
disk->private_data = drv;
+ disk->driverfs_dev = &pdev->dev;
/* we must register the controller even if no disks exist */
/* this is for the online array utilities */
if(!drv->heads && j)
* TODO (sorted by decreasing priority)
* -- set readonly flag for CDs, set removable flag for CF readers
* -- do inquiry and verify we got a disk and not a tape (for LUN mismatch)
- * -- special case some senses, e.g. 3a/0 -> no media present, reduce retries
* -- verify the 13 conditions and do bulk resets
- * -- kill last_pipe and simply do two-state clearing on both pipes
* -- highmem
* -- move top_sense and work_bcs into separate allocations (if they survive)
* for cache purists and esoteric architectures.
* -- Allocate structure for LUN 0 before the first ub_sync_tur, avoid NULL. ?
* -- prune comments, they are too volumnous
- * -- Exterminate P3 printks
* -- Resove XXX's
- * -- Redo "benh's retries", perhaps have spin-up code to handle them. V:D=?
* -- CLEAR, CLR2STS, CLRRS seem to be ripe for refactoring.
*/
#include <linux/kernel.h>
#define UB_DIR_ILLEGAL2 2
#define UB_DIR_WRITE 3
-/* P3 */
#define UB_DIR_CHAR(c) (((c)==UB_DIR_WRITE)? 'w': \
(((c)==UB_DIR_READ)? 'r': 'n'))
*/
n_elem = blk_rq_map_sg(lun->disk->queue, rq, &urq->sgv[0]);
if (n_elem < 0) {
+ /* Impossible, because blk_rq_map_sg should not hit ENOMEM. */
printk(KERN_INFO "%s: failed request map (%d)\n",
- lun->name, n_elem); /* P3 */
+ lun->name, n_elem);
goto drop;
}
if (n_elem > UB_MAX_REQ_SG) { /* Paranoia */
if (urq->current_try >= 3)
return -EIO;
urq->current_try++;
- /* P3 */ printk("%s: dir %c len/act %d/%d "
+
+ /* Remove this if anyone complains of flooding. */
+ printk(KERN_DEBUG "%s: dir %c len/act %d/%d "
"[sense %x %02x %02x] retry %d\n",
sc->name, UB_DIR_CHAR(cmd->dir), cmd->len, cmd->act_len,
cmd->key, cmd->asc, cmd->ascq, urq->current_try);
* to check. But it's not all right if the device
* counts disagree with our counts.
*/
- /* P3 */ printk("%s: resid %d len %d act %d\n",
- sc->name, len, cmd->len, cmd->act_len);
goto Bad_End;
}
ub_state_sense(sc, cmd);
return;
case US_BULK_STAT_PHASE:
- /* P3 */ printk("%s: status PHASE\n", sc->name);
goto Bad_End;
default:
printk(KERN_INFO "%s: unknown CSW status 0x%x\n",
}
if (atomic_read(&sc->poison)) {
- printk(KERN_NOTICE "%s: Not resetting disconnected device\n",
- sc->name); /* P3 This floods. Remove soon. XXX */
+ ;
} else if ((sc->reset & 1) == 0) {
ub_sync_reset(sc);
msleep(700); /* usb-storage sleeps 6s (!) */
ub_probe_clear_stall(sc, sc->recv_bulk_pipe);
ub_probe_clear_stall(sc, sc->send_bulk_pipe);
} else if (sc->dev->actconfig->desc.bNumInterfaces != 1) {
- printk(KERN_NOTICE "%s: Not resetting multi-interface device\n",
- sc->name); /* P3 This floods. Remove soon. XXX */
+ ;
} else {
if ((lkr = usb_lock_device_for_reset(sc->dev, sc->intf)) < 0) {
printk(KERN_NOTICE
static int ub_bd_open(struct inode *inode, struct file *filp)
{
struct gendisk *disk = inode->i_bdev->bd_disk;
- struct ub_lun *lun;
- struct ub_dev *sc;
+ struct ub_lun *lun = disk->private_data;
+ struct ub_dev *sc = lun->udev;
unsigned long flags;
int rc;
- if ((lun = disk->private_data) == NULL)
- return -ENXIO;
- sc = lun->udev;
-
spin_lock_irqsave(&ub_lock, flags);
if (atomic_read(&sc->poison)) {
spin_unlock_irqrestore(&ub_lock, flags);
rc = ub_submit_scsi(sc, cmd);
spin_unlock_irqrestore(sc->lock, flags);
- if (rc != 0) {
- printk("ub: testing ready: submit error (%d)\n", rc); /* P3 */
+ if (rc != 0)
goto err_submit;
- }
wait_for_completion(&compl);
rc = ub_submit_scsi(sc, cmd);
spin_unlock_irqrestore(sc->lock, flags);
- if (rc != 0) {
- printk("ub: reading capacity: submit error (%d)\n", rc); /* P3 */
+ if (rc != 0)
goto err_submit;
- }
wait_for_completion(&compl);
if (cmd->error != 0) {
- printk("ub: reading capacity: error %d\n", cmd->error); /* P3 */
rc = -EIO;
goto err_read;
}
if (cmd->act_len != 8) {
- printk("ub: reading capacity: size %d\n", cmd->act_len); /* P3 */
rc = -EIO;
goto err_read;
}
case 2048: shift = 2; break;
case 4096: shift = 3; break;
default:
- printk("ub: Bad sector size %u\n", bsize); /* P3 */
rc = -EDOM;
goto err_inv_bsize;
}
sc->work_urb.error_count = 0;
sc->work_urb.status = 0;
- if ((rc = usb_submit_urb(&sc->work_urb, GFP_KERNEL)) != 0) {
- if (rc == -EPIPE) {
- printk("%s: Stall submitting GetMaxLUN, using 1 LUN\n",
- sc->name); /* P3 */
- } else {
- printk(KERN_NOTICE
- "%s: Unable to submit GetMaxLUN (%d)\n",
- sc->name, rc);
- }
+ if ((rc = usb_submit_urb(&sc->work_urb, GFP_KERNEL)) != 0)
goto err_submit;
- }
init_timer(&timer);
timer.function = ub_probe_timeout;
del_timer_sync(&timer);
usb_kill_urb(&sc->work_urb);
- if ((rc = sc->work_urb.status) < 0) {
- if (rc == -EPIPE) {
- printk("%s: Stall at GetMaxLUN, using 1 LUN\n",
- sc->name); /* P3 */
- } else {
- printk(KERN_NOTICE
- "%s: Error at GetMaxLUN (%d)\n",
- sc->name, rc);
- }
+ if ((rc = sc->work_urb.status) < 0)
goto err_io;
- }
if (sc->work_urb.actual_length != 1) {
- printk("%s: GetMaxLUN returned %d bytes\n", sc->name,
- sc->work_urb.actual_length); /* P3 */
nluns = 0;
} else {
if ((nluns = *p) == 55) {
if (nluns > UB_MAX_LUNS)
nluns = UB_MAX_LUNS;
}
- printk("%s: GetMaxLUN returned %d, using %d LUNs\n", sc->name,
- *p, nluns); /* P3 */
}
kfree(p);
* has to succeed, so we clear checks with an additional one here.
* In any case it's not our business how revaliadation is implemented.
*/
- for (i = 0; i < 3; i++) { /* Retries for benh's key */
+ for (i = 0; i < 3; i++) { /* Retries for the schwag key from KS'04 */
if ((rc = ub_sync_tur(sc, NULL)) <= 0) break;
if (rc != 0x6) break;
msleep(10);
goto err_id;
lun->udev = sc;
- list_add(&lun->link, &sc->luns);
snprintf(lun->name, 16, DRV_NAME "%c(%d.%d.%d)",
lun->id + 'a', sc->dev->bus->busnum, sc->dev->devnum, lun->num);
if ((disk = alloc_disk(UB_PARTS_PER_LUN)) == NULL)
goto err_diskalloc;
- lun->disk = disk;
sprintf(disk->disk_name, DRV_NAME "%c", lun->id + 'a');
sprintf(disk->devfs_name, DEVFS_NAME "/%c", lun->id + 'a');
disk->major = UB_MAJOR;
blk_queue_max_sectors(q, UB_MAX_SECTORS);
blk_queue_hardsect_size(q, lun->capacity.bsize);
+ lun->disk = disk;
q->queuedata = lun;
+ list_add(&lun->link, &sc->luns);
set_capacity(disk, lun->capacity.nsec);
if (lun->removable)
err_blkqinit:
put_disk(disk);
err_diskalloc:
- list_del(&lun->link);
ub_id_put(lun->id);
err_id:
kfree(lun);
struct ub_dev *sc = usb_get_intfdata(intf);
struct list_head *p;
struct ub_lun *lun;
- struct gendisk *disk;
unsigned long flags;
/*
*/
list_for_each (p, &sc->luns) {
lun = list_entry(p, struct ub_lun, link);
- disk = lun->disk;
- if (disk->flags & GENHD_FL_UP)
- del_gendisk(disk);
+ del_gendisk(lun->disk);
/*
* I wish I could do:
* set_bit(QUEUE_FLAG_DEAD, &q->queue_flags);
config AGP
- tristate "/dev/agpgart (AGP Support)" if !GART_IOMMU
+ tristate "/dev/agpgart (AGP Support)"
depends on ALPHA || IA64 || PPC || X86
- default y if GART_IOMMU
---help---
AGP (Accelerated Graphics Port) is a bus system mainly used to
connect graphics cards to the rest of the system.
* This field is optional, if there is no known struct device for this
* tty device it can be set to NULL safely.
*
+ * Returns a pointer to the class device (or ERR_PTR(-EFOO) on error).
+ *
* This call is required to be made to register an individual tty device if
* the tty driver's flags have the TTY_DRIVER_NO_DEVFS bit set. If that
* bit is not set, this function should not be called.
*/
-void tty_register_device(struct tty_driver *driver, unsigned index,
- struct device *device)
+struct class_device *tty_register_device(struct tty_driver *driver,
+ unsigned index, struct device *device)
{
char name[64];
dev_t dev = MKDEV(driver->major, driver->minor_start) + index;
if (index >= driver->num) {
printk(KERN_ERR "Attempt to register invalid tty line number "
" (%d).\n", index);
- return;
+ return ERR_PTR(-EINVAL);
}
devfs_mk_cdev(dev, S_IFCHR | S_IRUSR | S_IWUSR,
pty_line_name(driver, index, name);
else
tty_line_name(driver, index, name);
- class_device_create(tty_class, NULL, dev, device, "%s", name);
+
+ return class_device_create(tty_class, NULL, dev, device, "%s", name);
}
/**
switch (cs->cs_init) {
default:
+ /* clear device sysfs */
+ gigaset_free_dev_sysfs(cs);
+
gigaset_if_free(cs);
gig_dbg(DEBUG_INIT, "clearing hw");
cs->open_count = 0;
cs->dev = NULL;
cs->tty = NULL;
+ cs->class = NULL;
cs->cidmode = cidmode != 0;
//if(onechannel) { //FIXME
gigaset_if_init(cs);
+ /* set up device sysfs */
+ gigaset_init_dev_sysfs(cs);
+
spin_lock_irqsave(&cs->lock, flags);
cs->running = 1;
spin_unlock_irqrestore(&cs->lock, flags);
wait_event(cs->waitqueue, !cs->waiting);
- /* set up device sysfs */
- gigaset_init_dev_sysfs(cs);
-
mutex_unlock(&cs->mutex);
return 1;
//FIXME
}
- /* clear device sysfs */
- gigaset_free_dev_sysfs(cs);
-
cleanup_cs(cs);
exit:
struct gigaset_driver *driver;
unsigned minor_index;
struct device *dev;
+ struct class_device *class;
const struct gigaset_ops *ops;
return;
tasklet_init(&cs->if_wake_tasklet, &if_wake, (unsigned long) cs);
- tty_register_device(drv->tty, cs->minor_index, NULL);
+ cs->class = tty_register_device(drv->tty, cs->minor_index, NULL);
+
+ if (!IS_ERR(cs->class))
+ class_set_devdata(cs->class, cs);
+ else {
+ warn("could not register device to the tty subsystem");
+ cs->class = NULL;
+ }
}
void gigaset_if_free(struct cardstate *cs)
tasklet_disable(&cs->if_wake_tasklet);
tasklet_kill(&cs->if_wake_tasklet);
+ cs->class = NULL;
tty_unregister_device(drv->tty, cs->minor_index);
}
#include "gigaset.h"
#include <linux/ctype.h>
-static ssize_t show_cidmode(struct device *dev, struct device_attribute *attr,
- char *buf)
+static ssize_t show_cidmode(struct class_device *class, char *buf)
{
int ret;
unsigned long flags;
- struct cardstate *cs = dev_get_drvdata(dev);
+ struct cardstate *cs = class_get_devdata(class);
spin_lock_irqsave(&cs->lock, flags);
ret = sprintf(buf, "%u\n", cs->cidmode);
return ret;
}
-static ssize_t set_cidmode(struct device *dev, struct device_attribute *attr,
+static ssize_t set_cidmode(struct class_device *class,
const char *buf, size_t count)
{
- struct cardstate *cs = dev_get_drvdata(dev);
+ struct cardstate *cs = class_get_devdata(class);
long int value;
char *end;
return count;
}
-static DEVICE_ATTR(cidmode, S_IRUGO|S_IWUSR, show_cidmode, set_cidmode);
+static CLASS_DEVICE_ATTR(cidmode, S_IRUGO|S_IWUSR, show_cidmode, set_cidmode);
/* free sysfs for device */
void gigaset_free_dev_sysfs(struct cardstate *cs)
{
+ if (!cs->class)
+ return;
+
gig_dbg(DEBUG_INIT, "removing sysfs entries");
- device_remove_file(cs->dev, &dev_attr_cidmode);
+ class_device_remove_file(cs->class, &class_device_attr_cidmode);
}
/* initialize sysfs for device */
void gigaset_init_dev_sysfs(struct cardstate *cs)
{
+ if (!cs->class)
+ return;
+
gig_dbg(DEBUG_INIT, "setting up sysfs");
- device_create_file(cs->dev, &dev_attr_cidmode);
+ class_device_create_file(cs->class, &class_device_attr_cidmode);
}
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
-#include <linux/input.h>
-#include <linux/usb_input.h>
+#include <linux/usb/input.h>
#include "usbvideo.h"
goto drop;
}
- if (skb_linearize(skb, GFP_ATOMIC))
+ if (skb_linearize(skb))
goto drop;
mgp->tx_linearized++;
obj-$(CONFIG_USB_SERIAL) += serial/
obj-$(CONFIG_USB_AUERSWALD) += misc/
+obj-$(CONFIG_USB_CY7C63) += misc/
obj-$(CONFIG_USB_CYTHERM) += misc/
obj-$(CONFIG_USB_EMI26) += misc/
obj-$(CONFIG_USB_EMI62) += misc/
obj-$(CONFIG_USB_USS720) += misc/
obj-$(CONFIG_USB_PHIDGETSERVO) += misc/
obj-$(CONFIG_USB_SISUSBVGA) += misc/
+obj-$(CONFIG_USB_APPLEDISPLAY) += misc/
obj-$(CONFIG_USB_ATM) += atm/
obj-$(CONFIG_USB_SPEEDTOUCH) += atm/
tasklet_schedule((struct tasklet_struct *) data);
}
-static inline void usbatm_init_channel(struct usbatm_channel *channel)
+static void usbatm_init_channel(struct usbatm_channel *channel)
{
spin_lock_init(&channel->lock);
INIT_LIST_HEAD(&channel->list);
******************************************************************************/
#include <linux/module.h>
-#include <linux/netdevice.h> /* FIXME: required by linux/etherdevice.h */
#include <linux/etherdevice.h> /* for random_ether_addr() */
#include "usbatm.h"
#include <linux/mutex.h>
#include <asm/uaccess.h>
#include <linux/usb.h>
-#include <linux/usb_cdc.h>
+#include <linux/usb/cdc.h>
#include <asm/byteorder.h>
#include <asm/unaligned.h>
#include <linux/list.h>
wb->use = 1;
return wbn;
}
- wbn = (wbn + 1) % ACM_NWB;
- if (++i >= ACM_NWB)
+ wbn = (wbn + 1) % ACM_NW;
+ if (++i >= ACM_NW)
return -1;
}
}
{
int i, n;
- n = 0;
- for (i = 0; i < ACM_NWB; i++) {
- if (!acm->wb[i].use)
- n++;
+ n = ACM_NW;
+ for (i = 0; i < ACM_NW; i++) {
+ n -= acm->wb[i].use;
}
return n;
}
acm->write_ready = 1;
wbn = acm->write_current;
acm_wb_free(acm, wbn);
- acm->write_current = (wbn + 1) % ACM_NWB;
+ acm->write_current = (wbn + 1) % ACM_NW;
spin_unlock_irqrestore(&acm->write_lock, flags);
}
struct acm_rb *buf;
struct acm_ru *rcv = urb->context;
struct acm *acm = rcv->instance;
+ int status = urb->status;
dbg("Entering acm_read_bulk with status %d\n", urb->status);
if (!ACM_READY(acm))
return;
- if (urb->status)
- dev_dbg(&acm->data->dev, "bulk rx status %d\n", urb->status);
+ if (status)
+ dev_dbg(&acm->data->dev, "bulk rx status %d\n", status);
buf = rcv->buffer;
buf->size = urb->actual_length;
- spin_lock(&acm->read_lock);
- list_add_tail(&rcv->list, &acm->spare_read_urbs);
- list_add_tail(&buf->list, &acm->filled_read_bufs);
- spin_unlock(&acm->read_lock);
-
+ if (likely(status == 0)) {
+ spin_lock(&acm->read_lock);
+ list_add_tail(&rcv->list, &acm->spare_read_urbs);
+ list_add_tail(&buf->list, &acm->filled_read_bufs);
+ spin_unlock(&acm->read_lock);
+ } else {
+ /* we drop the buffer due to an error */
+ spin_lock(&acm->read_lock);
+ list_add_tail(&rcv->list, &acm->spare_read_urbs);
+ list_add(&buf->list, &acm->spare_read_bufs);
+ spin_unlock(&acm->read_lock);
+ /* nevertheless the tasklet must be kicked unconditionally
+ so the queue cannot dry up */
+ }
tasklet_schedule(&acm->urb_task);
}
INIT_LIST_HEAD(&acm->spare_read_urbs);
INIT_LIST_HEAD(&acm->spare_read_bufs);
INIT_LIST_HEAD(&acm->filled_read_bufs);
- for (i = 0; i < ACM_NRU; i++) {
+ for (i = 0; i < acm->rx_buflimit; i++) {
list_add(&(acm->ru[i].list), &acm->spare_read_urbs);
}
- for (i = 0; i < ACM_NRB; i++) {
+ for (i = 0; i < acm->rx_buflimit; i++) {
list_add(&(acm->rb[i].list), &acm->spare_read_bufs);
}
static void acm_tty_unregister(struct acm *acm)
{
- int i;
+ int i,nr;
+ nr = acm->rx_buflimit;
tty_unregister_device(acm_tty_driver, acm->minor);
usb_put_intf(acm->control);
acm_table[acm->minor] = NULL;
usb_free_urb(acm->ctrlurb);
usb_free_urb(acm->writeurb);
- for (i = 0; i < ACM_NRU; i++)
+ for (i = 0; i < nr; i++)
usb_free_urb(acm->ru[i].urb);
kfree(acm);
}
static void acm_tty_close(struct tty_struct *tty, struct file *filp)
{
struct acm *acm = tty->driver_data;
- int i;
+ int i,nr;
if (!acm || !acm->used)
return;
+ nr = acm->rx_buflimit;
mutex_lock(&open_mutex);
if (!--acm->used) {
if (acm->dev) {
acm_set_control(acm, acm->ctrlout = 0);
usb_kill_urb(acm->ctrlurb);
usb_kill_urb(acm->writeurb);
- for (i = 0; i < ACM_NRU; i++)
+ for (i = 0; i < nr; i++)
usb_kill_urb(acm->ru[i].urb);
} else
acm_tty_unregister(acm);
/*
* This is inaccurate (overcounts), but it works.
*/
- return (ACM_NWB - acm_wb_is_avail(acm)) * acm->writesize;
+ return (ACM_NW - acm_wb_is_avail(acm)) * acm->writesize;
}
static void acm_tty_throttle(struct tty_struct *tty)
int i;
struct acm_wb *wb;
- for (wb = &acm->wb[0], i = 0; i < ACM_NWB; i++, wb++) {
+ for (wb = &acm->wb[0], i = 0; i < ACM_NW; i++, wb++) {
usb_buffer_free(acm->dev, acm->writesize, wb->buf, wb->dmah);
}
}
int i;
struct acm_wb *wb;
- for (wb = &acm->wb[0], i = 0; i < ACM_NWB; i++, wb++) {
+ for (wb = &acm->wb[0], i = 0; i < ACM_NW; i++, wb++) {
wb->buf = usb_buffer_alloc(acm->dev, acm->writesize, GFP_KERNEL,
&wb->dmah);
if (!wb->buf) {
int call_interface_num = -1;
int data_interface_num;
unsigned long quirks;
+ int num_rx_buf;
int i;
- /* handle quirks deadly to normal probing*/
+ /* normal quirks */
quirks = (unsigned long)id->driver_info;
+ num_rx_buf = (quirks == SINGLE_RX_URB) ? 1 : ACM_NR;
+
+ /* handle quirks deadly to normal probing*/
if (quirks == NO_UNION_NORMAL) {
data_interface = usb_ifnum_to_if(usb_dev, 1);
control_interface = usb_ifnum_to_if(usb_dev, 0);
}
ctrlsize = le16_to_cpu(epctrl->wMaxPacketSize);
- readsize = le16_to_cpu(epread->wMaxPacketSize)*2;
+ readsize = le16_to_cpu(epread->wMaxPacketSize)* ( quirks == SINGLE_RX_URB ? 1 : 2);
acm->writesize = le16_to_cpu(epwrite->wMaxPacketSize);
acm->control = control_interface;
acm->data = data_interface;
acm->ctrl_caps = ac_management_function;
acm->ctrlsize = ctrlsize;
acm->readsize = readsize;
+ acm->rx_buflimit = num_rx_buf;
acm->urb_task.func = acm_rx_tasklet;
acm->urb_task.data = (unsigned long) acm;
INIT_WORK(&acm->work, acm_softint, acm);
dev_dbg(&intf->dev, "out of memory (ctrlurb kmalloc)\n");
goto alloc_fail5;
}
- for (i = 0; i < ACM_NRU; i++) {
+ for (i = 0; i < num_rx_buf; i++) {
struct acm_ru *rcv = &(acm->ru[i]);
if (!(rcv->urb = usb_alloc_urb(0, GFP_KERNEL))) {
rcv->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
rcv->instance = acm;
}
- for (i = 0; i < ACM_NRB; i++) {
+ for (i = 0; i < num_rx_buf; i++) {
struct acm_rb *buf = &(acm->rb[i]);
- // Using usb_buffer_alloc instead of kmalloc as Oliver suggested
if (!(buf->base = usb_buffer_alloc(acm->dev, readsize, GFP_KERNEL, &buf->dma))) {
dev_dbg(&intf->dev, "out of memory (read bufs usb_buffer_alloc)\n");
goto alloc_fail7;
return 0;
alloc_fail7:
- for (i = 0; i < ACM_NRB; i++)
+ for (i = 0; i < num_rx_buf; i++)
usb_buffer_free(usb_dev, acm->readsize, acm->rb[i].base, acm->rb[i].dma);
- for (i = 0; i < ACM_NRU; i++)
+ for (i = 0; i < num_rx_buf; i++)
usb_free_urb(acm->ru[i].urb);
usb_free_urb(acm->ctrlurb);
alloc_fail5:
usb_kill_urb(acm->ctrlurb);
usb_kill_urb(acm->writeurb);
- for (i = 0; i < ACM_NRU; i++)
+ for (i = 0; i < acm->rx_buflimit; i++)
usb_kill_urb(acm->ru[i].urb);
INIT_LIST_HEAD(&acm->filled_read_bufs);
acm_write_buffers_free(acm);
usb_buffer_free(usb_dev, acm->ctrlsize, acm->ctrl_buffer, acm->ctrl_dma);
- for (i = 0; i < ACM_NRB; i++)
+ for (i = 0; i < acm->rx_buflimit; i++)
usb_buffer_free(usb_dev, acm->readsize, acm->rb[i].base, acm->rb[i].dma);
usb_driver_release_interface(&acm_driver, intf == acm->control ? acm->data : intf);
{ USB_DEVICE(0x0482, 0x0203), /* KYOCERA AH-K3001V */
.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
},
+ { USB_DEVICE(0x0ace, 0x1608), /* ZyDAS 56K USB MODEM */
+ .driver_info = SINGLE_RX_URB, /* firmware bug */
+ },
/* control interfaces with various AT-command sets */
{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
USB_CDC_ACM_PROTO_AT_V25TER) },
* in line disciplines. They ask for empty space amount, receive our URB size,
* and proceed to issue several 1-character writes, assuming they will fit.
* The very first write takes a complete URB. Fortunately, this only happens
- * when processing onlcr, so we only need 2 buffers.
+ * when processing onlcr, so we only need 2 buffers. These values must be
+ * powers of 2.
*/
-#define ACM_NWB 2
-#define ACM_NRU 16
-#define ACM_NRB 16
+#define ACM_NW 2
+#define ACM_NR 16
struct acm_wb {
unsigned char *buf;
struct urb *ctrlurb, *writeurb; /* urbs */
u8 *ctrl_buffer; /* buffers of urbs */
dma_addr_t ctrl_dma; /* dma handles of buffers */
- struct acm_wb wb[ACM_NWB];
- struct acm_ru ru[ACM_NRU];
- struct acm_rb rb[ACM_NRB];
+ struct acm_wb wb[ACM_NW];
+ struct acm_ru ru[ACM_NR];
+ struct acm_rb rb[ACM_NR];
+ int rx_buflimit;
int rx_endpoint;
spinlock_t read_lock;
struct list_head spare_read_urbs;
/* constants describing various quirks and errors */
#define NO_UNION_NORMAL 1
+#define SINGLE_RX_URB 2
#
usbcore-objs := usb.o hub.o hcd.o urb.o message.o driver.o \
- config.o file.o buffer.o sysfs.o devio.o notify.o
+ config.o file.o buffer.o sysfs.o endpoint.o \
+ devio.o notify.o
ifeq ($(CONFIG_PCI),y)
usbcore-objs += hcd-pci.o
static struct usb_device *usbdev_lookup_minor(int minor)
{
- struct class_device *class_dev;
- struct usb_device *dev = NULL;
+ struct device *device;
+ struct usb_device *udev = NULL;
down(&usb_device_class->sem);
- list_for_each_entry(class_dev, &usb_device_class->children, node) {
- if (class_dev->devt == MKDEV(USB_DEVICE_MAJOR, minor)) {
- dev = class_dev->class_data;
+ list_for_each_entry(device, &usb_device_class->devices, node) {
+ if (device->devt == MKDEV(USB_DEVICE_MAJOR, minor)) {
+ udev = device->platform_data;
break;
}
}
up(&usb_device_class->sem);
- return dev;
+ return udev;
};
/*
static int proc_resetdevice(struct dev_state *ps)
{
- return usb_reset_device(ps->dev);
-
+ return usb_reset_composite_device(ps->dev, NULL);
}
static int proc_setintf(struct dev_state *ps, void __user *arg)
if ((ep->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
!= USB_ENDPOINT_XFER_CONTROL)
return -EINVAL;
- /* min 8 byte setup packet, max arbitrary */
- if (uurb->buffer_length < 8 || uurb->buffer_length > PAGE_SIZE)
+ /* min 8 byte setup packet, max 8 byte setup plus an arbitrary data stage */
+ if (uurb->buffer_length < 8 || uurb->buffer_length > (8 + MAX_USBFS_BUFFER_SIZE))
return -EINVAL;
if (!(dr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL)))
return -ENOMEM;
return -EFAULT;
}
for (totlen = u = 0; u < uurb->number_of_packets; u++) {
- if (isopkt[u].length > 1023) {
+ /* arbitrary limit, sufficient for USB 2.0 high-bandwidth iso */
+ if (isopkt[u].length > 8192) {
kfree(isopkt);
return -EINVAL;
}
if (copy_from_user(&uurb, arg, sizeof(uurb)))
return -EFAULT;
- return proc_do_submiturb(ps, &uurb, (((struct usbdevfs_urb __user *)arg)->iso_frame_desc), arg);
+ return proc_do_submiturb(ps, &uurb,
+ (struct usbdevfs_iso_packet_desc __user *)uurb.iso_frame_desc,
+ arg);
}
static int proc_unlinkurb(struct dev_state *ps, void __user *arg)
if (get_urb32(&uurb,(struct usbdevfs_urb32 *)arg))
return -EFAULT;
- return proc_do_submiturb(ps, &uurb, ((struct usbdevfs_urb32 __user *)arg)->iso_frame_desc, arg);
+ return proc_do_submiturb(ps, &uurb,
+ (struct usbdevfs_iso_packet_desc __user *)uurb.iso_frame_desc,
+ arg);
}
static int processcompl_compat(struct async *as, void __user * __user *arg)
{
int minor = ((dev->bus->busnum-1) * 128) + (dev->devnum-1);
- dev->class_dev = class_device_create(usb_device_class, NULL,
- MKDEV(USB_DEVICE_MAJOR, minor), &dev->dev,
+ dev->usbfs_dev = device_create(usb_device_class, &dev->dev,
+ MKDEV(USB_DEVICE_MAJOR, minor),
"usbdev%d.%d", dev->bus->busnum, dev->devnum);
- dev->class_dev->class_data = dev;
+ dev->usbfs_dev->platform_data = dev;
}
static void usbdev_remove(struct usb_device *dev)
{
- class_device_unregister(dev->class_dev);
+ device_unregister(dev->usbfs_dev);
}
static int usbdev_notify(struct notifier_block *self, unsigned long action,
--- /dev/null
+/*
+ * drivers/usb/core/endpoint.c
+ *
+ * (C) Copyright 2002,2004,2006 Greg Kroah-Hartman
+ * (C) Copyright 2002,2004 IBM Corp.
+ * (C) Copyright 2006 Novell Inc.
+ *
+ * Endpoint sysfs stuff
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/usb.h>
+#include "usb.h"
+
+/* endpoint stuff */
+
+struct ep_device {
+ struct usb_endpoint_descriptor *desc;
+ struct usb_device *udev;
+ struct device dev;
+};
+#define to_ep_device(_dev) \
+ container_of(_dev, struct ep_device, dev)
+
+struct ep_attribute {
+ struct attribute attr;
+ ssize_t (*show)(struct usb_device *,
+ struct usb_endpoint_descriptor *, char *);
+};
+#define to_ep_attribute(_attr) \
+ container_of(_attr, struct ep_attribute, attr)
+
+#define usb_ep_attr(field, format_string) \
+static ssize_t show_ep_##field(struct device *dev, \
+ struct device_attribute *attr, \
+ char *buf) \
+{ \
+ struct ep_device *ep = to_ep_device(dev); \
+ return sprintf(buf, format_string, ep->desc->field); \
+} \
+static DEVICE_ATTR(field, S_IRUGO, show_ep_##field, NULL);
+
+usb_ep_attr(bLength, "%02x\n")
+usb_ep_attr(bEndpointAddress, "%02x\n")
+usb_ep_attr(bmAttributes, "%02x\n")
+usb_ep_attr(bInterval, "%02x\n")
+
+static ssize_t show_ep_wMaxPacketSize(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ep_device *ep = to_ep_device(dev);
+ return sprintf(buf, "%04x\n",
+ le16_to_cpu(ep->desc->wMaxPacketSize) & 0x07ff);
+}
+static DEVICE_ATTR(wMaxPacketSize, S_IRUGO, show_ep_wMaxPacketSize, NULL);
+
+static ssize_t show_ep_type(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct ep_device *ep = to_ep_device(dev);
+ char *type = "unknown";
+
+ switch (ep->desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) {
+ case USB_ENDPOINT_XFER_CONTROL:
+ type = "Control";
+ break;
+ case USB_ENDPOINT_XFER_ISOC:
+ type = "Isoc";
+ break;
+ case USB_ENDPOINT_XFER_BULK:
+ type = "Bulk";
+ break;
+ case USB_ENDPOINT_XFER_INT:
+ type = "Interrupt";
+ break;
+ }
+ return sprintf(buf, "%s\n", type);
+}
+static DEVICE_ATTR(type, S_IRUGO, show_ep_type, NULL);
+
+static ssize_t show_ep_interval(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ep_device *ep = to_ep_device(dev);
+ char unit;
+ unsigned interval = 0;
+ unsigned in;
+
+ in = (ep->desc->bEndpointAddress & USB_DIR_IN);
+
+ switch (ep->desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) {
+ case USB_ENDPOINT_XFER_CONTROL:
+ if (ep->udev->speed == USB_SPEED_HIGH) /* uframes per NAK */
+ interval = ep->desc->bInterval;
+ break;
+ case USB_ENDPOINT_XFER_ISOC:
+ interval = 1 << (ep->desc->bInterval - 1);
+ break;
+ case USB_ENDPOINT_XFER_BULK:
+ if (ep->udev->speed == USB_SPEED_HIGH && !in) /* uframes per NAK */
+ interval = ep->desc->bInterval;
+ break;
+ case USB_ENDPOINT_XFER_INT:
+ if (ep->udev->speed == USB_SPEED_HIGH)
+ interval = 1 << (ep->desc->bInterval - 1);
+ else
+ interval = ep->desc->bInterval;
+ break;
+ }
+ interval *= (ep->udev->speed == USB_SPEED_HIGH) ? 125 : 1000;
+ if (interval % 1000)
+ unit = 'u';
+ else {
+ unit = 'm';
+ interval /= 1000;
+ }
+
+ return sprintf(buf, "%d%cs\n", interval, unit);
+}
+static DEVICE_ATTR(interval, S_IRUGO, show_ep_interval, NULL);
+
+static ssize_t show_ep_direction(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ep_device *ep = to_ep_device(dev);
+ char *direction;
+
+ if ((ep->desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
+ USB_ENDPOINT_XFER_CONTROL)
+ direction = "both";
+ else if (ep->desc->bEndpointAddress & USB_DIR_IN)
+ direction = "in";
+ else
+ direction = "out";
+ return sprintf(buf, "%s\n", direction);
+}
+static DEVICE_ATTR(direction, S_IRUGO, show_ep_direction, NULL);
+
+static struct attribute *ep_dev_attrs[] = {
+ &dev_attr_bLength.attr,
+ &dev_attr_bEndpointAddress.attr,
+ &dev_attr_bmAttributes.attr,
+ &dev_attr_bInterval.attr,
+ &dev_attr_wMaxPacketSize.attr,
+ &dev_attr_interval.attr,
+ &dev_attr_type.attr,
+ &dev_attr_direction.attr,
+ NULL,
+};
+static struct attribute_group ep_dev_attr_grp = {
+ .attrs = ep_dev_attrs,
+};
+
+static struct endpoint_class {
+ struct kref kref;
+ struct class *class;
+} *ep_class;
+
+static int init_endpoint_class(void)
+{
+ int result = 0;
+
+ if (ep_class != NULL) {
+ kref_get(&ep_class->kref);
+ goto exit;
+ }
+
+ ep_class = kmalloc(sizeof(*ep_class), GFP_KERNEL);
+ if (!ep_class) {
+ result = -ENOMEM;
+ goto exit;
+ }
+
+ kref_init(&ep_class->kref);
+ ep_class->class = class_create(THIS_MODULE, "usb_endpoint");
+ if (IS_ERR(ep_class->class)) {
+ result = IS_ERR(ep_class->class);
+ kfree(ep_class);
+ ep_class = NULL;
+ goto exit;
+ }
+
+exit:
+ return result;
+}
+
+static void release_endpoint_class(struct kref *kref)
+{
+ /* Ok, we cheat as we know we only have one ep_class */
+ class_destroy(ep_class->class);
+ kfree(ep_class);
+ ep_class = NULL;
+}
+
+static void destroy_endpoint_class(void)
+{
+ if (ep_class)
+ kref_put(&ep_class->kref, release_endpoint_class);
+}
+
+static void ep_device_release(struct device *dev)
+{
+ struct ep_device *ep_dev = to_ep_device(dev);
+
+ dev_dbg(dev, "%s called for %s\n", __FUNCTION__, dev->bus_id);
+ kfree(ep_dev);
+}
+
+void usb_create_ep_files(struct device *parent,
+ struct usb_host_endpoint *endpoint,
+ struct usb_device *udev)
+{
+ char name[8];
+ struct ep_device *ep_dev;
+ int minor;
+ int retval;
+
+ retval = init_endpoint_class();
+ if (retval)
+ goto exit;
+
+ ep_dev = kzalloc(sizeof(*ep_dev), GFP_KERNEL);
+ if (!ep_dev) {
+ retval = -ENOMEM;
+ goto exit;
+ }
+
+ /* fun calculation to determine the minor of this endpoint */
+ minor = (((udev->bus->busnum - 1) * 128) * 16) + (udev->devnum - 1);
+
+ ep_dev->desc = &endpoint->desc;
+ ep_dev->udev = udev;
+ ep_dev->dev.devt = MKDEV(442, minor); // FIXME fake number...
+ ep_dev->dev.class = ep_class->class;
+ ep_dev->dev.parent = parent;
+ ep_dev->dev.release = ep_device_release;
+ snprintf(ep_dev->dev.bus_id, BUS_ID_SIZE, "usbdev%d.%d_ep%02x",
+ udev->bus->busnum, udev->devnum,
+ endpoint->desc.bEndpointAddress);
+
+ retval = device_register(&ep_dev->dev);
+ if (retval)
+ goto error;
+ sysfs_create_group(&ep_dev->dev.kobj, &ep_dev_attr_grp);
+
+ endpoint->ep_dev = ep_dev;
+
+ /* create the symlink to the old-style "ep_XX" directory */
+ sprintf(name, "ep_%02x", endpoint->desc.bEndpointAddress);
+ sysfs_create_link(&parent->kobj, &endpoint->ep_dev->dev.kobj, name);
+
+exit:
+ return;
+error:
+ kfree(ep_dev);
+ return;
+}
+
+void usb_remove_ep_files(struct usb_host_endpoint *endpoint)
+{
+
+ if (endpoint->ep_dev) {
+ char name[8];
+
+ sprintf(name, "ep_%02x", endpoint->desc.bEndpointAddress);
+ sysfs_remove_link(&endpoint->ep_dev->dev.parent->kobj, name);
+ sysfs_remove_group(&endpoint->ep_dev->dev.kobj, &ep_dev_attr_grp);
+ device_unregister(&endpoint->ep_dev->dev);
+ endpoint->ep_dev = NULL;
+ }
+ destroy_endpoint_class();
+}
+
+
.open = usb_open,
};
-static struct class *usb_class;
+static struct usb_class {
+ struct kref kref;
+ struct class *class;
+} *usb_class;
-int usb_major_init(void)
+static int init_usb_class(void)
{
- int error;
+ int result = 0;
- error = register_chrdev(USB_MAJOR, "usb", &usb_fops);
- if (error) {
- err("unable to get major %d for usb devices", USB_MAJOR);
- goto out;
+ if (usb_class != NULL) {
+ kref_get(&usb_class->kref);
+ goto exit;
+ }
+
+ usb_class = kmalloc(sizeof(*usb_class), GFP_KERNEL);
+ if (!usb_class) {
+ result = -ENOMEM;
+ goto exit;
}
- usb_class = class_create(THIS_MODULE, "usb");
- if (IS_ERR(usb_class)) {
- error = PTR_ERR(usb_class);
+ kref_init(&usb_class->kref);
+ usb_class->class = class_create(THIS_MODULE, "usb");
+ if (IS_ERR(usb_class->class)) {
+ result = IS_ERR(usb_class->class);
err("class_create failed for usb devices");
- unregister_chrdev(USB_MAJOR, "usb");
- goto out;
+ kfree(usb_class);
+ usb_class = NULL;
}
-out:
+exit:
+ return result;
+}
+
+static void release_usb_class(struct kref *kref)
+{
+ /* Ok, we cheat as we know we only have one usb_class */
+ class_destroy(usb_class->class);
+ kfree(usb_class);
+ usb_class = NULL;
+}
+
+static void destroy_usb_class(void)
+{
+ if (usb_class)
+ kref_put(&usb_class->kref, release_usb_class);
+}
+
+int usb_major_init(void)
+{
+ int error;
+
+ error = register_chrdev(USB_MAJOR, "usb", &usb_fops);
+ if (error)
+ err("unable to get major %d for usb devices", USB_MAJOR);
+
return error;
}
void usb_major_cleanup(void)
{
- class_destroy(usb_class);
unregister_chrdev(USB_MAJOR, "usb");
}
}
spin_unlock (&minor_lock);
+ if (retval)
+ goto exit;
+
+ retval = init_usb_class();
if (retval)
goto exit;
++temp;
else
temp = name;
- intf->class_dev = class_device_create(usb_class, NULL,
- MKDEV(USB_MAJOR, minor),
- &intf->dev, "%s", temp);
- if (IS_ERR(intf->class_dev)) {
+ intf->usb_dev = device_create(usb_class->class, &intf->dev,
+ MKDEV(USB_MAJOR, minor), "%s", temp);
+ if (IS_ERR(intf->usb_dev)) {
spin_lock (&minor_lock);
usb_minors[intf->minor] = NULL;
spin_unlock (&minor_lock);
- retval = PTR_ERR(intf->class_dev);
+ retval = PTR_ERR(intf->usb_dev);
}
exit:
return retval;
spin_unlock (&minor_lock);
snprintf(name, BUS_ID_SIZE, class_driver->name, intf->minor - minor_base);
- class_device_destroy(usb_class, MKDEV(USB_MAJOR, intf->minor));
- intf->class_dev = NULL;
+ device_destroy(usb_class->class, MKDEV(USB_MAJOR, intf->minor));
+ intf->usb_dev = NULL;
intf->minor = -1;
+ destroy_usb_class();
}
EXPORT_SYMBOL(usb_deregister_dev);
{
int port1;
unsigned pgood_delay = hub->descriptor->bPwrOn2PwrGood * 2;
- u16 wHubCharacteristics = le16_to_cpu(hub->descriptor->wHubCharacteristics);
-
- /* if hub supports power switching, enable power on each port */
- if ((wHubCharacteristics & HUB_CHAR_LPSM) < 2) {
+ u16 wHubCharacteristics =
+ le16_to_cpu(hub->descriptor->wHubCharacteristics);
+
+ /* Enable power on each port. Some hubs have reserved values
+ * of LPSM (> 2) in their descriptors, even though they are
+ * USB 2.0 hubs. Some hubs do not implement port-power switching
+ * but only emulate it. In all cases, the ports won't work
+ * unless we send these messages to the hub.
+ */
+ if ((wHubCharacteristics & HUB_CHAR_LPSM) < 2)
dev_dbg(hub->intfdev, "enabling power on all ports\n");
- for (port1 = 1; port1 <= hub->descriptor->bNbrPorts; port1++)
- set_port_feature(hub->hdev, port1,
- USB_PORT_FEAT_POWER);
- }
+ else
+ dev_dbg(hub->intfdev, "trying to enable port power on "
+ "non-switchable hub\n");
+ for (port1 = 1; port1 <= hub->descriptor->bNbrPorts; port1++)
+ set_port_feature(hub->hdev, port1, USB_PORT_FEAT_POWER);
/* Wait at least 100 msec for power to become stable */
msleep(max(pgood_delay, (unsigned) 100));
/* caller has locked the hub device */
-static void hub_pre_reset(struct usb_hub *hub, int disable_ports)
+static void hub_pre_reset(struct usb_interface *intf)
{
+ struct usb_hub *hub = usb_get_intfdata(intf);
struct usb_device *hdev = hub->hdev;
int port1;
for (port1 = 1; port1 <= hdev->maxchild; ++port1) {
if (hdev->children[port1 - 1]) {
usb_disconnect(&hdev->children[port1 - 1]);
- if (disable_ports)
+ if (hub->error == 0)
hub_port_disable(hub, port1, 0);
}
}
}
/* caller has locked the hub device */
-static void hub_post_reset(struct usb_hub *hub)
+static void hub_post_reset(struct usb_interface *intf)
{
+ struct usb_hub *hub = usb_get_intfdata(intf);
+
hub_activate(hub);
hub_power_on(hub);
}
struct usb_hub *hub = usb_get_intfdata (intf);
struct usb_device *hdev;
+ /* Disconnect all children and quiesce the hub */
+ hub->error = 0;
+ hub_pre_reset(intf);
+
usb_set_intfdata (intf, NULL);
hdev = hub->hdev;
if (hdev->speed == USB_SPEED_HIGH)
highspeed_hubs--;
- /* Disconnect all children and quiesce the hub */
- hub_pre_reset(hub, 1);
-
usb_free_urb(hub->urb);
hub->urb = NULL;
{
int i;
int num_configs;
+ int insufficient_power = 0;
struct usb_host_config *c, *best;
best = NULL;
*/
/* Rule out configs that draw too much bus current */
- if (c->desc.bMaxPower * 2 > udev->bus_mA)
+ if (c->desc.bMaxPower * 2 > udev->bus_mA) {
+ insufficient_power++;
continue;
+ }
/* If the first config's first interface is COMM/2/0xff
* (MSFT RNDIS), rule it out unless Linux has host-side
&& desc->bInterfaceClass == USB_CLASS_COMM
&& desc->bInterfaceSubClass == 2
&& desc->bInterfaceProtocol == 0xff) {
-#ifndef CONFIG_USB_NET_RNDIS
+#ifndef CONFIG_USB_NET_RNDIS_HOST
continue;
#else
best = c;
best = c;
}
+ if (insufficient_power > 0)
+ dev_info(&udev->dev, "rejected %d configuration%s "
+ "due to insufficient available bus power\n",
+ insufficient_power, plural(insufficient_power));
+
if (best) {
i = best->desc.bConfigurationValue;
dev_info(&udev->dev,
/* If the hub has died, clean up after it */
if (hdev->state == USB_STATE_NOTATTACHED) {
- hub_pre_reset(hub, 0);
+ hub->error = -ENODEV;
+ hub_pre_reset(intf);
goto loop;
}
dev_dbg (hub_dev, "resetting for error %d\n",
hub->error);
- ret = usb_reset_device(hdev);
+ ret = usb_reset_composite_device(hdev, intf);
if (ret) {
dev_dbg (hub_dev,
"error resetting hub: %d\n", ret);
.disconnect = hub_disconnect,
.suspend = hub_suspend,
.resume = hub_resume,
+ .pre_reset = hub_pre_reset,
+ .post_reset = hub_post_reset,
.ioctl = hub_ioctl,
.id_table = hub_id_table,
};
* usb_reset_device - perform a USB port reset to reinitialize a device
* @udev: device to reset (not in SUSPENDED or NOTATTACHED state)
*
- * WARNING - don't reset any device unless drivers for all of its
- * interfaces are expecting that reset! Maybe some driver->reset()
- * method should eventually help ensure sufficient cooperation.
+ * WARNING - don't use this routine to reset a composite device
+ * (one with multiple interfaces owned by separate drivers)!
+ * Use usb_reset_composite_device() instead.
*
* Do a port reset, reassign the device's address, and establish its
* former operating configuration. If the reset fails, or the device's
struct usb_device *parent_hdev = udev->parent;
struct usb_hub *parent_hub;
struct usb_device_descriptor descriptor = udev->descriptor;
- struct usb_hub *hub = NULL;
int i, ret = 0;
int port1 = udev->portnum;
}
parent_hub = hdev_to_hub(parent_hdev);
- /* If we're resetting an active hub, take some special actions */
- if (udev->actconfig && udev->actconfig->desc.bNumInterfaces > 0 &&
- udev->actconfig->interface[0]->dev.driver ==
- &hub_driver.driver &&
- (hub = hdev_to_hub(udev)) != NULL) {
- hub_pre_reset(hub, 0);
- }
-
set_bit(port1, parent_hub->busy_bits);
for (i = 0; i < SET_CONFIG_TRIES; ++i) {
}
done:
- if (hub)
- hub_post_reset(hub);
return 0;
re_enumerate:
hub_port_logical_disconnect(parent_hub, port1);
return -ENODEV;
}
+
+/**
+ * usb_reset_composite_device - warn interface drivers and perform a USB port reset
+ * @udev: device to reset (not in SUSPENDED or NOTATTACHED state)
+ * @iface: interface bound to the driver making the request (optional)
+ *
+ * Warns all drivers bound to registered interfaces (using their pre_reset
+ * method), performs the port reset, and then lets the drivers know that
+ * the reset is over (using their post_reset method).
+ *
+ * Return value is the same as for usb_reset_device().
+ *
+ * The caller must own the device lock. For example, it's safe to use
+ * this from a driver probe() routine after downloading new firmware.
+ * For calls that might not occur during probe(), drivers should lock
+ * the device using usb_lock_device_for_reset().
+ *
+ * The interface locks are acquired during the pre_reset stage and released
+ * during the post_reset stage. However if iface is not NULL and is
+ * currently being probed, we assume that the caller already owns its
+ * lock.
+ */
+int usb_reset_composite_device(struct usb_device *udev,
+ struct usb_interface *iface)
+{
+ int ret;
+ struct usb_host_config *config = udev->actconfig;
+
+ if (udev->state == USB_STATE_NOTATTACHED ||
+ udev->state == USB_STATE_SUSPENDED) {
+ dev_dbg(&udev->dev, "device reset not allowed in state %d\n",
+ udev->state);
+ return -EINVAL;
+ }
+
+ if (iface && iface->condition != USB_INTERFACE_BINDING)
+ iface = NULL;
+
+ if (config) {
+ int i;
+ struct usb_interface *cintf;
+ struct usb_driver *drv;
+
+ for (i = 0; i < config->desc.bNumInterfaces; ++i) {
+ cintf = config->interface[i];
+ if (cintf != iface)
+ down(&cintf->dev.sem);
+ if (device_is_registered(&cintf->dev) &&
+ cintf->dev.driver) {
+ drv = to_usb_driver(cintf->dev.driver);
+ if (drv->pre_reset)
+ (drv->pre_reset)(cintf);
+ }
+ }
+ }
+
+ ret = usb_reset_device(udev);
+
+ if (config) {
+ int i;
+ struct usb_interface *cintf;
+ struct usb_driver *drv;
+
+ for (i = config->desc.bNumInterfaces - 1; i >= 0; --i) {
+ cintf = config->interface[i];
+ if (device_is_registered(&cintf->dev) &&
+ cintf->dev.driver) {
+ drv = to_usb_driver(cintf->dev.driver);
+ if (drv->post_reset)
+ (drv->post_reset)(cintf);
+ }
+ if (cintf != iface)
+ up(&cintf->dev.sem);
+ }
+ }
+
+ return ret;
+}
}
+/**
+ * usb_interrupt_msg - Builds an interrupt urb, sends it off and waits for completion
+ * @usb_dev: pointer to the usb device to send the message to
+ * @pipe: endpoint "pipe" to send the message to
+ * @data: pointer to the data to send
+ * @len: length in bytes of the data to send
+ * @actual_length: pointer to a location to put the actual length transferred in bytes
+ * @timeout: time in msecs to wait for the message to complete before
+ * timing out (if 0 the wait is forever)
+ * Context: !in_interrupt ()
+ *
+ * This function sends a simple interrupt message to a specified endpoint and
+ * waits for the message to complete, or timeout.
+ *
+ * If successful, it returns 0, otherwise a negative error number. The number
+ * of actual bytes transferred will be stored in the actual_length paramater.
+ *
+ * Don't use this function from within an interrupt context, like a bottom half
+ * handler. If you need an asynchronous message, or need to send a message
+ * from within interrupt context, use usb_submit_urb() If a thread in your
+ * driver uses this call, make sure your disconnect() method can wait for it to
+ * complete. Since you don't have a handle on the URB used, you can't cancel
+ * the request.
+ */
+int usb_interrupt_msg(struct usb_device *usb_dev, unsigned int pipe,
+ void *data, int len, int *actual_length, int timeout)
+{
+ return usb_bulk_msg(usb_dev, pipe, data, len, actual_length, timeout);
+}
+EXPORT_SYMBOL_GPL(usb_interrupt_msg);
+
/**
* usb_bulk_msg - Builds a bulk urb, sends it off and waits for completion
* @usb_dev: pointer to the usb device to send the message to
return ret;
}
}
- }
-
- /* if it's already configured, clear out old state first.
- * getting rid of old interfaces means unbinding their drivers.
- */
- if (dev->state != USB_STATE_ADDRESS)
- usb_disable_device (dev, 1); // Skip ep0
- if (cp) {
i = dev->bus_mA - cp->desc.bMaxPower * 2;
if (i < 0)
dev_warn(&dev->dev, "new config #%d exceeds power "
configuration, -i);
}
+ /* if it's already configured, clear out old state first.
+ * getting rid of old interfaces means unbinding their drivers.
+ */
+ if (dev->state != USB_STATE_ADDRESS)
+ usb_disable_device (dev, 1); // Skip ep0
+
if ((ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
USB_REQ_SET_CONFIGURATION, 0, configuration, 0,
- NULL, 0, USB_CTRL_SET_TIMEOUT)) < 0)
- goto free_interfaces;
+ NULL, 0, USB_CTRL_SET_TIMEOUT)) < 0) {
+
+ /* All the old state is gone, so what else can we do?
+ * The device is probably useless now anyway.
+ */
+ cp = NULL;
+ }
dev->actconfig = cp;
- if (!cp)
+ if (!cp) {
usb_set_device_state(dev, USB_STATE_ADDRESS);
- else {
- usb_set_device_state(dev, USB_STATE_CONFIGURED);
+ goto free_interfaces;
+ }
+ usb_set_device_state(dev, USB_STATE_CONFIGURED);
- /* Initialize the new interface structures and the
- * hc/hcd/usbcore interface/endpoint state.
- */
- for (i = 0; i < nintf; ++i) {
- struct usb_interface_cache *intfc;
- struct usb_interface *intf;
- struct usb_host_interface *alt;
-
- cp->interface[i] = intf = new_interfaces[i];
- intfc = cp->intf_cache[i];
- intf->altsetting = intfc->altsetting;
- intf->num_altsetting = intfc->num_altsetting;
- kref_get(&intfc->ref);
-
- alt = usb_altnum_to_altsetting(intf, 0);
-
- /* No altsetting 0? We'll assume the first altsetting.
- * We could use a GetInterface call, but if a device is
- * so non-compliant that it doesn't have altsetting 0
- * then I wouldn't trust its reply anyway.
- */
- if (!alt)
- alt = &intf->altsetting[0];
-
- intf->cur_altsetting = alt;
- usb_enable_interface(dev, intf);
- intf->dev.parent = &dev->dev;
- intf->dev.driver = NULL;
- intf->dev.bus = &usb_bus_type;
- intf->dev.dma_mask = dev->dev.dma_mask;
- intf->dev.release = release_interface;
- device_initialize (&intf->dev);
- mark_quiesced(intf);
- sprintf (&intf->dev.bus_id[0], "%d-%s:%d.%d",
- dev->bus->busnum, dev->devpath,
- configuration,
- alt->desc.bInterfaceNumber);
- }
- kfree(new_interfaces);
+ /* Initialize the new interface structures and the
+ * hc/hcd/usbcore interface/endpoint state.
+ */
+ for (i = 0; i < nintf; ++i) {
+ struct usb_interface_cache *intfc;
+ struct usb_interface *intf;
+ struct usb_host_interface *alt;
+
+ cp->interface[i] = intf = new_interfaces[i];
+ intfc = cp->intf_cache[i];
+ intf->altsetting = intfc->altsetting;
+ intf->num_altsetting = intfc->num_altsetting;
+ kref_get(&intfc->ref);
- if (cp->string == NULL)
- cp->string = usb_cache_string(dev,
- cp->desc.iConfiguration);
+ alt = usb_altnum_to_altsetting(intf, 0);
- /* Now that all the interfaces are set up, register them
- * to trigger binding of drivers to interfaces. probe()
- * routines may install different altsettings and may
- * claim() any interfaces not yet bound. Many class drivers
- * need that: CDC, audio, video, etc.
+ /* No altsetting 0? We'll assume the first altsetting.
+ * We could use a GetInterface call, but if a device is
+ * so non-compliant that it doesn't have altsetting 0
+ * then I wouldn't trust its reply anyway.
*/
- for (i = 0; i < nintf; ++i) {
- struct usb_interface *intf = cp->interface[i];
-
- dev_dbg (&dev->dev,
- "adding %s (config #%d, interface %d)\n",
- intf->dev.bus_id, configuration,
- intf->cur_altsetting->desc.bInterfaceNumber);
- ret = device_add (&intf->dev);
- if (ret != 0) {
- dev_err(&dev->dev,
- "device_add(%s) --> %d\n",
- intf->dev.bus_id,
- ret);
- continue;
- }
- usb_create_sysfs_intf_files (intf);
+ if (!alt)
+ alt = &intf->altsetting[0];
+
+ intf->cur_altsetting = alt;
+ usb_enable_interface(dev, intf);
+ intf->dev.parent = &dev->dev;
+ intf->dev.driver = NULL;
+ intf->dev.bus = &usb_bus_type;
+ intf->dev.dma_mask = dev->dev.dma_mask;
+ intf->dev.release = release_interface;
+ device_initialize (&intf->dev);
+ mark_quiesced(intf);
+ sprintf (&intf->dev.bus_id[0], "%d-%s:%d.%d",
+ dev->bus->busnum, dev->devpath,
+ configuration, alt->desc.bInterfaceNumber);
+ }
+ kfree(new_interfaces);
+
+ if (cp->string == NULL)
+ cp->string = usb_cache_string(dev, cp->desc.iConfiguration);
+
+ /* Now that all the interfaces are set up, register them
+ * to trigger binding of drivers to interfaces. probe()
+ * routines may install different altsettings and may
+ * claim() any interfaces not yet bound. Many class drivers
+ * need that: CDC, audio, video, etc.
+ */
+ for (i = 0; i < nintf; ++i) {
+ struct usb_interface *intf = cp->interface[i];
+
+ dev_dbg (&dev->dev,
+ "adding %s (config #%d, interface %d)\n",
+ intf->dev.bus_id, configuration,
+ intf->cur_altsetting->desc.bInterfaceNumber);
+ ret = device_add (&intf->dev);
+ if (ret != 0) {
+ dev_err(&dev->dev, "device_add(%s) --> %d\n",
+ intf->dev.bus_id, ret);
+ continue;
}
+ usb_create_sysfs_intf_files (intf);
}
return 0;
#include <linux/usb.h>
#include "usb.h"
-/* endpoint stuff */
-struct ep_object {
- struct usb_endpoint_descriptor *desc;
- struct usb_device *udev;
- struct kobject kobj;
-};
-#define to_ep_object(_kobj) \
- container_of(_kobj, struct ep_object, kobj)
-
-struct ep_attribute {
- struct attribute attr;
- ssize_t (*show)(struct usb_device *,
- struct usb_endpoint_descriptor *, char *);
-};
-#define to_ep_attribute(_attr) \
- container_of(_attr, struct ep_attribute, attr)
-
-#define EP_ATTR(_name) \
-struct ep_attribute ep_##_name = { \
- .attr = {.name = #_name, .owner = THIS_MODULE, \
- .mode = S_IRUGO}, \
- .show = show_ep_##_name}
-
-#define usb_ep_attr(field, format_string) \
-static ssize_t show_ep_##field(struct usb_device *udev, \
- struct usb_endpoint_descriptor *desc, \
- char *buf) \
-{ \
- return sprintf(buf, format_string, desc->field); \
-} \
-static EP_ATTR(field);
-
-usb_ep_attr(bLength, "%02x\n")
-usb_ep_attr(bEndpointAddress, "%02x\n")
-usb_ep_attr(bmAttributes, "%02x\n")
-usb_ep_attr(bInterval, "%02x\n")
-
-static ssize_t show_ep_wMaxPacketSize(struct usb_device *udev,
- struct usb_endpoint_descriptor *desc, char *buf)
-{
- return sprintf(buf, "%04x\n",
- le16_to_cpu(desc->wMaxPacketSize) & 0x07ff);
-}
-static EP_ATTR(wMaxPacketSize);
-
-static ssize_t show_ep_type(struct usb_device *udev,
- struct usb_endpoint_descriptor *desc, char *buf)
-{
- char *type = "unknown";
-
- switch (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) {
- case USB_ENDPOINT_XFER_CONTROL:
- type = "Control";
- break;
- case USB_ENDPOINT_XFER_ISOC:
- type = "Isoc";
- break;
- case USB_ENDPOINT_XFER_BULK:
- type = "Bulk";
- break;
- case USB_ENDPOINT_XFER_INT:
- type = "Interrupt";
- break;
- }
- return sprintf(buf, "%s\n", type);
-}
-static EP_ATTR(type);
-
-static ssize_t show_ep_interval(struct usb_device *udev,
- struct usb_endpoint_descriptor *desc, char *buf)
-{
- char unit;
- unsigned interval = 0;
- unsigned in;
-
- in = (desc->bEndpointAddress & USB_DIR_IN);
-
- switch (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) {
- case USB_ENDPOINT_XFER_CONTROL:
- if (udev->speed == USB_SPEED_HIGH) /* uframes per NAK */
- interval = desc->bInterval;
- break;
- case USB_ENDPOINT_XFER_ISOC:
- interval = 1 << (desc->bInterval - 1);
- break;
- case USB_ENDPOINT_XFER_BULK:
- if (udev->speed == USB_SPEED_HIGH && !in) /* uframes per NAK */
- interval = desc->bInterval;
- break;
- case USB_ENDPOINT_XFER_INT:
- if (udev->speed == USB_SPEED_HIGH)
- interval = 1 << (desc->bInterval - 1);
- else
- interval = desc->bInterval;
- break;
- }
- interval *= (udev->speed == USB_SPEED_HIGH) ? 125 : 1000;
- if (interval % 1000)
- unit = 'u';
- else {
- unit = 'm';
- interval /= 1000;
- }
-
- return sprintf(buf, "%d%cs\n", interval, unit);
-}
-static EP_ATTR(interval);
-
-static ssize_t show_ep_direction(struct usb_device *udev,
- struct usb_endpoint_descriptor *desc, char *buf)
-{
- char *direction;
-
- if ((desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
- USB_ENDPOINT_XFER_CONTROL)
- direction = "both";
- else if (desc->bEndpointAddress & USB_DIR_IN)
- direction = "in";
- else
- direction = "out";
- return sprintf(buf, "%s\n", direction);
-}
-static EP_ATTR(direction);
-
-static struct attribute *ep_attrs[] = {
- &ep_bLength.attr,
- &ep_bEndpointAddress.attr,
- &ep_bmAttributes.attr,
- &ep_bInterval.attr,
- &ep_wMaxPacketSize.attr,
- &ep_type.attr,
- &ep_interval.attr,
- &ep_direction.attr,
- NULL,
-};
-
-static void ep_object_release(struct kobject *kobj)
-{
- kfree(to_ep_object(kobj));
-}
-
-static ssize_t ep_object_show(struct kobject *kobj, struct attribute *attr,
- char *buf)
-{
- struct ep_object *ep_obj = to_ep_object(kobj);
- struct ep_attribute *ep_attr = to_ep_attribute(attr);
-
- return (ep_attr->show)(ep_obj->udev, ep_obj->desc, buf);
-}
-
-static struct sysfs_ops ep_object_sysfs_ops = {
- .show = ep_object_show,
-};
-
-static struct kobj_type ep_object_ktype = {
- .release = ep_object_release,
- .sysfs_ops = &ep_object_sysfs_ops,
- .default_attrs = ep_attrs,
-};
-
-static void usb_create_ep_files(struct kobject *parent,
- struct usb_host_endpoint *endpoint,
- struct usb_device *udev)
-{
- struct ep_object *ep_obj;
- struct kobject *kobj;
-
- ep_obj = kzalloc(sizeof(struct ep_object), GFP_KERNEL);
- if (!ep_obj)
- return;
-
- ep_obj->desc = &endpoint->desc;
- ep_obj->udev = udev;
-
- kobj = &ep_obj->kobj;
- kobject_set_name(kobj, "ep_%02x", endpoint->desc.bEndpointAddress);
- kobj->parent = parent;
- kobj->ktype = &ep_object_ktype;
-
- /* Don't use kobject_register, because it generates a hotplug event */
- kobject_init(kobj);
- if (kobject_add(kobj) == 0)
- endpoint->kobj = kobj;
- else
- kobject_put(kobj);
-}
-
-static void usb_remove_ep_files(struct usb_host_endpoint *endpoint)
-{
-
- if (endpoint->kobj) {
- kobject_del(endpoint->kobj);
- kobject_put(endpoint->kobj);
- endpoint->kobj = NULL;
- }
-}
-
/* Active configuration fields */
#define usb_actconfig_show(field, multiplier, format_string) \
static ssize_t show_##field (struct device *dev, \
if (udev->serial)
device_create_file (dev, &dev_attr_serial);
device_create_file (dev, &dev_attr_configuration);
- usb_create_ep_files(&dev->kobj, &udev->ep0, udev);
+ usb_create_ep_files(dev, &udev->ep0, udev);
}
void usb_remove_sysfs_dev_files (struct usb_device *udev)
iface_desc = intf->cur_altsetting;
for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i)
- usb_create_ep_files(&intf->dev.kobj, &iface_desc->endpoint[i],
+ usb_create_ep_files(&intf->dev, &iface_desc->endpoint[i],
udev);
}
EXPORT_SYMBOL(usb_altnum_to_altsetting);
EXPORT_SYMBOL(usb_reset_device);
+EXPORT_SYMBOL(usb_reset_composite_device);
EXPORT_SYMBOL(__usb_get_extra_descriptor);
extern void usb_remove_sysfs_dev_files (struct usb_device *dev);
extern void usb_create_sysfs_intf_files (struct usb_interface *intf);
extern void usb_remove_sysfs_intf_files (struct usb_interface *intf);
+extern void usb_create_ep_files(struct device *parent, struct usb_host_endpoint *endpoint,
+ struct usb_device *udev);
+extern void usb_remove_ep_files(struct usb_host_endpoint *endpoint);
extern void usb_disable_endpoint (struct usb_device *dev, unsigned int epaddr);
extern void usb_disable_interface (struct usb_device *dev,
#include <asm/unaligned.h>
#include <linux/usb_ch9.h>
-#include <linux/usb_cdc.h>
+#include <linux/usb/cdc.h>
#include <linux/usb_gadget.h>
#include <linux/random.h>
/* CDC and RNDIS support the same host-chosen outgoing packet filters. */
#define DEFAULT_FILTER (USB_CDC_PACKET_TYPE_BROADCAST \
- |USB_CDC_PACKET_TYPE_ALL_MULTICAST \
- |USB_CDC_PACKET_TYPE_PROMISCUOUS \
- |USB_CDC_PACKET_TYPE_DIRECTED)
+ |USB_CDC_PACKET_TYPE_ALL_MULTICAST \
+ |USB_CDC_PACKET_TYPE_PROMISCUOUS \
+ |USB_CDC_PACKET_TYPE_DIRECTED)
/*-------------------------------------------------------------------------*/
#define DEFAULT_QLEN 2 /* double buffering by default */
/* peak bulk transfer bits-per-second */
-#define HS_BPS (13 * 512 * 8 * 1000 * 8)
+#define HS_BPS (13 * 512 * 8 * 1000 * 8)
#define FS_BPS (19 * 64 * 1 * 1000 * 8)
#ifdef CONFIG_USB_GADGET_DUALSPEED
};
#ifdef CONFIG_USB_ETH_RNDIS
-static struct usb_config_descriptor
+static struct usb_config_descriptor
rndis_config = {
.bLength = sizeof rndis_config,
.bDescriptorType = USB_DT_CONFIG,
rndis_control_intf = {
.bLength = sizeof rndis_control_intf,
.bDescriptorType = USB_DT_INTERFACE,
-
+
.bInterfaceNumber = 0,
.bNumEndpoints = 1,
.bInterfaceClass = USB_CLASS_COMM,
#ifdef CONFIG_USB_ETH_RNDIS
static const struct usb_cdc_call_mgmt_descriptor call_mgmt_descriptor = {
- .bLength = sizeof call_mgmt_descriptor,
- .bDescriptorType = USB_DT_CS_INTERFACE,
- .bDescriptorSubType = USB_CDC_CALL_MANAGEMENT_TYPE,
+ .bLength = sizeof call_mgmt_descriptor,
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubType = USB_CDC_CALL_MANAGEMENT_TYPE,
- .bmCapabilities = 0x00,
- .bDataInterface = 0x01,
+ .bmCapabilities = 0x00,
+ .bDataInterface = 0x01,
};
static const struct usb_cdc_acm_descriptor acm_descriptor = {
- .bLength = sizeof acm_descriptor,
- .bDescriptorType = USB_DT_CS_INTERFACE,
- .bDescriptorSubType = USB_CDC_ACM_TYPE,
+ .bLength = sizeof acm_descriptor,
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubType = USB_CDC_ACM_TYPE,
- .bmCapabilities = 0x00,
+ .bmCapabilities = 0x00,
};
#endif
* RNDIS requires the status endpoint, since it uses that encapsulation
* mechanism for its funky RPC scheme.
*/
-
+
#define LOG2_STATUS_INTERVAL_MSEC 5 /* 1 << 5 == 32 msec */
#define STATUS_BYTECOUNT 16 /* 8 byte header + data */
result = usb_ep_enable (dev->status_ep, dev->status);
if (result != 0) {
- DEBUG (dev, "enable %s --> %d\n",
+ DEBUG (dev, "enable %s --> %d\n",
dev->status_ep->name, result);
goto done;
}
if (!cdc_active(dev)) {
result = usb_ep_enable (dev->in_ep, dev->in);
if (result != 0) {
- DEBUG(dev, "enable %s --> %d\n",
+ DEBUG(dev, "enable %s --> %d\n",
dev->in_ep->name, result);
goto done;
}
result = usb_ep_enable (dev->out_ep, dev->out);
if (result != 0) {
- DEBUG (dev, "enable %s --> %d\n",
- dev->in_ep->name, result);
+ DEBUG (dev, "enable %s --> %d\n",
+ dev->out_ep->name, result);
goto done;
}
}
#ifdef CONFIG_USB_GADGET_DUALSPEED
case USB_SPEED_HIGH: speed = "high"; break;
#endif
- default: speed = "?"; break;
+ default: speed = "?"; break;
}
dev->config = number;
struct usb_request *req = dev->stat_req;
struct usb_cdc_notification *event;
int value;
-
+
DEBUG (dev, "%s, flush old status first\n", __FUNCTION__);
/* flush old status
{
struct eth_dev *dev = ep->driver_data;
int status;
-
+
/* received RNDIS command from USB_CDC_SEND_ENCAPSULATED_COMMAND */
spin_lock(&dev->lock);
status = rndis_msg_parser (dev->rndis_config, (u8 *) req->buf);
#endif /* DEV_CONFIG_CDC */
-#ifdef CONFIG_USB_ETH_RNDIS
+#ifdef CONFIG_USB_ETH_RNDIS
/* RNDIS uses the CDC command encapsulation mechanism to implement
* an RPC scheme, with much getting/setting of attributes by OID.
*/
req->complete = rndis_command_complete;
/* later, rndis_control_ack () sends a notification */
break;
-
+
case USB_CDC_GET_ENCAPSULATED_RESPONSE:
if ((USB_DIR_IN|USB_TYPE_CLASS|USB_RECIP_INTERFACE)
== ctrl->bRequestType
DEBUG (dev, "no rx skb\n");
goto enomem;
}
-
+
/* Some platforms perform better when IP packets are aligned,
* but on at least one, checksumming fails otherwise. Note:
* RNDIS headers involve variable numbers of LE32 values.
case -EOVERFLOW:
dev->stats.rx_over_errors++;
// FALLTHROUGH
-
+
default:
dev->stats.rx_errors++;
DEBUG (dev, "rx status %d\n", status);
sizeof (struct rndis_packet_msg_type));
if (!skb_rndis)
goto drop;
-
+
dev_kfree_skb_any (skb);
skb = skb_rndis;
rndis_add_hdr (skb);
struct eth_dev *dev = netdev_priv(net);
u32 length;
struct usb_request *resp = dev->stat_req;
-
+
/* in case RNDIS calls this after disconnect */
if (!dev->status) {
DEBUG (dev, "status ENODEV\n");
resp->length = 8;
resp->complete = rndis_control_ack_complete;
resp->context = dev;
-
+
*((__le32 *) resp->buf) = __constant_cpu_to_le32 (1);
*((__le32 *) resp->buf + 1) = __constant_cpu_to_le32 (0);
-
+
length = usb_ep_queue (dev->status_ep, resp, GFP_ATOMIC);
if (length < 0) {
resp->status = 0;
rndis_control_ack_complete (dev->status_ep, resp);
}
-
+
return 0;
}
/* fill the rx queue */
rx_fill (dev, gfp_flags);
- /* and open the tx floodgates */
+ /* and open the tx floodgates */
atomic_set (&dev->tx_qlen, 0);
netif_wake_queue (dev->net);
if (rndis_active(dev)) {
netif_stop_queue (net);
DEBUG (dev, "stop stats: rx/tx %ld/%ld, errs %ld/%ld\n",
- dev->stats.rx_packets, dev->stats.tx_packets,
+ dev->stats.rx_packets, dev->stats.tx_packets,
dev->stats.rx_errors, dev->stats.tx_errors
);
usb_ep_enable (dev->status_ep, dev->status);
}
}
-
+
if (rndis_active(dev)) {
rndis_set_param_medium (dev->rndis_config,
NDIS_MEDIUM_802_3, 0);
return -ENODEV;
}
in_ep->driver_data = in_ep; /* claim */
-
+
out_ep = usb_ep_autoconfig (gadget, &fs_sink_desc);
if (!out_ep)
goto autoconf_fail;
#endif
}
- net = alloc_etherdev (sizeof *dev);
- if (!net)
+ net = alloc_etherdev (sizeof *dev);
+ if (!net)
return status;
dev = netdev_priv(net);
spin_lock_init (&dev->lock);
dev->gadget = gadget;
set_gadget_data (gadget, dev);
gadget->ep0->driver_data = dev;
-
+
/* two kinds of host-initiated state changes:
* - iff DATA transfer is active, carrier is "on"
* - tx queueing enabled if open *and* carrier is "on"
netif_stop_queue (dev->net);
netif_carrier_off (dev->net);
- SET_NETDEV_DEV (dev->net, &gadget->dev);
- status = register_netdev (dev->net);
+ SET_NETDEV_DEV (dev->net, &gadget->dev);
+ status = register_netdev (dev->net);
if (status < 0)
goto fail1;
u32 vendorID = 0;
/* FIXME RNDIS vendor id == "vendor NIC code" == ? */
-
+
dev->rndis_config = rndis_register (rndis_control_ack);
if (dev->rndis_config < 0) {
fail0:
status = -ENODEV;
goto fail;
}
-
+
/* these set up a lot of the OIDs that RNDIS needs */
rndis_set_host_mac (dev->rndis_config, dev->host_mac);
if (rndis_set_param_dev (dev->rndis_config, dev->net,
.suspend = eth_suspend,
.resume = eth_resume,
- .driver = {
+ .driver = {
.name = (char *) shortname,
.owner = THIS_MODULE,
},
struct usb_request *req;
struct ep_data *epdata;
void *buf;
- char __user *ubuf;
+ char __user *ubuf; /* NULL for writes */
unsigned actual;
};
status = priv->actual;
kfree(priv->buf);
kfree(priv);
- aio_put_req(iocb);
return status;
}
spin_lock(&epdata->dev->lock);
priv->req = NULL;
priv->epdata = NULL;
- if (NULL == iocb->ki_retry
- || unlikely(0 == req->actual)
+ if (priv->ubuf == NULL
+ || unlikely(req->actual == 0)
|| unlikely(kiocbIsCancelled(iocb))) {
kfree(req->buf);
kfree(priv);
char __user *ubuf
)
{
- struct kiocb_priv *priv = (void *) &iocb->private;
+ struct kiocb_priv *priv;
struct usb_request *req;
ssize_t value;
kfree(priv);
put_ep(epdata);
} else
- value = -EIOCBQUEUED;
+ value = (ubuf ? -EIOCBRETRY : -EIOCBQUEUED);
return value;
}
/* ep0 can't deliver events when STATE_SETUP */
for (i = 0; i < n; i++) {
if (dev->event [i].type == GADGETFS_SETUP) {
- len = n = i + 1;
+ len = i + 1;
len *= sizeof (struct usb_gadgetfs_event);
n = 0;
break;
static int activate_ep_files (struct dev_data *dev)
{
struct usb_ep *ep;
+ struct ep_data *data;
gadget_for_each_ep (ep, dev->gadget) {
- struct ep_data *data;
data = kzalloc(sizeof(*data), GFP_KERNEL);
if (!data)
- goto enomem;
+ goto enomem0;
data->state = STATE_EP_DISABLED;
init_MUTEX (&data->lock);
init_waitqueue_head (&data->wait);
data->req = usb_ep_alloc_request (ep, GFP_KERNEL);
if (!data->req)
- goto enomem;
+ goto enomem1;
data->inode = gadgetfs_create_file (dev->sb, data->name,
data, &ep_config_operations,
&data->dentry);
- if (!data->inode) {
- usb_ep_free_request(ep, data->req);
- kfree (data);
- goto enomem;
- }
+ if (!data->inode)
+ goto enomem2;
list_add_tail (&data->epfiles, &dev->epfiles);
}
return 0;
-enomem:
+enomem2:
+ usb_ep_free_request (ep, data->req);
+enomem1:
+ put_dev (dev);
+ kfree (data);
+enomem0:
DBG (dev, "%s enomem\n", __FUNCTION__);
destroy_ep_files (dev);
return -ENOMEM;
*
* After initialization, the device stays active for as long as that
* $CHIP file is open. Events may then be read from that descriptor,
- * such configuration notifications. More complex drivers will handle
+ * such as configuration notifications. More complex drivers will handle
* some control requests in user space.
*/
NULL, &simple_dir_operations,
S_IFDIR | S_IRUGO | S_IXUGO);
if (!inode)
- return -ENOMEM;
+ goto enomem0;
inode->i_op = &simple_dir_inode_operations;
- if (!(d = d_alloc_root (inode))) {
- iput (inode);
- return -ENOMEM;
- }
+ if (!(d = d_alloc_root (inode)))
+ goto enomem1;
sb->s_root = d;
/* the ep0 file is named after the controller we expect;
*/
dev = dev_new ();
if (!dev)
- return -ENOMEM;
+ goto enomem2;
dev->sb = sb;
- if (!(inode = gadgetfs_create_file (sb, CHIP,
+ if (!gadgetfs_create_file (sb, CHIP,
dev, &dev_init_operations,
- &dev->dentry))) {
- put_dev(dev);
- return -ENOMEM;
- }
+ &dev->dentry))
+ goto enomem3;
/* other endpoint files are available after hardware setup,
* from binding to a controller.
*/
the_device = dev;
return 0;
+
+enomem3:
+ put_dev (dev);
+enomem2:
+ dput (d);
+enomem1:
+ iput (inode);
+enomem0:
+ return -ENOMEM;
}
/* "mount -t gadgetfs path /dev/gadget" ends up here */
return retval;
}
+/* make sure the board is quiescent; otherwise it will continue
+ * generating IRQs across the upcoming reboot.
+ */
+
+static void net2280_shutdown (struct pci_dev *pdev)
+{
+ struct net2280 *dev = pci_get_drvdata (pdev);
+
+ /* disable IRQs */
+ writel (0, &dev->regs->pciirqenb0);
+ writel (0, &dev->regs->pciirqenb1);
+
+ /* disable the pullup so the host will think we're gone */
+ writel (0, &dev->usb->usbctl);
+}
+
/*-------------------------------------------------------------------------*/
.probe = net2280_probe,
.remove = net2280_remove,
+ .shutdown = net2280_shutdown,
/* FIXME add power management support */
};
#include <asm/mach-types.h>
#include <asm/unaligned.h>
#include <asm/hardware.h>
+#ifdef CONFIG_ARCH_PXA
#include <asm/arch/pxa-regs.h>
+#endif
#include <linux/usb_ch9.h>
#include <linux/usb_gadget.h>
-#include <asm/arch/udc.h>
+#include <asm/arch/hardware/intel_udc.h>
/*
count = req->req.length;
done (ep, req, 0);
ep0_idle(ep->dev);
+#ifndef CONFIG_ARCH_IXP4XX
#if 1
/* This seems to get rid of lost status irqs in some cases:
* host responds quickly, or next request involves config
udelay(1);
} while (count);
}
+#endif
#endif
} else if (ep->dev->req_pending)
ep0start(ep->dev, 0, "IN");
int retval;
if (!driver
- || driver->speed != USB_SPEED_FULL
+ || driver->speed < USB_SPEED_FULL
|| !driver->bind
|| !driver->unbind
|| !driver->disconnect
#define PXA210_B1 0x00000123
#define PXA210_B0 0x00000122
#define IXP425_A0 0x000001c1
+#define IXP465_AD 0x00000200
/*
* probe - binds to the platform device
break;
#elif defined(CONFIG_ARCH_IXP4XX)
case IXP425_A0:
+ case IXP465_AD:
+ dev->has_cfr = 1;
out_dma = 0;
break;
#endif
free_irq(IRQ_USB, dev);
dev->got_irq = 0;
}
+#ifdef CONFIG_ARCH_LUBBOCK
if (machine_is_lubbock()) {
free_irq(LUBBOCK_USB_DISC_IRQ, dev);
free_irq(LUBBOCK_USB_IRQ, dev);
}
+#endif
platform_set_drvdata(pdev, NULL);
the_controller = NULL;
return 0;
-/*
+/*
* RNDIS MSG parser
- *
+ *
* Version: $Id: rndis.c,v 1.19 2004/03/25 21:33:46 robert Exp $
- *
+ *
* Authors: Benedikt Spranger, Pengutronix
- * Robert Schwebel, Pengutronix
- *
+ * Robert Schwebel, Pengutronix
+ *
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
- * version 2, as published by the Free Software Foundation.
- *
+ * version 2, as published by the Free Software Foundation.
+ *
* This software was originally developed in conformance with
* Microsoft's Remote NDIS Specification License Agreement.
- *
+ *
* 03/12/2004 Kai-Uwe Bloem <linux-development@auerswald.de>
* Fixed message length bug in init_response
- *
+ *
* 03/25/2004 Kai-Uwe Bloem <linux-development@auerswald.de>
- * Fixed rndis_rm_hdr length bug.
+ * Fixed rndis_rm_hdr length bug.
*
* Copyright (C) 2004 by David Brownell
* updates to merge with Linux 2.6, better match RNDIS spec
/* supported OIDs */
-static const u32 oid_supported_list [] =
+static const u32 oid_supported_list [] =
{
/* the general stuff */
OID_GEN_SUPPORTED_LIST,
#if 0
OID_GEN_RNDIS_CONFIG_PARAMETER,
#endif
-
+
/* the statistical stuff */
OID_GEN_XMIT_OK,
OID_GEN_RCV_OK,
OID_GEN_TRANSMIT_QUEUE_LENGTH,
#endif /* RNDIS_OPTIONAL_STATS */
- /* mandatory 802.3 */
+ /* mandatory 802.3 */
/* the general stuff */
OID_802_3_PERMANENT_ADDRESS,
OID_802_3_CURRENT_ADDRESS,
OID_802_3_MULTICAST_LIST,
OID_802_3_MAC_OPTIONS,
OID_802_3_MAXIMUM_LIST_SIZE,
-
+
/* the statistical stuff */
OID_802_3_RCV_ERROR_ALIGNMENT,
OID_802_3_XMIT_ONE_COLLISION,
gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len,
rndis_resp_t *r)
{
- int retval = -ENOTSUPP;
- u32 length = 4; /* usually */
+ int retval = -ENOTSUPP;
+ u32 length = 4; /* usually */
__le32 *outbuf;
int i, count;
rndis_query_cmplt_type *resp;
outbuf[i] = cpu_to_le32 (oid_supported_list[i]);
retval = 0;
break;
-
+
/* mandatory */
case OID_GEN_HARDWARE_STATUS:
DEBUG("%s: OID_GEN_HARDWARE_STATUS\n", __FUNCTION__);
- /* Bogus question!
+ /* Bogus question!
* Hardware must be ready to receive high level protocols.
- * BTW:
+ * BTW:
* reddite ergo quae sunt Caesaris Caesari
* et quae sunt Dei Deo!
*/
*outbuf = __constant_cpu_to_le32 (0);
retval = 0;
break;
-
+
/* mandatory */
case OID_GEN_MEDIA_SUPPORTED:
DEBUG("%s: OID_GEN_MEDIA_SUPPORTED\n", __FUNCTION__);
*outbuf = cpu_to_le32 (rndis_per_dev_params [configNr].medium);
retval = 0;
break;
-
+
/* mandatory */
case OID_GEN_MEDIA_IN_USE:
DEBUG("%s: OID_GEN_MEDIA_IN_USE\n", __FUNCTION__);
*outbuf = cpu_to_le32 (rndis_per_dev_params [configNr].medium);
retval = 0;
break;
-
+
/* mandatory */
case OID_GEN_MAXIMUM_FRAME_SIZE:
DEBUG("%s: OID_GEN_MAXIMUM_FRAME_SIZE\n", __FUNCTION__);
retval = 0;
}
break;
-
+
/* mandatory */
case OID_GEN_LINK_SPEED:
if (rndis_debug > 1)
retval = 0;
}
break;
-
+
/* mandatory */
case OID_GEN_RECEIVE_BLOCK_SIZE:
DEBUG("%s: OID_GEN_RECEIVE_BLOCK_SIZE\n", __FUNCTION__);
retval = 0;
}
break;
-
+
/* mandatory */
case OID_GEN_VENDOR_ID:
DEBUG("%s: OID_GEN_VENDOR_ID\n", __FUNCTION__);
rndis_per_dev_params [configNr].vendorID);
retval = 0;
break;
-
+
/* mandatory */
case OID_GEN_VENDOR_DESCRIPTION:
DEBUG("%s: OID_GEN_VENDOR_DESCRIPTION\n", __FUNCTION__);
DEBUG("%s: OID_GEN_XMIT_OK\n", __FUNCTION__);
if (rndis_per_dev_params [configNr].stats) {
*outbuf = cpu_to_le32 (
- rndis_per_dev_params [configNr].stats->tx_packets -
+ rndis_per_dev_params [configNr].stats->tx_packets -
rndis_per_dev_params [configNr].stats->tx_errors -
rndis_per_dev_params [configNr].stats->tx_dropped);
retval = 0;
DEBUG("%s: OID_GEN_RCV_OK\n", __FUNCTION__);
if (rndis_per_dev_params [configNr].stats) {
*outbuf = cpu_to_le32 (
- rndis_per_dev_params [configNr].stats->rx_packets -
+ rndis_per_dev_params [configNr].stats->rx_packets -
rndis_per_dev_params [configNr].stats->rx_errors -
rndis_per_dev_params [configNr].stats->rx_dropped);
retval = 0;
}
break;
-
+
/* mandatory */
case OID_GEN_XMIT_ERROR:
if (rndis_debug > 1)
retval = 0;
}
break;
-
+
/* mandatory */
case OID_GEN_RCV_ERROR:
if (rndis_debug > 1)
retval = 0;
}
break;
-
+
/* mandatory */
case OID_GEN_RCV_NO_BUFFER:
DEBUG("%s: OID_GEN_RCV_NO_BUFFER\n", __FUNCTION__);
#ifdef RNDIS_OPTIONAL_STATS
case OID_GEN_DIRECTED_BYTES_XMIT:
DEBUG("%s: OID_GEN_DIRECTED_BYTES_XMIT\n", __FUNCTION__);
- /*
+ /*
* Aunt Tilly's size of shoes
* minus antarctica count of penguins
* divided by weight of Alpha Centauri
if (rndis_per_dev_params [configNr].stats) {
*outbuf = cpu_to_le32 (
(rndis_per_dev_params [configNr]
- .stats->tx_packets -
+ .stats->tx_packets -
rndis_per_dev_params [configNr]
.stats->tx_errors -
rndis_per_dev_params [configNr]
retval = 0;
}
break;
-
+
case OID_GEN_DIRECTED_FRAMES_XMIT:
DEBUG("%s: OID_GEN_DIRECTED_FRAMES_XMIT\n", __FUNCTION__);
/* dito */
if (rndis_per_dev_params [configNr].stats) {
*outbuf = cpu_to_le32 (
(rndis_per_dev_params [configNr]
- .stats->tx_packets -
+ .stats->tx_packets -
rndis_per_dev_params [configNr]
.stats->tx_errors -
rndis_per_dev_params [configNr]
retval = 0;
}
break;
-
+
case OID_GEN_MULTICAST_BYTES_XMIT:
DEBUG("%s: OID_GEN_MULTICAST_BYTES_XMIT\n", __FUNCTION__);
if (rndis_per_dev_params [configNr].stats) {
retval = 0;
}
break;
-
+
case OID_GEN_MULTICAST_FRAMES_XMIT:
DEBUG("%s: OID_GEN_MULTICAST_FRAMES_XMIT\n", __FUNCTION__);
if (rndis_per_dev_params [configNr].stats) {
retval = 0;
}
break;
-
+
case OID_GEN_BROADCAST_BYTES_XMIT:
DEBUG("%s: OID_GEN_BROADCAST_BYTES_XMIT\n", __FUNCTION__);
if (rndis_per_dev_params [configNr].stats) {
retval = 0;
}
break;
-
+
case OID_GEN_BROADCAST_FRAMES_XMIT:
DEBUG("%s: OID_GEN_BROADCAST_FRAMES_XMIT\n", __FUNCTION__);
if (rndis_per_dev_params [configNr].stats) {
retval = 0;
}
break;
-
+
case OID_GEN_DIRECTED_BYTES_RCV:
DEBUG("%s: OID_GEN_DIRECTED_BYTES_RCV\n", __FUNCTION__);
*outbuf = __constant_cpu_to_le32 (0);
retval = 0;
break;
-
+
case OID_GEN_DIRECTED_FRAMES_RCV:
DEBUG("%s: OID_GEN_DIRECTED_FRAMES_RCV\n", __FUNCTION__);
*outbuf = __constant_cpu_to_le32 (0);
retval = 0;
break;
-
+
case OID_GEN_MULTICAST_BYTES_RCV:
DEBUG("%s: OID_GEN_MULTICAST_BYTES_RCV\n", __FUNCTION__);
if (rndis_per_dev_params [configNr].stats) {
retval = 0;
}
break;
-
+
case OID_GEN_MULTICAST_FRAMES_RCV:
DEBUG("%s: OID_GEN_MULTICAST_FRAMES_RCV\n", __FUNCTION__);
if (rndis_per_dev_params [configNr].stats) {
retval = 0;
}
break;
-
+
case OID_GEN_BROADCAST_BYTES_RCV:
DEBUG("%s: OID_GEN_BROADCAST_BYTES_RCV\n", __FUNCTION__);
if (rndis_per_dev_params [configNr].stats) {
retval = 0;
}
break;
-
+
case OID_GEN_BROADCAST_FRAMES_RCV:
DEBUG("%s: OID_GEN_BROADCAST_FRAMES_RCV\n", __FUNCTION__);
if (rndis_per_dev_params [configNr].stats) {
retval = 0;
}
break;
-
+
case OID_GEN_RCV_CRC_ERROR:
DEBUG("%s: OID_GEN_RCV_CRC_ERROR\n", __FUNCTION__);
if (rndis_per_dev_params [configNr].stats) {
retval = 0;
}
break;
-
+
case OID_GEN_TRANSMIT_QUEUE_LENGTH:
DEBUG("%s: OID_GEN_TRANSMIT_QUEUE_LENGTH\n", __FUNCTION__);
*outbuf = __constant_cpu_to_le32 (0);
retval = 0;
}
break;
-
+
/* mandatory */
case OID_802_3_CURRENT_ADDRESS:
DEBUG("%s: OID_802_3_CURRENT_ADDRESS\n", __FUNCTION__);
retval = 0;
}
break;
-
+
/* mandatory */
case OID_802_3_MULTICAST_LIST:
DEBUG("%s: OID_802_3_MULTICAST_LIST\n", __FUNCTION__);
*outbuf = __constant_cpu_to_le32 (0xE0000000);
retval = 0;
break;
-
+
/* mandatory */
case OID_802_3_MAXIMUM_LIST_SIZE:
DEBUG("%s: OID_802_3_MAXIMUM_LIST_SIZE\n", __FUNCTION__);
*outbuf = __constant_cpu_to_le32 (1);
retval = 0;
break;
-
+
case OID_802_3_MAC_OPTIONS:
DEBUG("%s: OID_802_3_MAC_OPTIONS\n", __FUNCTION__);
break;
retval = 0;
}
break;
-
+
/* mandatory */
case OID_802_3_XMIT_ONE_COLLISION:
DEBUG("%s: OID_802_3_XMIT_ONE_COLLISION\n", __FUNCTION__);
*outbuf = __constant_cpu_to_le32 (0);
retval = 0;
break;
-
+
/* mandatory */
case OID_802_3_XMIT_MORE_COLLISIONS:
DEBUG("%s: OID_802_3_XMIT_MORE_COLLISIONS\n", __FUNCTION__);
*outbuf = __constant_cpu_to_le32 (0);
retval = 0;
break;
-
+
#ifdef RNDIS_OPTIONAL_STATS
case OID_802_3_XMIT_DEFERRED:
DEBUG("%s: OID_802_3_XMIT_DEFERRED\n", __FUNCTION__);
/* TODO */
break;
-
+
case OID_802_3_XMIT_MAX_COLLISIONS:
DEBUG("%s: OID_802_3_XMIT_MAX_COLLISIONS\n", __FUNCTION__);
/* TODO */
break;
-
+
case OID_802_3_RCV_OVERRUN:
DEBUG("%s: OID_802_3_RCV_OVERRUN\n", __FUNCTION__);
/* TODO */
break;
-
+
case OID_802_3_XMIT_UNDERRUN:
DEBUG("%s: OID_802_3_XMIT_UNDERRUN\n", __FUNCTION__);
/* TODO */
break;
-
+
case OID_802_3_XMIT_HEARTBEAT_FAILURE:
DEBUG("%s: OID_802_3_XMIT_HEARTBEAT_FAILURE\n", __FUNCTION__);
/* TODO */
break;
-
+
case OID_802_3_XMIT_TIMES_CRS_LOST:
DEBUG("%s: OID_802_3_XMIT_TIMES_CRS_LOST\n", __FUNCTION__);
/* TODO */
break;
-
+
case OID_802_3_XMIT_LATE_COLLISIONS:
DEBUG("%s: OID_802_3_XMIT_LATE_COLLISIONS\n", __FUNCTION__);
/* TODO */
- break;
+ break;
#endif /* RNDIS_OPTIONAL_STATS */
#ifdef RNDIS_PM
#endif
default:
- printk (KERN_WARNING "%s: query unknown OID 0x%08X\n",
+ printk (KERN_WARNING "%s: query unknown OID 0x%08X\n",
__FUNCTION__, OID);
}
if (retval < 0)
length = 0;
-
+
resp->InformationBufferLength = cpu_to_le32 (length);
r->length = length + sizeof *resp;
resp->MessageLength = cpu_to_le32 (r->length);
return retval;
}
-static int gen_ndis_set_resp (u8 configNr, u32 OID, u8 *buf, u32 buf_len,
- rndis_resp_t *r)
+static int gen_ndis_set_resp (u8 configNr, u32 OID, u8 *buf, u32 buf_len,
+ rndis_resp_t *r)
{
rndis_set_cmplt_type *resp;
- int i, retval = -ENOTSUPP;
+ int i, retval = -ENOTSUPP;
struct rndis_params *params;
if (!r)
netif_stop_queue (params->dev);
}
break;
-
+
case OID_802_3_MULTICAST_LIST:
- /* I think we can ignore this */
+ /* I think we can ignore this */
DEBUG("%s: OID_802_3_MULTICAST_LIST\n", __FUNCTION__);
retval = 0;
break;
#endif /* RNDIS_PM */
default:
- printk (KERN_WARNING "%s: set unknown OID 0x%08X, size %d\n",
+ printk (KERN_WARNING "%s: set unknown OID 0x%08X, size %d\n",
__FUNCTION__, OID, buf_len);
}
-
+
return retval;
}
-/*
- * Response Functions
+/*
+ * Response Functions
*/
static int rndis_init_response (int configNr, rndis_init_msg_type *buf)
{
- rndis_init_cmplt_type *resp;
+ rndis_init_cmplt_type *resp;
rndis_resp_t *r;
-
+
if (!rndis_per_dev_params [configNr].dev) return -ENOTSUPP;
-
+
r = rndis_add_response (configNr, sizeof (rndis_init_cmplt_type));
if (!r)
return -ENOMEM;
resp = (rndis_init_cmplt_type *) r->buf;
-
+
resp->MessageType = __constant_cpu_to_le32 (
REMOTE_NDIS_INITIALIZE_CMPLT);
resp->MessageLength = __constant_cpu_to_le32 (52);
resp->PacketAlignmentFactor = __constant_cpu_to_le32 (0);
resp->AFListOffset = __constant_cpu_to_le32 (0);
resp->AFListSize = __constant_cpu_to_le32 (0);
-
+
if (rndis_per_dev_params [configNr].ack)
- rndis_per_dev_params [configNr].ack (
- rndis_per_dev_params [configNr].dev);
-
+ rndis_per_dev_params [configNr].ack (
+ rndis_per_dev_params [configNr].dev);
+
return 0;
}
{
rndis_query_cmplt_type *resp;
rndis_resp_t *r;
-
+
// DEBUG("%s: OID = %08X\n", __FUNCTION__, cpu_to_le32(buf->OID));
if (!rndis_per_dev_params [configNr].dev) return -ENOTSUPP;
-
+
/*
* we need more memory:
* gen_ndis_query_resp expects enough space for
if (!r)
return -ENOMEM;
resp = (rndis_query_cmplt_type *) r->buf;
-
+
resp->MessageType = __constant_cpu_to_le32 (REMOTE_NDIS_QUERY_CMPLT);
resp->RequestID = buf->RequestID; /* Still LE in msg buffer */
-
+
if (gen_ndis_query_resp (configNr, le32_to_cpu (buf->OID),
le32_to_cpu(buf->InformationBufferOffset)
+ 8 + (u8 *) buf,
resp->InformationBufferOffset = __constant_cpu_to_le32 (0);
} else
resp->Status = __constant_cpu_to_le32 (RNDIS_STATUS_SUCCESS);
-
+
if (rndis_per_dev_params [configNr].ack)
- rndis_per_dev_params [configNr].ack (
- rndis_per_dev_params [configNr].dev);
+ rndis_per_dev_params [configNr].ack (
+ rndis_per_dev_params [configNr].dev);
return 0;
}
u32 BufLength, BufOffset;
rndis_set_cmplt_type *resp;
rndis_resp_t *r;
-
+
r = rndis_add_response (configNr, sizeof (rndis_set_cmplt_type));
if (!r)
return -ENOMEM;
DEBUG("%s: Length: %d\n", __FUNCTION__, BufLength);
DEBUG("%s: Offset: %d\n", __FUNCTION__, BufOffset);
DEBUG("%s: InfoBuffer: ", __FUNCTION__);
-
+
for (i = 0; i < BufLength; i++) {
DEBUG ("%02x ", *(((u8 *) buf) + i + 8 + BufOffset));
}
-
+
DEBUG ("\n");
#endif
-
+
resp->MessageType = __constant_cpu_to_le32 (REMOTE_NDIS_SET_CMPLT);
resp->MessageLength = __constant_cpu_to_le32 (16);
resp->RequestID = buf->RequestID; /* Still LE in msg buffer */
- if (gen_ndis_set_resp (configNr, le32_to_cpu (buf->OID),
- ((u8 *) buf) + 8 + BufOffset, BufLength, r))
- resp->Status = __constant_cpu_to_le32 (RNDIS_STATUS_NOT_SUPPORTED);
- else resp->Status = __constant_cpu_to_le32 (RNDIS_STATUS_SUCCESS);
-
+ if (gen_ndis_set_resp (configNr, le32_to_cpu (buf->OID),
+ ((u8 *) buf) + 8 + BufOffset, BufLength, r))
+ resp->Status = __constant_cpu_to_le32 (RNDIS_STATUS_NOT_SUPPORTED);
+ else
+ resp->Status = __constant_cpu_to_le32 (RNDIS_STATUS_SUCCESS);
+
if (rndis_per_dev_params [configNr].ack)
- rndis_per_dev_params [configNr].ack (
- rndis_per_dev_params [configNr].dev);
-
+ rndis_per_dev_params [configNr].ack (
+ rndis_per_dev_params [configNr].dev);
+
return 0;
}
{
rndis_reset_cmplt_type *resp;
rndis_resp_t *r;
-
+
r = rndis_add_response (configNr, sizeof (rndis_reset_cmplt_type));
if (!r)
return -ENOMEM;
resp = (rndis_reset_cmplt_type *) r->buf;
-
+
resp->MessageType = __constant_cpu_to_le32 (REMOTE_NDIS_RESET_CMPLT);
resp->MessageLength = __constant_cpu_to_le32 (16);
resp->Status = __constant_cpu_to_le32 (RNDIS_STATUS_SUCCESS);
/* resent information */
resp->AddressingReset = __constant_cpu_to_le32 (1);
-
+
if (rndis_per_dev_params [configNr].ack)
- rndis_per_dev_params [configNr].ack (
- rndis_per_dev_params [configNr].dev);
+ rndis_per_dev_params [configNr].ack (
+ rndis_per_dev_params [configNr].dev);
return 0;
}
static int rndis_keepalive_response (int configNr,
- rndis_keepalive_msg_type *buf)
+ rndis_keepalive_msg_type *buf)
{
rndis_keepalive_cmplt_type *resp;
rndis_resp_t *r;
if (!r)
return -ENOMEM;
resp = (rndis_keepalive_cmplt_type *) r->buf;
-
+
resp->MessageType = __constant_cpu_to_le32 (
REMOTE_NDIS_KEEPALIVE_CMPLT);
resp->MessageLength = __constant_cpu_to_le32 (16);
resp->RequestID = buf->RequestID; /* Still LE in msg buffer */
resp->Status = __constant_cpu_to_le32 (RNDIS_STATUS_SUCCESS);
-
+
if (rndis_per_dev_params [configNr].ack)
- rndis_per_dev_params [configNr].ack (
- rndis_per_dev_params [configNr].dev);
-
+ rndis_per_dev_params [configNr].ack (
+ rndis_per_dev_params [configNr].dev);
+
return 0;
}
-/*
- * Device to Host Comunication
+/*
+ * Device to Host Comunication
*/
static int rndis_indicate_status_msg (int configNr, u32 status)
{
- rndis_indicate_status_msg_type *resp;
+ rndis_indicate_status_msg_type *resp;
rndis_resp_t *r;
-
+
if (rndis_per_dev_params [configNr].state == RNDIS_UNINITIALIZED)
- return -ENOTSUPP;
-
- r = rndis_add_response (configNr,
+ return -ENOTSUPP;
+
+ r = rndis_add_response (configNr,
sizeof (rndis_indicate_status_msg_type));
if (!r)
return -ENOMEM;
resp = (rndis_indicate_status_msg_type *) r->buf;
-
+
resp->MessageType = __constant_cpu_to_le32 (
REMOTE_NDIS_INDICATE_STATUS_MSG);
resp->MessageLength = __constant_cpu_to_le32 (20);
resp->Status = cpu_to_le32 (status);
resp->StatusBufferLength = __constant_cpu_to_le32 (0);
resp->StatusBufferOffset = __constant_cpu_to_le32 (0);
-
- if (rndis_per_dev_params [configNr].ack)
- rndis_per_dev_params [configNr].ack (
- rndis_per_dev_params [configNr].dev);
+
+ if (rndis_per_dev_params [configNr].ack)
+ rndis_per_dev_params [configNr].ack (
+ rndis_per_dev_params [configNr].dev);
return 0;
}
{
rndis_per_dev_params [configNr].media_state
= NDIS_MEDIA_STATE_CONNECTED;
- return rndis_indicate_status_msg (configNr,
+ return rndis_indicate_status_msg (configNr,
RNDIS_STATUS_MEDIA_CONNECT);
}
rndis_per_dev_params [configNr].host_mac = addr;
}
-/*
- * Message Parser
+/*
+ * Message Parser
*/
int rndis_msg_parser (u8 configNr, u8 *buf)
{
u32 MsgType, MsgLength;
__le32 *tmp;
struct rndis_params *params;
-
+
if (!buf)
return -ENOMEM;
-
- tmp = (__le32 *) buf;
+
+ tmp = (__le32 *) buf;
MsgType = le32_to_cpup(tmp++);
MsgLength = le32_to_cpup(tmp++);
-
+
if (configNr >= RNDIS_MAX_CONFIGS)
return -ENOTSUPP;
params = &rndis_per_dev_params [configNr];
-
+
/* NOTE: RNDIS is *EXTREMELY* chatty ... Windows constantly polls for
* rx/tx statistics and link status, in addition to KEEPALIVE traffic
* and normal HC level polling to see if there's any IN traffic.
/* For USB: responses may take up to 10 seconds */
switch (MsgType) {
case REMOTE_NDIS_INITIALIZE_MSG:
- DEBUG("%s: REMOTE_NDIS_INITIALIZE_MSG\n",
+ DEBUG("%s: REMOTE_NDIS_INITIALIZE_MSG\n",
__FUNCTION__ );
params->state = RNDIS_INITIALIZED;
return rndis_init_response (configNr,
- (rndis_init_msg_type *) buf);
-
+ (rndis_init_msg_type *) buf);
+
case REMOTE_NDIS_HALT_MSG:
DEBUG("%s: REMOTE_NDIS_HALT_MSG\n",
__FUNCTION__ );
netif_stop_queue (params->dev);
}
return 0;
-
+
case REMOTE_NDIS_QUERY_MSG:
- return rndis_query_response (configNr,
- (rndis_query_msg_type *) buf);
-
+ return rndis_query_response (configNr,
+ (rndis_query_msg_type *) buf);
+
case REMOTE_NDIS_SET_MSG:
- return rndis_set_response (configNr,
- (rndis_set_msg_type *) buf);
-
+ return rndis_set_response (configNr,
+ (rndis_set_msg_type *) buf);
+
case REMOTE_NDIS_RESET_MSG:
- DEBUG("%s: REMOTE_NDIS_RESET_MSG\n",
+ DEBUG("%s: REMOTE_NDIS_RESET_MSG\n",
__FUNCTION__ );
return rndis_reset_response (configNr,
- (rndis_reset_msg_type *) buf);
+ (rndis_reset_msg_type *) buf);
case REMOTE_NDIS_KEEPALIVE_MSG:
/* For USB: host does this every 5 seconds */
if (rndis_debug > 1)
- DEBUG("%s: REMOTE_NDIS_KEEPALIVE_MSG\n",
+ DEBUG("%s: REMOTE_NDIS_KEEPALIVE_MSG\n",
__FUNCTION__ );
return rndis_keepalive_response (configNr,
- (rndis_keepalive_msg_type *)
+ (rndis_keepalive_msg_type *)
buf);
-
- default:
+
+ default:
/* At least Windows XP emits some undefined RNDIS messages.
* In one case those messages seemed to relate to the host
* suspending itself.
*/
printk (KERN_WARNING
- "%s: unknown RNDIS message 0x%08X len %d\n",
+ "%s: unknown RNDIS message 0x%08X len %d\n",
__FUNCTION__ , MsgType, MsgLength);
{
unsigned i;
}
break;
}
-
+
return -ENOTSUPP;
}
int rndis_register (int (* rndis_control_ack) (struct net_device *))
{
u8 i;
-
+
for (i = 0; i < RNDIS_MAX_CONFIGS; i++) {
if (!rndis_per_dev_params [i].used) {
rndis_per_dev_params [i].used = 1;
}
}
DEBUG("failed\n");
-
+
return -1;
}
void rndis_deregister (int configNr)
{
DEBUG("%s: \n", __FUNCTION__ );
-
+
if (configNr >= RNDIS_MAX_CONFIGS) return;
rndis_per_dev_params [configNr].used = 0;
-
+
return;
}
-int rndis_set_param_dev (u8 configNr, struct net_device *dev,
+int rndis_set_param_dev (u8 configNr, struct net_device *dev,
struct net_device_stats *stats,
u16 *cdc_filter)
{
DEBUG("%s:\n", __FUNCTION__ );
if (!dev || !stats) return -1;
if (configNr >= RNDIS_MAX_CONFIGS) return -1;
-
+
rndis_per_dev_params [configNr].dev = dev;
rndis_per_dev_params [configNr].stats = stats;
rndis_per_dev_params [configNr].filter = cdc_filter;
-
+
return 0;
}
DEBUG("%s:\n", __FUNCTION__ );
if (!vendorDescr) return -1;
if (configNr >= RNDIS_MAX_CONFIGS) return -1;
-
+
rndis_per_dev_params [configNr].vendorID = vendorID;
rndis_per_dev_params [configNr].vendorDescr = vendorDescr;
-
+
return 0;
}
{
DEBUG("%s: %u %u\n", __FUNCTION__, medium, speed);
if (configNr >= RNDIS_MAX_CONFIGS) return -1;
-
+
rndis_per_dev_params [configNr].medium = medium;
rndis_per_dev_params [configNr].speed = speed;
-
+
return 0;
}
{
rndis_resp_t *r;
struct list_head *act, *tmp;
-
- list_for_each_safe (act, tmp,
- &(rndis_per_dev_params [configNr].resp_queue))
+
+ list_for_each_safe (act, tmp,
+ &(rndis_per_dev_params [configNr].resp_queue))
{
r = list_entry (act, rndis_resp_t, list);
if (r && r->buf == buf) {
u8 *rndis_get_next_response (int configNr, u32 *length)
{
rndis_resp_t *r;
- struct list_head *act, *tmp;
-
+ struct list_head *act, *tmp;
+
if (!length) return NULL;
-
- list_for_each_safe (act, tmp,
- &(rndis_per_dev_params [configNr].resp_queue))
+
+ list_for_each_safe (act, tmp,
+ &(rndis_per_dev_params [configNr].resp_queue))
{
r = list_entry (act, rndis_resp_t, list);
if (!r->send) {
return r->buf;
}
}
-
+
return NULL;
}
static rndis_resp_t *rndis_add_response (int configNr, u32 length)
{
rndis_resp_t *r;
-
+
/* NOTE: this gets copied into ether.c USB_BUFSIZ bytes ... */
r = kmalloc (sizeof (rndis_resp_t) + length, GFP_ATOMIC);
if (!r) return NULL;
-
+
r->buf = (u8 *) (r + 1);
r->length = length;
r->send = 0;
-
- list_add_tail (&r->list,
- &(rndis_per_dev_params [configNr].resp_queue));
+
+ list_add_tail (&r->list,
+ &(rndis_per_dev_params [configNr].resp_queue));
return r;
}
#ifdef CONFIG_USB_GADGET_DEBUG_FILES
-static int rndis_proc_read (char *page, char **start, off_t off, int count, int *eof,
- void *data)
+static int rndis_proc_read (char *page, char **start, off_t off, int count, int *eof,
+ void *data)
{
char *out = page;
int len;
rndis_params *param = (rndis_params *) data;
-
- out += snprintf (out, count,
+
+ out += snprintf (out, count,
"Config Nr. %d\n"
"used : %s\n"
"state : %s\n"
"speed : %d\n"
"cable : %s\n"
"vendor ID : 0x%08X\n"
- "vendor : %s\n",
- param->confignr, (param->used) ? "y" : "n",
+ "vendor : %s\n",
+ param->confignr, (param->used) ? "y" : "n",
({ char *s = "?";
switch (param->state) {
case RNDIS_UNINITIALIZED:
case RNDIS_DATA_INITIALIZED:
s = "RNDIS_DATA_INITIALIZED"; break;
}; s; }),
- param->medium,
- (param->media_state) ? 0 : param->speed*100,
+ param->medium,
+ (param->media_state) ? 0 : param->speed*100,
(param->media_state) ? "disconnected" : "connected",
- param->vendorID, param->vendorDescr);
-
+ param->vendorID, param->vendorDescr);
+
len = out - page;
len -= off;
-
+
if (len < count) {
*eof = 1;
if (len <= 0)
return 0;
} else
len = count;
-
+
*start = page + off;
return len;
}
-static int rndis_proc_write (struct file *file, const char __user *buffer,
- unsigned long count, void *data)
+static int rndis_proc_write (struct file *file, const char __user *buffer,
+ unsigned long count, void *data)
{
rndis_params *p = data;
u32 speed = 0;
int i, fl_speed = 0;
-
+
for (i = 0; i < count; i++) {
char c;
if (get_user(c, buffer))
case 'd':
rndis_signal_disconnect(p->confignr);
break;
- default:
+ default:
if (fl_speed) p->speed = speed;
else DEBUG ("%c is not valid\n", c);
break;
}
-
+
buffer++;
}
-
+
return count;
}
sprintf (name, NAME_TEMPLATE, i);
if (!(rndis_connect_state [i]
- = create_proc_entry (name, 0660, NULL)))
+ = create_proc_entry (name, 0660, NULL)))
{
DEBUG ("%s :remove entries", __FUNCTION__);
while (i) {
= NDIS_MEDIA_STATE_DISCONNECTED;
INIT_LIST_HEAD (&(rndis_per_dev_params [i].resp_queue));
}
-
+
return 0;
}
#ifdef CONFIG_USB_GADGET_DEBUG_FILES
u8 i;
char name [20];
-
+
for (i = 0; i < RNDIS_MAX_CONFIGS; i++) {
sprintf (name, NAME_TEMPLATE, i);
remove_proc_entry (name, NULL);
-/*
+/*
* RNDIS Definitions for Remote NDIS
- *
+ *
* Version: $Id: rndis.h,v 1.15 2004/03/25 21:33:46 robert Exp $
- *
+ *
* Authors: Benedikt Spranger, Pengutronix
- * Robert Schwebel, Pengutronix
- *
- * This program is free software; you can redistribute it and/or
+ * Robert Schwebel, Pengutronix
+ *
+ * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
- * version 2, as published by the Free Software Foundation.
- *
+ * version 2, as published by the Free Software Foundation.
+ *
* This software was originally developed in conformance with
* Microsoft's Remote NDIS Specification License Agreement.
*/
#define RNDIS_STATUS_MEDIA_CONNECT 0x4001000BU /* Device connected */
#define RNDIS_STATUS_MEDIA_DISCONNECT 0x4001000CU /* Device disconnected */
/* For all not specified status messages:
- * RNDIS_STATUS_Xxx -> NDIS_STATUS_Xxx
+ * RNDIS_STATUS_Xxx -> NDIS_STATUS_Xxx
*/
/* Message Set for Connectionless (802.3) Devices */
#define OID_PNP_ENABLE_WAKE_UP 0xFD010106
-typedef struct rndis_init_msg_type
+typedef struct rndis_init_msg_type
{
__le32 MessageType;
__le32 MessageLength;
const u8 *host_mac;
u16 *filter;
- struct net_device *dev;
+ struct net_device *dev;
struct net_device_stats *stats;
u32 vendorID;
const char *vendorDescr;
- int (*ack) (struct net_device *);
+ int (*ack) (struct net_device *);
struct list_head resp_queue;
} rndis_params;
int rndis_set_param_dev (u8 configNr, struct net_device *dev,
struct net_device_stats *stats,
u16 *cdc_filter);
-int rndis_set_param_vendor (u8 configNr, u32 vendorID,
+int rndis_set_param_vendor (u8 configNr, u32 vendorID,
const char *vendorDescr);
int rndis_set_param_medium (u8 configNr, u32 medium, u32 speed);
void rndis_add_hdr (struct sk_buff *skb);
#include <asm/uaccess.h>
#include <linux/usb_ch9.h>
-#include <linux/usb_cdc.h>
+#include <linux/usb/cdc.h>
#include <linux/usb_gadget.h>
#include "gadget_chips.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 "v2.0"
-#define GS_VERSION_NUM 0x0200
+#define GS_VERSION_STR "v2.2"
+#define GS_VERSION_NUM 0x0202
#define GS_LONG_NAME "Gadget Serial"
#define GS_SHORT_NAME "g_serial"
/*
* gs_close
*/
+
+#define GS_WRITE_FINISHED_EVENT_SAFELY(p) \
+({ \
+ int cond; \
+ \
+ spin_lock_irq(&(p)->port_lock); \
+ cond = !(p)->port_dev || !gs_buf_data_avail((p)->port_write_buf); \
+ spin_unlock_irq(&(p)->port_lock); \
+ cond; \
+})
+
static void gs_close(struct tty_struct *tty, struct file *file)
{
- unsigned long flags;
struct gs_port *port = tty->driver_data;
struct semaphore *sem;
sem = &gs_open_close_sem[port->port_num];
down(sem);
- spin_lock_irqsave(&port->port_lock, flags);
+ spin_lock_irq(&port->port_lock);
if (port->port_open_count == 0) {
printk(KERN_ERR
/* wait for write buffer to drain, or */
/* at most GS_CLOSE_TIMEOUT seconds */
if (gs_buf_data_avail(port->port_write_buf) > 0) {
- spin_unlock_irqrestore(&port->port_lock, flags);
- 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);
- spin_lock_irqsave(&port->port_lock, flags);
+ spin_unlock_irq(&port->port_lock);
+ wait_event_interruptible_timeout(port->port_write_wait,
+ GS_WRITE_FINISHED_EVENT_SAFELY(port),
+ GS_CLOSE_TIMEOUT * HZ);
+ spin_lock_irq(&port->port_lock);
}
/* free disconnected port on final close */
port->port_num, tty, file);
exit:
- spin_unlock_irqrestore(&port->port_lock, flags);
+ spin_unlock_irq(&port->port_lock);
up(sem);
}
controller is needed. It's safe to say "y" even if your
controller doesn't support this feature.
- This supports the EHCI implementation from TransDimension Inc.
+ This supports the EHCI implementation that's originally
+ from ARC, and has since changed hands a few times.
+
+config USB_EHCI_TT_NEWSCHED
+ bool "Improved Transaction Translator scheduling (EXPERIMENTAL)"
+ depends on USB_EHCI_HCD && EXPERIMENTAL
+ ---help---
+ This changes the periodic scheduling code to fill more of the low
+ and full speed bandwidth available from the Transaction Translator
+ (TT) in USB 2.0 hubs. Without this, only one transfer will be
+ issued in each microframe, significantly reducing the number of
+ periodic low/fullspeed transfers possible.
+
+ If you have multiple periodic low/fullspeed devices connected to a
+ highspeed USB hub which is connected to a highspeed USB Host
+ Controller, and some of those devices will not work correctly
+ (possibly due to "ENOSPC" or "-28" errors), say Y.
+
+ If unsure, say N.
config USB_ISP116X_HCD
tristate "ISP116X HCD support"
#include <linux/platform_device.h>
#include <asm/mach-au1x00/au1000.h>
-#ifndef CONFIG_SOC_AU1200
-#error "this Alchemy chip doesn't have EHCI"
-#else /* Au1200 */
-
#define USB_HOST_CONFIG (USB_MSR_BASE + USB_MSR_MCFG)
#define USB_MCFG_PFEN (1<<31)
#define USB_MCFG_RDCOMB (1<<30)
return 0;
}
*/
+MODULE_ALIAS("au1xxx-ehci");
+/* FIXME use "struct platform_driver" */
static struct device_driver ehci_hcd_au1xxx_driver = {
.name = "au1xxx-ehci",
.bus = &platform_bus_type,
/*.suspend = ehci_hcd_au1xxx_drv_suspend, */
/*.resume = ehci_hcd_au1xxx_drv_resume, */
};
-
-static int __init ehci_hcd_au1xxx_init(void)
-{
- pr_debug(DRIVER_INFO " (Au1xxx)\n");
-
- return driver_register(&ehci_hcd_au1xxx_driver);
-}
-
-static void __exit ehci_hcd_au1xxx_cleanup(void)
-{
- driver_unregister(&ehci_hcd_au1xxx_driver);
-}
-
-module_init(ehci_hcd_au1xxx_init);
-module_exit(ehci_hcd_au1xxx_cleanup);
return 0;
}
-static struct platform_driver ehci_fsl_dr_driver = {
- .probe = ehci_fsl_drv_probe,
- .remove = ehci_fsl_drv_remove,
- .driver = {
- .name = "fsl-usb2-dr",
- },
-};
+MODULE_ALIAS("fsl-ehci");
-static struct platform_driver ehci_fsl_mph_driver = {
+static struct platform_driver ehci_fsl_driver = {
.probe = ehci_fsl_drv_probe,
.remove = ehci_fsl_drv_remove,
.driver = {
- .name = "fsl-usb2-mph",
+ .name = "fsl-ehci",
},
};
-
-static int __init ehci_fsl_init(void)
-{
- int retval;
-
- pr_debug("%s: block sizes: qh %Zd qtd %Zd itd %Zd sitd %Zd\n",
- hcd_name,
- sizeof(struct ehci_qh), sizeof(struct ehci_qtd),
- sizeof(struct ehci_itd), sizeof(struct ehci_sitd));
-
- retval = platform_driver_register(&ehci_fsl_dr_driver);
- if (retval)
- return retval;
-
- return platform_driver_register(&ehci_fsl_mph_driver);
-}
-
-static void __exit ehci_fsl_cleanup(void)
-{
- platform_driver_unregister(&ehci_fsl_mph_driver);
- platform_driver_unregister(&ehci_fsl_dr_driver);
-}
-
-module_init(ehci_fsl_init);
-module_exit(ehci_fsl_cleanup);
#ifdef CONFIG_PCI
#include "ehci-pci.c"
-#define EHCI_BUS_GLUED
+#define PCI_DRIVER ehci_pci_driver
#endif
#ifdef CONFIG_PPC_83xx
#include "ehci-fsl.c"
-#define EHCI_BUS_GLUED
+#define PLATFORM_DRIVER ehci_fsl_driver
#endif
-#ifdef CONFIG_SOC_AU1X00
+#ifdef CONFIG_SOC_AU1200
#include "ehci-au1xxx.c"
-#define EHCI_BUS_GLUED
+#define PLATFORM_DRIVER ehci_hcd_au1xxx_driver
#endif
-#ifndef EHCI_BUS_GLUED
+#if !defined(PCI_DRIVER) && !defined(PLATFORM_DRIVER)
#error "missing bus glue for ehci-hcd"
#endif
+
+static int __init ehci_hcd_init(void)
+{
+ int retval = 0;
+
+ pr_debug("%s: block sizes: qh %Zd qtd %Zd itd %Zd sitd %Zd\n",
+ hcd_name,
+ sizeof(struct ehci_qh), sizeof(struct ehci_qtd),
+ sizeof(struct ehci_itd), sizeof(struct ehci_sitd));
+
+#ifdef PLATFORM_DRIVER
+ retval = platform_driver_register(&PLATFORM_DRIVER);
+ if (retval < 0)
+ return retval;
+#endif
+
+#ifdef PCI_DRIVER
+ retval = pci_register_driver(&PCI_DRIVER);
+ if (retval < 0) {
+#ifdef PLATFORM_DRIVER
+ platform_driver_unregister(&PLATFORM_DRIVER);
+#endif
+ }
+#endif
+
+ return retval;
+}
+module_init(ehci_hcd_init);
+
+static void __exit ehci_hcd_cleanup(void)
+{
+#ifdef PLATFORM_DRIVER
+ platform_driver_unregister(&PLATFORM_DRIVER);
+#endif
+#ifdef PCI_DRIVER
+ pci_unregister_driver(&PCI_DRIVER);
+#endif
+}
+module_exit(ehci_hcd_cleanup);
+
dbg_hcs_params(ehci, "reset");
dbg_hcc_params(ehci, "reset");
+ /* ehci_init() causes memory for DMA transfers to be
+ * allocated. Thus, any vendor-specific workarounds based on
+ * limiting the type of memory used for DMA transfers must
+ * happen before ehci_init() is called. */
+ switch (pdev->vendor) {
+ case PCI_VENDOR_ID_NVIDIA:
+ /* NVidia reports that certain chips don't handle
+ * QH, ITD, or SITD addresses above 2GB. (But TD,
+ * data buffer, and periodic schedule are normal.)
+ */
+ switch (pdev->device) {
+ case 0x003c: /* MCP04 */
+ case 0x005b: /* CK804 */
+ case 0x00d8: /* CK8 */
+ case 0x00e8: /* CK8S */
+ if (pci_set_consistent_dma_mask(pdev,
+ DMA_31BIT_MASK) < 0)
+ ehci_warn(ehci, "can't enable NVidia "
+ "workaround for >2GB RAM\n");
+ break;
+ }
+ break;
+ }
+
/* cache this readonly data; minimize chip reads */
ehci->hcs_params = readl(&ehci->caps->hcs_params);
if (retval)
return retval;
- /* NOTE: only the parts below this line are PCI-specific */
-
switch (pdev->vendor) {
case PCI_VENDOR_ID_TDI:
if (pdev->device == PCI_DEVICE_ID_TDI_EHCI) {
break;
case PCI_VENDOR_ID_NVIDIA:
switch (pdev->device) {
- /* NVidia reports that certain chips don't handle
- * QH, ITD, or SITD addresses above 2GB. (But TD,
- * data buffer, and periodic schedule are normal.)
- */
- case 0x003c: /* MCP04 */
- case 0x005b: /* CK804 */
- case 0x00d8: /* CK8 */
- case 0x00e8: /* CK8S */
- if (pci_set_consistent_dma_mask(pdev,
- DMA_31BIT_MASK) < 0)
- ehci_warn(ehci, "can't enable NVidia "
- "workaround for >2GB RAM\n");
- break;
/* Some NForce2 chips have problems with selective suspend;
* fixed in newer silicon.
*/
.resume = usb_hcd_pci_resume,
#endif
};
-
-static int __init ehci_hcd_pci_init(void)
-{
- if (usb_disabled())
- return -ENODEV;
-
- pr_debug("%s: block sizes: qh %Zd qtd %Zd itd %Zd sitd %Zd\n",
- hcd_name,
- sizeof(struct ehci_qh), sizeof(struct ehci_qtd),
- sizeof(struct ehci_itd), sizeof(struct ehci_sitd));
-
- return pci_register_driver(&ehci_pci_driver);
-}
-module_init(ehci_hcd_pci_init);
-
-static void __exit ehci_hcd_pci_cleanup(void)
-{
- pci_unregister_driver(&ehci_pci_driver);
-}
-module_exit(ehci_hcd_pci_cleanup);
return 1;
}
+#ifdef CONFIG_USB_EHCI_TT_NEWSCHED
+
+/* Which uframe does the low/fullspeed transfer start in?
+ *
+ * The parameter is the mask of ssplits in "H-frame" terms
+ * and this returns the transfer start uframe in "B-frame" terms,
+ * which allows both to match, e.g. a ssplit in "H-frame" uframe 0
+ * will cause a transfer in "B-frame" uframe 0. "B-frames" lag
+ * "H-frames" by 1 uframe. See the EHCI spec sec 4.5 and figure 4.7.
+ */
+static inline unsigned char tt_start_uframe(struct ehci_hcd *ehci, __le32 mask)
+{
+ unsigned char smask = QH_SMASK & le32_to_cpu(mask);
+ if (!smask) {
+ ehci_err(ehci, "invalid empty smask!\n");
+ /* uframe 7 can't have bw so this will indicate failure */
+ return 7;
+ }
+ return ffs(smask) - 1;
+}
+
+static const unsigned char
+max_tt_usecs[] = { 125, 125, 125, 125, 125, 125, 30, 0 };
+
+/* carryover low/fullspeed bandwidth that crosses uframe boundries */
+static inline void carryover_tt_bandwidth(unsigned short tt_usecs[8])
+{
+ int i;
+ for (i=0; i<7; i++) {
+ if (max_tt_usecs[i] < tt_usecs[i]) {
+ tt_usecs[i+1] += tt_usecs[i] - max_tt_usecs[i];
+ tt_usecs[i] = max_tt_usecs[i];
+ }
+ }
+}
+
+/* How many of the tt's periodic downstream 1000 usecs are allocated?
+ *
+ * While this measures the bandwidth in terms of usecs/uframe,
+ * the low/fullspeed bus has no notion of uframes, so any particular
+ * low/fullspeed transfer can "carry over" from one uframe to the next,
+ * since the TT just performs downstream transfers in sequence.
+ *
+ * For example two seperate 100 usec transfers can start in the same uframe,
+ * and the second one would "carry over" 75 usecs into the next uframe.
+ */
+static void
+periodic_tt_usecs (
+ struct ehci_hcd *ehci,
+ struct usb_device *dev,
+ unsigned frame,
+ unsigned short tt_usecs[8]
+)
+{
+ __le32 *hw_p = &ehci->periodic [frame];
+ union ehci_shadow *q = &ehci->pshadow [frame];
+ unsigned char uf;
+
+ memset(tt_usecs, 0, 16);
+
+ while (q->ptr) {
+ switch (Q_NEXT_TYPE(*hw_p)) {
+ case Q_TYPE_ITD:
+ hw_p = &q->itd->hw_next;
+ q = &q->itd->itd_next;
+ continue;
+ case Q_TYPE_QH:
+ if (same_tt(dev, q->qh->dev)) {
+ uf = tt_start_uframe(ehci, q->qh->hw_info2);
+ tt_usecs[uf] += q->qh->tt_usecs;
+ }
+ hw_p = &q->qh->hw_next;
+ q = &q->qh->qh_next;
+ continue;
+ case Q_TYPE_SITD:
+ if (same_tt(dev, q->sitd->urb->dev)) {
+ uf = tt_start_uframe(ehci, q->sitd->hw_uframe);
+ tt_usecs[uf] += q->sitd->stream->tt_usecs;
+ }
+ hw_p = &q->sitd->hw_next;
+ q = &q->sitd->sitd_next;
+ continue;
+ // case Q_TYPE_FSTN:
+ default:
+ ehci_dbg(ehci,
+ "ignoring periodic frame %d FSTN\n", frame);
+ hw_p = &q->fstn->hw_next;
+ q = &q->fstn->fstn_next;
+ }
+ }
+
+ carryover_tt_bandwidth(tt_usecs);
+
+ if (max_tt_usecs[7] < tt_usecs[7])
+ ehci_err(ehci, "frame %d tt sched overrun: %d usecs\n",
+ frame, tt_usecs[7] - max_tt_usecs[7]);
+}
+
+/*
+ * Return true if the device's tt's downstream bus is available for a
+ * periodic transfer of the specified length (usecs), starting at the
+ * specified frame/uframe. Note that (as summarized in section 11.19
+ * of the usb 2.0 spec) TTs can buffer multiple transactions for each
+ * uframe.
+ *
+ * The uframe parameter is when the fullspeed/lowspeed transfer
+ * should be executed in "B-frame" terms, which is the same as the
+ * highspeed ssplit's uframe (which is in "H-frame" terms). For example
+ * a ssplit in "H-frame" 0 causes a transfer in "B-frame" 0.
+ * See the EHCI spec sec 4.5 and fig 4.7.
+ *
+ * This checks if the full/lowspeed bus, at the specified starting uframe,
+ * has the specified bandwidth available, according to rules listed
+ * in USB 2.0 spec section 11.18.1 fig 11-60.
+ *
+ * This does not check if the transfer would exceed the max ssplit
+ * limit of 16, specified in USB 2.0 spec section 11.18.4 requirement #4,
+ * since proper scheduling limits ssplits to less than 16 per uframe.
+ */
+static int tt_available (
+ struct ehci_hcd *ehci,
+ unsigned period,
+ struct usb_device *dev,
+ unsigned frame,
+ unsigned uframe,
+ u16 usecs
+)
+{
+ if ((period == 0) || (uframe >= 7)) /* error */
+ return 0;
+
+ for (; frame < ehci->periodic_size; frame += period) {
+ unsigned short tt_usecs[8];
+
+ periodic_tt_usecs (ehci, dev, frame, tt_usecs);
+
+ ehci_vdbg(ehci, "tt frame %d check %d usecs start uframe %d in"
+ " schedule %d/%d/%d/%d/%d/%d/%d/%d\n",
+ frame, usecs, uframe,
+ tt_usecs[0], tt_usecs[1], tt_usecs[2], tt_usecs[3],
+ tt_usecs[4], tt_usecs[5], tt_usecs[6], tt_usecs[7]);
+
+ if (max_tt_usecs[uframe] <= tt_usecs[uframe]) {
+ ehci_vdbg(ehci, "frame %d uframe %d fully scheduled\n",
+ frame, uframe);
+ return 0;
+ }
+
+ /* special case for isoc transfers larger than 125us:
+ * the first and each subsequent fully used uframe
+ * must be empty, so as to not illegally delay
+ * already scheduled transactions
+ */
+ if (125 < usecs) {
+ int ufs = (usecs / 125) - 1;
+ int i;
+ for (i = uframe; i < (uframe + ufs) && i < 8; i++)
+ if (0 < tt_usecs[i]) {
+ ehci_vdbg(ehci,
+ "multi-uframe xfer can't fit "
+ "in frame %d uframe %d\n",
+ frame, i);
+ return 0;
+ }
+ }
+
+ tt_usecs[uframe] += usecs;
+
+ carryover_tt_bandwidth(tt_usecs);
+
+ /* fail if the carryover pushed bw past the last uframe's limit */
+ if (max_tt_usecs[7] < tt_usecs[7]) {
+ ehci_vdbg(ehci,
+ "tt unavailable usecs %d frame %d uframe %d\n",
+ usecs, frame, uframe);
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+#else
+
/* return true iff the device's transaction translator is available
* for a periodic transfer starting at the specified frame, using
* all the uframes in the mask.
return 1;
}
+#endif /* CONFIG_USB_EHCI_TT_NEWSCHED */
+
/*-------------------------------------------------------------------------*/
static int enable_periodic (struct ehci_hcd *ehci)
)
{
int retval = -ENOSPC;
- u8 mask;
+ u8 mask = 0;
if (qh->c_usecs && uframe >= 6) /* FSTN territory? */
goto done;
goto done;
}
+#ifdef CONFIG_USB_EHCI_TT_NEWSCHED
+ if (tt_available (ehci, qh->period, qh->dev, frame, uframe,
+ qh->tt_usecs)) {
+ unsigned i;
+
+ /* TODO : this may need FSTN for SSPLIT in uframe 5. */
+ for (i=uframe+1; i<8 && i<uframe+4; i++)
+ if (!check_period (ehci, frame, i,
+ qh->period, qh->c_usecs))
+ goto done;
+ else
+ mask |= 1 << i;
+
+ retval = 0;
+
+ *c_maskp = cpu_to_le32 (mask << 8);
+ }
+#else
/* Make sure this tt's buffer is also available for CSPLITs.
* We pessimize a bit; probably the typical full speed case
* doesn't need the second CSPLIT.
goto done;
retval = 0;
}
+#endif
done:
return retval;
}
frame = uframe >> 3;
uf = uframe & 7;
+#ifdef CONFIG_USB_EHCI_TT_NEWSCHED
+ /* The tt's fullspeed bus bandwidth must be available.
+ * tt_available scheduling guarantees 10+% for control/bulk.
+ */
+ if (!tt_available (ehci, period_uframes << 3,
+ stream->udev, frame, uf, stream->tt_usecs))
+ return 0;
+#else
/* tt must be idle for start(s), any gap, and csplit.
* assume scheduling slop leaves 10+% for control/bulk.
*/
if (!tt_no_collision (ehci, period_uframes << 3,
stream->udev, frame, mask))
return 0;
+#endif
/* check starts (OUT uses more than one) */
max_used = 100 - stream->usecs;
#include <linux/init.h>
#include <linux/list.h>
#include <linux/usb.h>
-#include <linux/usb_isp116x.h>
+#include <linux/usb/isp116x.h>
#include <linux/platform_device.h>
#include <asm/io.h>
if (ep->branch < PERIODIC_SIZE)
break;
- ret = ep->branch = balance(isp116x, ep->period, ep->load);
+ ep->branch = ret = balance(isp116x, ep->period, ep->load);
if (ret < 0)
goto fail;
ret = 0;
#include <linux/list.h>
#include <linux/interrupt.h>
#include <linux/usb.h>
-#include <linux/usb_sl811.h>
+#include <linux/usb/sl811.h>
#include <linux/platform_device.h>
#include <asm/io.h>
#include <pcmcia/cisreg.h>
#include <pcmcia/ds.h>
-#include <linux/usb_sl811.h>
+#include <linux/usb/sl811.h>
MODULE_AUTHOR("Botond Botyanszki");
MODULE_DESCRIPTION("REX-CFU1U PCMCIA driver for 2.6");
char *out = buf;
struct uhci_td *td;
int i, nactive, ninactive;
+ char *ptype;
if (len < 200)
return 0;
(usb_pipein(urbp->urb->pipe) ? "IN" : "OUT"));
switch (usb_pipetype(urbp->urb->pipe)) {
- case PIPE_ISOCHRONOUS: out += sprintf(out, "ISO"); break;
- case PIPE_INTERRUPT: out += sprintf(out, "INT"); break;
- case PIPE_BULK: out += sprintf(out, "BLK"); break;
- case PIPE_CONTROL: out += sprintf(out, "CTL"); break;
+ case PIPE_ISOCHRONOUS: ptype = "ISO"; break;
+ case PIPE_INTERRUPT: ptype = "INT"; break;
+ case PIPE_BULK: ptype = "BLK"; break;
+ default:
+ case PIPE_CONTROL: ptype = "CTL"; break;
}
- out += sprintf(out, "%s", (urbp->fsbr ? " FSBR" : ""));
+ out += sprintf(out, "%s%s", ptype, (urbp->fsbr ? " FSBR" : ""));
+ out += sprintf(out, " Actlen=%d", urbp->urb->actual_length);
if (urbp->urb->status != -EINPROGRESS)
out += sprintf(out, " Status=%d", urbp->urb->status);
i = nactive = ninactive = 0;
list_for_each_entry(td, &urbp->td_list, list) {
- if (++i <= 10 || debug > 2) {
+ if (urbp->qh->type != USB_ENDPOINT_XFER_ISOC &&
+ (++i <= 10 || debug > 2)) {
out += sprintf(out, "%*s%d: ", space + 2, "", i);
out += uhci_show_td(td, out, len - (out - buf), 0);
} else {
char *out = buf;
int i, nurbs;
__le32 element = qh_element(qh);
+ char *qtype;
/* Try to make sure there's enough memory */
- if (len < 80 * 6)
+ if (len < 80 * 7)
return 0;
- out += sprintf(out, "%*s[%p] link (%08x) element (%08x)\n", space, "",
- qh, le32_to_cpu(qh->link), le32_to_cpu(element));
+ switch (qh->type) {
+ case USB_ENDPOINT_XFER_ISOC: qtype = "ISO"; break;
+ case USB_ENDPOINT_XFER_INT: qtype = "INT"; break;
+ case USB_ENDPOINT_XFER_BULK: qtype = "BLK"; break;
+ case USB_ENDPOINT_XFER_CONTROL: qtype = "CTL"; break;
+ default: qtype = "Skel" ; break;
+ }
+
+ out += sprintf(out, "%*s[%p] %s QH link (%08x) element (%08x)\n",
+ space, "", qh, qtype,
+ le32_to_cpu(qh->link), le32_to_cpu(element));
+ if (qh->type == USB_ENDPOINT_XFER_ISOC)
+ out += sprintf(out, "%*s period %d frame %x desc [%p]\n",
+ space, "", qh->period, qh->iso_frame,
+ qh->iso_packet_desc);
if (element & UHCI_PTR_QH)
out += sprintf(out, "%*s Element points to QH (bug?)\n", space, "");
default:
rh_state = "?"; break;
}
- out += sprintf(out, "Root-hub state: %s\n", rh_state);
+ out += sprintf(out, "Root-hub state: %s FSBR: %d\n",
+ rh_state, uhci->fsbr_is_on);
return out - buf;
}
unsigned short portsc1, portsc2;
/* Try to make sure there's enough memory */
- if (len < 80 * 6)
+ if (len < 80 * 9)
return 0;
usbcmd = inw(io_addr + 0);
out += sprintf(out, " sof = %02x\n", sof);
out += uhci_show_sc(1, portsc1, out, len - (out - buf));
out += uhci_show_sc(2, portsc2, out, len - (out - buf));
+ out += sprintf(out, "Most recent frame: %x (%d) "
+ "Last ISO frame: %x (%d)\n",
+ uhci->frame_number, uhci->frame_number & 1023,
+ uhci->last_iso_frame, uhci->last_iso_frame & 1023);
return out - buf;
}
* (C) Copyright 2000 Yggdrasil Computing, Inc. (port of new PCI interface
* support from usb-ohci.c by Adam Richter, adam@yggdrasil.com).
* (C) Copyright 1999 Gregory P. Smith (from usb-ohci.c)
- * (C) Copyright 2004-2005 Alan Stern, stern@rowland.harvard.edu
+ * (C) Copyright 2004-2006 Alan Stern, stern@rowland.harvard.edu
*
* Intel documents this fairly well, and as far as I know there
* are no royalties or anything like that, but even so there are
#include <linux/ioport.h>
#include <linux/sched.h>
#include <linux/slab.h>
-#include <linux/smp_lock.h>
#include <linux/errno.h>
#include <linux/unistd.h>
#include <linux/interrupt.h>
static void wakeup_rh(struct uhci_hcd *uhci);
static void uhci_get_current_frame_number(struct uhci_hcd *uhci);
-/* If a transfer is still active after this much time, turn off FSBR */
-#define IDLE_TIMEOUT msecs_to_jiffies(50)
-#define FSBR_DELAY msecs_to_jiffies(50)
-
-/* When we timeout an idle transfer for FSBR, we'll switch it over to */
-/* depth first traversal. We'll do it in groups of this number of TDs */
-/* to make sure it doesn't hog all of the bandwidth */
-#define DEPTH_INTERVAL 5
-
#include "uhci-debug.c"
#include "uhci-q.c"
#include "uhci-hub.c"
uhci->is_stopped = UHCI_IS_STOPPED;
uhci_to_hcd(uhci)->state = HC_STATE_HALT;
uhci_to_hcd(uhci)->poll_rh = 0;
+
+ uhci->dead = 0; /* Full reset resurrects the controller */
}
/*
* Last rites for a defunct/nonfunctional controller
* or one we don't want to use any more.
*/
-static void hc_died(struct uhci_hcd *uhci)
+static void uhci_hc_died(struct uhci_hcd *uhci)
{
+ uhci_get_current_frame_number(uhci);
uhci_reset_hc(to_pci_dev(uhci_dev(uhci)), uhci->io_addr);
finish_reset(uhci);
- uhci->hc_inaccessible = 1;
+ uhci->dead = 1;
+
+ /* The current frame may already be partway finished */
+ ++uhci->frame_number;
}
/*
- * Initialize a controller that was newly discovered or has just been
- * resumed. In either case we can't be sure of its previous state.
+ * Initialize a controller that was newly discovered or has lost power
+ * or otherwise been reset while it was suspended. In none of these cases
+ * can we be sure of its previous state.
*/
static void check_and_reset_hc(struct uhci_hcd *uhci)
{
outl(uhci->frame_dma_handle, uhci->io_addr + USBFLBASEADD);
/* Set the current frame number */
- outw(uhci->frame_number, uhci->io_addr + USBFRNUM);
+ outw(uhci->frame_number & UHCI_MAX_SOF_NUMBER,
+ uhci->io_addr + USBFRNUM);
/* Mark controller as not halted before we enable interrupts */
uhci_to_hcd(uhci)->state = HC_STATE_SUSPENDED;
int int_enable;
auto_stop = (new_state == UHCI_RH_AUTO_STOPPED);
- dev_dbg(uhci_dev(uhci), "%s%s\n", __FUNCTION__,
+ dev_dbg(&uhci_to_hcd(uhci)->self.root_hub->dev,
+ "%s%s\n", __FUNCTION__,
(auto_stop ? " (auto-stop)" : ""));
/* If we get a suspend request when we're already auto-stopped
spin_unlock_irq(&uhci->lock);
msleep(1);
spin_lock_irq(&uhci->lock);
- if (uhci->hc_inaccessible) /* Died */
+ if (uhci->dead)
return;
}
if (!(inw(uhci->io_addr + USBSTS) & USBSTS_HCH))
- dev_warn(uhci_dev(uhci), "Controller not stopped yet!\n");
+ dev_warn(&uhci_to_hcd(uhci)->self.root_hub->dev,
+ "Controller not stopped yet!\n");
uhci_get_current_frame_number(uhci);
- smp_wmb();
uhci->rh_state = new_state;
uhci->is_stopped = UHCI_IS_STOPPED;
uhci_to_hcd(uhci)->poll_rh = !int_enable;
uhci_scan_schedule(uhci, NULL);
+ uhci_fsbr_off(uhci);
}
static void start_rh(struct uhci_hcd *uhci)
{
uhci_to_hcd(uhci)->state = HC_STATE_RUNNING;
uhci->is_stopped = 0;
- smp_wmb();
/* Mark it configured and running with a 64-byte max packet.
* All interrupts are enabled, even though RESUME won't do anything.
__releases(uhci->lock)
__acquires(uhci->lock)
{
- dev_dbg(uhci_dev(uhci), "%s%s\n", __FUNCTION__,
+ dev_dbg(&uhci_to_hcd(uhci)->self.root_hub->dev,
+ "%s%s\n", __FUNCTION__,
uhci->rh_state == UHCI_RH_AUTO_STOPPED ?
" (auto-start)" : "");
spin_unlock_irq(&uhci->lock);
msleep(20);
spin_lock_irq(&uhci->lock);
- if (uhci->hc_inaccessible) /* Died */
+ if (uhci->dead)
return;
/* End Global Resume and wait for EOP to be sent */
errbuf, ERRBUF_LEN);
lprintk(errbuf);
}
- hc_died(uhci);
+ uhci_hc_died(uhci);
/* Force a callback in case there are
* pending unlinks */
/*
* Store the current frame number in uhci->frame_number if the controller
- * is runnning
+ * is runnning. Expand from 11 bits (of which we use only 10) to a
+ * full-sized integer.
+ *
+ * Like many other parts of the driver, this code relies on being polled
+ * more than once per second as long as the controller is running.
*/
static void uhci_get_current_frame_number(struct uhci_hcd *uhci)
{
- if (!uhci->is_stopped)
- uhci->frame_number = inw(uhci->io_addr + USBFRNUM);
+ if (!uhci->is_stopped) {
+ unsigned delta;
+
+ delta = (inw(uhci->io_addr + USBFRNUM) - uhci->frame_number) &
+ (UHCI_NUMFRAMES - 1);
+ uhci->frame_number += delta;
+ }
}
/*
uhci->frame, uhci->frame_dma_handle);
}
-static int uhci_reset(struct usb_hcd *hcd)
+static int uhci_init(struct usb_hcd *hcd)
{
struct uhci_hcd *uhci = hcd_to_uhci(hcd);
unsigned io_size = (unsigned) hcd->rsrc_len;
{
struct usb_hcd *hcd = (struct usb_hcd *) pci_get_drvdata(pdev);
- hc_died(hcd_to_uhci(hcd));
+ uhci_hc_died(hcd_to_uhci(hcd));
}
/*
hcd->uses_new_polling = 1;
- uhci->fsbr = 0;
- uhci->fsbrtimeout = 0;
-
spin_lock_init(&uhci->lock);
-
- INIT_LIST_HEAD(&uhci->td_remove_list);
+ setup_timer(&uhci->fsbr_timer, uhci_fsbr_timeout,
+ (unsigned long) uhci);
INIT_LIST_HEAD(&uhci->idle_qh_list);
-
init_waitqueue_head(&uhci->waitqh);
if (DEBUG_CONFIGURED) {
struct uhci_hcd *uhci = hcd_to_uhci(hcd);
spin_lock_irq(&uhci->lock);
- if (!uhci->hc_inaccessible)
- hc_died(uhci);
+ if (test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags) && !uhci->dead)
+ uhci_hc_died(uhci);
uhci_scan_schedule(uhci, NULL);
spin_unlock_irq(&uhci->lock);
+ del_timer_sync(&uhci->fsbr_timer);
release_uhci(uhci);
}
static int uhci_rh_suspend(struct usb_hcd *hcd)
{
struct uhci_hcd *uhci = hcd_to_uhci(hcd);
+ int rc = 0;
spin_lock_irq(&uhci->lock);
- if (!uhci->hc_inaccessible) /* Not dead */
+ if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags))
+ rc = -ESHUTDOWN;
+ else if (!uhci->dead)
suspend_rh(uhci, UHCI_RH_SUSPENDED);
spin_unlock_irq(&uhci->lock);
- return 0;
+ return rc;
}
static int uhci_rh_resume(struct usb_hcd *hcd)
int rc = 0;
spin_lock_irq(&uhci->lock);
- if (uhci->hc_inaccessible) {
- if (uhci->rh_state == UHCI_RH_SUSPENDED) {
- dev_warn(uhci_dev(uhci), "HC isn't running!\n");
- rc = -ENODEV;
- }
- /* Otherwise the HC is dead */
- } else
+ if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) {
+ dev_warn(&hcd->self.root_hub->dev, "HC isn't running!\n");
+ rc = -ESHUTDOWN;
+ } else if (!uhci->dead)
wakeup_rh(uhci);
spin_unlock_irq(&uhci->lock);
return rc;
dev_dbg(uhci_dev(uhci), "%s\n", __FUNCTION__);
spin_lock_irq(&uhci->lock);
- if (uhci->hc_inaccessible) /* Dead or already suspended */
- goto done;
+ if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags) || uhci->dead)
+ goto done_okay; /* Already suspended or dead */
if (uhci->rh_state > UHCI_RH_SUSPENDED) {
dev_warn(uhci_dev(uhci), "Root hub isn't suspended!\n");
*/
pci_write_config_word(to_pci_dev(uhci_dev(uhci)), USBLEGSUP, 0);
mb();
- clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
- uhci->hc_inaccessible = 1;
hcd->poll_rh = 0;
/* FIXME: Enable non-PME# remote wakeup? */
+done_okay:
+ clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
done:
spin_unlock_irq(&uhci->lock);
return rc;
dev_dbg(uhci_dev(uhci), "%s\n", __FUNCTION__);
/* Since we aren't in D3 any more, it's safe to set this flag
- * even if the controller was dead. It might not even be dead
- * any more, if the firmware or quirks code has reset it.
+ * even if the controller was dead.
*/
set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
mb();
- if (uhci->rh_state == UHCI_RH_RESET) /* Dead */
- return 0;
spin_lock_irq(&uhci->lock);
/* FIXME: Disable non-PME# remote wakeup? */
- uhci->hc_inaccessible = 0;
-
- /* The BIOS may have changed the controller settings during a
- * system wakeup. Check it and reconfigure to avoid problems.
+ /* The firmware or a boot kernel may have changed the controller
+ * settings during a system wakeup. Check it and reconfigure
+ * to avoid problems.
*/
check_and_reset_hc(uhci);
+
+ /* If the controller was dead before, it's back alive now */
configure_hc(uhci);
if (uhci->rh_state == UHCI_RH_RESET) {
static int uhci_hcd_get_frame_number(struct usb_hcd *hcd)
{
struct uhci_hcd *uhci = hcd_to_uhci(hcd);
- unsigned long flags;
- int is_stopped;
- int frame_number;
+ unsigned frame_number;
+ unsigned delta;
/* Minimize latency by avoiding the spinlock */
- local_irq_save(flags);
- is_stopped = uhci->is_stopped;
- smp_rmb();
- frame_number = (is_stopped ? uhci->frame_number :
- inw(uhci->io_addr + USBFRNUM));
- local_irq_restore(flags);
- return frame_number;
+ frame_number = uhci->frame_number;
+ barrier();
+ delta = (inw(uhci->io_addr + USBFRNUM) - frame_number) &
+ (UHCI_NUMFRAMES - 1);
+ return frame_number + delta;
}
static const char hcd_name[] = "uhci_hcd";
.flags = HCD_USB11,
/* Basic lifecycle operations */
- .reset = uhci_reset,
+ .reset = uhci_init,
.start = uhci_start,
#ifdef CONFIG_PM
.suspend = uhci_suspend,
#define CAN_SCHEDULE_FRAMES 1000 /* how far in the future frames
* can be scheduled */
+/* When no queues need Full-Speed Bandwidth Reclamation,
+ * delay this long before turning FSBR off */
+#define FSBR_OFF_DELAY msecs_to_jiffies(10)
+
+/* If a queue hasn't advanced after this much time, assume it is stuck */
+#define QH_WAIT_TIMEOUT msecs_to_jiffies(200)
+
/*
* Queue Headers
__le32 element; /* Queue element (TD) pointer */
/* Software fields */
- dma_addr_t dma_handle;
-
struct list_head node; /* Node in the list of QHs */
struct usb_host_endpoint *hep; /* Endpoint information */
struct usb_device *udev;
struct list_head queue; /* Queue of urbps for this QH */
struct uhci_qh *skel; /* Skeleton for this QH */
struct uhci_td *dummy_td; /* Dummy TD to end the queue */
+ struct uhci_td *post_td; /* Last TD completed */
+ struct usb_iso_packet_descriptor *iso_packet_desc;
+ /* Next urb->iso_frame_desc entry */
+ unsigned long advance_jiffies; /* Time of last queue advance */
unsigned int unlink_frame; /* When the QH was unlinked */
+ unsigned int period; /* For Interrupt and Isochronous QHs */
+ unsigned int iso_frame; /* Frame # for iso_packet_desc */
+ int iso_status; /* Status for Isochronous URBs */
+
int state; /* QH_STATE_xxx; see above */
+ int type; /* Queue type (control, bulk, etc) */
+
+ dma_addr_t dma_handle;
unsigned int initial_toggle:1; /* Endpoint's current toggle value */
unsigned int needs_fixup:1; /* Must fix the TD toggle values */
- unsigned int is_stopped:1; /* Queue was stopped by an error */
+ unsigned int is_stopped:1; /* Queue was stopped by error/unlink */
+ unsigned int wait_expired:1; /* QH_WAIT_TIMEOUT has expired */
} __attribute__((aligned(16)));
/*
dma_addr_t dma_handle;
struct list_head list;
- struct list_head remove_list;
int frame; /* for iso: what frame? */
struct list_head fl_list;
#define skel_bulk_qh skelqh[12]
#define skel_term_qh skelqh[13]
-/*
- * Search tree for determining where <interval> fits in the skelqh[]
- * skeleton.
- *
- * An interrupt request should be placed into the slowest skelqh[]
- * which meets the interval/period/frequency requirement.
- * An interrupt request is allowed to be faster than <interval> but not slower.
- *
- * For a given <interval>, this function returns the appropriate/matching
- * skelqh[] index value.
- */
-static inline int __interval_to_skel(int interval)
-{
- if (interval < 16) {
- if (interval < 4) {
- if (interval < 2)
- return 9; /* int1 for 0-1 ms */
- return 8; /* int2 for 2-3 ms */
- }
- if (interval < 8)
- return 7; /* int4 for 4-7 ms */
- return 6; /* int8 for 8-15 ms */
- }
- if (interval < 64) {
- if (interval < 32)
- return 5; /* int16 for 16-31 ms */
- return 4; /* int32 for 32-63 ms */
- }
- if (interval < 128)
- return 3; /* int64 for 64-127 ms */
- return 2; /* int128 for 128-255 ms (Max.) */
-}
+/* Find the skelqh entry corresponding to an interval exponent */
+#define UHCI_SKEL_INDEX(exponent) (9 - exponent)
/*
__le32 *frame;
void **frame_cpu; /* CPU's frame list */
- int fsbr; /* Full-speed bandwidth reclamation */
- unsigned long fsbrtimeout; /* FSBR delay */
-
enum uhci_rh_state rh_state;
unsigned long auto_stop_time; /* When to AUTO_STOP */
unsigned int frame_number; /* As of last check */
unsigned int is_stopped;
#define UHCI_IS_STOPPED 9999 /* Larger than a frame # */
+ unsigned int last_iso_frame; /* Frame of last scan */
+ unsigned int cur_iso_frame; /* Frame for current scan */
unsigned int scan_in_progress:1; /* Schedule scan is running */
unsigned int need_rescan:1; /* Redo the schedule scan */
- unsigned int hc_inaccessible:1; /* HC is suspended or dead */
+ unsigned int dead:1; /* Controller has died */
unsigned int working_RD:1; /* Suspended root hub doesn't
need to be polled */
unsigned int is_initialized:1; /* Data structure is usable */
+ unsigned int fsbr_is_on:1; /* FSBR is turned on */
+ unsigned int fsbr_is_wanted:1; /* Does any URB want FSBR? */
+ unsigned int fsbr_expiring:1; /* FSBR is timing out */
+
+ struct timer_list fsbr_timer; /* For turning off FBSR */
/* Support for port suspend/resume/reset */
unsigned long port_c_suspend; /* Bit-arrays of ports */
unsigned long resuming_ports;
unsigned long ports_timeout; /* Time to stop signalling */
- /* List of TDs that are done, but waiting to be freed (race) */
- struct list_head td_remove_list;
- unsigned int td_remove_age; /* Age in frames */
-
struct list_head idle_qh_list; /* Where the idle QHs live */
int rh_numports; /* Number of root-hub ports */
#define uhci_dev(u) (uhci_to_hcd(u)->self.controller)
+/* Utility macro for comparing frame numbers */
+#define uhci_frame_before_eq(f1, f2) (0 <= (int) ((f2) - (f1)))
+
/*
* Private per-URB data
struct uhci_qh *qh; /* QH for this URB */
struct list_head td_list;
- unsigned fsbr : 1; /* URB turned on FSBR */
- unsigned short_transfer : 1; /* URB got a short transfer, no
- * need to rescan */
+ unsigned fsbr:1; /* URB wants FSBR */
};
spin_lock_irqsave(&uhci->lock, flags);
uhci_scan_schedule(uhci, NULL);
- if (uhci->hc_inaccessible)
+ if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags) || uhci->dead)
goto done;
- check_fsbr(uhci);
uhci_check_ports(uhci);
status = get_hub_status_data(uhci, buf);
u16 wPortChange, wPortStatus;
unsigned long flags;
- if (uhci->hc_inaccessible)
+ if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags) || uhci->dead)
return -ETIMEDOUT;
spin_lock_irqsave(&uhci->lock, flags);
* (C) Copyright 2000 Yggdrasil Computing, Inc. (port of new PCI interface
* support from usb-ohci.c by Adam Richter, adam@yggdrasil.com).
* (C) Copyright 1999 Gregory P. Smith (from usb-ohci.c)
- * (C) Copyright 2004-2005 Alan Stern, stern@rowland.harvard.edu
+ * (C) Copyright 2004-2006 Alan Stern, stern@rowland.harvard.edu
*/
-static void uhci_free_pending_tds(struct uhci_hcd *uhci);
/*
* Technically, updating td->status here is a race, but it's not really a
uhci->term_td->status &= ~cpu_to_le32(TD_CTRL_IOC);
}
+
+/*
+ * Full-Speed Bandwidth Reclamation (FSBR).
+ * We turn on FSBR whenever a queue that wants it is advancing,
+ * and leave it on for a short time thereafter.
+ */
+static void uhci_fsbr_on(struct uhci_hcd *uhci)
+{
+ uhci->fsbr_is_on = 1;
+ uhci->skel_term_qh->link = cpu_to_le32(
+ uhci->skel_fs_control_qh->dma_handle) | UHCI_PTR_QH;
+}
+
+static void uhci_fsbr_off(struct uhci_hcd *uhci)
+{
+ uhci->fsbr_is_on = 0;
+ uhci->skel_term_qh->link = UHCI_PTR_TERM;
+}
+
+static void uhci_add_fsbr(struct uhci_hcd *uhci, struct urb *urb)
+{
+ struct urb_priv *urbp = urb->hcpriv;
+
+ if (!(urb->transfer_flags & URB_NO_FSBR))
+ urbp->fsbr = 1;
+}
+
+static void uhci_urbp_wants_fsbr(struct uhci_hcd *uhci, struct urb_priv *urbp)
+{
+ if (urbp->fsbr) {
+ uhci->fsbr_is_wanted = 1;
+ if (!uhci->fsbr_is_on)
+ uhci_fsbr_on(uhci);
+ else if (uhci->fsbr_expiring) {
+ uhci->fsbr_expiring = 0;
+ del_timer(&uhci->fsbr_timer);
+ }
+ }
+}
+
+static void uhci_fsbr_timeout(unsigned long _uhci)
+{
+ struct uhci_hcd *uhci = (struct uhci_hcd *) _uhci;
+ unsigned long flags;
+
+ spin_lock_irqsave(&uhci->lock, flags);
+ if (uhci->fsbr_expiring) {
+ uhci->fsbr_expiring = 0;
+ uhci_fsbr_off(uhci);
+ }
+ spin_unlock_irqrestore(&uhci->lock, flags);
+}
+
+
static struct uhci_td *uhci_alloc_td(struct uhci_hcd *uhci)
{
dma_addr_t dma_handle;
td->frame = -1;
INIT_LIST_HEAD(&td->list);
- INIT_LIST_HEAD(&td->remove_list);
INIT_LIST_HEAD(&td->fl_list);
return td;
{
if (!list_empty(&td->list))
dev_warn(uhci_dev(uhci), "td %p still in list!\n", td);
- if (!list_empty(&td->remove_list))
- dev_warn(uhci_dev(uhci), "td %p still in remove_list!\n", td);
if (!list_empty(&td->fl_list))
dev_warn(uhci_dev(uhci), "td %p still in fl_list!\n", td);
td->buffer = cpu_to_le32(buffer);
}
+static void uhci_add_td_to_urbp(struct uhci_td *td, struct urb_priv *urbp)
+{
+ list_add_tail(&td->list, &urbp->td_list);
+}
+
+static void uhci_remove_td_from_urbp(struct uhci_td *td)
+{
+ list_del_init(&td->list);
+}
+
/*
* We insert Isochronous URBs directly into the frame list at the beginning
*/
td->frame = -1;
}
+static inline void uhci_remove_tds_from_frame(struct uhci_hcd *uhci,
+ unsigned int framenum)
+{
+ struct uhci_td *ftd, *ltd;
+
+ framenum &= (UHCI_NUMFRAMES - 1);
+
+ ftd = uhci->frame_cpu[framenum];
+ if (ftd) {
+ ltd = list_entry(ftd->fl_list.prev, struct uhci_td, fl_list);
+ uhci->frame[framenum] = ltd->link;
+ uhci->frame_cpu[framenum] = NULL;
+
+ while (!list_empty(&ftd->fl_list))
+ list_del_init(ftd->fl_list.prev);
+ }
+}
+
/*
* Remove all the TDs for an Isochronous URB from the frame list
*/
list_for_each_entry(td, &urbp->td_list, list)
uhci_remove_td_from_frame_list(uhci, td);
- wmb();
}
static struct uhci_qh *uhci_alloc_qh(struct uhci_hcd *uhci,
if (!qh)
return NULL;
+ memset(qh, 0, sizeof(*qh));
qh->dma_handle = dma_handle;
qh->element = UHCI_PTR_TERM;
qh->hep = hep;
qh->udev = udev;
hep->hcpriv = qh;
+ qh->type = hep->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
} else { /* Skeleton QH */
qh->state = QH_STATE_ACTIVE;
- qh->udev = NULL;
+ qh->type = -1;
}
return qh;
}
}
/*
- * When the currently executing URB is dequeued, save its current toggle value
+ * When a queue is stopped and a dequeued URB is given back, adjust
+ * the previous TD link (if the URB isn't first on the queue) or
+ * save its toggle value (if it is first and is currently executing).
+ *
+ * Returns 0 if the URB should not yet be given back, 1 otherwise.
*/
-static void uhci_save_toggle(struct uhci_qh *qh, struct urb *urb)
+static int uhci_cleanup_queue(struct uhci_hcd *uhci, struct uhci_qh *qh,
+ struct urb *urb)
{
- struct urb_priv *urbp = (struct urb_priv *) urb->hcpriv;
+ struct urb_priv *urbp = urb->hcpriv;
struct uhci_td *td;
+ int ret = 1;
+
+ /* Isochronous pipes don't use toggles and their TD link pointers
+ * get adjusted during uhci_urb_dequeue(). But since their queues
+ * cannot truly be stopped, we have to watch out for dequeues
+ * occurring after the nominal unlink frame. */
+ if (qh->type == USB_ENDPOINT_XFER_ISOC) {
+ ret = (uhci->frame_number + uhci->is_stopped !=
+ qh->unlink_frame);
+ goto done;
+ }
+
+ /* If the URB isn't first on its queue, adjust the link pointer
+ * of the last TD in the previous URB. The toggle doesn't need
+ * to be saved since this URB can't be executing yet. */
+ if (qh->queue.next != &urbp->node) {
+ struct urb_priv *purbp;
+ struct uhci_td *ptd;
+
+ purbp = list_entry(urbp->node.prev, struct urb_priv, node);
+ WARN_ON(list_empty(&purbp->td_list));
+ ptd = list_entry(purbp->td_list.prev, struct uhci_td,
+ list);
+ td = list_entry(urbp->td_list.prev, struct uhci_td,
+ list);
+ ptd->link = td->link;
+ goto done;
+ }
/* If the QH element pointer is UHCI_PTR_TERM then then currently
* executing URB has already been unlinked, so this one isn't it. */
- if (qh_element(qh) == UHCI_PTR_TERM ||
- qh->queue.next != &urbp->node)
- return;
+ if (qh_element(qh) == UHCI_PTR_TERM)
+ goto done;
qh->element = UHCI_PTR_TERM;
- /* Only bulk and interrupt pipes have to worry about toggles */
- if (!(usb_pipetype(urb->pipe) == PIPE_BULK ||
- usb_pipetype(urb->pipe) == PIPE_INTERRUPT))
- return;
+ /* Control pipes have to worry about toggles */
+ if (qh->type == USB_ENDPOINT_XFER_CONTROL)
+ goto done;
- /* Find the first active TD; that's the device's toggle state */
- list_for_each_entry(td, &urbp->td_list, list) {
- if (td_status(td) & TD_CTRL_ACTIVE) {
- qh->needs_fixup = 1;
- qh->initial_toggle = uhci_toggle(td_token(td));
- return;
- }
- }
+ /* Save the next toggle value */
+ WARN_ON(list_empty(&urbp->td_list));
+ td = list_entry(urbp->td_list.next, struct uhci_td, list);
+ qh->needs_fixup = 1;
+ qh->initial_toggle = uhci_toggle(td_token(td));
- WARN_ON(1);
+done:
+ return ret;
}
/*
qh->element = cpu_to_le32(td->dma_handle);
}
+ /* Treat the queue as if it has just advanced */
+ qh->wait_expired = 0;
+ qh->advance_jiffies = jiffies;
+
if (qh->state == QH_STATE_ACTIVE)
return;
qh->state = QH_STATE_ACTIVE;
list_move(&qh->node, &uhci->idle_qh_list);
qh->state = QH_STATE_IDLE;
+ /* Now that the QH is idle, its post_td isn't being used */
+ if (qh->post_td) {
+ uhci_free_td(uhci, qh->post_td);
+ qh->post_td = NULL;
+ }
+
/* If anyone is waiting for a QH to become idle, wake them up */
if (uhci->num_waiting)
wake_up_all(&uhci->waitqh);
return urbp;
}
-static void uhci_add_td_to_urb(struct urb *urb, struct uhci_td *td)
-{
- struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
-
- list_add_tail(&td->list, &urbp->td_list);
-}
-
-static void uhci_remove_td_from_urb(struct uhci_td *td)
-{
- if (list_empty(&td->list))
- return;
-
- list_del_init(&td->list);
-}
-
static void uhci_free_urb_priv(struct uhci_hcd *uhci,
struct urb_priv *urbp)
{
dev_warn(uhci_dev(uhci), "urb %p still on QH's list!\n",
urbp->urb);
- uhci_get_current_frame_number(uhci);
- if (uhci->frame_number + uhci->is_stopped != uhci->td_remove_age) {
- uhci_free_pending_tds(uhci);
- uhci->td_remove_age = uhci->frame_number;
- }
-
- /* Check to see if the remove list is empty. Set the IOC bit */
- /* to force an interrupt so we can remove the TDs. */
- if (list_empty(&uhci->td_remove_list))
- uhci_set_next_interrupt(uhci);
-
list_for_each_entry_safe(td, tmp, &urbp->td_list, list) {
- uhci_remove_td_from_urb(td);
- list_add(&td->remove_list, &uhci->td_remove_list);
+ uhci_remove_td_from_urbp(td);
+ uhci_free_td(uhci, td);
}
urbp->urb->hcpriv = NULL;
kmem_cache_free(uhci_up_cachep, urbp);
}
-static void uhci_inc_fsbr(struct uhci_hcd *uhci, struct urb *urb)
-{
- struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
-
- if ((!(urb->transfer_flags & URB_NO_FSBR)) && !urbp->fsbr) {
- urbp->fsbr = 1;
- if (!uhci->fsbr++ && !uhci->fsbrtimeout)
- uhci->skel_term_qh->link = cpu_to_le32(uhci->skel_fs_control_qh->dma_handle) | UHCI_PTR_QH;
- }
-}
-
-static void uhci_dec_fsbr(struct uhci_hcd *uhci, struct urb *urb)
-{
- struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
-
- if ((!(urb->transfer_flags & URB_NO_FSBR)) && urbp->fsbr) {
- urbp->fsbr = 0;
- if (!--uhci->fsbr)
- uhci->fsbrtimeout = jiffies + FSBR_DELAY;
- }
-}
-
/*
* Map status to standard result codes
*
return -ENOSR;
if (status & TD_CTRL_STALLED) /* Stalled */
return -EPIPE;
- WARN_ON(status & TD_CTRL_ACTIVE); /* Active */
return 0;
}
int len = urb->transfer_buffer_length;
dma_addr_t data = urb->transfer_dma;
__le32 *plink;
+ struct urb_priv *urbp = urb->hcpriv;
/* The "pipe" thing contains the destination in bits 8--18 */
destination = (urb->pipe & PIPE_DEVEP_MASK) | USB_PID_SETUP;
* Build the TD for the control request setup packet
*/
td = qh->dummy_td;
- uhci_add_td_to_urb(urb, td);
+ uhci_add_td_to_urbp(td, urbp);
uhci_fill_td(td, status, destination | uhci_explen(8),
urb->setup_dma);
plink = &td->link;
/* Alternate Data0/1 (start with Data1) */
destination ^= TD_TOKEN_TOGGLE;
- uhci_add_td_to_urb(urb, td);
+ uhci_add_td_to_urbp(td, urbp);
uhci_fill_td(td, status, destination | uhci_explen(pktsze),
data);
plink = &td->link;
status &= ~TD_CTRL_SPD;
- uhci_add_td_to_urb(urb, td);
+ uhci_add_td_to_urbp(td, urbp);
uhci_fill_td(td, status | TD_CTRL_IOC,
destination | uhci_explen(0), 0);
plink = &td->link;
qh->skel = uhci->skel_ls_control_qh;
else {
qh->skel = uhci->skel_fs_control_qh;
- uhci_inc_fsbr(uhci, urb);
+ uhci_add_fsbr(uhci, urb);
}
+
+ urb->actual_length = -8; /* Account for the SETUP packet */
return 0;
nomem:
/* Remove the dummy TD from the td_list so it doesn't get freed */
- uhci_remove_td_from_urb(qh->dummy_td);
+ uhci_remove_td_from_urbp(qh->dummy_td);
return -ENOMEM;
}
-/*
- * If control-IN transfer was short, the status packet wasn't sent.
- * This routine changes the element pointer in the QH to point at the
- * status TD. It's safe to do this even while the QH is live, because
- * the hardware only updates the element pointer following a successful
- * transfer. The inactive TD for the short packet won't cause an update,
- * so the pointer won't get overwritten. The next time the controller
- * sees this QH, it will send the status packet.
- */
-static int usb_control_retrigger_status(struct uhci_hcd *uhci, struct urb *urb)
-{
- struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
- struct uhci_td *td;
-
- urbp->short_transfer = 1;
-
- td = list_entry(urbp->td_list.prev, struct uhci_td, list);
- urbp->qh->element = cpu_to_le32(td->dma_handle);
-
- return -EINPROGRESS;
-}
-
-
-static int uhci_result_control(struct uhci_hcd *uhci, struct urb *urb)
-{
- struct list_head *tmp, *head;
- struct urb_priv *urbp = urb->hcpriv;
- struct uhci_td *td;
- unsigned int status;
- int ret = 0;
-
- head = &urbp->td_list;
- if (urbp->short_transfer) {
- tmp = head->prev;
- goto status_stage;
- }
-
- urb->actual_length = 0;
-
- tmp = head->next;
- td = list_entry(tmp, struct uhci_td, list);
-
- /* The first TD is the SETUP stage, check the status, but skip */
- /* the count */
- status = uhci_status_bits(td_status(td));
- if (status & TD_CTRL_ACTIVE)
- return -EINPROGRESS;
-
- if (status)
- goto td_error;
-
- /* The rest of the TDs (but the last) are data */
- tmp = tmp->next;
- while (tmp != head && tmp->next != head) {
- unsigned int ctrlstat;
-
- td = list_entry(tmp, struct uhci_td, list);
- tmp = tmp->next;
-
- ctrlstat = td_status(td);
- status = uhci_status_bits(ctrlstat);
- if (status & TD_CTRL_ACTIVE)
- return -EINPROGRESS;
-
- urb->actual_length += uhci_actual_length(ctrlstat);
-
- if (status)
- goto td_error;
-
- /* Check to see if we received a short packet */
- if (uhci_actual_length(ctrlstat) <
- uhci_expected_length(td_token(td))) {
- if (urb->transfer_flags & URB_SHORT_NOT_OK) {
- ret = -EREMOTEIO;
- goto err;
- }
-
- return usb_control_retrigger_status(uhci, urb);
- }
- }
-
-status_stage:
- td = list_entry(tmp, struct uhci_td, list);
-
- /* Control status stage */
- status = td_status(td);
-
-#ifdef I_HAVE_BUGGY_APC_BACKUPS
- /* APC BackUPS Pro kludge */
- /* It tries to send all of the descriptor instead of the amount */
- /* we requested */
- if (status & TD_CTRL_IOC && /* IOC is masked out by uhci_status_bits */
- status & TD_CTRL_ACTIVE &&
- status & TD_CTRL_NAK)
- return 0;
-#endif
-
- status = uhci_status_bits(status);
- if (status & TD_CTRL_ACTIVE)
- return -EINPROGRESS;
-
- if (status)
- goto td_error;
-
- return 0;
-
-td_error:
- ret = uhci_map_status(status, uhci_packetout(td_token(td)));
-
-err:
- if ((debug == 1 && ret != -EPIPE) || debug > 1) {
- /* Some debugging code */
- dev_dbg(uhci_dev(uhci), "%s: failed with status %x\n",
- __FUNCTION__, status);
-
- if (errbuf) {
- /* Print the chain for debugging purposes */
- uhci_show_qh(urbp->qh, errbuf, ERRBUF_LEN, 0);
- lprintk(errbuf);
- }
- }
-
- /* Note that the queue has stopped */
- urbp->qh->element = UHCI_PTR_TERM;
- urbp->qh->is_stopped = 1;
- return ret;
-}
-
/*
* Common submit for bulk and interrupt
*/
int len = urb->transfer_buffer_length;
dma_addr_t data = urb->transfer_dma;
__le32 *plink;
+ struct urb_priv *urbp = urb->hcpriv;
unsigned int toggle;
if (len < 0)
goto nomem;
*plink = cpu_to_le32(td->dma_handle);
}
- uhci_add_td_to_urb(urb, td);
+ uhci_add_td_to_urbp(td, urbp);
uhci_fill_td(td, status,
destination | uhci_explen(pktsze) |
(toggle << TD_TOKEN_TOGGLE_SHIFT),
goto nomem;
*plink = cpu_to_le32(td->dma_handle);
- uhci_add_td_to_urb(urb, td);
+ uhci_add_td_to_urbp(td, urbp);
uhci_fill_td(td, status,
destination | uhci_explen(0) |
(toggle << TD_TOKEN_TOGGLE_SHIFT),
wmb();
qh->dummy_td->status |= __constant_cpu_to_le32(TD_CTRL_ACTIVE);
qh->dummy_td = td;
+ qh->period = urb->interval;
usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe),
usb_pipeout(urb->pipe), toggle);
nomem:
/* Remove the dummy TD from the td_list so it doesn't get freed */
- uhci_remove_td_from_urb(qh->dummy_td);
+ uhci_remove_td_from_urbp(qh->dummy_td);
return -ENOMEM;
}
-/*
- * Common result for bulk and interrupt
- */
-static int uhci_result_common(struct uhci_hcd *uhci, struct urb *urb)
-{
- struct urb_priv *urbp = urb->hcpriv;
- struct uhci_td *td;
- unsigned int status = 0;
- int ret = 0;
-
- urb->actual_length = 0;
-
- list_for_each_entry(td, &urbp->td_list, list) {
- unsigned int ctrlstat = td_status(td);
-
- status = uhci_status_bits(ctrlstat);
- if (status & TD_CTRL_ACTIVE)
- return -EINPROGRESS;
-
- urb->actual_length += uhci_actual_length(ctrlstat);
-
- if (status)
- goto td_error;
-
- if (uhci_actual_length(ctrlstat) <
- uhci_expected_length(td_token(td))) {
- if (urb->transfer_flags & URB_SHORT_NOT_OK) {
- ret = -EREMOTEIO;
- goto err;
- }
-
- /*
- * This URB stopped short of its end. We have to
- * fix up the toggles of the following URBs on the
- * queue and restart the queue.
- *
- * Do this only the first time we encounter the
- * short URB.
- */
- if (!urbp->short_transfer) {
- urbp->short_transfer = 1;
- urbp->qh->initial_toggle =
- uhci_toggle(td_token(td)) ^ 1;
- uhci_fixup_toggles(urbp->qh, 1);
-
- td = list_entry(urbp->td_list.prev,
- struct uhci_td, list);
- urbp->qh->element = td->link;
- }
- break;
- }
- }
-
- return 0;
-
-td_error:
- ret = uhci_map_status(status, uhci_packetout(td_token(td)));
-
- if ((debug == 1 && ret != -EPIPE) || debug > 1) {
- /* Some debugging code */
- dev_dbg(uhci_dev(uhci), "%s: failed with status %x\n",
- __FUNCTION__, status);
-
- if (debug > 1 && errbuf) {
- /* Print the chain for debugging purposes */
- uhci_show_qh(urbp->qh, errbuf, ERRBUF_LEN, 0);
- lprintk(errbuf);
- }
- }
-err:
-
- /* Note that the queue has stopped and save the next toggle value */
- urbp->qh->element = UHCI_PTR_TERM;
- urbp->qh->is_stopped = 1;
- urbp->qh->needs_fixup = 1;
- urbp->qh->initial_toggle = uhci_toggle(td_token(td)) ^
- (ret == -EREMOTEIO);
- return ret;
-}
-
static inline int uhci_submit_bulk(struct uhci_hcd *uhci, struct urb *urb,
struct uhci_qh *qh)
{
qh->skel = uhci->skel_bulk_qh;
ret = uhci_submit_common(uhci, urb, qh);
if (ret == 0)
- uhci_inc_fsbr(uhci, urb);
+ uhci_add_fsbr(uhci, urb);
return ret;
}
-static inline int uhci_submit_interrupt(struct uhci_hcd *uhci, struct urb *urb,
+static int uhci_submit_interrupt(struct uhci_hcd *uhci, struct urb *urb,
struct uhci_qh *qh)
{
+ int exponent;
+
/* USB 1.1 interrupt transfers only involve one packet per interval.
* Drivers can submit URBs of any length, but longer ones will need
* multiple intervals to complete.
*/
- qh->skel = uhci->skelqh[__interval_to_skel(urb->interval)];
+
+ /* Figure out which power-of-two queue to use */
+ for (exponent = 7; exponent >= 0; --exponent) {
+ if ((1 << exponent) <= urb->interval)
+ break;
+ }
+ if (exponent < 0)
+ return -EINVAL;
+ urb->interval = 1 << exponent;
+
+ if (qh->period == 0)
+ qh->skel = uhci->skelqh[UHCI_SKEL_INDEX(exponent)];
+ else if (qh->period != urb->interval)
+ return -EINVAL; /* Can't change the period */
+
return uhci_submit_common(uhci, urb, qh);
}
+/*
+ * Fix up the data structures following a short transfer
+ */
+static int uhci_fixup_short_transfer(struct uhci_hcd *uhci,
+ struct uhci_qh *qh, struct urb_priv *urbp)
+{
+ struct uhci_td *td;
+ struct list_head *tmp;
+ int ret;
+
+ td = list_entry(urbp->td_list.prev, struct uhci_td, list);
+ if (qh->type == USB_ENDPOINT_XFER_CONTROL) {
+
+ /* When a control transfer is short, we have to restart
+ * the queue at the status stage transaction, which is
+ * the last TD. */
+ WARN_ON(list_empty(&urbp->td_list));
+ qh->element = cpu_to_le32(td->dma_handle);
+ tmp = td->list.prev;
+ ret = -EINPROGRESS;
+
+ } else {
+
+ /* When a bulk/interrupt transfer is short, we have to
+ * fix up the toggles of the following URBs on the queue
+ * before restarting the queue at the next URB. */
+ qh->initial_toggle = uhci_toggle(td_token(qh->post_td)) ^ 1;
+ uhci_fixup_toggles(qh, 1);
+
+ if (list_empty(&urbp->td_list))
+ td = qh->post_td;
+ qh->element = td->link;
+ tmp = urbp->td_list.prev;
+ ret = 0;
+ }
+
+ /* Remove all the TDs we skipped over, from tmp back to the start */
+ while (tmp != &urbp->td_list) {
+ td = list_entry(tmp, struct uhci_td, list);
+ tmp = tmp->prev;
+
+ uhci_remove_td_from_urbp(td);
+ uhci_free_td(uhci, td);
+ }
+ return ret;
+}
+
+/*
+ * Common result for control, bulk, and interrupt
+ */
+static int uhci_result_common(struct uhci_hcd *uhci, struct urb *urb)
+{
+ struct urb_priv *urbp = urb->hcpriv;
+ struct uhci_qh *qh = urbp->qh;
+ struct uhci_td *td, *tmp;
+ unsigned status;
+ int ret = 0;
+
+ list_for_each_entry_safe(td, tmp, &urbp->td_list, list) {
+ unsigned int ctrlstat;
+ int len;
+
+ ctrlstat = td_status(td);
+ status = uhci_status_bits(ctrlstat);
+ if (status & TD_CTRL_ACTIVE)
+ return -EINPROGRESS;
+
+ len = uhci_actual_length(ctrlstat);
+ urb->actual_length += len;
+
+ if (status) {
+ ret = uhci_map_status(status,
+ uhci_packetout(td_token(td)));
+ if ((debug == 1 && ret != -EPIPE) || debug > 1) {
+ /* Some debugging code */
+ dev_dbg(&urb->dev->dev,
+ "%s: failed with status %x\n",
+ __FUNCTION__, status);
+
+ if (debug > 1 && errbuf) {
+ /* Print the chain for debugging */
+ uhci_show_qh(urbp->qh, errbuf,
+ ERRBUF_LEN, 0);
+ lprintk(errbuf);
+ }
+ }
+
+ } else if (len < uhci_expected_length(td_token(td))) {
+
+ /* We received a short packet */
+ if (urb->transfer_flags & URB_SHORT_NOT_OK)
+ ret = -EREMOTEIO;
+ else if (ctrlstat & TD_CTRL_SPD)
+ ret = 1;
+ }
+
+ uhci_remove_td_from_urbp(td);
+ if (qh->post_td)
+ uhci_free_td(uhci, qh->post_td);
+ qh->post_td = td;
+
+ if (ret != 0)
+ goto err;
+ }
+ return ret;
+
+err:
+ if (ret < 0) {
+ /* In case a control transfer gets an error
+ * during the setup stage */
+ urb->actual_length = max(urb->actual_length, 0);
+
+ /* Note that the queue has stopped and save
+ * the next toggle value */
+ qh->element = UHCI_PTR_TERM;
+ qh->is_stopped = 1;
+ qh->needs_fixup = (qh->type != USB_ENDPOINT_XFER_CONTROL);
+ qh->initial_toggle = uhci_toggle(td_token(td)) ^
+ (ret == -EREMOTEIO);
+
+ } else /* Short packet received */
+ ret = uhci_fixup_short_transfer(uhci, qh, urbp);
+ return ret;
+}
+
/*
* Isochronous transfers
*/
unsigned long destination, status;
struct urb_priv *urbp = (struct urb_priv *) urb->hcpriv;
- if (urb->number_of_packets > 900) /* 900? Why? */
+ /* Values must not be too big (could overflow below) */
+ if (urb->interval >= UHCI_NUMFRAMES ||
+ urb->number_of_packets >= UHCI_NUMFRAMES)
return -EFBIG;
- status = TD_CTRL_ACTIVE | TD_CTRL_IOS;
- destination = (urb->pipe & PIPE_DEVEP_MASK) | usb_packetid(urb->pipe);
-
- /* Figure out the starting frame number */
- if (urb->transfer_flags & URB_ISO_ASAP) {
- if (list_empty(&qh->queue)) {
+ /* Check the period and figure out the starting frame number */
+ if (qh->period == 0) {
+ if (urb->transfer_flags & URB_ISO_ASAP) {
uhci_get_current_frame_number(uhci);
- urb->start_frame = (uhci->frame_number + 10);
+ urb->start_frame = uhci->frame_number + 10;
+ } else {
+ i = urb->start_frame - uhci->last_iso_frame;
+ if (i <= 0 || i >= UHCI_NUMFRAMES)
+ return -EINVAL;
+ }
+ } else if (qh->period != urb->interval) {
+ return -EINVAL; /* Can't change the period */
- } else { /* Go right after the last one */
- struct urb *last_urb;
+ } else { /* Pick up where the last URB leaves off */
+ if (list_empty(&qh->queue)) {
+ frame = qh->iso_frame;
+ } else {
+ struct urb *lurb;
- last_urb = list_entry(qh->queue.prev,
+ lurb = list_entry(qh->queue.prev,
struct urb_priv, node)->urb;
- urb->start_frame = (last_urb->start_frame +
- last_urb->number_of_packets *
- last_urb->interval);
+ frame = lurb->start_frame +
+ lurb->number_of_packets *
+ lurb->interval;
}
- } else {
- /* FIXME: Sanity check */
+ if (urb->transfer_flags & URB_ISO_ASAP)
+ urb->start_frame = frame;
+ else if (urb->start_frame != frame)
+ return -EINVAL;
}
- urb->start_frame &= (UHCI_NUMFRAMES - 1);
+
+ /* Make sure we won't have to go too far into the future */
+ if (uhci_frame_before_eq(uhci->last_iso_frame + UHCI_NUMFRAMES,
+ urb->start_frame + urb->number_of_packets *
+ urb->interval))
+ return -EFBIG;
+
+ status = TD_CTRL_ACTIVE | TD_CTRL_IOS;
+ destination = (urb->pipe & PIPE_DEVEP_MASK) | usb_packetid(urb->pipe);
for (i = 0; i < urb->number_of_packets; i++) {
td = uhci_alloc_td(uhci);
if (!td)
return -ENOMEM;
- uhci_add_td_to_urb(urb, td);
+ uhci_add_td_to_urbp(td, urbp);
uhci_fill_td(td, status, destination |
uhci_explen(urb->iso_frame_desc[i].length),
urb->transfer_dma +
td->status |= __constant_cpu_to_le32(TD_CTRL_IOC);
qh->skel = uhci->skel_iso_qh;
+ qh->period = urb->interval;
/* Add the TDs to the frame list */
frame = urb->start_frame;
list_for_each_entry(td, &urbp->td_list, list) {
uhci_insert_td_in_frame_list(uhci, td, frame);
- frame += urb->interval;
+ frame += qh->period;
+ }
+
+ if (list_empty(&qh->queue)) {
+ qh->iso_packet_desc = &urb->iso_frame_desc[0];
+ qh->iso_frame = urb->start_frame;
+ qh->iso_status = 0;
}
return 0;
static int uhci_result_isochronous(struct uhci_hcd *uhci, struct urb *urb)
{
- struct uhci_td *td;
- struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
- int status;
- int i, ret = 0;
-
- urb->actual_length = urb->error_count = 0;
+ struct uhci_td *td, *tmp;
+ struct urb_priv *urbp = urb->hcpriv;
+ struct uhci_qh *qh = urbp->qh;
- i = 0;
- list_for_each_entry(td, &urbp->td_list, list) {
+ list_for_each_entry_safe(td, tmp, &urbp->td_list, list) {
+ unsigned int ctrlstat;
+ int status;
int actlength;
- unsigned int ctrlstat = td_status(td);
- if (ctrlstat & TD_CTRL_ACTIVE)
+ if (uhci_frame_before_eq(uhci->cur_iso_frame, qh->iso_frame))
return -EINPROGRESS;
- actlength = uhci_actual_length(ctrlstat);
- urb->iso_frame_desc[i].actual_length = actlength;
- urb->actual_length += actlength;
+ uhci_remove_tds_from_frame(uhci, qh->iso_frame);
+
+ ctrlstat = td_status(td);
+ if (ctrlstat & TD_CTRL_ACTIVE) {
+ status = -EXDEV; /* TD was added too late? */
+ } else {
+ status = uhci_map_status(uhci_status_bits(ctrlstat),
+ usb_pipeout(urb->pipe));
+ actlength = uhci_actual_length(ctrlstat);
+
+ urb->actual_length += actlength;
+ qh->iso_packet_desc->actual_length = actlength;
+ qh->iso_packet_desc->status = status;
+ }
- status = uhci_map_status(uhci_status_bits(ctrlstat),
- usb_pipeout(urb->pipe));
- urb->iso_frame_desc[i].status = status;
if (status) {
urb->error_count++;
- ret = status;
+ qh->iso_status = status;
}
- i++;
+ uhci_remove_td_from_urbp(td);
+ uhci_free_td(uhci, td);
+ qh->iso_frame += qh->period;
+ ++qh->iso_packet_desc;
}
-
- return ret;
+ return qh->iso_status;
}
static int uhci_urb_enqueue(struct usb_hcd *hcd,
}
urbp->qh = qh;
- switch (usb_pipetype(urb->pipe)) {
- case PIPE_CONTROL:
+ switch (qh->type) {
+ case USB_ENDPOINT_XFER_CONTROL:
ret = uhci_submit_control(uhci, urb, qh);
break;
- case PIPE_BULK:
+ case USB_ENDPOINT_XFER_BULK:
ret = uhci_submit_bulk(uhci, urb, qh);
break;
- case PIPE_INTERRUPT:
+ case USB_ENDPOINT_XFER_INT:
if (list_empty(&qh->queue)) {
bustime = usb_check_bandwidth(urb->dev, urb);
if (bustime < 0)
ret = uhci_submit_interrupt(uhci, urb, qh);
}
break;
- case PIPE_ISOCHRONOUS:
+ case USB_ENDPOINT_XFER_ISOC:
+ urb->error_count = 0;
bustime = usb_check_bandwidth(urb->dev, urb);
if (bustime < 0) {
ret = bustime;
/* If the new URB is the first and only one on this QH then either
* the QH is new and idle or else it's unlinked and waiting to
- * become idle, so we can activate it right away. */
- if (qh->queue.next == &urbp->node)
+ * become idle, so we can activate it right away. But only if the
+ * queue isn't stopped. */
+ if (qh->queue.next == &urbp->node && !qh->is_stopped) {
uhci_activate_qh(uhci, qh);
+ uhci_urbp_wants_fsbr(uhci, urbp);
+ }
goto done;
err_submit_failed:
struct uhci_hcd *uhci = hcd_to_uhci(hcd);
unsigned long flags;
struct urb_priv *urbp;
+ struct uhci_qh *qh;
spin_lock_irqsave(&uhci->lock, flags);
urbp = urb->hcpriv;
if (!urbp) /* URB was never linked! */
goto done;
+ qh = urbp->qh;
/* Remove Isochronous TDs from the frame list ASAP */
- if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS)
+ if (qh->type == USB_ENDPOINT_XFER_ISOC) {
uhci_unlink_isochronous_tds(uhci, urb);
- uhci_unlink_qh(uhci, urbp->qh);
+ mb();
+
+ /* If the URB has already started, update the QH unlink time */
+ uhci_get_current_frame_number(uhci);
+ if (uhci_frame_before_eq(urb->start_frame, uhci->frame_number))
+ qh->unlink_frame = uhci->frame_number;
+ }
+
+ uhci_unlink_qh(uhci, qh);
done:
spin_unlock_irqrestore(&uhci->lock, flags);
{
struct urb_priv *urbp = (struct urb_priv *) urb->hcpriv;
- /* Isochronous TDs get unlinked directly from the frame list */
- if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS)
- uhci_unlink_isochronous_tds(uhci, urb);
-
- /* If the URB isn't first on its queue, adjust the link pointer
- * of the last TD in the previous URB. */
- else if (qh->queue.next != &urbp->node) {
- struct urb_priv *purbp;
- struct uhci_td *ptd, *ltd;
-
- purbp = list_entry(urbp->node.prev, struct urb_priv, node);
- ptd = list_entry(purbp->td_list.prev, struct uhci_td,
- list);
- ltd = list_entry(urbp->td_list.prev, struct uhci_td,
- list);
- ptd->link = ltd->link;
+ /* When giving back the first URB in an Isochronous queue,
+ * reinitialize the QH's iso-related members for the next URB. */
+ if (qh->type == USB_ENDPOINT_XFER_ISOC &&
+ urbp->node.prev == &qh->queue &&
+ urbp->node.next != &qh->queue) {
+ struct urb *nurb = list_entry(urbp->node.next,
+ struct urb_priv, node)->urb;
+
+ qh->iso_packet_desc = &nurb->iso_frame_desc[0];
+ qh->iso_frame = nurb->start_frame;
+ qh->iso_status = 0;
}
/* Take the URB off the QH's queue. If the queue is now empty,
qh->needs_fixup = 0;
}
- uhci_dec_fsbr(uhci, urb); /* Safe since it checks */
uhci_free_urb_priv(uhci, urbp);
- switch (usb_pipetype(urb->pipe)) {
- case PIPE_ISOCHRONOUS:
+ switch (qh->type) {
+ case USB_ENDPOINT_XFER_ISOC:
/* Release bandwidth for Interrupt or Isoc. transfers */
if (urb->bandwidth)
usb_release_bandwidth(urb->dev, urb, 1);
break;
- case PIPE_INTERRUPT:
+ case USB_ENDPOINT_XFER_INT:
/* Release bandwidth for Interrupt or Isoc. transfers */
/* Make sure we don't release if we have a queued URB */
if (list_empty(&qh->queue) && urb->bandwidth)
uhci_unlink_qh(uhci, qh);
/* Bandwidth stuff not yet implemented */
+ qh->period = 0;
}
}
urbp = list_entry(qh->queue.next, struct urb_priv, node);
urb = urbp->urb;
- switch (usb_pipetype(urb->pipe)) {
- case PIPE_CONTROL:
- status = uhci_result_control(uhci, urb);
- break;
- case PIPE_ISOCHRONOUS:
+ if (qh->type == USB_ENDPOINT_XFER_ISOC)
status = uhci_result_isochronous(uhci, urb);
- break;
- default: /* PIPE_BULK or PIPE_INTERRUPT */
+ else
status = uhci_result_common(uhci, urb);
- break;
- }
if (status == -EINPROGRESS)
break;
if (urb->status == -EINPROGRESS) /* Not dequeued */
urb->status = status;
else
- status = -ECONNRESET;
+ status = ECONNRESET; /* Not -ECONNRESET */
spin_unlock(&urb->lock);
/* Dequeued but completed URBs can't be given back unless
* the QH is stopped or has finished unlinking. */
- if (status == -ECONNRESET &&
- !(qh->is_stopped || QH_FINISHED_UNLINKING(qh)))
- return;
+ if (status == ECONNRESET) {
+ if (QH_FINISHED_UNLINKING(qh))
+ qh->is_stopped = 1;
+ else if (!qh->is_stopped)
+ return;
+ }
uhci_giveback_urb(uhci, qh, urb, regs);
- if (qh->is_stopped)
+ if (status < 0)
break;
}
/* If the QH is neither stopped nor finished unlinking (normal case),
* our work here is done. */
- restart:
- if (!(qh->is_stopped || QH_FINISHED_UNLINKING(qh)))
+ if (QH_FINISHED_UNLINKING(qh))
+ qh->is_stopped = 1;
+ else if (!qh->is_stopped)
return;
/* Otherwise give back each of the dequeued URBs */
+restart:
list_for_each_entry(urbp, &qh->queue, node) {
urb = urbp->urb;
if (urb->status != -EINPROGRESS) {
- uhci_save_toggle(qh, urb);
+
+ /* Fix up the TD links and save the toggles for
+ * non-Isochronous queues. For Isochronous queues,
+ * test for too-recent dequeues. */
+ if (!uhci_cleanup_queue(uhci, qh, urb)) {
+ qh->is_stopped = 0;
+ return;
+ }
uhci_giveback_urb(uhci, qh, urb, regs);
goto restart;
}
if (!list_empty(&qh->queue)) {
if (qh->needs_fixup)
uhci_fixup_toggles(qh, 0);
+
+ /* If the first URB on the queue wants FSBR but its time
+ * limit has expired, set the next TD to interrupt on
+ * completion before reactivating the QH. */
+ urbp = list_entry(qh->queue.next, struct urb_priv, node);
+ if (urbp->fsbr && qh->wait_expired) {
+ struct uhci_td *td = list_entry(urbp->td_list.next,
+ struct uhci_td, list);
+
+ td->status |= __cpu_to_le32(TD_CTRL_IOC);
+ }
+
uhci_activate_qh(uhci, qh);
}
uhci_make_qh_idle(uhci, qh);
}
-static void uhci_free_pending_tds(struct uhci_hcd *uhci)
+/*
+ * Check for queues that have made some forward progress.
+ * Returns 0 if the queue is not Isochronous, is ACTIVE, and
+ * has not advanced since last examined; 1 otherwise.
+ *
+ * Early Intel controllers have a bug which causes qh->element sometimes
+ * not to advance when a TD completes successfully. The queue remains
+ * stuck on the inactive completed TD. We detect such cases and advance
+ * the element pointer by hand.
+ */
+static int uhci_advance_check(struct uhci_hcd *uhci, struct uhci_qh *qh)
{
- struct uhci_td *td, *tmp;
+ struct urb_priv *urbp = NULL;
+ struct uhci_td *td;
+ int ret = 1;
+ unsigned status;
- list_for_each_entry_safe(td, tmp, &uhci->td_remove_list, remove_list) {
- list_del_init(&td->remove_list);
+ if (qh->type == USB_ENDPOINT_XFER_ISOC)
+ goto done;
- uhci_free_td(uhci, td);
+ /* Treat an UNLINKING queue as though it hasn't advanced.
+ * This is okay because reactivation will treat it as though
+ * it has advanced, and if it is going to become IDLE then
+ * this doesn't matter anyway. Furthermore it's possible
+ * for an UNLINKING queue not to have any URBs at all, or
+ * for its first URB not to have any TDs (if it was dequeued
+ * just as it completed). So it's not easy in any case to
+ * test whether such queues have advanced. */
+ if (qh->state != QH_STATE_ACTIVE) {
+ urbp = NULL;
+ status = 0;
+
+ } else {
+ urbp = list_entry(qh->queue.next, struct urb_priv, node);
+ td = list_entry(urbp->td_list.next, struct uhci_td, list);
+ status = td_status(td);
+ if (!(status & TD_CTRL_ACTIVE)) {
+
+ /* We're okay, the queue has advanced */
+ qh->wait_expired = 0;
+ qh->advance_jiffies = jiffies;
+ goto done;
+ }
+ ret = 0;
+ }
+
+ /* The queue hasn't advanced; check for timeout */
+ if (qh->wait_expired)
+ goto done;
+
+ if (time_after(jiffies, qh->advance_jiffies + QH_WAIT_TIMEOUT)) {
+
+ /* Detect the Intel bug and work around it */
+ if (qh->post_td && qh_element(qh) ==
+ cpu_to_le32(qh->post_td->dma_handle)) {
+ qh->element = qh->post_td->link;
+ qh->advance_jiffies = jiffies;
+ ret = 1;
+ goto done;
+ }
+
+ qh->wait_expired = 1;
+
+ /* If the current URB wants FSBR, unlink it temporarily
+ * so that we can safely set the next TD to interrupt on
+ * completion. That way we'll know as soon as the queue
+ * starts moving again. */
+ if (urbp && urbp->fsbr && !(status & TD_CTRL_IOC))
+ uhci_unlink_qh(uhci, qh);
+
+ } else {
+ /* Unmoving but not-yet-expired queues keep FSBR alive */
+ if (urbp)
+ uhci_urbp_wants_fsbr(uhci, urbp);
}
+
+done:
+ return ret;
}
/*
return;
}
uhci->scan_in_progress = 1;
- rescan:
+rescan:
uhci->need_rescan = 0;
+ uhci->fsbr_is_wanted = 0;
uhci_clear_next_interrupt(uhci);
uhci_get_current_frame_number(uhci);
-
- if (uhci->frame_number + uhci->is_stopped != uhci->td_remove_age)
- uhci_free_pending_tds(uhci);
+ uhci->cur_iso_frame = uhci->frame_number;
/* Go through all the QH queues and process the URBs in each one */
for (i = 0; i < UHCI_NUM_SKELQH - 1; ++i) {
while ((qh = uhci->next_qh) != uhci->skelqh[i]) {
uhci->next_qh = list_entry(qh->node.next,
struct uhci_qh, node);
- uhci_scan_qh(uhci, qh, regs);
+
+ if (uhci_advance_check(uhci, qh)) {
+ uhci_scan_qh(uhci, qh, regs);
+ if (qh->state == QH_STATE_ACTIVE) {
+ uhci_urbp_wants_fsbr(uhci,
+ list_entry(qh->queue.next, struct urb_priv, node));
+ }
+ }
}
}
+ uhci->last_iso_frame = uhci->cur_iso_frame;
if (uhci->need_rescan)
goto rescan;
uhci->scan_in_progress = 0;
- /* If the controller is stopped, we can finish these off right now */
- if (uhci->is_stopped)
- uhci_free_pending_tds(uhci);
+ if (uhci->fsbr_is_on && !uhci->fsbr_is_wanted &&
+ !uhci->fsbr_expiring) {
+ uhci->fsbr_expiring = 1;
+ mod_timer(&uhci->fsbr_timer, jiffies + FSBR_OFF_DELAY);
+ }
- if (list_empty(&uhci->td_remove_list) &&
- list_empty(&uhci->skel_unlink_qh->node))
+ if (list_empty(&uhci->skel_unlink_qh->node))
uhci_clear_next_interrupt(uhci);
else
uhci_set_next_interrupt(uhci);
}
-
-static void check_fsbr(struct uhci_hcd *uhci)
-{
- /* For now, don't scan URBs for FSBR timeouts.
- * Add it back in later... */
-
- /* Really disable FSBR */
- if (!uhci->fsbr && uhci->fsbrtimeout && time_after_eq(jiffies, uhci->fsbrtimeout)) {
- uhci->fsbrtimeout = 0;
- uhci->skel_term_qh->link = UHCI_PTR_TERM;
- }
-}
#include <linux/kernel.h>
#include <linux/slab.h>
-#include <linux/input.h>
#include <linux/module.h>
#include <linux/init.h>
-#include <linux/usb.h>
-#include <linux/usb_input.h>
+#include <linux/usb/input.h>
/*
* Version Information
#include <linux/jiffies.h>
#include <linux/kernel.h>
#include <linux/slab.h>
-#include <linux/input.h>
#include <linux/module.h>
#include <linux/init.h>
-#include <linux/usb.h>
-#include <linux/usb_input.h>
+#include <linux/usb/input.h>
#include <linux/sched.h>
#include <asm/uaccess.h>
#include <asm/unaligned.h>
/*
- * Apple USB Touchpad (for post-February 2005 PowerBooks) driver
+ * Apple USB Touchpad (for post-February 2005 PowerBooks and MacBooks) driver
*
* Copyright (C) 2001-2004 Greg Kroah-Hartman (greg@kroah.com)
* Copyright (C) 2005 Johannes Berg (johannes@sipsolutions.net)
* Copyright (C) 2005 Frank Arnold (frank@scirocco-5v-turbo.de)
* Copyright (C) 2005 Peter Osterlund (petero2@telia.com)
* Copyright (C) 2005 Michael Hanselmann (linux-kernel@hansmi.ch)
+ * Copyright (C) 2006 Nicolas Boichat (nicolas@boichat.ch)
*
* Thanks to Alex Harper <basilisk@foobox.net> for his inputs.
*
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/module.h>
-#include <linux/usb.h>
-#include <linux/input.h>
-#include <linux/usb_input.h>
+#include <linux/usb/input.h>
/* Apple has powerbooks which have the keyboard with different Product IDs */
#define APPLE_VENDOR_ID 0x05AC
#define GEYSER_ISO_PRODUCT_ID 0x0215
#define GEYSER_JIS_PRODUCT_ID 0x0216
+/* MacBook devices */
+#define GEYSER3_ANSI_PRODUCT_ID 0x0217
+#define GEYSER3_ISO_PRODUCT_ID 0x0218
+#define GEYSER3_JIS_PRODUCT_ID 0x0219
+
#define ATP_DEVICE(prod) \
.match_flags = USB_DEVICE_ID_MATCH_DEVICE | \
USB_DEVICE_ID_MATCH_INT_CLASS | \
{ ATP_DEVICE(GEYSER_ISO_PRODUCT_ID) },
{ ATP_DEVICE(GEYSER_JIS_PRODUCT_ID) },
+ { ATP_DEVICE(GEYSER3_ANSI_PRODUCT_ID) },
+ { ATP_DEVICE(GEYSER3_ISO_PRODUCT_ID) },
+ { ATP_DEVICE(GEYSER3_JIS_PRODUCT_ID) },
+
/* Terminating entry */
{ }
};
*/
#define ATP_THRESHOLD 5
+/* MacBook Pro (Geyser 3) initialization constants */
+#define ATP_GEYSER3_MODE_READ_REQUEST_ID 1
+#define ATP_GEYSER3_MODE_WRITE_REQUEST_ID 9
+#define ATP_GEYSER3_MODE_REQUEST_VALUE 0x300
+#define ATP_GEYSER3_MODE_REQUEST_INDEX 0
+#define ATP_GEYSER3_MODE_VENDOR_VALUE 0x04
+
/* Structure to hold all of our device specific stuff */
struct atp {
char phys[64];
/* Checks if the device a Geyser 2 (ANSI, ISO, JIS) */
static inline int atp_is_geyser_2(struct atp *dev)
{
- int16_t productId = le16_to_cpu(dev->udev->descriptor.idProduct);
+ u16 productId = le16_to_cpu(dev->udev->descriptor.idProduct);
return (productId == GEYSER_ANSI_PRODUCT_ID) ||
(productId == GEYSER_ISO_PRODUCT_ID) ||
(productId == GEYSER_JIS_PRODUCT_ID);
}
+static inline int atp_is_geyser_3(struct atp *dev)
+{
+ u16 productId = le16_to_cpu(dev->udev->descriptor.idProduct);
+
+ return (productId == GEYSER3_ANSI_PRODUCT_ID) ||
+ (productId == GEYSER3_ISO_PRODUCT_ID) ||
+ (productId == GEYSER3_JIS_PRODUCT_ID);
+}
+
static int atp_calculate_abs(int *xy_sensors, int nb_sensors, int fact,
int *z, int *fingers)
{
/* drop incomplete datasets */
if (dev->urb->actual_length != dev->datalen) {
- dprintk("appletouch: incomplete data package.\n");
+ dprintk("appletouch: incomplete data package"
+ " (first byte: %d, length: %d).\n",
+ dev->data[0], dev->urb->actual_length);
goto exit;
}
/* reorder the sensors values */
- if (atp_is_geyser_2(dev)) {
+ if (atp_is_geyser_3(dev)) {
+ memset(dev->xy_cur, 0, sizeof(dev->xy_cur));
+
+ /*
+ * The values are laid out like this:
+ * -, Y1, Y2, -, Y3, Y4, -, ..., -, X1, X2, -, X3, X4, ...
+ * '-' is an unused value.
+ */
+
+ /* read X values */
+ for (i = 0, j = 19; i < 20; i += 2, j += 3) {
+ dev->xy_cur[i] = dev->data[j + 1];
+ dev->xy_cur[i + 1] = dev->data[j + 2];
+ }
+ /* read Y values */
+ for (i = 0, j = 1; i < 9; i += 2, j += 3) {
+ dev->xy_cur[ATP_XSENSORS + i] = dev->data[j + 1];
+ dev->xy_cur[ATP_XSENSORS + i + 1] = dev->data[j + 2];
+ }
+ } else if (atp_is_geyser_2(dev)) {
memset(dev->xy_cur, 0, sizeof(dev->xy_cur));
/*
dev->x_old = dev->y_old = -1;
memcpy(dev->xy_old, dev->xy_cur, sizeof(dev->xy_old));
+ if (atp_is_geyser_3(dev)) /* No 17" Macbooks (yet) */
+ goto exit;
+
/* 17" Powerbooks have extra X sensors */
for (i = (atp_is_geyser_2(dev)?15:16); i < ATP_XSENSORS; i++) {
if (!dev->xy_cur[i]) continue;
dev->udev = udev;
dev->input = input_dev;
dev->overflowwarn = 0;
- dev->datalen = (atp_is_geyser_2(dev)?64:81);
+ if (atp_is_geyser_3(dev))
+ dev->datalen = 64;
+ else if (atp_is_geyser_2(dev))
+ dev->datalen = 64;
+ else
+ dev->datalen = 81;
+
+ if (atp_is_geyser_3(dev)) {
+ /*
+ * By default Geyser 3 device sends standard USB HID mouse
+ * packets (Report ID 2). This code changes device mode, so it
+ * sends raw sensor reports (Report ID 5).
+ */
+ char data[8];
+ int size;
+
+ size = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
+ ATP_GEYSER3_MODE_READ_REQUEST_ID,
+ USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+ ATP_GEYSER3_MODE_REQUEST_VALUE,
+ ATP_GEYSER3_MODE_REQUEST_INDEX, &data, 8, 5000);
+
+ if (size != 8) {
+ err("Could not do mode read request from device"
+ " (Geyser 3 mode)");
+ goto err_free_devs;
+ }
+
+ /* Apply the mode switch */
+ data[0] = ATP_GEYSER3_MODE_VENDOR_VALUE;
+
+ size = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+ ATP_GEYSER3_MODE_WRITE_REQUEST_ID,
+ USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+ ATP_GEYSER3_MODE_REQUEST_VALUE,
+ ATP_GEYSER3_MODE_REQUEST_INDEX, &data, 8, 5000);
+
+ if (size != 8) {
+ err("Could not do mode write request to device"
+ " (Geyser 3 mode)");
+ goto err_free_devs;
+ }
+ printk("appletouch Geyser 3 inited.\n");
+ }
dev->urb = usb_alloc_urb(0, GFP_KERNEL);
if (!dev->urb) {
set_bit(EV_ABS, input_dev->evbit);
- if (atp_is_geyser_2(dev)) {
+ if (atp_is_geyser_3(dev)) {
+ /*
+ * MacBook have 20 X sensors, 10 Y sensors
+ */
+ input_set_abs_params(input_dev, ABS_X, 0,
+ ((20 - 1) * ATP_XFACT) - 1, ATP_FUZZ, 0);
+ input_set_abs_params(input_dev, ABS_Y, 0,
+ ((10 - 1) * ATP_YFACT) - 1, ATP_FUZZ, 0);
+ } else if (atp_is_geyser_2(dev)) {
/*
* Oct 2005 15" PowerBooks have 15 X sensors, 17" are detected
* later.
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
-#include <linux/input.h>
-#include <linux/usb.h>
-#include <linux/usb_input.h>
+#include <linux/usb/input.h>
#include <linux/wait.h>
#include <linux/jiffies.h>
* as published by the Free Software Foundation.
*/
-#include <linux/usb_input.h>
+#include <linux/usb/input.h>
#define DRIVER_DESC "ATI/Philips USB RF remote driver"
#define DRIVER_VERSION "0.1"
dev_dbg(&hid->intf->dev, "resetting device\n");
rc = rc_lock = usb_lock_device_for_reset(hid->dev, hid->intf);
if (rc_lock >= 0) {
- rc = usb_reset_device(hid->dev);
+ rc = usb_reset_composite_device(hid->dev, hid->intf);
if (rc_lock)
usb_unlock_device(hid->dev);
}
clear_bit(HID_RESET_PENDING, &hid->iofl);
- if (rc == 0) {
- hid->retry_delay = 0;
- if (hid_start_in(hid))
+ switch (rc) {
+ case 0:
+ if (!test_bit(HID_IN_RUNNING, &hid->iofl))
hid_io_error(hid);
- } else if (!(rc == -ENODEV || rc == -EHOSTUNREACH || rc == -EINTR))
+ break;
+ default:
err("can't reset device, %s-%s/input%d, status %d",
hid->dev->bus->bus_name,
hid->dev->devpath,
hid->ifnum, rc);
+ /* FALLTHROUGH */
+ case -EHOSTUNREACH:
+ case -ENODEV:
+ case -EINTR:
+ break;
+ }
}
/* Main I/O error handler */
#define USB_VENDOR_ID_PANJIT 0x134c
-#define USB_VENDOR_ID_SILVERCREST 0x062a
-#define USB_DEVICE_ID_SILVERCREST_KB 0x0201
-
/*
* Initialize all reports
*/
#define USB_VENDOR_ID_ONTRAK 0x0a07
#define USB_DEVICE_ID_ONTRAK_ADU100 0x0064
-#define USB_VENDOR_ID_TANGTOP 0x0d3d
-#define USB_DEVICE_ID_TANGTOP_USBPS2 0x0001
-
#define USB_VENDOR_ID_ESSENTIAL_REALITY 0x0d7f
#define USB_DEVICE_ID_ESSENTIAL_REALITY_P5 0x0100
#define USB_DEVICE_ID_MCC_PMD1024LS 0x0076
#define USB_DEVICE_ID_MCC_PMD1208LS 0x007a
-#define USB_VENDOR_ID_CHICONY 0x04f2
-#define USB_DEVICE_ID_CHICONY_USBHUB_KB 0x0100
-
-#define USB_VENDOR_ID_BTC 0x046e
-#define USB_DEVICE_ID_BTC_KEYBOARD 0x5303
-
#define USB_VENDOR_ID_VERNIER 0x08f7
#define USB_DEVICE_ID_VERNIER_LABPRO 0x0001
#define USB_DEVICE_ID_VERNIER_GOTEMP 0x0002
#define USB_DEVICE_ID_LD_MACHINETEST 0x2040
#define USB_VENDOR_ID_APPLE 0x05ac
-#define USB_DEVICE_ID_APPLE_POWERMOUSE 0x0304
+#define USB_DEVICE_ID_APPLE_MIGHTYMOUSE 0x0304
#define USB_VENDOR_ID_CHERRY 0x046a
#define USB_DEVICE_ID_CHERRY_CYMOTION 0x0023
-#define USB_VENDOR_ID_HP 0x03f0
-#define USB_DEVICE_ID_HP_USBHUB_KB 0x020c
-
-#define USB_VENDOR_ID_IBM 0x04b3
-#define USB_DEVICE_ID_IBM_USBHUB_KB 0x3005
-
-#define USB_VENDOR_ID_CREATIVELABS 0x062a
-#define USB_DEVICE_ID_CREATIVELABS_SILVERCREST 0x0201
-
+#define USB_VENDOR_ID_YEALINK 0x6993
+#define USB_DEVICE_ID_YEALINK_P1K_P4K_B2K 0xb001
/*
* Alphabetically sorted blacklist by quirk type.
*/
{ USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_DTF + 3, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_4_PHIDGETSERVO_20, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_1_PHIDGETSERVO_20, HID_QUIRK_IGNORE },
+ { USB_VENDOR_ID_YEALINK, USB_DEVICE_ID_YEALINK_P1K_P4K_B2K, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_ACECAD, USB_DEVICE_ID_ACECAD_FLAIR, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_ACECAD, USB_DEVICE_ID_ACECAD_302, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_2PORTKVM, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_4PORTKVM, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_4PORTKVMC, HID_QUIRK_NOGET },
- { USB_VENDOR_ID_BTC, USB_DEVICE_ID_BTC_KEYBOARD, HID_QUIRK_NOGET},
- { USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_USBHUB_KB, HID_QUIRK_NOGET},
- { USB_VENDOR_ID_CREATIVELABS, USB_DEVICE_ID_CREATIVELABS_SILVERCREST, HID_QUIRK_NOGET },
- { USB_VENDOR_ID_HP, USB_DEVICE_ID_HP_USBHUB_KB, HID_QUIRK_NOGET },
- { USB_VENDOR_ID_IBM, USB_DEVICE_ID_IBM_USBHUB_KB, HID_QUIRK_NOGET },
- { USB_VENDOR_ID_TANGTOP, USB_DEVICE_ID_TANGTOP_USBPS2, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_DUAL_USB_JOYPAD, HID_QUIRK_NOGET | HID_QUIRK_MULTI_INPUT },
- { USB_VENDOR_ID_SILVERCREST, USB_DEVICE_ID_SILVERCREST_KB, HID_QUIRK_NOGET },
- { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_POWERMOUSE, HID_QUIRK_2WHEEL_POWERMOUSE },
+ { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MIGHTYMOUSE, HID_QUIRK_MIGHTYMOUSE | HID_QUIRK_INVERT_HWHEEL },
{ USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_WCP32PU, HID_QUIRK_2WHEEL_MOUSE_HACK_7 },
{ USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_MOUSE, HID_QUIRK_2WHEEL_MOUSE_HACK_5 },
{ USB_VENDOR_ID_APPLE, 0x0214, HID_QUIRK_POWERBOOK_HAS_FN },
{ USB_VENDOR_ID_APPLE, 0x0215, HID_QUIRK_POWERBOOK_HAS_FN },
{ USB_VENDOR_ID_APPLE, 0x0216, HID_QUIRK_POWERBOOK_HAS_FN },
+ { USB_VENDOR_ID_APPLE, 0x0217, HID_QUIRK_POWERBOOK_HAS_FN },
+ { USB_VENDOR_ID_APPLE, 0x0218, HID_QUIRK_POWERBOOK_HAS_FN },
+ { USB_VENDOR_ID_APPLE, 0x0219, HID_QUIRK_POWERBOOK_HAS_FN },
{ USB_VENDOR_ID_APPLE, 0x030A, HID_QUIRK_POWERBOOK_HAS_FN },
{ USB_VENDOR_ID_APPLE, 0x030B, HID_QUIRK_POWERBOOK_HAS_FN },
(hid_blacklist[n].idProduct == le16_to_cpu(dev->descriptor.idProduct)))
quirks = hid_blacklist[n].quirks;
+ /* Many keyboards and mice don't like to be polled for reports,
+ * so we will always set the HID_QUIRK_NOGET flag for them. */
+ if (interface->desc.bInterfaceSubClass == USB_INTERFACE_SUBCLASS_BOOT) {
+ if (interface->desc.bInterfaceProtocol == USB_INTERFACE_PROTOCOL_KEYBOARD ||
+ interface->desc.bInterfaceProtocol == USB_INTERFACE_PROTOCOL_MOUSE)
+ quirks |= HID_QUIRK_NOGET;
+ }
+
if (quirks & HID_QUIRK_IGNORE)
return NULL;
int status;
clear_bit(HID_SUSPENDED, &hid->iofl);
+ hid->retry_delay = 0;
status = hid_start_in(hid);
dev_dbg(&intf->dev, "resume status %d\n", status);
return status;
}
+/* Treat USB reset pretty much the same as suspend/resume */
+static void hid_pre_reset(struct usb_interface *intf)
+{
+ /* FIXME: What if the interface is already suspended? */
+ hid_suspend(intf, PMSG_ON);
+}
+
+static void hid_post_reset(struct usb_interface *intf)
+{
+ struct usb_device *dev = interface_to_usbdev (intf);
+
+ hid_set_idle(dev, intf->cur_altsetting->desc.bInterfaceNumber, 0, 0);
+ /* FIXME: Any more reinitialization needed? */
+
+ hid_resume(intf);
+}
+
static struct usb_device_id hid_usb_ids [] = {
{ .match_flags = USB_DEVICE_ID_MATCH_INT_CLASS,
.bInterfaceClass = USB_INTERFACE_CLASS_HID },
.disconnect = hid_disconnect,
.suspend = hid_suspend,
.resume = hid_resume,
+ .pre_reset = hid_pre_reset,
+ .post_reset = hid_post_reset,
.id_table = hid_usb_ids,
};
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/kernel.h>
-#include <linux/input.h>
-#include <linux/usb.h>
-#include <linux/usb_input.h>
+#include <linux/usb/input.h>
#undef DEBUG
break;
}
- set_bit(usage->type, input->evbit);
-
- while (usage->code <= max && test_and_set_bit(usage->code, bit))
- usage->code = find_next_zero_bit(bit, max + 1, usage->code);
-
- if (usage->code > max)
- goto ignore;
-
- if (((device->quirks & (HID_QUIRK_2WHEEL_POWERMOUSE)) && (usage->hid == 0x00010032)))
- map_rel(REL_HWHEEL);
+ if (device->quirks & HID_QUIRK_MIGHTYMOUSE) {
+ if (usage->hid == HID_GD_Z)
+ map_rel(REL_HWHEEL);
+ else if (usage->code == BTN_1)
+ map_key(BTN_2);
+ else if (usage->code == BTN_2)
+ map_key(BTN_1);
+ }
if ((device->quirks & (HID_QUIRK_2WHEEL_MOUSE_HACK_7 | HID_QUIRK_2WHEEL_MOUSE_HACK_5)) &&
(usage->type == EV_REL) && (usage->code == REL_WHEEL))
|| ((device->quirks & HID_QUIRK_2WHEEL_MOUSE_HACK_7) && (usage->hid == 0x00090007)))
goto ignore;
+ set_bit(usage->type, input->evbit);
+
+ while (usage->code <= max && test_and_set_bit(usage->code, bit))
+ usage->code = find_next_zero_bit(bit, max + 1, usage->code);
+
+ if (usage->code > max)
+ goto ignore;
+
+
if (usage->type == EV_ABS) {
int a = field->logical_minimum;
return;
}
+ if ((hid->quirks & HID_QUIRK_INVERT_HWHEEL) && (usage->code == REL_HWHEEL)) {
+ input_event(input, usage->type, usage->code, -value);
+ return;
+ }
+
if ((hid->quirks & HID_QUIRK_2WHEEL_MOUSE_HACK_ON) && (usage->code == REL_WHEEL)) {
input_event(input, usage->type, REL_HWHEEL, value);
return;
#define USB_INTERFACE_CLASS_HID 3
+/*
+ * USB HID interface subclass and protocol codes
+ */
+
+#define USB_INTERFACE_SUBCLASS_BOOT 1
+#define USB_INTERFACE_PROTOCOL_KEYBOARD 1
+#define USB_INTERFACE_PROTOCOL_MOUSE 2
+
/*
* HID class requests
*/
#define HID_QUIRK_2WHEEL_MOUSE_HACK_7 0x00000080
#define HID_QUIRK_2WHEEL_MOUSE_HACK_5 0x00000100
#define HID_QUIRK_2WHEEL_MOUSE_HACK_ON 0x00000200
-#define HID_QUIRK_2WHEEL_POWERMOUSE 0x00000400
+#define HID_QUIRK_MIGHTYMOUSE 0x00000400
#define HID_QUIRK_CYMOTION 0x00000800
#define HID_QUIRK_POWERBOOK_HAS_FN 0x00001000
#define HID_QUIRK_POWERBOOK_FN_ON 0x00002000
+#define HID_QUIRK_INVERT_HWHEEL 0x00004000
/*
* This is the global environment of the parser. This information is
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/slab.h>
-#include <linux/input.h>
#include <linux/module.h>
#include <linux/init.h>
-#include <linux/usb.h>
-#include <linux/usb_input.h>
+#include <linux/usb/input.h>
/* only an 8 byte buffer necessary for a single packet */
#define ITM_BUFSIZE 8
#include <linux/kernel.h>
#include <linux/slab.h>
-#include <linux/input.h>
#include <linux/module.h>
#include <linux/init.h>
-#include <linux/usb.h>
-#include <linux/usb_input.h>
+#include <linux/usb/input.h>
#include <asm/unaligned.h>
-#include <asm/byteorder.h>
/*
* Version Information
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
-#include <linux/input.h>
-#include <linux/usb.h>
-#include <linux/usb_input.h>
+#include <linux/usb/input.h>
#define DRIVER_VERSION "v0.1"
#define DRIVER_AUTHOR "Michael Downey <downey@zymeta.com>"
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/slab.h>
-#include <linux/input.h>
#include <linux/module.h>
#include <linux/init.h>
-#include <linux/usb.h>
-#include <linux/usb_input.h>
+#include <linux/usb/input.h>
#define MTOUCHUSB_MIN_XC 0x0
#define MTOUCHUSB_MAX_RAW_XC 0x4000
#include <linux/kernel.h>
#include <linux/slab.h>
-#include <linux/input.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/spinlock.h>
-#include <linux/usb.h>
-#include <linux/usb_input.h>
+#include <linux/usb/input.h>
#define POWERMATE_VENDOR 0x077d /* Griffin Technology, Inc. */
#define POWERMATE_PRODUCT_NEW 0x0410 /* Griffin PowerMate */
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/slab.h>
-#include <linux/input.h>
#include <linux/module.h>
#include <linux/init.h>
-#include <linux/usb.h>
-#include <linux/usb_input.h>
+#include <linux/usb/input.h>
#define TOUCHKIT_MIN_XC 0x0
#define TOUCHKIT_MAX_XC 0x07ff
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/module.h>
-#include <linux/input.h>
#include <linux/init.h>
-#include <linux/usb.h>
-#include <linux/usb_input.h>
+#include <linux/usb/input.h>
/*
* Version Information
#include <linux/kernel.h>
#include <linux/slab.h>
-#include <linux/input.h>
#include <linux/module.h>
#include <linux/init.h>
-#include <linux/usb.h>
-#include <linux/usb_input.h>
+#include <linux/usb/input.h>
/*
* Version Information
#include <linux/module.h>
#include <linux/init.h>
#include <linux/usb.h>
-#include <linux/usb_input.h>
+#include <linux/usb/input.h>
#define DRIVER_VERSION "v0.3"
#include <linux/kernel.h>
#include <linux/slab.h>
-#include <linux/input.h>
#include <linux/module.h>
#include <linux/init.h>
-#include <linux/usb.h>
-#include <linux/usb_input.h>
+#include <linux/usb/input.h>
#include <asm/unaligned.h>
-#include <asm/byteorder.h>
/*
* Version Information
#include <linux/config.h>
#include <linux/kernel.h>
-#include <linux/input.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/smp_lock.h>
-#include <linux/usb.h>
-#include <linux/usb_input.h>
+#include <linux/usb/input.h>
#define DRIVER_VERSION "v0.0.5"
#define DRIVER_AUTHOR "Marko Friedemann <mfr@bmx-chemnitz.de>"
#include <linux/config.h>
#include <linux/kernel.h>
-#include <linux/input.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/rwsem.h>
-#include <linux/usb.h>
-#include <linux/usb_input.h>
+#include <linux/usb/input.h>
#include "map_to_7segment.h"
#include "yealink.h"
To compile this driver as a module, choose M here: the
module will be called usbled.
+config USB_CY7C63
+ tristate "Cypress CY7C63xxx USB driver support"
+ depends on USB
+ help
+ Say Y here if you want to connect a Cypress CY7C63xxx
+ micro controller to your computer's USB port. This driver
+ supports the pre-programmed devices (incl. firmware) by
+ AK Modul-Bus Computer GmbH.
+
+ Please see: http://www.ak-modul-bus.de/stat/mikrocontroller.html
+
+ To compile this driver as a module, choose M here: the
+ module will be called cy7c63.
+
config USB_CYTHERM
tristate "Cypress USB thermometer driver support"
depends on USB
See also <http://www.fs.tum.de/~echtler/idmouse/>.
+config USB_APPLEDISPLAY
+ tristate "Apple Cinema Display support"
+ depends on USB
+ select BACKLIGHT_LCD_SUPPORT
+ select BACKLIGHT_CLASS_DEVICE
+ help
+ Say Y here if you want to control the backlight of Apple Cinema
+ Displays over USB. This driver provides a sysfs interface.
+
source "drivers/usb/misc/sisusbvga/Kconfig"
config USB_LD
#
obj-$(CONFIG_USB_AUERSWALD) += auerswald.o
+obj-$(CONFIG_USB_CY7C63) += cy7c63.o
obj-$(CONFIG_USB_CYTHERM) += cytherm.o
obj-$(CONFIG_USB_EMI26) += emi26.o
obj-$(CONFIG_USB_EMI62) += emi62.o
obj-$(CONFIG_USB_RIO500) += rio500.o
obj-$(CONFIG_USB_TEST) += usbtest.o
obj-$(CONFIG_USB_USS720) += uss720.o
+obj-$(CONFIG_USB_APPLEDISPLAY) += appledisplay.o
obj-$(CONFIG_USB_SISUSBVGA) += sisusbvga/
--- /dev/null
+/*
+ * Apple Cinema Display driver
+ *
+ * Copyright (C) 2006 Michael Hanselmann (linux-kernel@hansmi.ch)
+ *
+ * Thanks to Caskey L. Dickson for his work with acdctl.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <linux/backlight.h>
+#include <linux/timer.h>
+#include <linux/workqueue.h>
+#include <asm/atomic.h>
+#include <asm/semaphore.h>
+
+#define APPLE_VENDOR_ID 0x05AC
+
+#define USB_REQ_GET_REPORT 0x01
+#define USB_REQ_SET_REPORT 0x09
+
+#define ACD_USB_TIMEOUT 250
+
+#define ACD_USB_EDID 0x0302
+#define ACD_USB_BRIGHTNESS 0x0310
+
+#define ACD_BTN_NONE 0
+#define ACD_BTN_BRIGHT_UP 3
+#define ACD_BTN_BRIGHT_DOWN 4
+
+#define ACD_URB_BUFFER_LEN 2
+#define ACD_MSG_BUFFER_LEN 2
+
+#define APPLEDISPLAY_DEVICE(prod) \
+ .match_flags = USB_DEVICE_ID_MATCH_DEVICE | \
+ USB_DEVICE_ID_MATCH_INT_CLASS | \
+ USB_DEVICE_ID_MATCH_INT_PROTOCOL, \
+ .idVendor = APPLE_VENDOR_ID, \
+ .idProduct = (prod), \
+ .bInterfaceClass = USB_CLASS_HID, \
+ .bInterfaceProtocol = 0x00
+
+/* table of devices that work with this driver */
+static struct usb_device_id appledisplay_table [] = {
+ { APPLEDISPLAY_DEVICE(0x9218) },
+ { APPLEDISPLAY_DEVICE(0x9219) },
+ { APPLEDISPLAY_DEVICE(0x921d) },
+
+ /* Terminating entry */
+ { }
+};
+MODULE_DEVICE_TABLE(usb, appledisplay_table);
+
+/* Structure to hold all of our device specific stuff */
+struct appledisplay {
+ struct usb_device *udev; /* usb device */
+ struct urb *urb; /* usb request block */
+ struct backlight_device *bd; /* backlight device */
+ char *urbdata; /* interrupt URB data buffer */
+ char *msgdata; /* control message data buffer */
+
+ struct work_struct work;
+ int button_pressed;
+ spinlock_t lock;
+};
+
+static atomic_t count_displays = ATOMIC_INIT(0);
+static struct workqueue_struct *wq;
+
+static void appledisplay_complete(struct urb *urb, struct pt_regs *regs)
+{
+ struct appledisplay *pdata = urb->context;
+ unsigned long flags;
+ int retval;
+
+ switch (urb->status) {
+ case 0:
+ /* success */
+ break;
+ case -EOVERFLOW:
+ printk(KERN_ERR "appletouch: OVERFLOW with data "
+ "length %d, actual length is %d\n",
+ ACD_URB_BUFFER_LEN, pdata->urb->actual_length);
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
+ /* This urb is terminated, clean up */
+ dbg("%s - urb shutting down with status: %d",
+ __FUNCTION__, urb->status);
+ return;
+ default:
+ dbg("%s - nonzero urb status received: %d",
+ __FUNCTION__, urb->status);
+ goto exit;
+ }
+
+ spin_lock_irqsave(&pdata->lock, flags);
+
+ switch(pdata->urbdata[1]) {
+ case ACD_BTN_BRIGHT_UP:
+ case ACD_BTN_BRIGHT_DOWN:
+ pdata->button_pressed = 1;
+ queue_work(wq, &pdata->work);
+ break;
+ case ACD_BTN_NONE:
+ default:
+ pdata->button_pressed = 0;
+ break;
+ }
+
+ spin_unlock_irqrestore(&pdata->lock, flags);
+
+exit:
+ retval = usb_submit_urb(pdata->urb, GFP_ATOMIC);
+ if (retval) {
+ err("%s - usb_submit_urb failed with result %d",
+ __FUNCTION__, retval);
+ }
+}
+
+static int appledisplay_bl_update_status(struct backlight_device *bd)
+{
+ struct appledisplay *pdata = class_get_devdata(&bd->class_dev);
+ int retval;
+
+ pdata->msgdata[0] = 0x10;
+ pdata->msgdata[1] = bd->props->brightness;
+
+ retval = usb_control_msg(
+ pdata->udev,
+ usb_sndctrlpipe(pdata->udev, 0),
+ USB_REQ_SET_REPORT,
+ USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+ ACD_USB_BRIGHTNESS,
+ 0,
+ pdata->msgdata, 2,
+ ACD_USB_TIMEOUT);
+
+ return retval;
+}
+
+static int appledisplay_bl_get_brightness(struct backlight_device *bd)
+{
+ struct appledisplay *pdata = class_get_devdata(&bd->class_dev);
+ int retval;
+
+ retval = usb_control_msg(
+ pdata->udev,
+ usb_rcvctrlpipe(pdata->udev, 0),
+ USB_REQ_GET_REPORT,
+ USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+ ACD_USB_BRIGHTNESS,
+ 0,
+ pdata->msgdata, 2,
+ ACD_USB_TIMEOUT);
+
+ if (retval < 0)
+ return retval;
+ else
+ return pdata->msgdata[1];
+}
+
+static struct backlight_properties appledisplay_bl_data = {
+ .owner = THIS_MODULE,
+ .get_brightness = appledisplay_bl_get_brightness,
+ .update_status = appledisplay_bl_update_status,
+ .max_brightness = 0xFF
+};
+
+static void appledisplay_work(void *private)
+{
+ struct appledisplay *pdata = private;
+ int retval;
+
+ up(&pdata->bd->sem);
+ retval = appledisplay_bl_get_brightness(pdata->bd);
+ if (retval >= 0)
+ pdata->bd->props->brightness = retval;
+ down(&pdata->bd->sem);
+
+ /* Poll again in about 125ms if there's still a button pressed */
+ if (pdata->button_pressed)
+ schedule_delayed_work(&pdata->work, HZ / 8);
+}
+
+static int appledisplay_probe(struct usb_interface *iface,
+ const struct usb_device_id *id)
+{
+ struct appledisplay *pdata;
+ struct usb_device *udev = interface_to_usbdev(iface);
+ struct usb_host_interface *iface_desc;
+ struct usb_endpoint_descriptor *endpoint;
+ int int_in_endpointAddr = 0;
+ int i, retval = -ENOMEM, brightness;
+ char bl_name[20];
+
+ /* set up the endpoint information */
+ /* use only the first interrupt-in endpoint */
+ iface_desc = iface->cur_altsetting;
+ for (i = 0; i < iface_desc->desc.bNumEndpoints; i++) {
+ endpoint = &iface_desc->endpoint[i].desc;
+ if (!int_in_endpointAddr &&
+ (endpoint->bEndpointAddress & USB_DIR_IN) &&
+ ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
+ USB_ENDPOINT_XFER_INT)) {
+ /* we found an interrupt in endpoint */
+ int_in_endpointAddr = endpoint->bEndpointAddress;
+ break;
+ }
+ }
+ if (!int_in_endpointAddr) {
+ err("Could not find int-in endpoint");
+ return -EIO;
+ }
+
+ /* allocate memory for our device state and initialize it */
+ pdata = kzalloc(sizeof(struct appledisplay), GFP_KERNEL);
+ if (!pdata) {
+ retval = -ENOMEM;
+ err("Out of memory");
+ goto error;
+ }
+
+ pdata->udev = udev;
+
+ spin_lock_init(&pdata->lock);
+ INIT_WORK(&pdata->work, appledisplay_work, pdata);
+
+ /* Allocate buffer for control messages */
+ pdata->msgdata = kmalloc(ACD_MSG_BUFFER_LEN, GFP_KERNEL);
+ if (!pdata->msgdata) {
+ retval = -ENOMEM;
+ err("appledisplay: Allocating buffer for control messages "
+ "failed");
+ goto error;
+ }
+
+ /* Allocate interrupt URB */
+ pdata->urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!pdata->urb) {
+ retval = -ENOMEM;
+ err("appledisplay: Allocating URB failed");
+ goto error;
+ }
+
+ /* Allocate buffer for interrupt data */
+ pdata->urbdata = usb_buffer_alloc(pdata->udev, ACD_URB_BUFFER_LEN,
+ GFP_KERNEL, &pdata->urb->transfer_dma);
+ if (!pdata->urbdata) {
+ retval = -ENOMEM;
+ err("appledisplay: Allocating URB buffer failed");
+ goto error;
+ }
+
+ /* Configure interrupt URB */
+ usb_fill_int_urb(pdata->urb, udev,
+ usb_rcvintpipe(udev, int_in_endpointAddr),
+ pdata->urbdata, ACD_URB_BUFFER_LEN, appledisplay_complete,
+ pdata, 1);
+ if (usb_submit_urb(pdata->urb, GFP_KERNEL)) {
+ retval = -EIO;
+ err("appledisplay: Submitting URB failed");
+ goto error;
+ }
+
+ /* Register backlight device */
+ snprintf(bl_name, sizeof(bl_name), "appledisplay%d",
+ atomic_inc_return(&count_displays) - 1);
+ pdata->bd = backlight_device_register(bl_name, pdata,
+ &appledisplay_bl_data);
+ if (IS_ERR(pdata->bd)) {
+ err("appledisplay: Backlight registration failed");
+ goto error;
+ }
+
+ /* Try to get brightness */
+ up(&pdata->bd->sem);
+ brightness = appledisplay_bl_get_brightness(pdata->bd);
+ down(&pdata->bd->sem);
+
+ if (brightness < 0) {
+ retval = brightness;
+ err("appledisplay: Error while getting initial brightness: %d", retval);
+ goto error;
+ }
+
+ /* Set brightness in backlight device */
+ up(&pdata->bd->sem);
+ pdata->bd->props->brightness = brightness;
+ down(&pdata->bd->sem);
+
+ /* save our data pointer in the interface device */
+ usb_set_intfdata(iface, pdata);
+
+ printk(KERN_INFO "appledisplay: Apple Cinema Display connected\n");
+
+ return 0;
+
+error:
+ if (pdata) {
+ if (pdata->urb) {
+ usb_kill_urb(pdata->urb);
+ if (pdata->urbdata)
+ usb_buffer_free(pdata->udev, ACD_URB_BUFFER_LEN,
+ pdata->urbdata, pdata->urb->transfer_dma);
+ usb_free_urb(pdata->urb);
+ }
+ if (pdata->bd)
+ backlight_device_unregister(pdata->bd);
+ kfree(pdata->msgdata);
+ }
+ usb_set_intfdata(iface, NULL);
+ kfree(pdata);
+ return retval;
+}
+
+static void appledisplay_disconnect(struct usb_interface *iface)
+{
+ struct appledisplay *pdata = usb_get_intfdata(iface);
+
+ if (pdata) {
+ usb_kill_urb(pdata->urb);
+ cancel_delayed_work(&pdata->work);
+ backlight_device_unregister(pdata->bd);
+ usb_buffer_free(pdata->udev, ACD_URB_BUFFER_LEN,
+ pdata->urbdata, pdata->urb->transfer_dma);
+ usb_free_urb(pdata->urb);
+ kfree(pdata->msgdata);
+ kfree(pdata);
+ }
+
+ printk(KERN_INFO "appledisplay: Apple Cinema Display disconnected\n");
+}
+
+static struct usb_driver appledisplay_driver = {
+ .name = "appledisplay",
+ .probe = appledisplay_probe,
+ .disconnect = appledisplay_disconnect,
+ .id_table = appledisplay_table,
+};
+
+static int __init appledisplay_init(void)
+{
+ wq = create_singlethread_workqueue("appledisplay");
+ if (!wq) {
+ err("Could not create work queue\n");
+ return -ENOMEM;
+ }
+
+ return usb_register(&appledisplay_driver);
+}
+
+static void __exit appledisplay_exit(void)
+{
+ flush_workqueue(wq);
+ destroy_workqueue(wq);
+ usb_deregister(&appledisplay_driver);
+}
+
+MODULE_AUTHOR("Michael Hanselmann");
+MODULE_DESCRIPTION("Apple Cinema Display driver");
+MODULE_LICENSE("GPL");
+
+module_init(appledisplay_init);
+module_exit(appledisplay_exit);
--- /dev/null
+/*
+* cy7c63.c
+*
+* Copyright (c) 2006 Oliver Bock (bock@fh-wolfenbuettel.de)
+*
+* This driver is based on the Cypress Thermometer USB Driver by
+* Marcus Maul and the 2.0 version of Greg Kroah-Hartman's
+* USB Skeleton driver.
+*
+* Is is a generic driver for the Cypress CY7C63000 family.
+* For the time being it enables you to toggle the single I/O ports
+* of the device.
+*
+* Supported vendors: AK Modul-Bus Computer GmbH
+* Supported devices: CY7C63001A-PC (to be continued...)
+* Supported functions: Read/Write Ports (to be continued...)
+*
+* Chipsets families: CY7C63000, CY7C63001, CY7C63100, CY7C63101
+*
+*
+* 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, version 2.
+*/
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/usb.h>
+
+#define DRIVER_AUTHOR "Oliver Bock (bock@fh-wolfenbuettel.de)"
+#define DRIVER_DESC "Cypress CY7C63xxx USB driver"
+
+#define CY7C63_VENDOR_ID 0xa2c
+#define CY7C63_PRODUCT_ID 0x8
+
+#define CY7C63_READ_PORT 0x4
+#define CY7C63_WRITE_PORT 0x5
+#define CY7C63_READ_RAM 0x2
+#define CY7C63_WRITE_RAM 0x3
+#define CY7C63_READ_ROM 0x1
+
+#define CY7C63_READ_PORT_ID0 0
+#define CY7C63_WRITE_PORT_ID0 0
+#define CY7C63_READ_PORT_ID1 0x2
+#define CY7C63_WRITE_PORT_ID1 1
+
+#define CY7C63_MAX_REQSIZE 8
+
+
+/* table of devices that work with this driver */
+static struct usb_device_id cy7c63_table [] = {
+ { USB_DEVICE(CY7C63_VENDOR_ID, CY7C63_PRODUCT_ID) },
+ { }
+};
+MODULE_DEVICE_TABLE(usb, cy7c63_table);
+
+/* structure to hold all of our device specific stuff */
+struct cy7c63 {
+ struct usb_device * udev;
+ char port0;
+ char port1;
+};
+
+/* used to send usb control messages to device */
+int vendor_command(struct cy7c63 *dev, unsigned char request,
+ unsigned char address, unsigned char data) {
+
+ int retval = 0;
+ unsigned int pipe;
+ unsigned char *iobuf;
+
+ /* allocate some memory for the i/o buffer*/
+ iobuf = kzalloc(CY7C63_MAX_REQSIZE, GFP_KERNEL);
+ if (!iobuf) {
+ dev_err(&dev->udev->dev, "Out of memory!\n");
+ retval = -ENOMEM;
+ goto error;
+ }
+
+ dev_dbg(&dev->udev->dev, "Sending usb_control_msg (data: %d)\n", data);
+
+ /* prepare usb control message and send it upstream */
+ pipe = usb_rcvctrlpipe(dev->udev, 0);
+ retval = usb_control_msg(dev->udev, pipe, request,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_OTHER,
+ address, data, iobuf, CY7C63_MAX_REQSIZE,
+ USB_CTRL_GET_TIMEOUT);
+
+ /* store returned data (more READs to be added!) */
+ switch (request) {
+ case CY7C63_READ_PORT:
+ if (address == CY7C63_READ_PORT_ID0) {
+ dev->port0 = iobuf[1];
+ dev_dbg(&dev->udev->dev,
+ "READ_PORT0 returned: %d\n",dev->port0);
+ }
+ else if (address == CY7C63_READ_PORT_ID1) {
+ dev->port1 = iobuf[1];
+ dev_dbg(&dev->udev->dev,
+ "READ_PORT1 returned: %d\n",dev->port1);
+ }
+ break;
+ }
+
+ kfree(iobuf);
+error:
+ return retval;
+}
+
+#define get_set_port(num,read_id,write_id) \
+static ssize_t set_port##num(struct device *dev, struct device_attribute *attr, \
+ const char *buf, size_t count) { \
+ \
+ int value; \
+ int result = 0; \
+ \
+ struct usb_interface *intf = to_usb_interface(dev); \
+ struct cy7c63 *cyp = usb_get_intfdata(intf); \
+ \
+ dev_dbg(&cyp->udev->dev, "WRITE_PORT%d called\n", num); \
+ \
+ /* validate input data */ \
+ if (sscanf(buf, "%d", &value) < 1) { \
+ result = -EINVAL; \
+ goto error; \
+ } \
+ if (value>255 || value<0) { \
+ result = -EINVAL; \
+ goto error; \
+ } \
+ \
+ result = vendor_command(cyp, CY7C63_WRITE_PORT, write_id, \
+ (unsigned char)value); \
+ \
+ dev_dbg(&cyp->udev->dev, "Result of vendor_command: %d\n\n",result); \
+error: \
+ return result < 0 ? result : count; \
+} \
+ \
+static ssize_t get_port##num(struct device *dev, \
+ struct device_attribute *attr, char *buf) { \
+ \
+ int result = 0; \
+ \
+ struct usb_interface *intf = to_usb_interface(dev); \
+ struct cy7c63 *cyp = usb_get_intfdata(intf); \
+ \
+ dev_dbg(&cyp->udev->dev, "READ_PORT%d called\n", num); \
+ \
+ result = vendor_command(cyp, CY7C63_READ_PORT, read_id, 0); \
+ \
+ dev_dbg(&cyp->udev->dev, "Result of vendor_command: %d\n\n", result); \
+ \
+ return sprintf(buf, "%d", cyp->port##num); \
+} \
+static DEVICE_ATTR(port##num, S_IWUGO | S_IRUGO, get_port##num, set_port##num);
+
+get_set_port(0, CY7C63_READ_PORT_ID0, CY7C63_WRITE_PORT_ID0);
+get_set_port(1, CY7C63_READ_PORT_ID1, CY7C63_WRITE_PORT_ID1);
+
+static int cy7c63_probe(struct usb_interface *interface,
+ const struct usb_device_id *id) {
+
+ struct cy7c63 *dev = NULL;
+ int retval = -ENOMEM;
+
+ /* allocate memory for our device state and initialize it */
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (dev == NULL) {
+ dev_err(&dev->udev->dev, "Out of memory!\n");
+ goto error;
+ }
+
+ dev->udev = usb_get_dev(interface_to_usbdev(interface));
+
+ /* save our data pointer in this interface device */
+ usb_set_intfdata(interface, dev);
+
+ /* create device attribute files */
+ device_create_file(&interface->dev, &dev_attr_port0);
+ device_create_file(&interface->dev, &dev_attr_port1);
+
+ /* let the user know what node this device is now attached to */
+ dev_info(&interface->dev,
+ "Cypress CY7C63xxx device now attached\n");
+
+ retval = 0;
+error:
+ return retval;
+}
+
+static void cy7c63_disconnect(struct usb_interface *interface) {
+
+ struct cy7c63 *dev;
+
+ dev = usb_get_intfdata(interface);
+ usb_set_intfdata(interface, NULL);
+
+ /* remove device attribute files */
+ device_remove_file(&interface->dev, &dev_attr_port0);
+ device_remove_file(&interface->dev, &dev_attr_port1);
+
+ usb_put_dev(dev->udev);
+
+ dev_info(&interface->dev,
+ "Cypress CY7C63xxx device now disconnected\n");
+
+ kfree(dev);
+}
+
+static struct usb_driver cy7c63_driver = {
+ .name = "cy7c63",
+ .probe = cy7c63_probe,
+ .disconnect = cy7c63_disconnect,
+ .id_table = cy7c63_table,
+};
+
+static int __init cy7c63_init(void) {
+
+ int result;
+
+ /* register this driver with the USB subsystem */
+ result = usb_register(&cy7c63_driver);
+ if (result) {
+ err("Function usb_register failed! Error number: %d\n", result);
+ }
+
+ return result;
+}
+
+static void __exit cy7c63_exit(void) {
+
+ /* deregister this driver with the USB subsystem */
+ usb_deregister(&cy7c63_driver);
+}
+
+module_init(cy7c63_init);
+module_exit(cy7c63_exit);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+
+MODULE_LICENSE("GPL");
/*
* USB PhidgetInterfaceKit driver 1.0
*
- * Copyright (C) 2004 Sean Young <sean@mess.org>
+ * Copyright (C) 2004, 2006 Sean Young <sean@mess.org>
+ * Copyright (C) 2005 Daniel Saakes <daniel@saakes.net>
* Copyright (C) 2004 Greg Kroah-Hartman <greg@kroah.com>
*
* This program is free software; you can redistribute it and/or modify
#define USB_VENDOR_ID_GLAB 0x06c2
#define USB_DEVICE_ID_INTERFACEKIT004 0x0040
+#define USB_DEVICE_ID_INTERFACEKIT01616 0x0044
#define USB_DEVICE_ID_INTERFACEKIT888 0x0045
#define USB_DEVICE_ID_INTERFACEKIT047 0x0051
#define USB_DEVICE_ID_INTERFACEKIT088 0x0053
#define USB_VENDOR_ID_WISEGROUP 0x0925
#define USB_DEVICE_ID_INTERFACEKIT884 0x8201
-#define MAX_INTERFACES 8
+#define MAX_INTERFACES 16
+
+#define URB_INT_SIZE 8
struct driver_interfacekit {
int sensors;
ifkit(0, 4, 7, 1);
ifkit(8, 8, 4, 0);
ifkit(0, 8, 8, 1);
+ifkit(0, 16, 16, 0);
-struct phidget_interfacekit {
+struct interfacekit {
struct usb_device *udev;
struct usb_interface *intf;
struct driver_interfacekit *ifkit;
- int outputs[MAX_INTERFACES];
- int inputs[MAX_INTERFACES];
- int sensors[MAX_INTERFACES];
+ unsigned long outputs;
+ u8 inputs[MAX_INTERFACES];
+ u16 sensors[MAX_INTERFACES];
u8 lcd_files_on;
struct urb *irq;
unsigned char *data;
dma_addr_t data_dma;
+
+ struct work_struct do_notify;
+ unsigned long input_events;
+ unsigned long sensor_events;
};
static struct usb_device_id id_table[] = {
.driver_info = (kernel_ulong_t)&ph_047},
{USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_INTERFACEKIT088),
.driver_info = (kernel_ulong_t)&ph_088},
+ {USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_INTERFACEKIT01616),
+ .driver_info = (kernel_ulong_t)&ph_01616},
{USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_INTERFACEKIT884),
.driver_info = (kernel_ulong_t)&ph_884},
{}
};
MODULE_DEVICE_TABLE(usb, id_table);
-static int change_outputs(struct phidget_interfacekit *kit, int output_num, int enable)
+static int change_outputs(struct interfacekit *kit, int output_num, int enable)
{
- unsigned char *buffer;
+ u8 *buffer;
int retval;
- int n;
+
+ if (enable)
+ set_bit(output_num, &kit->outputs);
+ else
+ clear_bit(output_num, &kit->outputs);
buffer = kzalloc(4, GFP_KERNEL);
if (!buffer) {
- dev_err(&kit->udev->dev, "%s - out of memory\n",
- __FUNCTION__);
+ dev_err(&kit->udev->dev, "%s - out of memory\n", __FUNCTION__);
return -ENOMEM;
}
+ buffer[0] = (u8)kit->outputs;
+ buffer[1] = (u8)(kit->outputs >> 8);
- kit->outputs[output_num] = enable;
- for (n=0; n<8; n++) {
- if (kit->outputs[n]) {
- buffer[0] |= 1 << n;
- }
- }
-
- dev_dbg(&kit->udev->dev, "sending data: %02x\n", buffer[0]);
+ dev_dbg(&kit->udev->dev, "sending data: 0x%04x\n", (u16)kit->outputs);
retval = usb_control_msg(kit->udev,
usb_sndctrlpipe(kit->udev, 0),
return retval < 0 ? retval : 0;
}
-static int change_string(struct phidget_interfacekit *kit, const char *display, unsigned char row)
+static int change_string(struct interfacekit *kit, const char *display, unsigned char row)
{
unsigned char *buffer;
- unsigned char *form_buffer;
+ unsigned char *form_buffer;
int retval = -ENOMEM;
int i,j, len, buf_ptr;
static ssize_t lcd_line_##number(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) \
{ \
struct usb_interface *intf = to_usb_interface(dev); \
- struct phidget_interfacekit *kit = usb_get_intfdata(intf); \
+ struct interfacekit *kit = usb_get_intfdata(intf); \
change_string(kit, buf, number - 1); \
return count; \
} \
static ssize_t set_backlight(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
struct usb_interface *intf = to_usb_interface(dev);
- struct phidget_interfacekit *kit = usb_get_intfdata(intf);
+ struct interfacekit *kit = usb_get_intfdata(intf);
int enabled;
unsigned char *buffer;
int retval = -ENOMEM;
}
static DEVICE_ATTR(backlight, S_IWUGO, NULL, set_backlight);
-static void remove_lcd_files(struct phidget_interfacekit *kit)
+static void remove_lcd_files(struct interfacekit *kit)
{
if (kit->lcd_files_on) {
dev_dbg(&kit->udev->dev, "Removing lcd files\n");
static ssize_t enable_lcd_files(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
struct usb_interface *intf = to_usb_interface(dev);
- struct phidget_interfacekit *kit = usb_get_intfdata(intf);
+ struct interfacekit *kit = usb_get_intfdata(intf);
int enable;
if (kit->ifkit->has_lcd == 0)
static void interfacekit_irq(struct urb *urb, struct pt_regs *regs)
{
- struct phidget_interfacekit *kit = urb->context;
+ struct interfacekit *kit = urb->context;
unsigned char *buffer = kit->data;
+ int i, level, sensor;
int status;
- int n;
switch (urb->status) {
case 0: /* success */
goto resubmit;
}
- for (n=0; n<8; n++) {
- kit->inputs[n] = buffer[1] & (1 << n) ? 1 : 0;
+ /* digital inputs */
+ if (kit->ifkit->inputs == 16) {
+ for (i=0; i < 8; i++) {
+ level = (buffer[0] >> i) & 1;
+ if (kit->inputs[i] != level) {
+ kit->inputs[i] = level;
+ set_bit(i, &kit->input_events);
+ }
+ level = (buffer[1] >> i) & 1;
+ if (kit->inputs[8 + i] != level) {
+ kit->inputs[8 + i] = level;
+ set_bit(8 + i, &kit->input_events);
+ }
+ }
+ }
+ else if (kit->ifkit->inputs == 8) {
+ for (i=0; i < 8; i++) {
+ level = (buffer[1] >> i) & 1;
+ if (kit->inputs[i] != level) {
+ kit->inputs[i] = level;
+ set_bit(i, &kit->input_events);
+ }
+ }
}
- if (buffer[0] & 1) {
- kit->sensors[4] = buffer[2] + (buffer[3] & 0x0f) * 256;
- kit->sensors[5] = buffer[4] + (buffer[3] & 0xf0) * 16;
- kit->sensors[6] = buffer[5] + (buffer[6] & 0x0f) * 256;
- kit->sensors[7] = buffer[7] + (buffer[6] & 0xf0) * 16;
- } else {
- kit->sensors[0] = buffer[2] + (buffer[3] & 0x0f) * 256;
- kit->sensors[1] = buffer[4] + (buffer[3] & 0xf0) * 16;
- kit->sensors[2] = buffer[5] + (buffer[6] & 0x0f) * 256;
- kit->sensors[3] = buffer[7] + (buffer[6] & 0xf0) * 16;
+ /* analog inputs */
+ if (kit->ifkit->sensors) {
+ sensor = (buffer[0] & 1) ? 4 : 0;
+
+ level = buffer[2] + (buffer[3] & 0x0f) * 256;
+ if (level != kit->sensors[sensor]) {
+ kit->sensors[sensor] = level;
+ set_bit(sensor, &kit->sensor_events);
+ }
+ sensor++;
+ level = buffer[4] + (buffer[3] & 0xf0) * 16;
+ if (level != kit->sensors[sensor]) {
+ kit->sensors[sensor] = level;
+ set_bit(sensor, &kit->sensor_events);
+ }
+ sensor++;
+ level = buffer[5] + (buffer[6] & 0x0f) * 256;
+ if (level != kit->sensors[sensor]) {
+ kit->sensors[sensor] = level;
+ set_bit(sensor, &kit->sensor_events);
+ }
+ sensor++;
+ level = buffer[7] + (buffer[6] & 0xf0) * 16;
+ if (level != kit->sensors[sensor]) {
+ kit->sensors[sensor] = level;
+ set_bit(sensor, &kit->sensor_events);
+ }
}
+ if (kit->input_events || kit->sensor_events)
+ schedule_work(&kit->do_notify);
+
resubmit:
status = usb_submit_urb(urb, SLAB_ATOMIC);
if (status)
kit->udev->devpath, status);
}
+static void do_notify(void *data)
+{
+ struct interfacekit *kit = data;
+ int i;
+ char sysfs_file[8];
+
+ for (i=0; i<kit->ifkit->inputs; i++) {
+ if (test_and_clear_bit(i, &kit->input_events)) {
+ sprintf(sysfs_file, "input%d", i + 1);
+ sysfs_notify(&kit->intf->dev.kobj, NULL, sysfs_file);
+ }
+ }
+
+ for (i=0; i<kit->ifkit->sensors; i++) {
+ if (test_and_clear_bit(i, &kit->sensor_events)) {
+ sprintf(sysfs_file, "sensor%d", i + 1);
+ sysfs_notify(&kit->intf->dev.kobj, NULL, sysfs_file);
+ }
+ }
+}
+
#define show_set_output(value) \
-static ssize_t set_output##value(struct device *dev, struct device_attribute *attr, const char *buf, \
+static ssize_t set_output##value(struct device *dev, struct device_attribute *attr, const char *buf, \
size_t count) \
{ \
struct usb_interface *intf = to_usb_interface(dev); \
- struct phidget_interfacekit *kit = usb_get_intfdata(intf); \
+ struct interfacekit *kit = usb_get_intfdata(intf); \
int enabled; \
int retval; \
\
- if (sscanf(buf, "%d", &enabled) < 1) { \
+ if (sscanf(buf, "%d", &enabled) < 1) \
return -EINVAL; \
- } \
\
- retval = change_outputs(kit, value - 1, enabled ? 1 : 0); \
+ retval = change_outputs(kit, value - 1, enabled); \
\
return retval ? retval : count; \
} \
static ssize_t show_output##value(struct device *dev, struct device_attribute *attr, char *buf) \
{ \
struct usb_interface *intf = to_usb_interface(dev); \
- struct phidget_interfacekit *kit = usb_get_intfdata(intf); \
+ struct interfacekit *kit = usb_get_intfdata(intf); \
\
- return sprintf(buf, "%d\n", kit->outputs[value - 1]); \
+ return sprintf(buf, "%d\n", !!test_bit(value - 1, &kit->outputs));\
} \
static DEVICE_ATTR(output##value, S_IWUGO | S_IRUGO, \
show_output##value, set_output##value);
show_set_output(5);
show_set_output(6);
show_set_output(7);
-show_set_output(8); /* should be MAX_INTERFACES - 1 */
+show_set_output(8);
+show_set_output(9);
+show_set_output(10);
+show_set_output(11);
+show_set_output(12);
+show_set_output(13);
+show_set_output(14);
+show_set_output(15);
+show_set_output(16);
#define show_input(value) \
static ssize_t show_input##value(struct device *dev, struct device_attribute *attr, char *buf) \
{ \
struct usb_interface *intf = to_usb_interface(dev); \
- struct phidget_interfacekit *kit = usb_get_intfdata(intf); \
+ struct interfacekit *kit = usb_get_intfdata(intf); \
\
- return sprintf(buf, "%d\n", kit->inputs[value - 1]); \
+ return sprintf(buf, "%d\n", (int)kit->inputs[value - 1]); \
} \
static DEVICE_ATTR(input##value, S_IRUGO, show_input##value, NULL);
show_input(5);
show_input(6);
show_input(7);
-show_input(8); /* should be MAX_INTERFACES - 1 */
+show_input(8);
+show_input(9);
+show_input(10);
+show_input(11);
+show_input(12);
+show_input(13);
+show_input(14);
+show_input(15);
+show_input(16);
#define show_sensor(value) \
static ssize_t show_sensor##value(struct device *dev, struct device_attribute *attr, char *buf) \
{ \
struct usb_interface *intf = to_usb_interface(dev); \
- struct phidget_interfacekit *kit = usb_get_intfdata(intf); \
+ struct interfacekit *kit = usb_get_intfdata(intf); \
\
- return sprintf(buf, "%d\n", kit->sensors[value - 1]); \
+ return sprintf(buf, "%d\n", (int)kit->sensors[value - 1]); \
} \
static DEVICE_ATTR(sensor##value, S_IRUGO, show_sensor##value, NULL);
show_sensor(5);
show_sensor(6);
show_sensor(7);
-show_sensor(8); /* should be MAX_INTERFACES - 1 */
+show_sensor(8);
static int interfacekit_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
struct usb_device *dev = interface_to_usbdev(intf);
struct usb_host_interface *interface;
struct usb_endpoint_descriptor *endpoint;
- struct phidget_interfacekit *kit;
+ struct interfacekit *kit;
struct driver_interfacekit *ifkit;
- int pipe, maxp;
+ int pipe, maxp, rc = -ENOMEM;
ifkit = (struct driver_interfacekit *)id->driver_info;
if (!ifkit)
maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
kit = kzalloc(sizeof(*kit), GFP_KERNEL);
- if (kit == NULL) {
- dev_err(&intf->dev, "%s - out of memory\n", __FUNCTION__);
- return -ENOMEM;
- }
- kit->ifkit = ifkit;
+ if (!kit)
+ goto out;
- kit->data = usb_buffer_alloc(dev, 8, SLAB_ATOMIC, &kit->data_dma);
- if (!kit->data) {
- kfree(kit);
- return -ENOMEM;
- }
+ kit->ifkit = ifkit;
+ kit->data = usb_buffer_alloc(dev, URB_INT_SIZE, SLAB_ATOMIC, &kit->data_dma);
+ if (!kit->data)
+ goto out;
kit->irq = usb_alloc_urb(0, GFP_KERNEL);
- if (!kit->irq) {
- usb_buffer_free(dev, 8, kit->data, kit->data_dma);
- kfree(kit);
- return -ENOMEM;
- }
+ if (!kit->irq)
+ goto out;
kit->udev = usb_get_dev(dev);
kit->intf = intf;
+ INIT_WORK(&kit->do_notify, do_notify, kit);
usb_fill_int_urb(kit->irq, kit->udev, pipe, kit->data,
- (maxp > 8 ? 8 : maxp),
+ maxp > URB_INT_SIZE ? URB_INT_SIZE : maxp,
interfacekit_irq, kit, endpoint->bInterval);
kit->irq->transfer_dma = kit->data_dma;
kit->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
usb_set_intfdata(intf, kit);
if (usb_submit_urb(kit->irq, GFP_KERNEL)) {
- return -EIO;
+ rc = -EIO;
+ goto out;
}
if (ifkit->outputs >= 4) {
device_create_file(&intf->dev, &dev_attr_output3);
device_create_file(&intf->dev, &dev_attr_output4);
}
- if (ifkit->outputs == 8) {
+ if (ifkit->outputs >= 8) {
device_create_file(&intf->dev, &dev_attr_output5);
device_create_file(&intf->dev, &dev_attr_output6);
device_create_file(&intf->dev, &dev_attr_output7);
device_create_file(&intf->dev, &dev_attr_output8);
}
+ if (ifkit->outputs == 16) {
+ device_create_file(&intf->dev, &dev_attr_output9);
+ device_create_file(&intf->dev, &dev_attr_output10);
+ device_create_file(&intf->dev, &dev_attr_output11);
+ device_create_file(&intf->dev, &dev_attr_output12);
+ device_create_file(&intf->dev, &dev_attr_output13);
+ device_create_file(&intf->dev, &dev_attr_output14);
+ device_create_file(&intf->dev, &dev_attr_output15);
+ device_create_file(&intf->dev, &dev_attr_output16);
+ }
if (ifkit->inputs >= 4) {
device_create_file(&intf->dev, &dev_attr_input1);
device_create_file(&intf->dev, &dev_attr_input3);
device_create_file(&intf->dev, &dev_attr_input4);
}
- if (ifkit->inputs == 8) {
+ if (ifkit->inputs >= 8) {
device_create_file(&intf->dev, &dev_attr_input5);
device_create_file(&intf->dev, &dev_attr_input6);
device_create_file(&intf->dev, &dev_attr_input7);
device_create_file(&intf->dev, &dev_attr_input8);
}
+ if (ifkit->inputs == 16) {
+ device_create_file(&intf->dev, &dev_attr_input9);
+ device_create_file(&intf->dev, &dev_attr_input10);
+ device_create_file(&intf->dev, &dev_attr_input11);
+ device_create_file(&intf->dev, &dev_attr_input12);
+ device_create_file(&intf->dev, &dev_attr_input13);
+ device_create_file(&intf->dev, &dev_attr_input14);
+ device_create_file(&intf->dev, &dev_attr_input15);
+ device_create_file(&intf->dev, &dev_attr_input16);
+ }
if (ifkit->sensors >= 4) {
device_create_file(&intf->dev, &dev_attr_sensor1);
device_create_file(&intf->dev, &dev_attr_sensor6);
device_create_file(&intf->dev, &dev_attr_sensor7);
}
- if (ifkit->sensors == 8) {
+ if (ifkit->sensors == 8)
device_create_file(&intf->dev, &dev_attr_sensor8);
- }
if (ifkit->has_lcd)
device_create_file(&intf->dev, &dev_attr_lcd);
ifkit->sensors, ifkit->inputs, ifkit->outputs);
return 0;
+
+out:
+ if (kit) {
+ if (kit->irq)
+ usb_free_urb(kit->irq);
+ if (kit->data)
+ usb_buffer_free(dev, URB_INT_SIZE, kit->data, kit->data_dma);
+ kfree(kit);
+ }
+
+ return rc;
}
static void interfacekit_disconnect(struct usb_interface *interface)
{
- struct phidget_interfacekit *kit;
+ struct interfacekit *kit;
kit = usb_get_intfdata(interface);
usb_set_intfdata(interface, NULL);
if (!kit)
return;
+ usb_kill_urb(kit->irq);
+ usb_free_urb(kit->irq);
+ usb_buffer_free(kit->udev, URB_INT_SIZE, kit->data, kit->data_dma);
+
+ cancel_delayed_work(&kit->do_notify);
+
if (kit->ifkit->outputs >= 4) {
device_remove_file(&interface->dev, &dev_attr_output1);
device_remove_file(&interface->dev, &dev_attr_output2);
device_remove_file(&interface->dev, &dev_attr_output3);
device_remove_file(&interface->dev, &dev_attr_output4);
}
- if (kit->ifkit->outputs == 8) {
+ if (kit->ifkit->outputs >= 8) {
device_remove_file(&interface->dev, &dev_attr_output5);
device_remove_file(&interface->dev, &dev_attr_output6);
device_remove_file(&interface->dev, &dev_attr_output7);
device_remove_file(&interface->dev, &dev_attr_output8);
}
+ if (kit->ifkit->outputs == 16) {
+ device_remove_file(&interface->dev, &dev_attr_output9);
+ device_remove_file(&interface->dev, &dev_attr_output10);
+ device_remove_file(&interface->dev, &dev_attr_output11);
+ device_remove_file(&interface->dev, &dev_attr_output12);
+ device_remove_file(&interface->dev, &dev_attr_output13);
+ device_remove_file(&interface->dev, &dev_attr_output14);
+ device_remove_file(&interface->dev, &dev_attr_output15);
+ device_remove_file(&interface->dev, &dev_attr_output16);
+ }
if (kit->ifkit->inputs >= 4) {
device_remove_file(&interface->dev, &dev_attr_input1);
device_remove_file(&interface->dev, &dev_attr_input3);
device_remove_file(&interface->dev, &dev_attr_input4);
}
- if (kit->ifkit->inputs == 8) {
+ if (kit->ifkit->inputs >= 8) {
device_remove_file(&interface->dev, &dev_attr_input5);
device_remove_file(&interface->dev, &dev_attr_input6);
device_remove_file(&interface->dev, &dev_attr_input7);
device_remove_file(&interface->dev, &dev_attr_input8);
}
+ if (kit->ifkit->inputs == 16) {
+ device_remove_file(&interface->dev, &dev_attr_input9);
+ device_remove_file(&interface->dev, &dev_attr_input10);
+ device_remove_file(&interface->dev, &dev_attr_input11);
+ device_remove_file(&interface->dev, &dev_attr_input12);
+ device_remove_file(&interface->dev, &dev_attr_input13);
+ device_remove_file(&interface->dev, &dev_attr_input14);
+ device_remove_file(&interface->dev, &dev_attr_input15);
+ device_remove_file(&interface->dev, &dev_attr_input16);
+ }
if (kit->ifkit->sensors >= 4) {
device_remove_file(&interface->dev, &dev_attr_sensor1);
device_remove_file(&interface->dev, &dev_attr_sensor6);
device_remove_file(&interface->dev, &dev_attr_sensor7);
}
- if (kit->ifkit->sensors == 8) {
+ if (kit->ifkit->sensors == 8)
device_remove_file(&interface->dev, &dev_attr_sensor8);
- }
+
if (kit->ifkit->has_lcd)
device_remove_file(&interface->dev, &dev_attr_lcd);
dev_info(&interface->dev, "USB PhidgetInterfaceKit %d/%d/%d detached\n",
kit->ifkit->sensors, kit->ifkit->inputs, kit->ifkit->outputs);
- usb_kill_urb(kit->irq);
- usb_free_urb(kit->irq);
- usb_buffer_free(kit->udev, 8, kit->data, kit->data_dma);
-
usb_put_dev(kit->udev);
kfree(kit);
}
*/
#include <linux/config.h>
+#include <linux/mutex.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/signal.h>
#include <linux/vmalloc.h>
#include "sisusb.h"
+#include "sisusb_init.h"
#ifdef INCL_SISUSB_CON
#include <linux/font.h>
/* Forward declarations / clean-up routines */
#ifdef INCL_SISUSB_CON
-int sisusb_setreg(struct sisusb_usb_data *sisusb, int port, u8 data);
-int sisusb_getreg(struct sisusb_usb_data *sisusb, int port, u8 *data);
-int sisusb_setidxreg(struct sisusb_usb_data *sisusb, int port, u8 index, u8 data);
-int sisusb_getidxreg(struct sisusb_usb_data *sisusb, int port, u8 index, u8 *data);
-int sisusb_setidxregandor(struct sisusb_usb_data *sisusb, int port, u8 idx, u8 myand, u8 myor);
-int sisusb_setidxregor(struct sisusb_usb_data *sisusb, int port, u8 index, u8 myor);
-int sisusb_setidxregand(struct sisusb_usb_data *sisusb, int port, u8 idx, u8 myand);
-
-int sisusb_writeb(struct sisusb_usb_data *sisusb, u32 adr, u8 data);
-int sisusb_readb(struct sisusb_usb_data *sisusb, u32 adr, u8 *data);
-int sisusb_writew(struct sisusb_usb_data *sisusb, u32 adr, u16 data);
-int sisusb_readw(struct sisusb_usb_data *sisusb, u32 adr, u16 *data);
-int sisusb_copy_memory(struct sisusb_usb_data *sisusb, char *src,
- u32 dest, int length, size_t *bytes_written);
-
-int sisusb_reset_text_mode(struct sisusb_usb_data *sisusb, int init);
-
-extern int SiSUSBSetMode(struct SiS_Private *SiS_Pr, unsigned short ModeNo);
-extern int SiSUSBSetVESAMode(struct SiS_Private *SiS_Pr, unsigned short VModeNo);
-
-extern void sisusb_init_concode(void);
-extern int sisusb_console_init(struct sisusb_usb_data *sisusb, int first, int last);
-extern void sisusb_console_exit(struct sisusb_usb_data *sisusb);
-
-extern void sisusb_set_cursor(struct sisusb_usb_data *sisusb, unsigned int location);
-
-extern int sisusbcon_do_font_op(struct sisusb_usb_data *sisusb, int set, int slot,
- u8 *arg, int cmapsz, int ch512, int dorecalc,
- struct vc_data *c, int fh, int uplock);
-
static int sisusb_first_vc = 0;
static int sisusb_last_vc = 0;
module_param_named(first, sisusb_first_vc, int, 0);
static struct usb_driver sisusb_driver;
-DECLARE_MUTEX(disconnect_sem);
+DEFINE_MUTEX(disconnect_mutex);
static void
sisusb_free_buffers(struct sisusb_usb_data *sisusb)
}
#endif
-#ifndef INCL_SISUSB_CON
-static
-#endif
int
sisusb_setidxreg(struct sisusb_usb_data *sisusb, int port, u8 index, u8 data)
{
return ret;
}
-#ifndef INCL_SISUSB_CON
-static
-#endif
int
sisusb_getidxreg(struct sisusb_usb_data *sisusb, int port, u8 index, u8 *data)
{
return ret;
}
-#ifndef INCL_SISUSB_CON
-static
-#endif
int
sisusb_setidxregandor(struct sisusb_usb_data *sisusb, int port, u8 idx,
u8 myand, u8 myor)
return ret;
}
-#ifndef INCL_SISUSB_CON
-static
-#endif
int
sisusb_setidxregor(struct sisusb_usb_data *sisusb, int port, u8 index, u8 myor)
{
return(sisusb_setidxregandor(sisusb, port, index, 0xff, myor));
}
-#ifndef INCL_SISUSB_CON
-static
-#endif
int
sisusb_setidxregand(struct sisusb_usb_data *sisusb, int port, u8 idx, u8 myand)
{
return(sisusb_read_memio_byte(sisusb, SISUSB_TYPE_MEM, adr, data));
}
+#if 0
+
int
sisusb_writew(struct sisusb_usb_data *sisusb, u32 adr, u16 data)
{
return(sisusb_read_memio_word(sisusb, SISUSB_TYPE_MEM, adr, data));
}
+#endif /* 0 */
+
int
sisusb_copy_memory(struct sisusb_usb_data *sisusb, char *src,
u32 dest, int length, size_t *bytes_written)
struct usb_interface *interface;
int subminor = iminor(inode);
- down(&disconnect_sem);
+ mutex_lock(&disconnect_mutex);
if (!(interface = usb_find_interface(&sisusb_driver, subminor))) {
printk(KERN_ERR "sisusb[%d]: Failed to find interface\n",
subminor);
- up(&disconnect_sem);
+ mutex_unlock(&disconnect_mutex);
return -ENODEV;
}
if (!(sisusb = usb_get_intfdata(interface))) {
- up(&disconnect_sem);
+ mutex_unlock(&disconnect_mutex);
return -ENODEV;
}
- down(&sisusb->lock);
+ mutex_lock(&sisusb->lock);
if (!sisusb->present || !sisusb->ready) {
- up(&sisusb->lock);
- up(&disconnect_sem);
+ mutex_unlock(&sisusb->lock);
+ mutex_unlock(&disconnect_mutex);
return -ENODEV;
}
if (sisusb->isopen) {
- up(&sisusb->lock);
- up(&disconnect_sem);
+ mutex_unlock(&sisusb->lock);
+ mutex_unlock(&disconnect_mutex);
return -EBUSY;
}
if (!sisusb->devinit) {
if (sisusb->sisusb_dev->speed == USB_SPEED_HIGH) {
if (sisusb_init_gfxdevice(sisusb, 0)) {
- up(&sisusb->lock);
- up(&disconnect_sem);
+ mutex_unlock(&sisusb->lock);
+ mutex_unlock(&disconnect_mutex);
printk(KERN_ERR
"sisusbvga[%d]: Failed to initialize "
"device\n",
return -EIO;
}
} else {
- up(&sisusb->lock);
- up(&disconnect_sem);
+ mutex_unlock(&sisusb->lock);
+ mutex_unlock(&disconnect_mutex);
printk(KERN_ERR
"sisusbvga[%d]: Device not attached to "
"USB 2.0 hub\n",
file->private_data = sisusb;
- up(&sisusb->lock);
+ mutex_unlock(&sisusb->lock);
- up(&disconnect_sem);
+ mutex_unlock(&disconnect_mutex);
return 0;
}
struct sisusb_usb_data *sisusb;
int myminor;
- down(&disconnect_sem);
+ mutex_lock(&disconnect_mutex);
if (!(sisusb = (struct sisusb_usb_data *)file->private_data)) {
- up(&disconnect_sem);
+ mutex_unlock(&disconnect_mutex);
return -ENODEV;
}
- down(&sisusb->lock);
+ mutex_lock(&sisusb->lock);
if (sisusb->present) {
/* Wait for all URBs to finish if device still present */
sisusb->isopen = 0;
file->private_data = NULL;
- up(&sisusb->lock);
+ mutex_unlock(&sisusb->lock);
/* decrement the usage count on our device */
kref_put(&sisusb->kref, sisusb_delete);
- up(&disconnect_sem);
+ mutex_unlock(&disconnect_mutex);
return 0;
}
if (!(sisusb = (struct sisusb_usb_data *)file->private_data))
return -ENODEV;
- down(&sisusb->lock);
+ mutex_lock(&sisusb->lock);
/* Sanity check */
if (!sisusb->present || !sisusb->ready || !sisusb->sisusb_dev) {
- up(&sisusb->lock);
+ mutex_unlock(&sisusb->lock);
return -ENODEV;
}
(*ppos) <= SISUSB_PCI_PSEUDO_PCIBASE + 0x5c) {
if (count != 4) {
- up(&sisusb->lock);
+ mutex_unlock(&sisusb->lock);
return -EINVAL;
}
(*ppos) += bytes_read;
- up(&sisusb->lock);
+ mutex_unlock(&sisusb->lock);
return errno ? errno : bytes_read;
}
if (!(sisusb = (struct sisusb_usb_data *)file->private_data))
return -ENODEV;
- down(&sisusb->lock);
+ mutex_lock(&sisusb->lock);
/* Sanity check */
if (!sisusb->present || !sisusb->ready || !sisusb->sisusb_dev) {
- up(&sisusb->lock);
+ mutex_unlock(&sisusb->lock);
return -ENODEV;
}
(*ppos) <= SISUSB_PCI_PSEUDO_PCIBASE + SISUSB_PCI_PCONFSIZE) {
if (count != 4) {
- up(&sisusb->lock);
+ mutex_unlock(&sisusb->lock);
return -EINVAL;
}
(*ppos) += bytes_written;
- up(&sisusb->lock);
+ mutex_unlock(&sisusb->lock);
return errno ? errno : bytes_written;
}
if (!(sisusb = (struct sisusb_usb_data *)file->private_data))
return -ENODEV;
- down(&sisusb->lock);
+ mutex_lock(&sisusb->lock);
/* Sanity check */
if (!sisusb->present || !sisusb->ready || !sisusb->sisusb_dev) {
- up(&sisusb->lock);
+ mutex_unlock(&sisusb->lock);
return -ENODEV;
}
ret = -EINVAL;
}
- up(&sisusb->lock);
+ mutex_unlock(&sisusb->lock);
return ret;
}
if (!(sisusb = (struct sisusb_usb_data *)file->private_data))
return -ENODEV;
- down(&sisusb->lock);
+ mutex_lock(&sisusb->lock);
/* Sanity check */
if (!sisusb->present || !sisusb->ready || !sisusb->sisusb_dev) {
}
err_out:
- up(&sisusb->lock);
+ mutex_unlock(&sisusb->lock);
return retval;
}
}
kref_init(&sisusb->kref);
- init_MUTEX(&(sisusb->lock));
+ mutex_init(&(sisusb->lock));
/* Register device */
if ((retval = usb_register_dev(intf, &usb_sisusb_class))) {
* protect all other routines from the disconnect
* case, not the other way round.
*/
- down(&disconnect_sem);
+ mutex_lock(&disconnect_mutex);
- down(&sisusb->lock);
+ mutex_lock(&sisusb->lock);
/* Wait for all URBs to complete and kill them in case (MUST do) */
if (!sisusb_wait_all_out_complete(sisusb))
sisusb->present = 0;
sisusb->ready = 0;
- up(&sisusb->lock);
+ mutex_unlock(&sisusb->lock);
/* decrement our usage count */
kref_put(&sisusb->kref, sisusb_delete);
- up(&disconnect_sem);
+ mutex_unlock(&disconnect_mutex);
printk(KERN_INFO "sisusbvga[%d]: Disconnected\n", minor);
}
#define SISUSB_NEW_CONFIG_COMPAT
#endif
+#include <linux/mutex.h>
+
/* For older kernels, support for text consoles is by default
* off. To ensable text console support, change the following:
*/
#define INCL_SISUSB_CON 1
#endif
-#ifdef INCL_SISUSB_CON
#include <linux/console.h>
#include <linux/vt_kern.h>
#include "sisusb_struct.h"
-#endif
/* USB related */
struct usb_interface *interface;
struct kref kref;
wait_queue_head_t wait_q; /* for syncind and timeouts */
- struct semaphore lock; /* general race avoidance */
+ struct mutex lock; /* general race avoidance */
unsigned int ifnum; /* interface number of the USB device */
int minor; /* minor (for logging clarity) */
int isopen; /* !=0 if open */
*/
#include <linux/config.h>
+#include <linux/mutex.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/signal.h>
#include <linux/vmalloc.h>
#include "sisusb.h"
+#include "sisusb_init.h"
#ifdef INCL_SISUSB_CON
-extern int sisusb_setreg(struct sisusb_usb_data *, int, u8);
-extern int sisusb_getreg(struct sisusb_usb_data *, int, u8 *);
-extern int sisusb_setidxreg(struct sisusb_usb_data *, int, u8, u8);
-extern int sisusb_getidxreg(struct sisusb_usb_data *, int, u8, u8 *);
-extern int sisusb_setidxregor(struct sisusb_usb_data *, int, u8, u8);
-extern int sisusb_setidxregand(struct sisusb_usb_data *, int, u8, u8);
-extern int sisusb_setidxregandor(struct sisusb_usb_data *, int, u8, u8, u8);
-
-extern int sisusb_writeb(struct sisusb_usb_data *sisusb, u32 adr, u8 data);
-extern int sisusb_readb(struct sisusb_usb_data *sisusb, u32 adr, u8 *data);
-extern int sisusb_writew(struct sisusb_usb_data *sisusb, u32 adr, u16 data);
-extern int sisusb_readw(struct sisusb_usb_data *sisusb, u32 adr, u16 *data);
-extern int sisusb_copy_memory(struct sisusb_usb_data *sisusb, char *src,
- u32 dest, int length, size_t *bytes_written);
-
-extern void sisusb_delete(struct kref *kref);
-extern int sisusb_reset_text_mode(struct sisusb_usb_data *sisusb, int init);
-
-extern int SiSUSBSetMode(struct SiS_Private *SiS_Pr, unsigned short ModeNo);
#define sisusbcon_writew(val, addr) (*(addr) = (val))
#define sisusbcon_readw(addr) (*(addr))
/* Forward declaration */
static const struct consw sisusb_con;
-extern struct semaphore disconnect_sem;
-
static inline void
sisusbcon_memsetw(u16 *s, u16 c, unsigned int count)
{
if (!(sisusb = sisusb_get_sisusb(console)))
return NULL;
- down(&sisusb->lock);
+ mutex_lock(&sisusb->lock);
if (!sisusb_sisusb_valid(sisusb) ||
!sisusb->havethisconsole[console]) {
- up(&sisusb->lock);
+ mutex_unlock(&sisusb->lock);
return NULL;
}
* are set up/restored.
*/
- down(&disconnect_sem);
+ mutex_lock(&disconnect_mutex);
if (!(sisusb = sisusb_get_sisusb(c->vc_num))) {
- up(&disconnect_sem);
+ mutex_unlock(&disconnect_mutex);
return;
}
- down(&sisusb->lock);
+ mutex_lock(&sisusb->lock);
if (!sisusb_sisusb_valid(sisusb)) {
- up(&sisusb->lock);
- up(&disconnect_sem);
+ mutex_unlock(&sisusb->lock);
+ mutex_unlock(&disconnect_mutex);
return;
}
if (!*c->vc_uni_pagedir_loc)
con_set_default_unimap(c);
- up(&sisusb->lock);
+ mutex_unlock(&sisusb->lock);
- up(&disconnect_sem);
+ mutex_unlock(&disconnect_mutex);
if (init) {
c->vc_cols = cols;
* and others, ie not under our control.
*/
- down(&disconnect_sem);
+ mutex_lock(&disconnect_mutex);
if (!(sisusb = sisusb_get_sisusb(c->vc_num))) {
- up(&disconnect_sem);
+ mutex_unlock(&disconnect_mutex);
return;
}
- down(&sisusb->lock);
+ mutex_lock(&sisusb->lock);
/* Clear ourselves in mysisusbs */
mysisusbs[c->vc_num] = NULL;
}
}
- up(&sisusb->lock);
+ mutex_unlock(&sisusb->lock);
/* decrement the usage count on our sisusb */
kref_put(&sisusb->kref, sisusb_delete);
- up(&disconnect_sem);
+ mutex_unlock(&disconnect_mutex);
}
/* interface routine */
#endif
if (sisusb_is_inactive(c, sisusb)) {
- up(&sisusb->lock);
+ mutex_unlock(&sisusb->lock);
return;
}
sisusb_copy_memory(sisusb, (char *)SISUSB_VADDR(x, y),
(u32)SISUSB_HADDR(x, y), 2, &written);
- up(&sisusb->lock);
+ mutex_unlock(&sisusb->lock);
}
/* Interface routine */
sisusbcon_writew(sisusbcon_readw(s++), dest++);
if (sisusb_is_inactive(c, sisusb)) {
- up(&sisusb->lock);
+ mutex_unlock(&sisusb->lock);
return;
}
sisusb_copy_memory(sisusb, (char *)SISUSB_VADDR(x, y),
(u32)SISUSB_HADDR(x, y), count * 2, &written);
- up(&sisusb->lock);
+ mutex_unlock(&sisusb->lock);
}
/* Interface routine */
}
if (sisusb_is_inactive(c, sisusb)) {
- up(&sisusb->lock);
+ mutex_unlock(&sisusb->lock);
return;
}
sisusb_copy_memory(sisusb, (unsigned char *)SISUSB_VADDR(x, y),
(u32)SISUSB_HADDR(x, y), length, &written);
- up(&sisusb->lock);
+ mutex_unlock(&sisusb->lock);
}
/* Interface routine */
#endif
if (sisusb_is_inactive(c, sisusb)) {
- up(&sisusb->lock);
+ mutex_unlock(&sisusb->lock);
return;
}
sisusb_copy_memory(sisusb, (unsigned char *)SISUSB_VADDR(dx, dy),
(u32)SISUSB_HADDR(dx, dy), length, &written);
- up(&sisusb->lock);
+ mutex_unlock(&sisusb->lock);
}
/* interface routine */
/* Don't write to screen if in gfx mode */
if (sisusb_is_inactive(c, sisusb)) {
- up(&sisusb->lock);
+ mutex_unlock(&sisusb->lock);
return 0;
}
* as origin.
*/
if (c->vc_origin == (unsigned long)c->vc_screenbuf) {
- up(&sisusb->lock);
+ mutex_unlock(&sisusb->lock);
printk(KERN_DEBUG "sisusb: ASSERT ORIGIN != SCREENBUF!\n");
return 0;
}
(u32)SISUSB_HADDR(0, 0),
length, &written);
- up(&sisusb->lock);
+ mutex_unlock(&sisusb->lock);
return 0;
}
/* sisusb->lock is down */
if (sisusb_is_inactive(c, sisusb)) {
- up(&sisusb->lock);
+ mutex_unlock(&sisusb->lock);
return;
}
sisusbcon_memcpyw((u16 *)c->vc_screenbuf, (u16 *)c->vc_origin,
length);
- up(&sisusb->lock);
+ mutex_unlock(&sisusb->lock);
}
/* interface routine */
/* sisusb->lock is down */
if (sisusb_is_inactive(c, sisusb)) {
- up(&sisusb->lock);
+ mutex_unlock(&sisusb->lock);
return -EINVAL;
}
break;
}
- up(&sisusb->lock);
+ mutex_unlock(&sisusb->lock);
return 0;
}
sisusb->is_gfx = blank ? 1 : 0;
if (sisusb_is_inactive(c, sisusb)) {
- up(&sisusb->lock);
+ mutex_unlock(&sisusb->lock);
return 0;
}
cr63 = 0x40;
break;
default:
- up(&sisusb->lock);
+ mutex_unlock(&sisusb->lock);
return -EINVAL;
}
}
- up(&sisusb->lock);
+ mutex_unlock(&sisusb->lock);
return ret;
}
/* sisusb->lock is down */
if (sisusb_is_inactive(c, sisusb)) {
- up(&sisusb->lock);
+ mutex_unlock(&sisusb->lock);
return 0;
}
sisusbcon_set_start_address(sisusb, c);
- up(&sisusb->lock);
+ mutex_unlock(&sisusb->lock);
return 1;
}
/* sisusb->lock is down */
if (sisusb_is_inactive(c, sisusb)) {
- up(&sisusb->lock);
+ mutex_unlock(&sisusb->lock);
return;
}
if (mode == CM_ERASE) {
sisusb_setidxregor(sisusb, SISCR, 0x0a, 0x20);
sisusb->sisusb_cursor_size_to = -1;
- up(&sisusb->lock);
+ mutex_unlock(&sisusb->lock);
return;
}
sisusb->sisusb_cursor_size_to = to;
}
- up(&sisusb->lock);
+ mutex_unlock(&sisusb->lock);
}
static int
sisusb_copy_memory(sisusb, (char *)SISUSB_VADDR(0, t),
(u32)SISUSB_HADDR(0, t), length, &written);
- up(&sisusb->lock);
+ mutex_unlock(&sisusb->lock);
return 1;
}
/* sisusb->lock is down */
if (sisusb_is_inactive(c, sisusb)) {
- up(&sisusb->lock);
+ mutex_unlock(&sisusb->lock);
return 0;
}
c->vc_pos = c->vc_pos - oldorigin + c->vc_origin;
- up(&sisusb->lock);
+ mutex_unlock(&sisusb->lock);
return 1;
}
/* sisusb->lock is down */
if (sisusb_is_inactive(c, sisusb) || sisusb->con_blanked) {
- up(&sisusb->lock);
+ mutex_unlock(&sisusb->lock);
return 0;
}
sisusb->con_rolled_over = 0;
- up(&sisusb->lock);
+ mutex_unlock(&sisusb->lock);
return 1;
}
fh = sisusb->current_font_height;
- up(&sisusb->lock);
+ mutex_unlock(&sisusb->lock);
/* We are quite unflexible as regards resizing. The vt code
* handles sizes where the line length isn't equal the pitch
if ((slot != 0 && slot != 2) || !fh) {
if (uplock)
- up(&sisusb->lock);
+ mutex_unlock(&sisusb->lock);
return -EINVAL;
}
}
if (uplock)
- up(&sisusb->lock);
+ mutex_unlock(&sisusb->lock);
if (dorecalc && c) {
int i, rows = c->vc_scan_lines / fh;
font_op_error:
if (uplock)
- up(&sisusb->lock);
+ mutex_unlock(&sisusb->lock);
return -EIO;
}
font->charcount = 256;
if (!font->data) {
- up(&sisusb->lock);
+ mutex_unlock(&sisusb->lock);
return 0;
}
if (!sisusb->font_backup) {
- up(&sisusb->lock);
+ mutex_unlock(&sisusb->lock);
return -ENODEV;
}
/* Copy 256 chars only, like vgacon */
memcpy(font->data, sisusb->font_backup, 256 * 32);
- up(&sisusb->lock);
+ mutex_unlock(&sisusb->lock);
return 0;
}
#define SISUSBCONDUMMY (void *)sisusbdummycon_dummy
-const struct consw sisusb_dummy_con = {
+static const struct consw sisusb_dummy_con = {
.owner = THIS_MODULE,
.con_startup = sisusbdummycon_startup,
.con_init = sisusbdummycon_init,
{
int i, ret, minor = sisusb->minor;
- down(&disconnect_sem);
+ mutex_lock(&disconnect_mutex);
- down(&sisusb->lock);
+ mutex_lock(&sisusb->lock);
/* Erm.. that should not happen */
if (sisusb->haveconsole || !sisusb->SiS_Pr) {
- up(&sisusb->lock);
- up(&disconnect_sem);
+ mutex_unlock(&sisusb->lock);
+ mutex_unlock(&disconnect_mutex);
return 1;
}
if (first > last ||
first > MAX_NR_CONSOLES ||
last > MAX_NR_CONSOLES) {
- up(&sisusb->lock);
- up(&disconnect_sem);
+ mutex_unlock(&sisusb->lock);
+ mutex_unlock(&disconnect_mutex);
return 1;
}
/* If gfxcore not initialized or no consoles given, quit graciously */
if (!sisusb->gfxinit || first < 1 || last < 1) {
- up(&sisusb->lock);
- up(&disconnect_sem);
+ mutex_unlock(&sisusb->lock);
+ mutex_unlock(&disconnect_mutex);
return 0;
}
/* Set up text mode (and upload default font) */
if (sisusb_reset_text_mode(sisusb, 1)) {
- up(&sisusb->lock);
- up(&disconnect_sem);
+ mutex_unlock(&sisusb->lock);
+ mutex_unlock(&disconnect_mutex);
printk(KERN_ERR
"sisusbvga[%d]: Failed to set up text mode\n",
minor);
/* Allocate screen buffer */
if (!(sisusb->scrbuf = (unsigned long)vmalloc(sisusb->scrbuf_size))) {
- up(&sisusb->lock);
- up(&disconnect_sem);
+ mutex_unlock(&sisusb->lock);
+ mutex_unlock(&disconnect_mutex);
printk(KERN_ERR
"sisusbvga[%d]: Failed to allocate screen buffer\n",
minor);
return 1;
}
- up(&sisusb->lock);
- up(&disconnect_sem);
+ mutex_unlock(&sisusb->lock);
+ mutex_unlock(&disconnect_mutex);
/* Now grab the desired console(s) */
ret = take_over_console(&sisusb_con, first - 1, last - 1, 0);
/* HELPER: Get ModeID */
/*********************************************/
+#if 0
unsigned short
SiSUSB_GetModeID(int HDisplay, int VDisplay, int Depth)
{
return ModeIndex;
}
+#endif /* 0 */
/*********************************************/
/* HELPER: SetReg, GetReg */
/* HELPER: Init Port Addresses */
/*********************************************/
-void
+static void
SiSUSBRegInit(struct SiS_Private *SiS_Pr, unsigned long BaseAddr)
{
SiS_Pr->SiS_P3c4 = BaseAddr + 0x14;
0x41}} /* 0x54 */
};
-static struct SiS_VCLKData SiSUSB_VCLKData[] =
+static const struct SiS_VCLKData SiSUSB_VCLKData[] =
{
{ 0x1b,0xe1, 25}, /* 0x00 */
{ 0x4e,0xe4, 28}, /* 0x01 */
{ 0x2b,0xc2, 35} /* 0x71 768@576@60 */
};
-void SiSUSBRegInit(struct SiS_Private *SiS_Pr, unsigned long BaseAddr);
-unsigned short SiSUSB_GetModeID(int HDisplay, int VDisplay, int Depth);
+extern struct mutex disconnect_mutex;
+
int SiSUSBSetMode(struct SiS_Private *SiS_Pr, unsigned short ModeNo);
int SiSUSBSetVESAMode(struct SiS_Private *SiS_Pr, unsigned short VModeNo);
extern int sisusb_setidxregand(struct sisusb_usb_data *sisusb, int port,
u8 idx, u8 myand);
+void sisusb_delete(struct kref *kref);
+int sisusb_writeb(struct sisusb_usb_data *sisusb, u32 adr, u8 data);
+int sisusb_readb(struct sisusb_usb_data *sisusb, u32 adr, u8 *data);
+int sisusb_copy_memory(struct sisusb_usb_data *sisusb, char *src,
+ u32 dest, int length, size_t *bytes_written);
+int sisusb_reset_text_mode(struct sisusb_usb_data *sisusb, int init);
+int sisusbcon_do_font_op(struct sisusb_usb_data *sisusb, int set, int slot,
+ u8 *arg, int cmapsz, int ch512, int dorecalc,
+ struct vc_data *c, int fh, int uplock);
+void sisusb_set_cursor(struct sisusb_usb_data *sisusb, unsigned int location);
+int sisusb_console_init(struct sisusb_usb_data *sisusb, int first, int last);
+void sisusb_console_exit(struct sisusb_usb_data *sisusb);
+void sisusb_init_concode(void);
+
#endif
const struct SiS_Ext *SiS_EModeIDTable;
const struct SiS_Ext2 *SiS_RefIndex;
const struct SiS_CRT1Table *SiS_CRT1Table;
- struct SiS_VCLKData *SiS_VCLKData;
+ const struct SiS_VCLKData *SiS_VCLKData;
const struct SiS_ModeResInfo *SiS_ModeResInfo;
};
if (u == urb || !u->dev)
continue;
+ spin_unlock(&ctx->lock);
status = usb_unlink_urb (u);
+ spin_lock(&ctx->lock);
switch (status) {
case -EINPROGRESS:
case -EBUSY:
unsigned pending;
spinlock_t lock;
struct completion done;
+ int submit_error;
unsigned long errors;
+ unsigned long packet_count;
struct usbtest_dev *dev;
};
spin_lock(&ctx->lock);
ctx->count--;
+ ctx->packet_count += urb->number_of_packets;
if (urb->error_count > 0)
ctx->errors += urb->error_count;
+ else if (urb->status != 0)
+ ctx->errors += urb->number_of_packets;
- if (urb->status == 0 && ctx->count > (ctx->pending - 1)) {
+ if (urb->status == 0 && ctx->count > (ctx->pending - 1)
+ && !ctx->submit_error) {
int status = usb_submit_urb (urb, GFP_ATOMIC);
switch (status) {
case 0:
status);
/* FALLTHROUGH */
case -ENODEV: /* disconnected */
+ case -ESHUTDOWN: /* endpoint disabled */
+ ctx->submit_error = 1;
break;
}
}
if (ctx->pending == 0) {
if (ctx->errors)
dev_dbg (&ctx->dev->intf->dev,
- "iso test, %lu errors\n",
- ctx->errors);
+ "iso test, %lu errors out of %lu\n",
+ ctx->errors, ctx->packet_count);
complete (&ctx->done);
}
done:
struct usb_device *udev;
unsigned i;
unsigned long packets = 0;
- int status;
+ int status = 0;
struct urb *urbs[10]; /* FIXME no limit */
if (param->sglen > 10)
return -EDOM;
+ memset(&context, 0, sizeof context);
context.count = param->iterations * param->sglen;
- context.pending = param->sglen;
- context.errors = 0;
context.dev = dev;
init_completion (&context.done);
spin_lock_init (&context.lock);
spin_lock_irq (&context.lock);
for (i = 0; i < param->sglen; i++) {
+ ++context.pending;
status = usb_submit_urb (urbs [i], SLAB_ATOMIC);
if (status < 0) {
ERROR (dev, "submit iso[%d], error %d\n", i, status);
simple_free_urb (urbs [i]);
context.pending--;
+ context.submit_error = 1;
+ break;
}
}
spin_unlock_irq (&context.lock);
wait_for_completion (&context.done);
- return 0;
+
+ /*
+ * Isochronous transfers are expected to fail sometimes. As an
+ * arbitrary limit, we will report an error if any submissions
+ * fail or if the transfer failure rate is > 10%.
+ */
+ if (status != 0)
+ ;
+ else if (context.submit_error)
+ status = -EACCES;
+ else if (context.errors > context.packet_count / 10)
+ status = -EIO;
+ return status;
fail:
for (i = 0; i < param->sglen; i++) {
#include <linux/usb.h> /* Only needed for declarations in usb_mon.h */
#include "usb_mon.h"
-#ifdef __i386__ /* CONFIG_ARCH_I386 does not exit */
+/*
+ * PC-compatibles, are, fortunately, sufficiently cache-coherent for this.
+ */
+#if defined(__i386__) || defined(__x86_64__) /* CONFIG_ARCH_I386 doesn't exit */
#define MON_HAS_UNMAP 1
#define phys_to_page(phys) pfn_to_page((phys) >> PAGE_SHIFT)
if (mbus->nreaders == 0)
goto out_locked;
+ mbus->cnt_events++;
list_for_each (pos, &mbus->r_list) {
r = list_entry(pos, struct mon_reader, r_link);
r->rnf_submit(r->r_data, urb);
/*
*/
-static void mon_submit_error(struct usb_bus *ubus, struct urb *urb, int err)
+static void mon_submit_error(struct usb_bus *ubus, struct urb *urb, int error)
{
struct mon_bus *mbus;
+ unsigned long flags;
+ struct list_head *pos;
+ struct mon_reader *r;
mbus = ubus->mon_bus;
if (mbus == NULL)
goto out_unlocked;
- /*
- * XXX Capture the error code and the 'E' event.
- */
+ spin_lock_irqsave(&mbus->lock, flags);
+ if (mbus->nreaders == 0)
+ goto out_locked;
+ mbus->cnt_events++;
+ list_for_each (pos, &mbus->r_list) {
+ r = list_entry(pos, struct mon_reader, r_link);
+ r->rnf_error(r->r_data, urb, error);
+ }
+
+ spin_unlock_irqrestore(&mbus->lock, flags);
return;
+out_locked:
+ spin_unlock_irqrestore(&mbus->lock, flags);
out_unlocked:
return;
}
}
spin_lock_irqsave(&mbus->lock, flags);
+ mbus->cnt_events++;
list_for_each (pos, &mbus->r_list) {
r = list_entry(pos, struct mon_reader, r_link);
r->rnf_complete(r->r_data, urb);
/*
* Stop monitoring.
- * Obviously this must be well locked, so no need to play with mb's.
*/
static void mon_stop(struct mon_bus *mbus)
{
mbus = inode->u.generic_ip;
sp->slen = snprintf(sp->str, STAT_BUF_SIZE,
- "nreaders %d text_lost %u\n",
- mbus->nreaders, mbus->cnt_text_lost);
+ "nreaders %d events %u text_lost %u\n",
+ mbus->nreaders, mbus->cnt_events, mbus->cnt_text_lost);
file->private_data = sp;
return 0;
/*
* This limit exists to prevent OOMs when the user process stops reading.
+ * If usbmon were available to unprivileged processes, it might be open
+ * to a local DoS. But we have to keep to root in order to prevent
+ * password sniffing from HID devices.
*/
-#define EVENT_MAX 25
+#define EVENT_MAX (2*PAGE_SIZE / sizeof(struct mon_event_text))
-#define PRINTF_DFL 130
+#define PRINTF_DFL 160
struct mon_event_text {
struct list_head e_link;
* number of corner cases, but it seems that the following is
* more or less safe.
*
- * We do not even try to look transfer_buffer, because it can
+ * We do not even try to look at transfer_buffer, because it can
* contain non-NULL garbage in case the upper level promised to
* set DMA for the HCD.
*/
mon_text_event(rp, urb, 'C');
}
+static void mon_text_error(void *data, struct urb *urb, int error)
+{
+ struct mon_reader_text *rp = data;
+ struct mon_event_text *ep;
+
+ if (rp->nevents >= EVENT_MAX ||
+ (ep = kmem_cache_alloc(rp->e_slab, SLAB_ATOMIC)) == NULL) {
+ rp->r.m_bus->cnt_text_lost++;
+ return;
+ }
+
+ ep->type = 'E';
+ ep->pipe = urb->pipe;
+ ep->id = (unsigned long) urb;
+ ep->tstamp = 0;
+ ep->length = 0;
+ ep->status = error;
+
+ ep->setup_flag = '-';
+ ep->data_flag = 'E';
+
+ rp->nevents++;
+ list_add_tail(&ep->e_link, &rp->e_list);
+ wake_up(&rp->wait);
+}
+
/*
* Fetch next event from the circular buffer.
*/
rp->r.m_bus = mbus;
rp->r.r_data = rp;
rp->r.rnf_submit = mon_text_submit;
+ rp->r.rnf_error = mon_text_error;
rp->r.rnf_complete = mon_text_complete;
snprintf(rp->slab_name, SLAB_NAME_SZ, "mon%dt_%lx", ubus->busnum,
struct kref ref; /* Under mon_lock */
/* Stats */
+ unsigned int cnt_events;
unsigned int cnt_text_lost;
};
void *r_data; /* Use container_of instead? */
void (*rnf_submit)(void *data, struct urb *urb);
+ void (*rnf_error)(void *data, struct urb *urb, int error);
void (*rnf_complete)(void *data, struct urb *urb);
};
// ASIX AX88772 10/100
USB_DEVICE (0x0b95, 0x7720),
.driver_info = (unsigned long) &ax88772_info,
+}, {
+ // ASIX AX88178 10/100/1000
+ USB_DEVICE (0x0b95, 0x1780),
+ .driver_info = (unsigned long) &ax88772_info,
}, {
// Linksys USB200M Rev 2
USB_DEVICE (0x13b1, 0x0018),
#include <linux/workqueue.h>
#include <linux/mii.h>
#include <linux/usb.h>
-#include <linux/usb_cdc.h>
+#include <linux/usb/cdc.h>
#include "usbnet.h"
.driver_info = 0,
},
+/* Olympus has some models with a Zaurus-compatible option.
+ * R-1000 uses a FreeScale i.MXL cpu (ARMv4T)
+ */
+{
+ .match_flags = USB_DEVICE_ID_MATCH_INT_INFO
+ | USB_DEVICE_ID_MATCH_DEVICE,
+ .idVendor = 0x07B4,
+ .idProduct = 0x0F02, /* R-1000 */
+ ZAURUS_MASTER_INTERFACE,
+ .driver_info = 0,
+},
+
/*
* WHITELIST!!!
*
/* using ATOMIC, we'd never wake up if we slept */
if ((ret = usb_submit_urb(pegasus->ctrl_urb, GFP_ATOMIC))) {
+ if (ret == -ENODEV)
+ netif_device_detach(pegasus->net);
if (netif_msg_drv(pegasus))
dev_err(&pegasus->intf->dev, "%s, status %d\n",
__FUNCTION__, ret);
set_current_state(TASK_UNINTERRUPTIBLE);
if ((ret = usb_submit_urb(pegasus->ctrl_urb, GFP_ATOMIC))) {
+ if (ret == -ENODEV)
+ netif_device_detach(pegasus->net);
if (netif_msg_drv(pegasus))
dev_err(&pegasus->intf->dev, "%s, status %d\n",
__FUNCTION__, ret);
set_current_state(TASK_UNINTERRUPTIBLE);
if ((ret = usb_submit_urb(pegasus->ctrl_urb, GFP_ATOMIC))) {
+ if (ret == -ENODEV)
+ netif_device_detach(pegasus->net);
if (netif_msg_drv(pegasus))
dev_err(&pegasus->intf->dev, "%s, status %d\n",
__FUNCTION__, ret);
(char *) &pegasus->dr,
pegasus->eth_regs, 3, ctrl_callback, pegasus);
- if ((ret = usb_submit_urb(pegasus->ctrl_urb, GFP_ATOMIC)))
+ if ((ret = usb_submit_urb(pegasus->ctrl_urb, GFP_ATOMIC))) {
+ if (ret == -ENODEV)
+ netif_device_detach(pegasus->net);
if (netif_msg_drv(pegasus))
dev_err(&pegasus->intf->dev, "%s, status %d\n",
__FUNCTION__, ret);
+ }
return ret;
}
usb_rcvbulkpipe(pegasus->usb, 1),
pegasus->rx_skb->data, PEGASUS_MTU + 8,
read_bulk_callback, pegasus);
- if (usb_submit_urb(pegasus->rx_urb, GFP_ATOMIC)) {
+ rx_status = usb_submit_urb(pegasus->rx_urb, GFP_ATOMIC);
+ if (rx_status == -ENODEV)
+ netif_device_detach(pegasus->net);
+ else if (rx_status) {
pegasus->flags |= PEGASUS_RX_URB_FAIL;
goto tl_sched;
} else {
{
pegasus_t *pegasus;
unsigned long flags;
+ int status;
pegasus = (pegasus_t *) data;
if (pegasus->flags & PEGASUS_UNPLUG)
pegasus->rx_skb->data, PEGASUS_MTU + 8,
read_bulk_callback, pegasus);
try_again:
- if (usb_submit_urb(pegasus->rx_urb, GFP_ATOMIC)) {
+ status = usb_submit_urb(pegasus->rx_urb, GFP_ATOMIC);
+ if (status == -ENODEV)
+ netif_device_detach(pegasus->net);
+ else if (status) {
pegasus->flags |= PEGASUS_RX_URB_FAIL;
tasklet_schedule(&pegasus->rx_tl);
} else {
}
status = usb_submit_urb(urb, SLAB_ATOMIC);
+ if (status == -ENODEV)
+ netif_device_detach(pegasus->net);
if (status && netif_msg_timer(pegasus))
printk(KERN_ERR "%s: can't resubmit interrupt urb, %d\n",
net->name, status);
/* cleanup should already have been scheduled */
break;
case -ENODEV: /* disconnect() upcoming */
+ netif_device_detach(pegasus->net);
break;
default:
pegasus->stats.tx_errors++;
pegasus->rx_skb->data, PEGASUS_MTU + 8,
read_bulk_callback, pegasus);
if ((res = usb_submit_urb(pegasus->rx_urb, GFP_KERNEL))) {
+ if (res == -ENODEV)
+ netif_device_detach(pegasus->net);
if (netif_msg_ifup(pegasus))
pr_debug("%s: failed rx_urb, %d", net->name, res);
goto exit;
pegasus->intr_buff, sizeof (pegasus->intr_buff),
intr_callback, pegasus, pegasus->intr_interval);
if ((res = usb_submit_urb(pegasus->intr_urb, GFP_KERNEL))) {
+ if (res == -ENODEV)
+ netif_device_detach(pegasus->net);
if (netif_msg_ifup(pegasus))
pr_debug("%s: failed intr_urb, %d\n", net->name, res);
usb_kill_urb(pegasus->rx_urb);
#include <linux/workqueue.h>
#include <linux/mii.h>
#include <linux/usb.h>
-#include <linux/usb_cdc.h>
+#include <linux/usb/cdc.h>
#include "usbnet.h"
#include <linux/mii.h>
#include <linux/crc32.h>
#include <linux/usb.h>
-#include <linux/usb_cdc.h>
+#include <linux/usb/cdc.h>
#include "usbnet.h"
.check_connect = always_connected,
.bind = zaurus_bind,
.unbind = usbnet_cdc_unbind,
- .tx_fixup = zaurus_tx_fixup,
+ .tx_fixup = zaurus_tx_fixup,
};
#define ZAURUS_STRONGARM_INFO ((unsigned long)&zaurus_sl5x00_info)
.check_connect = always_connected,
.bind = zaurus_bind,
.unbind = usbnet_cdc_unbind,
- .tx_fixup = zaurus_tx_fixup,
+ .tx_fixup = zaurus_tx_fixup,
};
#define ZAURUS_PXA_INFO ((unsigned long)&zaurus_pxa_info)
.check_connect = always_connected,
.bind = zaurus_bind,
.unbind = usbnet_cdc_unbind,
- .tx_fixup = zaurus_tx_fixup,
+ .tx_fixup = zaurus_tx_fixup,
};
#define OLYMPUS_MXL_INFO ((unsigned long)&olympus_mxl_info)
detail->bDetailData[2]);
goto bad_desc;
}
+
+ /* same extra framing as for non-BLAN mode */
+ dev->net->hard_header_len += 6;
+ dev->rx_urb_size = dev->net->hard_header_len
+ + dev->net->mtu;
break;
}
next_desc:
.description = "pseudo-MDLM (BLAN) device",
.flags = FLAG_FRAMING_Z,
.check_connect = always_connected,
- .tx_fixup = zaurus_tx_fixup,
+ .tx_fixup = zaurus_tx_fixup,
.bind = blan_mdlm_bind,
};
static int __init zaurus_init(void)
{
- return usb_register(&zaurus_driver);
+ return usb_register(&zaurus_driver);
}
module_init(zaurus_init);
static void __exit zaurus_exit(void)
{
- usb_deregister(&zaurus_driver);
+ usb_deregister(&zaurus_driver);
}
module_exit(zaurus_exit);
Magic Control Technology Corp. (U232 is one of the model numbers).
This driver also works with Sitecom U232-P25 and D-Link DU-H3SP USB
- BAY devices.
+ BAY, Belkin F5U109, and Belkin F5U409 devices.
To compile this driver as a module, choose M here: the
module will be called mct_u232.
module will be called keyspan_pda.
config USB_SERIAL_OPTION
- tristate "USB Option PCMCIA serial driver"
- depends on USB_SERIAL && USB_OHCI_HCD && PCCARD
+ tristate "USB driver for GSM modems"
+ depends on USB_SERIAL
help
- Say Y here if you want to use an Option card. This is a
- GSM card, controlled by three serial ports which are connected
- via an OHCI adapter located on a PC card.
+ Say Y here if you have an "Option" GSM PCMCIA card
+ (or an OEM version: branded Huawei, Audiovox, or Novatel).
+
+ These cards feature a built-in OHCI-USB adapter and an
+ internally-connected GSM modem. The USB bus is not
+ accessible externally.
To compile this driver as a module, choose M here: the
module will be called option.
+ If this driver doesn't recognize your device,
+ it might be accessible via the FTDI_SIO driver.
+
config USB_SERIAL_OMNINET
tristate "USB ZyXEL omni.net LCD Plus Driver (EXPERIMENTAL)"
depends on USB_SERIAL && EXPERIMENTAL
#include "usb-serial.h"
static struct usb_device_id id_table [] = {
+ { USB_DEVICE(0x0c88, 0x17da) }, /* Kyocera Wireless KPC650/Passport */
{ USB_DEVICE(0xf3d, 0x0112) }, /* AirPrime CDMA Wireless PC Card */
{ USB_DEVICE(0x1410, 0x1110) }, /* Novatel Wireless Merlin CDMA */
{ USB_DEVICE(0x1199, 0x0112) }, /* Sierra Wireless Aircard 580 */
+ { USB_DEVICE(0x1199, 0x0218) }, /* Sierra Wireless MC5720 */
{ },
};
MODULE_DEVICE_TABLE(usb, id_table);
struct usb_serial *serial;
int retval = -ENODEV;
- if (!port)
+ if (!port || port->serial->dev->state == USB_STATE_NOTATTACHED)
return;
serial = port->serial;
if (!port->open_count) {
dbg ("%s - port not opened", __FUNCTION__);
- goto exit;
+ return;
}
- /* pass on to the driver specific version of this function if it is available */
- if (serial->type->write)
- retval = serial->type->write(port, buf, count);
- else
- retval = usb_serial_generic_write(port, buf, count);
-
-exit:
- dbg("%s - return value (if we had one): %d", __FUNCTION__, retval);
+ while (count) {
+ unsigned int i;
+ unsigned int lf;
+ /* search for LF so we can insert CR if necessary */
+ for (i=0, lf=0 ; i < count ; i++) {
+ if (*(buf + i) == 10) {
+ lf = 1;
+ i++;
+ break;
+ }
+ }
+ /* pass on to the driver specific version of this function if it is available */
+ if (serial->type->write)
+ retval = serial->type->write(port, buf, i);
+ else
+ retval = usb_serial_generic_write(port, buf, i);
+ dbg("%s - return value : %d", __FUNCTION__, retval);
+ if (lf) {
+ /* append CR after LF */
+ unsigned char cr = 13;
+ if (serial->type->write)
+ retval = serial->type->write(port, &cr, 1);
+ else
+ retval = usb_serial_generic_write(port, &cr, 1);
+ dbg("%s - return value : %d", __FUNCTION__, retval);
+ }
+ buf += i;
+ count -= i;
+ }
}
static struct console usbcons = {
.index = -1,
};
+void usb_serial_console_disconnect(struct usb_serial *serial)
+{
+ if (serial && serial->port && serial->port[0] && serial->port[0] == usbcons_info.port) {
+ usb_serial_console_exit();
+ usb_serial_put(serial);
+ }
+}
+
void usb_serial_console_init (int serial_debug, int minor)
{
debug = serial_debug;
void usb_serial_console_exit (void)
{
- unregister_console(&usbcons);
+ if (usbcons_info.port) {
+ unregister_console(&usbcons);
+ if (usbcons_info.port->open_count)
+ usbcons_info.port->open_count--;
+ usbcons_info.port = NULL;
+ }
}
{ USB_DEVICE(0x10AB, 0x10C5) }, /* Siemens MC60 Cable */
{ USB_DEVICE(0x10B5, 0xAC70) }, /* Nokia CA-42 USB */
{ USB_DEVICE(0x10C4, 0x803B) }, /* Pololu USB-serial converter */
+ { USB_DEVICE(0x10C4, 0x8066) }, /* Argussoft In-System Programmer */
{ USB_DEVICE(0x10C4, 0x807A) }, /* Crumb128 board */
{ USB_DEVICE(0x10C4, 0x80CA) }, /* Degree Controls Inc */
{ USB_DEVICE(0x10C4, 0x80F6) }, /* Suunto sports instrument */
exit:
spin_unlock(&priv->lock);
- schedule_work(&port->work);
+ usb_serial_port_softint(port);
}
static int __init cyberjack_init (void)
priv->bytes_out += count; /* do not count the line control and size bytes */
spin_unlock_irqrestore(&priv->lock, flags);
- schedule_work(&port->work);
+ usb_serial_port_softint(port);
} /* cypress_send */
return;
}
- schedule_work(&port->work);
+ usb_serial_port_softint(port);
}
{ USB_DEVICE(ICOM_ID1_VID, ICOM_ID1_PID) },
{ USB_DEVICE(PAPOUCH_VID, PAPOUCH_TMU_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_ACG_HFDUAL_PID) },
+ { USB_DEVICE(FTDI_VID, FTDI_YEI_SERVOCENTER31_PID) },
{ }, /* Optional parameter entry */
{ } /* Terminating entry */
};
static int ftdi_open (struct usb_serial_port *port, struct file *filp)
{ /* ftdi_open */
- struct termios tmp_termios;
struct usb_device *dev = port->serial->dev;
struct ftdi_private *priv = usb_get_serial_port_data(port);
unsigned long flags;
dbg("%s", __FUNCTION__);
-
- port->tty->low_latency = (priv->flags & ASYNC_LOW_LATENCY) ? 1 : 0;
+ if (port->tty)
+ port->tty->low_latency = (priv->flags & ASYNC_LOW_LATENCY) ? 1 : 0;
/* No error checking for this (will get errors later anyway) */
/* See ftdi_sio.h for description of what is reset */
This is same behaviour as serial.c/rs_open() - Kuba */
/* ftdi_set_termios will send usb control messages */
- ftdi_set_termios(port, &tmp_termios);
+ if (port->tty)
+ ftdi_set_termios(port, NULL);
/* FIXME: Flow control might be enabled, so it should be checked -
we have no control of defaults! */
return;
}
- schedule_work(&port->work);
+ usb_serial_port_softint(port);
} /* ftdi_write_bulk_callback */
err("%s urb failed to set baudrate", __FUNCTION__);
}
/* Ensure RTS and DTR are raised when baudrate changed from 0 */
- if ((old_termios->c_cflag & CBAUD) == B0) {
+ if (!old_termios || (old_termios->c_cflag & CBAUD) == B0) {
set_mctrl(port, TIOCM_DTR | TIOCM_RTS);
}
}
*/
#define FTDI_ACG_HFDUAL_PID 0xDD20 /* HF Dual ISO Reader (RFID) */
+/*
+ * Yost Engineering, Inc. products (www.yostengineering.com).
+ * PID 0xE050 submitted by Aaron Prose.
+ */
+#define FTDI_YEI_SERVOCENTER31_PID 0xE050 /* YEI ServoCenter3.1 USB */
+
/* Commands */
#define FTDI_SIO_RESET 0 /* Reset the port */
#define FTDI_SIO_MODEM_CTRL 1 /* Set the modem control register */
garmin_data_p->flags |= CLEAR_HALT_REQUIRED;
}
- schedule_work(&port->work);
+ usb_serial_port_softint(port);
}
return;
}
- usb_serial_port_softint((void *)port);
-
- schedule_work(&port->work);
+ usb_serial_port_softint(port);
}
EXPORT_SYMBOL_GPL(usb_serial_generic_write_bulk_callback);
/* This structure holds all of the individual device information */
struct edgeport_serial {
- char name[MAX_NAME_LEN+1]; /* string name of this device */
+ char name[MAX_NAME_LEN+2]; /* string name of this device */
struct edge_manuf_descriptor manuf_descriptor; /* the manufacturer descriptor */
struct edge_boot_descriptor boot_descriptor; /* the boot firmware descriptor */
static void get_boot_desc (struct edgeport_serial *edge_serial);
static void load_application_firmware (struct edgeport_serial *edge_serial);
-static void unicode_to_ascii (char *string, __le16 *unicode, int unicode_size);
+static void unicode_to_ascii(char *string, int buflen, __le16 *unicode, int unicode_size);
// ************************************************************************
* Get string descriptor from device *
* *
************************************************************************/
-static int get_string (struct usb_device *dev, int Id, char *string)
+static int get_string (struct usb_device *dev, int Id, char *string, int buflen)
{
struct usb_string_descriptor StringDesc;
struct usb_string_descriptor *pStringDesc;
return 0;
}
- unicode_to_ascii(string, pStringDesc->wData, pStringDesc->bLength/2-1);
+ unicode_to_ascii(string, buflen, pStringDesc->wData, pStringDesc->bLength/2);
kfree(pStringDesc);
return strlen(string);
* ASCII range, but it's only for debugging...
* NOTE: expects the unicode in LE format
****************************************************************************/
-static void unicode_to_ascii (char *string, __le16 *unicode, int unicode_size)
+static void unicode_to_ascii(char *string, int buflen, __le16 *unicode, int unicode_size)
{
int i;
- if (unicode_size <= 0)
+ if (buflen <= 0) /* never happens, but... */
return;
+ --buflen; /* space for nul */
- for (i = 0; i < unicode_size; ++i)
+ for (i = 0; i < unicode_size; i++) {
+ if (i >= buflen)
+ break;
string[i] = (char)(le16_to_cpu(unicode[i]));
- string[unicode_size] = 0x00;
+ }
+ string[i] = 0x00;
}
dbg(" BoardRev: %d", edge_serial->manuf_descriptor.BoardRev);
dbg(" NumPorts: %d", edge_serial->manuf_descriptor.NumPorts);
dbg(" DescDate: %d/%d/%d", edge_serial->manuf_descriptor.DescDate[0], edge_serial->manuf_descriptor.DescDate[1], edge_serial->manuf_descriptor.DescDate[2]+1900);
- unicode_to_ascii (string, edge_serial->manuf_descriptor.SerialNumber, edge_serial->manuf_descriptor.SerNumLength/2-1);
+ unicode_to_ascii(string, sizeof(string),
+ edge_serial->manuf_descriptor.SerialNumber,
+ edge_serial->manuf_descriptor.SerNumLength/2);
dbg(" SerialNumber: %s", string);
- unicode_to_ascii (string, edge_serial->manuf_descriptor.AssemblyNumber, edge_serial->manuf_descriptor.AssemblyNumLength/2-1);
+ unicode_to_ascii(string, sizeof(string),
+ edge_serial->manuf_descriptor.AssemblyNumber,
+ edge_serial->manuf_descriptor.AssemblyNumLength/2);
dbg(" AssemblyNumber: %s", string);
- unicode_to_ascii (string, edge_serial->manuf_descriptor.OemAssyNumber, edge_serial->manuf_descriptor.OemAssyNumLength/2-1);
+ unicode_to_ascii(string, sizeof(string),
+ edge_serial->manuf_descriptor.OemAssyNumber,
+ edge_serial->manuf_descriptor.OemAssyNumLength/2);
dbg(" OemAssyNumber: %s", string);
dbg(" UartType: %d", edge_serial->manuf_descriptor.UartType);
dbg(" IonPid: %d", edge_serial->manuf_descriptor.IonPid);
struct edgeport_serial *edge_serial;
struct edgeport_port *edge_port;
struct usb_device *dev;
- int i;
+ int i, j;
dev = serial->dev;
usb_set_serial_data(serial, edge_serial);
/* get the name for the device from the device */
- if ( (i = get_string(dev, dev->descriptor.iManufacturer, &edge_serial->name[0])) != 0) {
- edge_serial->name[i-1] = ' ';
- }
-
- get_string(dev, dev->descriptor.iProduct, &edge_serial->name[i]);
+ i = get_string(dev, dev->descriptor.iManufacturer,
+ &edge_serial->name[0], MAX_NAME_LEN+1);
+ edge_serial->name[i++] = ' ';
+ get_string(dev, dev->descriptor.iProduct,
+ &edge_serial->name[i], MAX_NAME_LEN+2 - i);
dev_info(&serial->dev->dev, "%s detected\n", edge_serial->name);
edge_port = kmalloc (sizeof(struct edgeport_port), GFP_KERNEL);
if (edge_port == NULL) {
dev_err(&serial->dev->dev, "%s - Out of memory\n", __FUNCTION__);
+ for (j = 0; j < i; ++j) {
+ kfree (usb_get_serial_port_data(serial->port[j]));
+ usb_set_serial_port_data(serial->port[j], NULL);
+ }
usb_set_serial_data(serial, NULL);
kfree(edge_serial);
return -ENOMEM;
spin_unlock_irqrestore(&write_list_lock, flags);
}
- schedule_work(&port->work);
+ usb_serial_port_softint(port);
}
static int ipaq_write_room(struct usb_serial_port *port)
if (urb->status)
dbg("%s - nonzero write bulk status received: %d", __FUNCTION__, urb->status);
- schedule_work(&port->work);
+ usb_serial_port_softint(port);
}
static int ipw_write(struct usb_serial_port *port, const unsigned char *buf, int count)
urb->actual_length,
urb->transfer_buffer);
- schedule_work(&port->work);
+ usb_serial_port_softint(port);
}
static void ir_read_bulk_callback (struct urb *urb, struct pt_regs *regs)
dbg ("%s - urb %d", __FUNCTION__, urb == p_priv->out_urbs[1]);
if (port->open_count)
- schedule_work(&port->work);
+ usb_serial_port_softint(port);
}
static void usa26_inack_callback(struct urb *urb, struct pt_regs *regs)
return;
}
- /* from generic_write_bulk_callback */
- schedule_work(&port->work);
+ usb_serial_port_softint(port);
} /* klsi_105_write_bulk_completion_callback */
return;
}
- schedule_work(&port->work);
+ usb_serial_port_softint(port);
}
/*
- Option Card (PCMCIA to) USB to Serial Driver
+ USB Driver for GSM modems
Copyright (C) 2005 Matthias Urlichs <smurf@smurf.noris.de>
2005-09-10 v0.4.3 added HUAWEI E600 card and Audiovox AirCard
2005-09-20 v0.4.4 increased recv buffer size: the card sometimes
wants to send >2000 bytes.
- 2006-04-10 v0.4.2 fixed two array overrun errors :-/
+ 2006-04-10 v0.5 fixed two array overrun errors :-/
+ 2006-04-21 v0.5.1 added support for Sierra Wireless MC8755
+ 2006-05-15 v0.6 re-enable multi-port support
+ 2006-06-01 v0.6.1 add COBRA
+ 2006-06-01 v0.6.2 add backwards-compatibility stuff
+ 2006-06-01 v0.6.3 add Novatel Wireless
+ 2006-06-01 v0.7 Option => GSM
Work sponsored by: Sigos GmbH, Germany <info@sigos.de>
+ This driver exists because the "normal" serial driver doesn't work too well
+ with GSM modems. Issues:
+ - data loss -- one single Receive URB is not nearly enough
+ - nonstandard flow (Option devices) and multiplex (Sierra) control
+ - controlling the baud rate doesn't make sense
+
+ This driver is named "option" because the most common device it's
+ used for is a PC-Card (with an internal OHCI-USB interface, behind
+ which the GSM interface sits), made by Option Inc.
+
+ Some of the "one port" devices actually exhibit multiple USB instances
+ on the USB bus. This is not a bug, these ports are used for different
+ device features.
*/
-#define DRIVER_VERSION "v0.4"
+#define DRIVER_VERSION "v0.7.0"
#define DRIVER_AUTHOR "Matthias Urlichs <smurf@smurf.noris.de>"
-#define DRIVER_DESC "Option Card (PC-Card to) USB to Serial Driver"
+#define DRIVER_DESC "USB Driver for GSM modems"
#include <linux/config.h>
#include <linux/kernel.h>
static int option_send_setup(struct usb_serial_port *port);
/* Vendor and product IDs */
-#define OPTION_VENDOR_ID 0x0AF0
-#define HUAWEI_VENDOR_ID 0x12D1
-#define AUDIOVOX_VENDOR_ID 0x0F3D
-
-#define OPTION_PRODUCT_OLD 0x5000
-#define OPTION_PRODUCT_FUSION 0x6000
-#define OPTION_PRODUCT_FUSION2 0x6300
-#define HUAWEI_PRODUCT_E600 0x1001
-#define AUDIOVOX_PRODUCT_AIRCARD 0x0112
+#define OPTION_VENDOR_ID 0x0AF0
+#define HUAWEI_VENDOR_ID 0x12D1
+#define AUDIOVOX_VENDOR_ID 0x0F3D
+#define SIERRAWIRELESS_VENDOR_ID 0x1199
+#define NOVATELWIRELESS_VENDOR_ID 0x1410
+
+#define OPTION_PRODUCT_OLD 0x5000
+#define OPTION_PRODUCT_FUSION 0x6000
+#define OPTION_PRODUCT_FUSION2 0x6300
+#define OPTION_PRODUCT_COBRA 0x6500
+#define HUAWEI_PRODUCT_E600 0x1001
+#define AUDIOVOX_PRODUCT_AIRCARD 0x0112
+#define SIERRAWIRELESS_PRODUCT_MC8755 0x6802
+#define NOVATELWIRELESS_PRODUCT_U740 0x1400
static struct usb_device_id option_ids[] = {
{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_OLD) },
{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_FUSION) },
{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_FUSION2) },
+ { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COBRA) },
{ USB_DEVICE(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E600) },
{ USB_DEVICE(AUDIOVOX_VENDOR_ID, AUDIOVOX_PRODUCT_AIRCARD) },
+ { USB_DEVICE(SIERRAWIRELESS_VENDOR_ID, SIERRAWIRELESS_PRODUCT_MC8755) },
+ { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID,NOVATELWIRELESS_PRODUCT_U740) },
+ { } /* Terminating entry */
+};
+
+static struct usb_device_id option_ids1[] = {
+ { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_OLD) },
+ { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_FUSION) },
+ { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_FUSION2) },
+ { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COBRA) },
+ { USB_DEVICE(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E600) },
+ { USB_DEVICE(AUDIOVOX_VENDOR_ID, AUDIOVOX_PRODUCT_AIRCARD) },
+ { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID,NOVATELWIRELESS_PRODUCT_U740) },
+ { } /* Terminating entry */
+};
+static struct usb_device_id option_ids3[] = {
+ { USB_DEVICE(SIERRAWIRELESS_VENDOR_ID, SIERRAWIRELESS_PRODUCT_MC8755) },
{ } /* Terminating entry */
};
.owner = THIS_MODULE,
.name = "option",
},
- .description = "Option 3G data card",
- .id_table = option_ids,
+ .description = "GSM modem (3-port)",
+ .id_table = option_ids3,
.num_interrupt_in = NUM_DONT_CARE,
.num_bulk_in = NUM_DONT_CARE,
.num_bulk_out = NUM_DONT_CARE,
- .num_ports = 1, /* 3, but the card reports its ports separately */
+ .num_ports = 3,
+ .open = option_open,
+ .close = option_close,
+ .write = option_write,
+ .write_room = option_write_room,
+ .chars_in_buffer = option_chars_in_buffer,
+ .throttle = option_rx_throttle,
+ .unthrottle = option_rx_unthrottle,
+ .set_termios = option_set_termios,
+ .break_ctl = option_break_ctl,
+ .tiocmget = option_tiocmget,
+ .tiocmset = option_tiocmset,
+ .attach = option_startup,
+ .shutdown = option_shutdown,
+ .read_int_callback = option_instat_callback,
+};
+
+static struct usb_serial_driver option_1port_device = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "option",
+ },
+ .description = "GSM modem (1-port)",
+ .id_table = option_ids1,
+ .num_interrupt_in = NUM_DONT_CARE,
+ .num_bulk_in = NUM_DONT_CARE,
+ .num_bulk_out = NUM_DONT_CARE,
+ .num_ports = 1,
.open = option_open,
.close = option_close,
.write = option_write,
static int __init option_init(void)
{
int retval;
+ retval = usb_serial_register(&option_1port_device);
+ if (retval)
+ goto failed_1port_device_register;
retval = usb_serial_register(&option_3port_device);
if (retval)
goto failed_3port_device_register;
failed_driver_register:
usb_serial_deregister (&option_3port_device);
failed_3port_device_register:
+ usb_serial_deregister (&option_1port_device);
+failed_1port_device_register:
return retval;
}
{
usb_deregister (&option_driver);
usb_serial_deregister (&option_3port_device);
+ usb_serial_deregister (&option_1port_device);
}
module_init(option_init);
port = (struct usb_serial_port *) urb->context;
- if (port->open_count)
- schedule_work(&port->work);
+ usb_serial_port_softint(port);
}
static void option_instat_callback(struct urb *urb, struct pt_regs *regs)
/* Setup urbs */
static void option_setup_urbs(struct usb_serial *serial)
{
- int j;
+ int i,j;
struct usb_serial_port *port;
struct option_port_private *portdata;
dbg("%s", __FUNCTION__);
- port = serial->port[0];
- portdata = usb_get_serial_port_data(port);
+
+ for (i = 0; i < serial->num_ports; i++) {
+ port = serial->port[i];
+ portdata = usb_get_serial_port_data(port);
/* Do indat endpoints first */
- for (j = 0; j < N_IN_URB; ++j) {
- portdata->in_urbs[j] = option_setup_urb (serial,
- port->bulk_in_endpointAddress, USB_DIR_IN, port,
- portdata->in_buffer[j], IN_BUFLEN, option_indat_callback);
- }
+ for (j = 0; j < N_IN_URB; ++j) {
+ portdata->in_urbs[j] = option_setup_urb (serial,
+ port->bulk_in_endpointAddress, USB_DIR_IN, port,
+ portdata->in_buffer[j], IN_BUFLEN, option_indat_callback);
+ }
- /* outdat endpoints */
- for (j = 0; j < N_OUT_URB; ++j) {
- portdata->out_urbs[j] = option_setup_urb (serial,
- port->bulk_out_endpointAddress, USB_DIR_OUT, port,
- portdata->out_buffer[j], OUT_BUFLEN, option_outdat_callback);
+ /* outdat endpoints */
+ for (j = 0; j < N_OUT_URB; ++j) {
+ portdata->out_urbs[j] = option_setup_urb (serial,
+ port->bulk_out_endpointAddress, USB_DIR_OUT, port,
+ portdata->out_buffer[j], OUT_BUFLEN, option_outdat_callback);
+ }
}
}
// TODO: reschedule pl2303_send
}
- schedule_work(&port->work);
+ usb_serial_port_softint(port);
}
static int pl2303_write_room(struct usb_serial_port *port)
unsigned int c_cflag;
int bps;
long timeout;
- wait_queue_t wait; \
+ wait_queue_t wait;
dbg("%s - port %d", __FUNCTION__, port->number);
}
}
+ flush_scheduled_work(); /* port->work */
+
usb_put_dev(serial->dev);
/* free up any memory that we allocated */
kfree (serial);
}
+void usb_serial_put(struct usb_serial *serial)
+{
+ kref_put(&serial->kref, destroy_serial);
+}
+
/*****************************************************************************
* Driver tty interface functions
*****************************************************************************/
++port->open_count;
- if (port->open_count == 1) {
+ /* set up our port structure making the tty driver
+ * remember our port object, and us it */
+ tty->driver_data = port;
+ port->tty = tty;
- /* set up our port structure making the tty driver
- * remember our port object, and us it */
- tty->driver_data = port;
- port->tty = tty;
+ if (port->open_count == 1) {
/* lock this module before we call it
* this may fail, which means we must bail out,
module_put(serial->type->driver.owner);
bailout_mutex_unlock:
port->open_count = 0;
+ tty->driver_data = NULL;
+ port->tty = NULL;
mutex_unlock(&port->mutex);
bailout_kref_put:
- kref_put(&serial->kref, destroy_serial);
+ usb_serial_put(serial);
return retval;
}
}
mutex_unlock(&port->mutex);
- kref_put(&port->serial->kref, destroy_serial);
+ usb_serial_put(port->serial);
}
static int serial_write (struct tty_struct * tty, const unsigned char *buf, int count)
struct usb_serial_port *port = tty->driver_data;
int retval = -EINVAL;
- if (!port)
+ if (!port || port->serial->dev->state == USB_STATE_NOTATTACHED)
goto exit;
dbg("%s - port %d, %d byte(s)", __FUNCTION__, port->number, count);
static int serial_write_room (struct tty_struct *tty)
{
struct usb_serial_port *port = tty->driver_data;
- int retval = -EINVAL;
+ int retval = -ENODEV;
if (!port)
goto exit;
static int serial_chars_in_buffer (struct tty_struct *tty)
{
struct usb_serial_port *port = tty->driver_data;
- int retval = -EINVAL;
+ int retval = -ENODEV;
if (!port)
goto exit;
begin += length;
length = 0;
}
- kref_put(&serial->kref, destroy_serial);
+ usb_serial_put(serial);
}
*eof = 1;
done:
struct usb_serial_port *port = tty->driver_data;
if (!port)
- goto exit;
+ return -ENODEV;
dbg("%s - port %d", __FUNCTION__, port->number);
if (!port->open_count) {
dbg("%s - port not open", __FUNCTION__);
- goto exit;
+ return -ENODEV;
}
if (port->serial->type->tiocmget)
return port->serial->type->tiocmget(port, file);
-exit:
return -EINVAL;
}
struct usb_serial_port *port = tty->driver_data;
if (!port)
- goto exit;
+ return -ENODEV;
dbg("%s - port %d", __FUNCTION__, port->number);
if (!port->open_count) {
dbg("%s - port not open", __FUNCTION__);
- goto exit;
+ return -ENODEV;
}
if (port->serial->type->tiocmset)
return port->serial->type->tiocmset(port, file, set, clear);
-exit:
return -EINVAL;
}
-void usb_serial_port_softint(void *private)
+/*
+ * We would be calling tty_wakeup here, but unfortunately some line
+ * disciplines have an annoying habit of calling tty->write from
+ * the write wakeup callback (e.g. n_hdlc.c).
+ */
+void usb_serial_port_softint(struct usb_serial_port *port)
+{
+ schedule_work(&port->work);
+}
+
+static void usb_serial_port_work(void *private)
{
struct usb_serial_port *port = private;
struct tty_struct *tty;
port->serial = serial;
spin_lock_init(&port->lock);
mutex_init(&port->mutex);
- INIT_WORK(&port->work, usb_serial_port_softint, port);
+ INIT_WORK(&port->work, usb_serial_port_work, port);
serial->port[i] = port;
}
struct device *dev = &interface->dev;
struct usb_serial_port *port;
+ usb_serial_console_disconnect(serial);
dbg ("%s", __FUNCTION__);
usb_set_intfdata (interface, NULL);
}
/* let the last holder of this object
* cause it to be cleaned up */
- kref_put(&serial->kref, destroy_serial);
+ usb_serial_put(serial);
}
dev_info(dev, "device disconnected\n");
}
extern int usb_serial_register(struct usb_serial_driver *driver);
extern void usb_serial_deregister(struct usb_serial_driver *driver);
-extern void usb_serial_port_softint(void *private);
+extern void usb_serial_port_softint(struct usb_serial_port *port);
extern int usb_serial_probe(struct usb_interface *iface, const struct usb_device_id *id);
extern void usb_serial_disconnect(struct usb_interface *iface);
#ifdef CONFIG_USB_SERIAL_CONSOLE
extern void usb_serial_console_init (int debug, int minor);
extern void usb_serial_console_exit (void);
+extern void usb_serial_console_disconnect(struct usb_serial *serial);
#else
static inline void usb_serial_console_init (int debug, int minor) { }
static inline void usb_serial_console_exit (void) { }
+static inline void usb_serial_console_disconnect(struct usb_serial *serial) {}
#endif
/* Functions needed by other parts of the usbserial core */
extern struct usb_serial *usb_serial_get_by_index (unsigned int minor);
+extern void usb_serial_put(struct usb_serial *serial);
extern int usb_serial_generic_open (struct usb_serial_port *port, struct file *filp);
extern int usb_serial_generic_write (struct usb_serial_port *port, const unsigned char *buf, int count);
extern void usb_serial_generic_close (struct usb_serial_port *port, struct file *filp);
--priv->outstanding_urbs;
spin_unlock_irqrestore(&priv->lock, flags);
- schedule_work(&port->work);
+ usb_serial_port_softint(port);
}
if (ret) {
err("%s: Couldn't send command [%d]", serial->type->description, ret);
goto no_firmware;
- } else if (alen != sizeof(command)) {
+ } else if (alen != 2) {
err("%s: Send command incomplete [%d]", serial->type->description, alen);
goto no_firmware;
}
if (ret) {
err("%s: Couldn't get results [%d]", serial->type->description, ret);
goto no_firmware;
- } else if (alen != sizeof(result)) {
+ } else if (alen != sizeof(*hw_info) + 1) {
err("%s: Get results incomplete [%d]", serial->type->description, alen);
goto no_firmware;
} else if (result[0] != command[0]) {
return;
}
- usb_serial_port_softint((void *)port);
-
- schedule_work(&port->work);
+ usb_serial_port_softint(port);
}
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/module.h>
-#include <linux/usb.h>
#include <linux/usb_ch9.h>
-#include <linux/usb_input.h>
+#include <linux/usb/input.h>
#include "usb.h"
#include "onetouch.h"
#include "debug.h"
int result;
US_DEBUGP("%s called\n", __FUNCTION__);
-
- mutex_lock(&(us->dev_mutex));
result = usb_stor_port_reset(us);
- mutex_unlock(&us->dev_mutex);
-
return result < 0 ? FAILED : SUCCESS;
}
* Convenience function to perform a bulk read
*/
static int usbat_bulk_read(struct us_data *us,
- unsigned char *data,
- unsigned int len)
+ unsigned char *data,
+ unsigned int len,
+ int use_sg)
{
if (len == 0)
return USB_STOR_XFER_GOOD;
US_DEBUGP("usbat_bulk_read: len = %d\n", len);
- return usb_stor_bulk_transfer_buf(us, us->recv_bulk_pipe, data, len, NULL);
+ return usb_stor_bulk_transfer_sg(us, us->recv_bulk_pipe, data, len, use_sg, NULL);
}
/*
* Convenience function to perform a bulk write
*/
static int usbat_bulk_write(struct us_data *us,
- unsigned char *data,
- unsigned int len)
+ unsigned char *data,
+ unsigned int len,
+ int use_sg)
{
if (len == 0)
return USB_STOR_XFER_GOOD;
US_DEBUGP("usbat_bulk_write: len = %d\n", len);
- return usb_stor_bulk_transfer_buf(us, us->send_bulk_pipe, data, len, NULL);
+ return usb_stor_bulk_transfer_sg(us, us->send_bulk_pipe, data, len, use_sg, NULL);
}
/*
*/
static int usbat_read_block(struct us_data *us,
unsigned char *content,
- unsigned short len)
+ unsigned short len,
+ int use_sg)
{
int result;
unsigned char *command = us->iobuf;
if (result != USB_STOR_XFER_GOOD)
return USB_STOR_TRANSPORT_ERROR;
- result = usbat_bulk_read(us, content, len);
+ result = usbat_bulk_read(us, content, len, use_sg);
return (result == USB_STOR_XFER_GOOD ?
USB_STOR_TRANSPORT_GOOD : USB_STOR_TRANSPORT_ERROR);
}
unsigned char access,
unsigned char *content,
unsigned short len,
- int minutes)
+ int minutes,
+ int use_sg)
{
int result;
unsigned char *command = us->iobuf;
if (result != USB_STOR_XFER_GOOD)
return USB_STOR_TRANSPORT_ERROR;
- result = usbat_bulk_write(us, content, len);
+ result = usbat_bulk_write(us, content, len, use_sg);
if (result != USB_STOR_XFER_GOOD)
return USB_STOR_TRANSPORT_ERROR;
data[1+(j<<1)] = data_out[j];
}
- result = usbat_bulk_write(us, data, num_registers*2);
+ result = usbat_bulk_write(us, data, num_registers*2, 0);
if (result != USB_STOR_XFER_GOOD)
return USB_STOR_TRANSPORT_ERROR;
}
/* Send the data */
- result = usbat_bulk_write(us, data, num_registers*2);
+ result = usbat_bulk_write(us, data, num_registers*2, 0);
if (result != USB_STOR_XFER_GOOD)
return USB_STOR_TRANSPORT_ERROR;
* other related details) are defined beforehand with _set_shuttle_features().
*/
static int usbat_read_blocks(struct us_data *us,
- unsigned char *buffer,
- int len)
+ unsigned char *buffer,
+ int len,
+ int use_sg)
{
int result;
unsigned char *command = us->iobuf;
return USB_STOR_TRANSPORT_FAILED;
/* Read the blocks we just asked for */
- result = usbat_bulk_read(us, buffer, len);
+ result = usbat_bulk_read(us, buffer, len, use_sg);
if (result != USB_STOR_XFER_GOOD)
return USB_STOR_TRANSPORT_FAILED;
*/
static int usbat_write_blocks(struct us_data *us,
unsigned char *buffer,
- int len)
+ int len,
+ int use_sg)
{
int result;
unsigned char *command = us->iobuf;
return USB_STOR_TRANSPORT_FAILED;
/* Write the data */
- result = usbat_bulk_write(us, buffer, len);
+ result = usbat_bulk_write(us, buffer, len, use_sg);
if (result != USB_STOR_XFER_GOOD)
return USB_STOR_TRANSPORT_FAILED;
* Set the transport function based on the device type
*/
static int usbat_set_transport(struct us_data *us,
- struct usbat_info *info)
+ struct usbat_info *info,
+ int devicetype)
{
- int rc;
- if (!info->devicetype) {
- rc = usbat_identify_device(us, info);
- if (rc != USB_STOR_TRANSPORT_GOOD) {
- US_DEBUGP("usbat_set_transport: Could not identify device\n");
- return 1;
- }
- }
+ if (!info->devicetype)
+ info->devicetype = devicetype;
- if (usbat_get_device_type(us) == USBAT_DEV_HP8200)
+ if (!info->devicetype)
+ usbat_identify_device(us, info);
+
+ switch (info->devicetype) {
+ default:
+ return USB_STOR_TRANSPORT_ERROR;
+
+ case USBAT_DEV_HP8200:
us->transport = usbat_hp8200e_transport;
- else if (usbat_get_device_type(us) == USBAT_DEV_FLASH)
+ break;
+
+ case USBAT_DEV_FLASH:
us->transport = usbat_flash_transport;
+ break;
+ }
return 0;
}
msleep(100);
/* Read the device identification data */
- rc = usbat_read_block(us, reply, 512);
+ rc = usbat_read_block(us, reply, 512, 0);
if (rc != USB_STOR_TRANSPORT_GOOD)
goto leave;
goto leave;
/* Read the data we just requested */
- result = usbat_read_blocks(us, buffer, len);
+ result = usbat_read_blocks(us, buffer, len, 0);
if (result != USB_STOR_TRANSPORT_GOOD)
goto leave;
goto leave;
/* Write the data */
- result = usbat_write_blocks(us, buffer, len);
+ result = usbat_write_blocks(us, buffer, len, 0);
if (result != USB_STOR_TRANSPORT_GOOD)
goto leave;
/*
* Initialize the USBAT processor and the storage device
*/
-int init_usbat(struct us_data *us)
+static int init_usbat(struct us_data *us, int devicetype)
{
int rc;
struct usbat_info *info;
US_DEBUGP("INIT 9\n");
/* At this point, we need to detect which device we are using */
- if (usbat_set_transport(us, info))
+ if (usbat_set_transport(us, info, devicetype))
return USB_STOR_TRANSPORT_ERROR;
US_DEBUGP("INIT 10\n");
* AT SPEED 4 IS UNRELIABLE!!!
*/
- if ( (result = usbat_write_block(us,
+ if ((result = usbat_write_block(us,
USBAT_ATA, srb->cmnd, 12,
- srb->cmnd[0]==GPCMD_BLANK ? 75 : 10)) !=
- USB_STOR_TRANSPORT_GOOD) {
+ (srb->cmnd[0]==GPCMD_BLANK ? 75 : 10), 0) !=
+ USB_STOR_TRANSPORT_GOOD)) {
return result;
}
len = *status;
- result = usbat_read_block(us, srb->request_buffer, len);
+ result = usbat_read_block(us, srb->request_buffer, len, srb->use_sg);
/* Debug-print the first 32 bytes of the transfer */
return USB_STOR_TRANSPORT_FAILED;
}
+int init_usbat_cd(struct us_data *us)
+{
+ return init_usbat(us, USBAT_DEV_HP8200);
+}
+
+
+int init_usbat_flash(struct us_data *us)
+{
+ return init_usbat(us, USBAT_DEV_FLASH);
+}
+
+int init_usbat_probe(struct us_data *us)
+{
+ return init_usbat(us, 0);
+}
+
/*
* Default transport function. Attempts to detect which transport function
* should be called, makes it the new default, and calls it.
{
struct usbat_info *info = (struct usbat_info*) (us->extra);
- if (usbat_set_transport(us, info))
+ if (usbat_set_transport(us, info, 0))
return USB_STOR_TRANSPORT_ERROR;
return us->transport(srb, us);
}
-
#define USBAT_FEAT_ET2 0x01
extern int usbat_transport(struct scsi_cmnd *srb, struct us_data *us);
-extern int init_usbat(struct us_data *us);
+extern int init_usbat_cd(struct us_data *us);
+extern int init_usbat_flash(struct us_data *us);
+extern int init_usbat_probe(struct us_data *us);
struct usbat_info {
int devicetype;
complete(urb_done_ptr);
}
-
-/* This is the timeout handler which will cancel an URB when its timeout
- * expires.
- */
-static void timeout_handler(unsigned long us_)
-{
- struct us_data *us = (struct us_data *) us_;
-
- if (test_and_clear_bit(US_FLIDX_URB_ACTIVE, &us->flags)) {
- US_DEBUGP("Timeout -- cancelling URB\n");
- usb_unlink_urb(us->current_urb);
- }
-}
/* This is the common part of the URB message submission code
*
static int usb_stor_msg_common(struct us_data *us, int timeout)
{
struct completion urb_done;
- struct timer_list to_timer;
+ long timeleft;
int status;
/* don't submit URBs during abort/disconnect processing */
}
}
- /* submit the timeout timer, if a timeout was requested */
- if (timeout > 0) {
- init_timer(&to_timer);
- to_timer.expires = jiffies + timeout;
- to_timer.function = timeout_handler;
- to_timer.data = (unsigned long) us;
- add_timer(&to_timer);
- }
-
/* wait for the completion of the URB */
- wait_for_completion(&urb_done);
- clear_bit(US_FLIDX_URB_ACTIVE, &us->flags);
+ timeleft = wait_for_completion_interruptible_timeout(
+ &urb_done, timeout ? : MAX_SCHEDULE_TIMEOUT);
- /* clean up the timeout timer */
- if (timeout > 0)
- del_timer_sync(&to_timer);
+ clear_bit(US_FLIDX_URB_ACTIVE, &us->flags);
+
+ if (timeleft <= 0) {
+ US_DEBUGP("%s -- cancelling URB\n",
+ timeleft == 0 ? "Timeout" : "Signal");
+ usb_unlink_urb(us->current_urb);
+ }
/* return the URB status */
return us->current_urb->status;
* device reset. */
Handle_Errors:
- /* Let the SCSI layer know we are doing a reset, set the
- * RESETTING bit, and clear the ABORTING bit so that the reset
- * may proceed. */
+ /* Set the RESETTING bit, and clear the ABORTING bit so that
+ * the reset may proceed. */
scsi_lock(us_to_host(us));
- usb_stor_report_bus_reset(us);
set_bit(US_FLIDX_RESETTING, &us->flags);
clear_bit(US_FLIDX_ABORTING, &us->flags);
scsi_unlock(us_to_host(us));
+ /* We must release the device lock because the pre_reset routine
+ * will want to acquire it. */
+ mutex_unlock(&us->dev_mutex);
result = usb_stor_port_reset(us);
+ mutex_lock(&us->dev_mutex);
+
if (result < 0) {
scsi_lock(us_to_host(us));
usb_stor_report_device_reset(us);
0, us->ifnum, NULL, 0);
}
-/* Issue a USB port reset to the device. But don't do anything if
- * there's more than one interface in the device, so that other users
- * are not affected. */
+/* Issue a USB port reset to the device. The caller must not hold
+ * us->dev_mutex.
+ */
int usb_stor_port_reset(struct us_data *us)
{
- int result, rc;
+ int result, rc_lock;
- if (test_bit(US_FLIDX_DISCONNECTING, &us->flags)) {
- result = -EIO;
- US_DEBUGP("No reset during disconnect\n");
- } else if (us->pusb_dev->actconfig->desc.bNumInterfaces != 1) {
- result = -EBUSY;
- US_DEBUGP("Refusing to reset a multi-interface device\n");
- } else {
- result = rc =
- usb_lock_device_for_reset(us->pusb_dev, us->pusb_intf);
- if (result < 0) {
- US_DEBUGP("unable to lock device for reset: %d\n",
- result);
+ result = rc_lock =
+ usb_lock_device_for_reset(us->pusb_dev, us->pusb_intf);
+ if (result < 0)
+ US_DEBUGP("unable to lock device for reset: %d\n", result);
+ else {
+ /* Were we disconnected while waiting for the lock? */
+ if (test_bit(US_FLIDX_DISCONNECTING, &us->flags)) {
+ result = -EIO;
+ US_DEBUGP("No reset during disconnect\n");
} else {
- result = usb_reset_device(us->pusb_dev);
- if (rc)
- usb_unlock_device(us->pusb_dev);
- US_DEBUGP("usb_reset_device returns %d\n", result);
+ result = usb_reset_composite_device(
+ us->pusb_dev, us->pusb_intf);
+ US_DEBUGP("usb_reset_composite_device returns %d\n",
+ result);
}
+ if (rc_lock)
+ usb_unlock_device(us->pusb_dev);
}
return result;
}
UNUSUAL_DEV( 0x03f0, 0x0207, 0x0001, 0x0001,
"HP",
"CD-Writer+ 8200e",
- US_SC_8070, US_PR_USBAT, init_usbat, 0),
+ US_SC_8070, US_PR_USBAT, init_usbat_cd, 0),
UNUSUAL_DEV( 0x03f0, 0x0307, 0x0001, 0x0001,
"HP",
"CD-Writer+ CD-4e",
- US_SC_8070, US_PR_USBAT, init_usbat, 0),
+ US_SC_8070, US_PR_USBAT, init_usbat_cd, 0),
#endif
/* Reported by Sebastian Kapfer <sebastian_kapfer@gmx.net>
US_SC_DEVICE, US_PR_DEVICE, NULL,
US_FL_IGNORE_RESIDUE ),
+/* Reported by Jiri Slaby <jirislaby@gmail.com> and
+ * Rene C. Castberg <Rene@Castberg.org> */
+UNUSUAL_DEV( 0x0421, 0x0446, 0x0100, 0x0100,
+ "Nokia",
+ "N80",
+ US_SC_DEVICE, US_PR_DEVICE, NULL,
+ US_FL_IGNORE_RESIDUE | US_FL_FIX_CAPACITY ),
+
/* Reported by Olaf Hering <olh@suse.de> from novell bug #105878 */
UNUSUAL_DEV( 0x0424, 0x0fdc, 0x0210, 0x0210,
"SMSC",
"DVD-CAM DZ-MV100A Camcorder",
US_SC_SCSI, US_PR_CB, NULL, US_FL_SINGLE_LUN),
+/* Patch for Nikon coolpix 2000
+ * Submitted by Fabien Cosse <fabien.cosse@wanadoo.fr>*/
+UNUSUAL_DEV( 0x04b0, 0x0301, 0x0010, 0x0010,
+ "NIKON",
+ "NIKON DSC E2000",
+ US_SC_DEVICE, US_PR_DEVICE,NULL,
+ US_FL_NOT_LOCKABLE ),
+
/* Reported by Andreas Bockhold <andreas@bockionline.de> */
UNUSUAL_DEV( 0x04b0, 0x0405, 0x0100, 0x0100,
"NIKON",
US_SC_DEVICE, US_PR_DEVICE, NULL,
US_FL_FIX_CAPACITY),
-/* Patch for Nikon coolpix 2000
- * Submitted by Fabien Cosse <fabien.cosse@wanadoo.fr>*/
-UNUSUAL_DEV( 0x04b0, 0x0301, 0x0010, 0x0010,
+/* Reported by Jamie Kitson <jamie@staberinde.fsnet.co.uk> */
+UNUSUAL_DEV( 0x04b0, 0x040d, 0x0100, 0x0100,
"NIKON",
- "NIKON DSC E2000",
- US_SC_DEVICE, US_PR_DEVICE,NULL,
- US_FL_NOT_LOCKABLE ),
+ "NIKON DSC D70s",
+ US_SC_DEVICE, US_PR_DEVICE, NULL,
+ US_FL_FIX_CAPACITY),
/* BENQ DC5330
* Reported by Manuel Fombuena <mfombuena@ya.com> and
UNUSUAL_DEV( 0x04e6, 0x1010, 0x0000, 0x9999,
"Shuttle/SCM",
"USBAT-02",
- US_SC_SCSI, US_PR_USBAT, init_usbat,
+ US_SC_SCSI, US_PR_USBAT, init_usbat_flash,
US_FL_SINGLE_LUN),
#endif
UNUSUAL_DEV( 0x0781, 0x0005, 0x0005, 0x0005,
"Sandisk",
"ImageMate SDDR-05b",
- US_SC_SCSI, US_PR_USBAT, init_usbat,
+ US_SC_SCSI, US_PR_USBAT, init_usbat_flash,
US_FL_SINGLE_LUN ),
#endif
#endif /* CONFIG_PM */
+/*
+ * The next two routines get called just before and just after
+ * a USB port reset, whether from this driver or a different one.
+ */
+
+static void storage_pre_reset(struct usb_interface *iface)
+{
+ struct us_data *us = usb_get_intfdata(iface);
+
+ US_DEBUGP("%s\n", __FUNCTION__);
+
+ /* Make sure no command runs during the reset */
+ mutex_lock(&us->dev_mutex);
+}
+
+static void storage_post_reset(struct usb_interface *iface)
+{
+ struct us_data *us = usb_get_intfdata(iface);
+
+ US_DEBUGP("%s\n", __FUNCTION__);
+
+ /* Report the reset to the SCSI core */
+ scsi_lock(us_to_host(us));
+ usb_stor_report_bus_reset(us);
+ scsi_unlock(us_to_host(us));
+
+ /* FIXME: Notify the subdrivers that they need to reinitialize
+ * the device */
+ mutex_unlock(&us->dev_mutex);
+}
+
/*
* fill_inquiry_response takes an unsigned char array (which must
* be at least 36 characters) and populates the vendor name,
break;
#endif
+#ifdef CONFIG_USB_STORAGE_ALAUDA
+ case US_PR_ALAUDA:
+ us->transport_name = "Alauda Control/Bulk";
+ us->transport = alauda_transport;
+ us->transport_reset = usb_stor_Bulk_reset;
+ us->max_lun = 1;
+ break;
+#endif
+
default:
return -EIO;
}
break;
#endif
-#ifdef CONFIG_USB_STORAGE_ALAUDA
- case US_PR_ALAUDA:
- us->transport_name = "Alauda Control/Bulk";
- us->transport = alauda_transport;
- us->transport_reset = usb_stor_Bulk_reset;
- us->max_lun = 1;
- break;
-#endif
-
default:
return -EIO;
}
.suspend = storage_suspend,
.resume = storage_resume,
#endif
+ .pre_reset = storage_pre_reset,
+ .post_reset = storage_post_reset,
.id_table = storage_usb_ids,
};
mda_num_columns = 80;
mda_num_lines = 25;
- mda_vram_base = VGA_MAP_MEM(0xb0000);
mda_vram_len = 0x01000;
+ mda_vram_base = VGA_MAP_MEM(0xb0000, mda_vram_len);
mda_index_port = 0x3b4;
mda_value_port = 0x3b5;
static struct resource ega_console_resource =
{ "ega", 0x3B0, 0x3BF };
vga_video_type = VIDEO_TYPE_EGAM;
- vga_vram_end = 0xb8000;
+ vga_vram_size = 0x8000;
display_desc = "EGA+";
request_resource(&ioport_resource,
&ega_console_resource);
static struct resource mda2_console_resource =
{ "mda", 0x3BF, 0x3BF };
vga_video_type = VIDEO_TYPE_MDA;
- vga_vram_end = 0xb2000;
+ vga_vram_size = 0x2000;
display_desc = "*MDA";
request_resource(&ioport_resource,
&mda1_console_resource);
if ((ORIG_VIDEO_EGA_BX & 0xff) != 0x10) {
int i;
- vga_vram_end = 0xc0000;
+ vga_vram_size = 0x8000;
if (!ORIG_VIDEO_ISVGA) {
static struct resource ega_console_resource
* and COE=1 isn't necessarily a good idea)
*/
vga_vram_base = 0xa0000;
- vga_vram_end = 0xb0000;
+ vga_vram_size = 0x10000;
outb_p(6, VGA_GFX_I);
outb_p(6, VGA_GFX_D);
#endif
static struct resource cga_console_resource =
{ "cga", 0x3D4, 0x3D5 };
vga_video_type = VIDEO_TYPE_CGA;
- vga_vram_end = 0xba000;
+ vga_vram_size = 0x2000;
display_desc = "*CGA";
request_resource(&ioport_resource,
&cga_console_resource);
}
}
- vga_vram_base = VGA_MAP_MEM(vga_vram_base);
- vga_vram_end = VGA_MAP_MEM(vga_vram_end);
- vga_vram_size = vga_vram_end - vga_vram_base;
+ vga_vram_base = VGA_MAP_MEM(vga_vram_base, vga_vram_size);
+ vga_vram_end = vga_vram_base + vga_vram_size;
/*
* Find out if there is a graphics card present.
char *charmap;
if (vga_video_type != VIDEO_TYPE_EGAM) {
- charmap = (char *) VGA_MAP_MEM(colourmap);
+ charmap = (char *) VGA_MAP_MEM(colourmap, 0);
beg = 0x0e;
#ifdef VGA_CAN_DO_64KB
if (vga_video_type == VIDEO_TYPE_VGAC)
beg = 0x06;
#endif
} else {
- charmap = (char *) VGA_MAP_MEM(blackwmap);
+ charmap = (char *) VGA_MAP_MEM(blackwmap, 0);
beg = 0x0a;
}
}
/* XXX share VGA_FB_PHYS and I/O region with vgacon and others */
- info->screen_base = (void __iomem *)VGA_MAP_MEM(VGA_FB_PHYS);
+ info->screen_base = (void __iomem *)VGA_MAP_MEM(VGA_FB_PHYS, 0);
if (!info->screen_base) {
printk(KERN_ERR "vga16fb: unable to map device\n");
/* Discard our unneeded old files struct */
if (files) {
- steal_locks(files);
put_files_struct(files);
files = NULL;
}
goto _error;
if (files) {
- steal_locks(files);
put_files_struct(files);
files = NULL;
}
static struct block_device *bd_acquire(struct inode *inode)
{
struct block_device *bdev;
+
spin_lock(&bdev_lock);
bdev = inode->i_bdev;
- if (bdev && igrab(bdev->bd_inode)) {
+ if (bdev) {
+ atomic_inc(&bdev->bd_inode->i_count);
spin_unlock(&bdev_lock);
return bdev;
}
spin_unlock(&bdev_lock);
+
bdev = bdget(inode->i_rdev);
if (bdev) {
spin_lock(&bdev_lock);
- if (inode->i_bdev)
- __bd_forget(inode);
- inode->i_bdev = bdev;
- inode->i_mapping = bdev->bd_inode->i_mapping;
- list_add(&inode->i_devices, &bdev->bd_inodes);
+ if (!inode->i_bdev) {
+ /*
+ * We take an additional bd_inode->i_count for inode,
+ * and it's released in clear_inode() of inode.
+ * So, we can access it via ->i_mapping always
+ * without igrab().
+ */
+ atomic_inc(&bdev->bd_inode->i_count);
+ inode->i_bdev = bdev;
+ inode->i_mapping = bdev->bd_inode->i_mapping;
+ list_add(&inode->i_devices, &bdev->bd_inodes);
+ }
spin_unlock(&bdev_lock);
}
return bdev;
void bd_forget(struct inode *inode)
{
+ struct block_device *bdev = NULL;
+
spin_lock(&bdev_lock);
- if (inode->i_bdev)
+ if (inode->i_bdev) {
+ if (inode->i_sb != blockdev_superblock)
+ bdev = inode->i_bdev;
__bd_forget(inode);
+ }
spin_unlock(&bdev_lock);
+
+ if (bdev)
+ iput(bdev->bd_inode);
}
int bd_claim(struct block_device *bdev, void *holder)
}
/*
- * Throw away a dentry - free the inode, dput the parent.
- * This requires that the LRU list has already been
- * removed.
+ * Throw away a dentry - free the inode, dput the parent. This requires that
+ * the LRU list has already been removed.
+ *
* Called with dcache_lock, drops it and then regains.
+ * Called with dentry->d_lock held, drops it.
*/
-static inline void prune_one_dentry(struct dentry * dentry)
+static void prune_one_dentry(struct dentry * dentry)
{
struct dentry * parent;
/**
* prune_dcache - shrink the dcache
* @count: number of entries to try and free
+ * @sb: if given, ignore dentries for other superblocks
+ * which are being unmounted.
*
* Shrink the dcache. This is done when we need
* more memory, or simply when we need to unmount
* all the dentries are in use.
*/
-static void prune_dcache(int count)
+static void prune_dcache(int count, struct super_block *sb)
{
spin_lock(&dcache_lock);
for (; count ; count--) {
struct dentry *dentry;
struct list_head *tmp;
+ struct rw_semaphore *s_umount;
cond_resched_lock(&dcache_lock);
tmp = dentry_unused.prev;
+ if (unlikely(sb)) {
+ /* Try to find a dentry for this sb, but don't try
+ * too hard, if they aren't near the tail they will
+ * be moved down again soon
+ */
+ int skip = count;
+ while (skip && tmp != &dentry_unused &&
+ list_entry(tmp, struct dentry, d_lru)->d_sb != sb) {
+ skip--;
+ tmp = tmp->prev;
+ }
+ }
if (tmp == &dentry_unused)
break;
list_del_init(tmp);
spin_unlock(&dentry->d_lock);
continue;
}
- prune_one_dentry(dentry);
+ /*
+ * If the dentry is not DCACHED_REFERENCED, it is time
+ * to remove it from the dcache, provided the super block is
+ * NULL (which means we are trying to reclaim memory)
+ * or this dentry belongs to the same super block that
+ * we want to shrink.
+ */
+ /*
+ * If this dentry is for "my" filesystem, then I can prune it
+ * without taking the s_umount lock (I already hold it).
+ */
+ if (sb && dentry->d_sb == sb) {
+ prune_one_dentry(dentry);
+ continue;
+ }
+ /*
+ * ...otherwise we need to be sure this filesystem isn't being
+ * unmounted, otherwise we could race with
+ * generic_shutdown_super(), and end up holding a reference to
+ * an inode while the filesystem is unmounted.
+ * So we try to get s_umount, and make sure s_root isn't NULL.
+ * (Take a local copy of s_umount to avoid a use-after-free of
+ * `dentry').
+ */
+ s_umount = &dentry->d_sb->s_umount;
+ if (down_read_trylock(s_umount)) {
+ if (dentry->d_sb->s_root != NULL) {
+ prune_one_dentry(dentry);
+ up_read(s_umount);
+ continue;
+ }
+ up_read(s_umount);
+ }
+ spin_unlock(&dentry->d_lock);
+ /* Cannot remove the first dentry, and it isn't appropriate
+ * to move it to the head of the list, so give up, and try
+ * later
+ */
+ break;
}
spin_unlock(&dcache_lock);
}
int found;
while ((found = select_parent(parent)) != 0)
- prune_dcache(found);
+ prune_dcache(found, parent->d_sb);
}
/**
* done under dcache_lock.
*
*/
-void shrink_dcache_anon(struct hlist_head *head)
+void shrink_dcache_anon(struct super_block *sb)
{
struct hlist_node *lp;
+ struct hlist_head *head = &sb->s_anon;
int found;
do {
found = 0;
}
}
spin_unlock(&dcache_lock);
- prune_dcache(found);
+ prune_dcache(found, sb);
} while(found);
}
if (nr) {
if (!(gfp_mask & __GFP_FS))
return -1;
- prune_dcache(nr);
+ prune_dcache(nr, NULL);
}
return (dentry_stat.nr_unused / 100) * sysctl_vfs_cache_pressure;
}
bprm->mm = NULL; /* We're using it now */
/* This is the point of no return */
- steal_locks(files);
put_files_struct(files);
current->sas_ss_sp = current->sas_ss_size = 0;
EXPORT_SYMBOL(lock_may_write);
-static inline void __steal_locks(struct file *file, fl_owner_t from)
-{
- struct inode *inode = file->f_dentry->d_inode;
- struct file_lock *fl = inode->i_flock;
-
- while (fl) {
- if (fl->fl_file == file && fl->fl_owner == from)
- fl->fl_owner = current->files;
- fl = fl->fl_next;
- }
-}
-
-/* When getting ready for executing a binary, we make sure that current
- * has a files_struct on its own. Before dropping the old files_struct,
- * we take over ownership of all locks for all file descriptors we own.
- * Note that we may accidentally steal a lock for a file that a sibling
- * has created since the unshare_files() call.
- */
-void steal_locks(fl_owner_t from)
-{
- struct files_struct *files = current->files;
- int i, j;
- struct fdtable *fdt;
-
- if (from == files)
- return;
-
- lock_kernel();
- j = 0;
-
- /*
- * We are not taking a ref to the file structures, so
- * we need to acquire ->file_lock.
- */
- spin_lock(&files->file_lock);
- fdt = files_fdtable(files);
- for (;;) {
- unsigned long set;
- i = j * __NFDBITS;
- if (i >= fdt->max_fdset || i >= fdt->max_fds)
- break;
- set = fdt->open_fds->fds_bits[j++];
- while (set) {
- if (set & 1) {
- struct file *file = fdt->fd[i];
- if (file)
- __steal_locks(file, from);
- }
- i++;
- set >>= 1;
- }
- }
- spin_unlock(&files->file_lock);
- unlock_kernel();
-}
-EXPORT_SYMBOL(steal_locks);
-
static int __init filelock_init(void)
{
filelock_cache = kmem_cache_create("file_lock_cache",
unsigned nr_pages)
{
BUG_ON(!nr_pages);
+ /*
+ * Warning: Do not do the decrement at the same time as the call to
+ * flush_dcache_page() because it is a NULL macro on i386 and hence the
+ * decrement never happens so the loop never terminates.
+ */
do {
- /*
- * Warning: Do not do the decrement at the same time as the
- * call because flush_dcache_page() is a NULL macro on i386
- * and hence the decrement never happens.
- */
+ --nr_pages;
flush_dcache_page(pages[nr_pages]);
- } while (--nr_pages > 0);
+ } while (nr_pages > 0);
}
/**
p->ios[0] = p->ios[1] = 0;
p->sectors[0] = p->sectors[1] = 0;
devfs_remove("%s/part%d", disk->devfs_name, part);
+ sysfs_remove_link(&p->kobj, "subsystem");
if (p->holder_dir)
kobject_unregister(p->holder_dir);
kobject_uevent(&p->kobj, KOBJ_REMOVE);
kobject_add(&p->kobj);
if (!disk->part_uevent_suppress)
kobject_uevent(&p->kobj, KOBJ_ADD);
+ sysfs_create_link(&p->kobj, &block_subsys.kset.kobj, "subsystem");
partition_sysfs_add_subdir(p);
disk->part[part-1] = p;
}
kfree(disk_name);
}
}
+ sysfs_create_link(&disk->kobj, &block_subsys.kset.kobj, "subsystem");
}
/* Not exported, helper to add_disk(). */
put_device(disk->driverfs_dev);
disk->driverfs_dev = NULL;
}
+ sysfs_remove_link(&disk->kobj, "subsystem");
kobject_del(&disk->kobj);
}
if (root) {
sb->s_root = NULL;
shrink_dcache_parent(root);
- shrink_dcache_anon(&sb->s_anon);
+ shrink_dcache_anon(sb);
dput(root);
fsync_super(sb);
lock_super(sb);
config XFS_FS
tristate "XFS filesystem support"
- select EXPORTFS if NFSD!=n
help
XFS is a high performance journaling filesystem which originated
on the SGI IRIX platform. It is completely multi-threaded, can
system of your root partition is compiled as a module, you'll need
to use an initial ramdisk (initrd) to boot.
-config XFS_EXPORT
- bool
- depends on XFS_FS && EXPORTFS
- default y
-
config XFS_QUOTA
bool "XFS Quota support"
depends on XFS_FS
If you don't know what Access Control Lists are, say N.
config XFS_RT
- bool "XFS Realtime support (EXPERIMENTAL)"
- depends on XFS_FS && EXPERIMENTAL
+ bool "XFS Realtime subvolume support"
+ depends on XFS_FS
help
If you say Y here you will be able to mount and use XFS filesystems
- which contain a realtime subvolume. The realtime subvolume is a
- separate area of disk space where only file data is stored. The
- realtime subvolume is designed to provide very deterministic
- data rates suitable for media streaming applications.
-
- See the xfs man page in section 5 for a bit more information.
+ which contain a realtime subvolume. The realtime subvolume is a
+ separate area of disk space where only file data is stored. It was
+ originally designed to provide deterministic data rates suitable
+ for media streaming applications, but is also useful as a generic
+ mechanism for ensuring data and metadata/log I/Os are completely
+ separated. Regular file I/Os are isolated to a separate device
+ from all other requests, and this can be done quite transparently
+ to applications via the inherit-realtime directory inode flag.
- This feature is unsupported at this time, is not yet fully
- functional, and may cause serious problems.
+ See the xfs man page in section 5 for additional information.
If unsure, say N.
xfs-$(CONFIG_PROC_FS) += $(XFS_LINUX)/xfs_stats.o
xfs-$(CONFIG_SYSCTL) += $(XFS_LINUX)/xfs_sysctl.o
xfs-$(CONFIG_COMPAT) += $(XFS_LINUX)/xfs_ioctl32.o
-xfs-$(CONFIG_XFS_EXPORT) += $(XFS_LINUX)/xfs_export.o
xfs-y += xfs_alloc.o \
xfs_btree.o \
xfs_buf_item.o \
xfs_da_btree.o \
- xfs_dir.o \
xfs_dir2.o \
xfs_dir2_block.o \
xfs_dir2_data.o \
xfs_dir2_leaf.o \
xfs_dir2_node.o \
xfs_dir2_sf.o \
- xfs_dir_leaf.o \
xfs_error.o \
xfs_extfree_item.o \
xfs_fsops.o \
kmem.o \
xfs_aops.o \
xfs_buf.o \
+ xfs_export.o \
xfs_file.o \
xfs_fs_subr.o \
xfs_globals.o \
#include <linux/sched.h>
#include <linux/mm.h>
-/*
- * Process flags handling
- */
-
-#define PFLAGS_TEST_NOIO() (current->flags & PF_NOIO)
-#define PFLAGS_TEST_FSTRANS() (current->flags & PF_FSTRANS)
-
-#define PFLAGS_SET_NOIO() do { \
- current->flags |= PF_NOIO; \
-} while (0)
-
-#define PFLAGS_CLEAR_NOIO() do { \
- current->flags &= ~PF_NOIO; \
-} while (0)
-
-/* these could be nested, so we save state */
-#define PFLAGS_SET_FSTRANS(STATEP) do { \
- *(STATEP) = current->flags; \
- current->flags |= PF_FSTRANS; \
-} while (0)
-
-#define PFLAGS_CLEAR_FSTRANS(STATEP) do { \
- *(STATEP) = current->flags; \
- current->flags &= ~PF_FSTRANS; \
-} while (0)
-
-/* Restore the PF_FSTRANS state to what was saved in STATEP */
-#define PFLAGS_RESTORE_FSTRANS(STATEP) do { \
- current->flags = ((current->flags & ~PF_FSTRANS) | \
- (*(STATEP) & PF_FSTRANS)); \
-} while (0)
-
-#define PFLAGS_DUP(OSTATEP, NSTATEP) do { \
- *(NSTATEP) = *(OSTATEP); \
-} while (0)
-
/*
* General memory allocation interfaces
*/
lflags = GFP_ATOMIC | __GFP_NOWARN;
} else {
lflags = GFP_KERNEL | __GFP_NOWARN;
- if (PFLAGS_TEST_FSTRANS() || (flags & KM_NOFS))
+ if ((current->flags & PF_FSTRANS) || (flags & KM_NOFS))
lflags &= ~__GFP_FS;
}
return lflags;
/*
- * Copyright (c) 2000-2005 Silicon Graphics, Inc.
+ * Copyright (c) 2000-2006 Silicon Graphics, Inc.
* All Rights Reserved.
*
* This program is free software; you can redistribute it and/or
} mrlock_t;
#define mrinit(mrp, name) \
- ( (mrp)->mr_writer = 0, init_rwsem(&(mrp)->mr_lock) )
+ do { (mrp)->mr_writer = 0; init_rwsem(&(mrp)->mr_lock); } while (0)
#define mrlock_init(mrp, t,n,s) mrinit(mrp, n)
#define mrfree(mrp) do { } while (0)
#define mraccess(mrp) mraccessf(mrp, 0)
#define initnsema(sp, val, name) sema_init(sp, val)
#define psema(sp, b) down(sp)
#define vsema(sp) up(sp)
-#define valusema(sp) (atomic_read(&(sp)->count))
-#define freesema(sema)
+#define freesema(sema) do { } while (0)
+
+static inline int issemalocked(sema_t *sp)
+{
+ return down_trylock(sp) || (up(sp), 0);
+}
/*
* Map cpsema (try to get the sema) to down_trylock. We need to switch
* the return values since cpsema returns 1 (acquired) 0 (failed) and
* down_trylock returns the reverse 0 (acquired) 1 (failed).
*/
-
-#define cpsema(sp) (down_trylock(sp) ? 0 : 1)
-
-/*
- * Didn't do cvsema(sp). Not sure how to map this to up/down/...
- * It does a vsema if the values is < 0 other wise nothing.
- */
+static inline int cpsema(sema_t *sp)
+{
+ return down_trylock(sp) ? 0 : 1;
+}
#endif /* __XFS_SUPPORT_SEMA_H__ */
#include "xfs_inum.h"
#include "xfs_sb.h"
#include "xfs_ag.h"
-#include "xfs_dir.h"
#include "xfs_dir2.h"
#include "xfs_trans.h"
#include "xfs_dmapi.h"
#include "xfs_bmap_btree.h"
#include "xfs_alloc_btree.h"
#include "xfs_ialloc_btree.h"
-#include "xfs_dir_sf.h"
#include "xfs_dir2_sf.h"
#include "xfs_attr_sf.h"
#include "xfs_dinode.h"
int mask)
{
xfs_inode_t *ip;
- vnode_t *vp = vn_from_inode(inode);
+ bhv_vnode_t *vp = vn_from_inode(inode);
loff_t isize = i_size_read(inode);
loff_t offset = page_offset(page);
int delalloc = -1, unmapped = -1, unwritten = -1;
for (bh = ioend->io_buffer_head; bh; bh = next) {
next = bh->b_private;
- bh->b_end_io(bh, ioend->io_uptodate);
+ bh->b_end_io(bh, !ioend->io_error);
}
-
+ if (unlikely(ioend->io_error))
+ vn_ioerror(ioend->io_vnode, ioend->io_error, __FILE__,__LINE__);
vn_iowake(ioend->io_vnode);
mempool_free(ioend, xfs_ioend_pool);
}
void *data)
{
xfs_ioend_t *ioend = data;
- vnode_t *vp = ioend->io_vnode;
+ bhv_vnode_t *vp = ioend->io_vnode;
xfs_off_t offset = ioend->io_offset;
size_t size = ioend->io_size;
- int error;
- if (ioend->io_uptodate)
- VOP_BMAP(vp, offset, size, BMAPI_UNWRITTEN, NULL, NULL, error);
+ if (likely(!ioend->io_error))
+ bhv_vop_bmap(vp, offset, size, BMAPI_UNWRITTEN, NULL, NULL);
xfs_destroy_ioend(ioend);
}
* all the I/O from calling the completion routine too early.
*/
atomic_set(&ioend->io_remaining, 1);
- ioend->io_uptodate = 1; /* cleared if any I/O fails */
+ ioend->io_error = 0;
ioend->io_list = NULL;
ioend->io_type = type;
ioend->io_vnode = vn_from_inode(inode);
xfs_iomap_t *mapp,
int flags)
{
- vnode_t *vp = vn_from_inode(inode);
+ bhv_vnode_t *vp = vn_from_inode(inode);
int error, nmaps = 1;
- VOP_BMAP(vp, offset, count, flags, mapp, &nmaps, error);
+ error = bhv_vop_bmap(vp, offset, count, flags, mapp, &nmaps);
if (!error && (flags & (BMAPI_WRITE|BMAPI_ALLOCATE)))
VMODIFY(vp);
return -error;
if (bio->bi_size)
return 1;
- ASSERT(ioend);
ASSERT(atomic_read(&bio->bi_cnt) >= 1);
+ ioend->io_error = test_bit(BIO_UPTODATE, &bio->bi_flags) ? 0 : error;
/* Toss bio and pass work off to an xfsdatad thread */
- if (!test_bit(BIO_UPTODATE, &bio->bi_flags))
- ioend->io_uptodate = 0;
bio->bi_private = NULL;
bio->bi_end_io = NULL;
-
bio_put(bio);
+
xfs_finish_ioend(ioend);
return 0;
}
* then mark the page dirty again and leave the page
* as is.
*/
- if (PFLAGS_TEST_FSTRANS() && need_trans)
+ if (current_test_flags(PF_FSTRANS) && need_trans)
goto out_fail;
/*
return error;
}
+STATIC int
+xfs_vm_writepages(
+ struct address_space *mapping,
+ struct writeback_control *wbc)
+{
+ struct bhv_vnode *vp = vn_from_inode(mapping->host);
+
+ if (VN_TRUNC(vp))
+ VUNTRUNCATE(vp);
+ return generic_writepages(mapping, wbc);
+}
+
/*
* Called to move a page into cleanable state - and from there
* to be released. Possibly the page is already clean. We always
/* If we are already inside a transaction or the thread cannot
* do I/O, we cannot release this page.
*/
- if (PFLAGS_TEST_FSTRANS())
+ if (current_test_flags(PF_FSTRANS))
return 0;
/*
int direct,
bmapi_flags_t flags)
{
- vnode_t *vp = vn_from_inode(inode);
+ bhv_vnode_t *vp = vn_from_inode(inode);
xfs_iomap_t iomap;
xfs_off_t offset;
ssize_t size;
offset = (xfs_off_t)iblock << inode->i_blkbits;
ASSERT(bh_result->b_size >= (1 << inode->i_blkbits));
size = bh_result->b_size;
- VOP_BMAP(vp, offset, size,
- create ? flags : BMAPI_READ, &iomap, &niomap, error);
+ error = bhv_vop_bmap(vp, offset, size,
+ create ? flags : BMAPI_READ, &iomap, &niomap);
if (error)
return -error;
if (niomap == 0)
{
struct file *file = iocb->ki_filp;
struct inode *inode = file->f_mapping->host;
- vnode_t *vp = vn_from_inode(inode);
+ bhv_vnode_t *vp = vn_from_inode(inode);
xfs_iomap_t iomap;
int maps = 1;
int error;
ssize_t ret;
- VOP_BMAP(vp, offset, 0, BMAPI_DEVICE, &iomap, &maps, error);
+ error = bhv_vop_bmap(vp, offset, 0, BMAPI_DEVICE, &iomap, &maps);
if (error)
return -error;
sector_t block)
{
struct inode *inode = (struct inode *)mapping->host;
- vnode_t *vp = vn_from_inode(inode);
- int error;
+ bhv_vnode_t *vp = vn_from_inode(inode);
vn_trace_entry(vp, __FUNCTION__, (inst_t *)__return_address);
-
- VOP_RWLOCK(vp, VRWLOCK_READ);
- VOP_FLUSH_PAGES(vp, (xfs_off_t)0, -1, 0, FI_REMAPF, error);
- VOP_RWUNLOCK(vp, VRWLOCK_READ);
+ bhv_vop_rwlock(vp, VRWLOCK_READ);
+ bhv_vop_flush_pages(vp, (xfs_off_t)0, -1, 0, FI_REMAPF);
+ bhv_vop_rwunlock(vp, VRWLOCK_READ);
return generic_block_bmap(mapping, block, xfs_get_blocks);
}
.readpage = xfs_vm_readpage,
.readpages = xfs_vm_readpages,
.writepage = xfs_vm_writepage,
+ .writepages = xfs_vm_writepages,
.sync_page = block_sync_page,
.releasepage = xfs_vm_releasepage,
.invalidatepage = xfs_vm_invalidatepage,
/*
- * Copyright (c) 2005 Silicon Graphics, Inc.
+ * Copyright (c) 2005-2006 Silicon Graphics, Inc.
* All Rights Reserved.
*
* This program is free software; you can redistribute it and/or
typedef struct xfs_ioend {
struct xfs_ioend *io_list; /* next ioend in chain */
unsigned int io_type; /* delalloc / unwritten */
- unsigned int io_uptodate; /* I/O status register */
+ int io_error; /* I/O error code */
atomic_t io_remaining; /* hold count */
- struct vnode *io_vnode; /* file being written to */
+ struct bhv_vnode *io_vnode; /* file being written to */
struct buffer_head *io_buffer_head;/* buffer linked list head */
struct buffer_head *io_buffer_tail;/* buffer linked list tail */
size_t io_size; /* size of the extent */
extern struct address_space_operations xfs_address_space_operations;
extern int xfs_get_blocks(struct inode *, sector_t, struct buffer_head *, int);
-#endif /* __XFS_IOPS_H__ */
+#endif /* __XFS_AOPS_H__ */
#include "xfs_log.h"
#include "xfs_trans.h"
#include "xfs_sb.h"
-#include "xfs_dir.h"
#include "xfs_mount.h"
#include "xfs_export.h"
int len;
int is64 = 0;
#if XFS_BIG_INUMS
- vfs_t *vfs = vfs_from_sb(inode->i_sb);
+ bhv_vfs_t *vfs = vfs_from_sb(inode->i_sb);
if (!(vfs->vfs_flag & VFS_32BITINODES)) {
/* filesystem may contain 64bit inode numbers */
struct super_block *sb,
void *data)
{
- vnode_t *vp;
+ bhv_vnode_t *vp;
struct inode *inode;
struct dentry *result;
- vfs_t *vfsp = vfs_from_sb(sb);
+ bhv_vfs_t *vfsp = vfs_from_sb(sb);
int error;
- VFS_VGET(vfsp, &vp, (fid_t *)data, error);
+ error = bhv_vfs_vget(vfsp, &vp, (fid_t *)data);
if (error || vp == NULL)
return ERR_PTR(-ESTALE) ;
struct dentry *child)
{
int error;
- vnode_t *vp, *cvp;
+ bhv_vnode_t *vp, *cvp;
struct dentry *parent;
cvp = NULL;
vp = vn_from_inode(child->d_inode);
- VOP_LOOKUP(vp, &dotdot, &cvp, 0, NULL, NULL, error);
+ error = bhv_vop_lookup(vp, &dotdot, &cvp, 0, NULL, NULL);
if (unlikely(error))
return ERR_PTR(-error);
#include "xfs_inum.h"
#include "xfs_sb.h"
#include "xfs_ag.h"
-#include "xfs_dir.h"
#include "xfs_dir2.h"
#include "xfs_trans.h"
#include "xfs_dmapi.h"
#include "xfs_alloc.h"
#include "xfs_btree.h"
#include "xfs_attr_sf.h"
-#include "xfs_dir_sf.h"
#include "xfs_dir2_sf.h"
#include "xfs_dinode.h"
#include "xfs_inode.h"
{
struct iovec iov = {buf, count};
struct file *file = iocb->ki_filp;
- vnode_t *vp = vn_from_inode(file->f_dentry->d_inode);
- ssize_t rval;
+ bhv_vnode_t *vp = vn_from_inode(file->f_dentry->d_inode);
BUG_ON(iocb->ki_pos != pos);
-
if (unlikely(file->f_flags & O_DIRECT))
ioflags |= IO_ISDIRECT;
- VOP_READ(vp, iocb, &iov, 1, &iocb->ki_pos, ioflags, NULL, rval);
- return rval;
+ return bhv_vop_read(vp, iocb, &iov, 1, &iocb->ki_pos, ioflags, NULL);
}
STATIC ssize_t
struct iovec iov = {(void __user *)buf, count};
struct file *file = iocb->ki_filp;
struct inode *inode = file->f_mapping->host;
- vnode_t *vp = vn_from_inode(inode);
- ssize_t rval;
+ bhv_vnode_t *vp = vn_from_inode(inode);
BUG_ON(iocb->ki_pos != pos);
if (unlikely(file->f_flags & O_DIRECT))
ioflags |= IO_ISDIRECT;
-
- VOP_WRITE(vp, iocb, &iov, 1, &iocb->ki_pos, ioflags, NULL, rval);
- return rval;
+ return bhv_vop_write(vp, iocb, &iov, 1, &iocb->ki_pos, ioflags, NULL);
}
STATIC ssize_t
loff_t *ppos)
{
struct inode *inode = file->f_mapping->host;
- vnode_t *vp = vn_from_inode(inode);
+ bhv_vnode_t *vp = vn_from_inode(inode);
struct kiocb kiocb;
ssize_t rval;
if (unlikely(file->f_flags & O_DIRECT))
ioflags |= IO_ISDIRECT;
- VOP_READ(vp, &kiocb, iov, nr_segs, &kiocb.ki_pos, ioflags, NULL, rval);
+ rval = bhv_vop_read(vp, &kiocb, iov, nr_segs,
+ &kiocb.ki_pos, ioflags, NULL);
*ppos = kiocb.ki_pos;
return rval;
loff_t *ppos)
{
struct inode *inode = file->f_mapping->host;
- vnode_t *vp = vn_from_inode(inode);
+ bhv_vnode_t *vp = vn_from_inode(inode);
struct kiocb kiocb;
ssize_t rval;
if (unlikely(file->f_flags & O_DIRECT))
ioflags |= IO_ISDIRECT;
- VOP_WRITE(vp, &kiocb, iov, nr_segs, &kiocb.ki_pos, ioflags, NULL, rval);
+ rval = bhv_vop_write(vp, &kiocb, iov, nr_segs,
+ &kiocb.ki_pos, ioflags, NULL);
*ppos = kiocb.ki_pos;
return rval;
read_actor_t actor,
void *target)
{
- vnode_t *vp = vn_from_inode(filp->f_dentry->d_inode);
- ssize_t rval;
-
- VOP_SENDFILE(vp, filp, pos, 0, count, actor, target, NULL, rval);
- return rval;
+ return bhv_vop_sendfile(vn_from_inode(filp->f_dentry->d_inode),
+ filp, pos, 0, count, actor, target, NULL);
}
STATIC ssize_t
read_actor_t actor,
void *target)
{
- vnode_t *vp = vn_from_inode(filp->f_dentry->d_inode);
- ssize_t rval;
-
- VOP_SENDFILE(vp, filp, pos, IO_INVIS, count, actor, target, NULL, rval);
- return rval;
+ return bhv_vop_sendfile(vn_from_inode(filp->f_dentry->d_inode),
+ filp, pos, IO_INVIS, count, actor, target, NULL);
}
STATIC ssize_t
size_t len,
unsigned int flags)
{
- vnode_t *vp = vn_from_inode(infilp->f_dentry->d_inode);
- ssize_t rval;
-
- VOP_SPLICE_READ(vp, infilp, ppos, pipe, len, flags, 0, NULL, rval);
- return rval;
+ return bhv_vop_splice_read(vn_from_inode(infilp->f_dentry->d_inode),
+ infilp, ppos, pipe, len, flags, 0, NULL);
}
STATIC ssize_t
size_t len,
unsigned int flags)
{
- vnode_t *vp = vn_from_inode(infilp->f_dentry->d_inode);
- ssize_t rval;
-
- VOP_SPLICE_READ(vp, infilp, ppos, pipe, len, flags, IO_INVIS, NULL, rval);
- return rval;
+ return bhv_vop_splice_read(vn_from_inode(infilp->f_dentry->d_inode),
+ infilp, ppos, pipe, len, flags, IO_INVIS,
+ NULL);
}
STATIC ssize_t
size_t len,
unsigned int flags)
{
- vnode_t *vp = vn_from_inode(outfilp->f_dentry->d_inode);
- ssize_t rval;
-
- VOP_SPLICE_WRITE(vp, pipe, outfilp, ppos, len, flags, 0, NULL, rval);
- return rval;
+ return bhv_vop_splice_write(vn_from_inode(outfilp->f_dentry->d_inode),
+ pipe, outfilp, ppos, len, flags, 0, NULL);
}
STATIC ssize_t
size_t len,
unsigned int flags)
{
- vnode_t *vp = vn_from_inode(outfilp->f_dentry->d_inode);
- ssize_t rval;
-
- VOP_SPLICE_WRITE(vp, pipe, outfilp, ppos, len, flags, IO_INVIS, NULL, rval);
- return rval;
+ return bhv_vop_splice_write(vn_from_inode(outfilp->f_dentry->d_inode),
+ pipe, outfilp, ppos, len, flags, IO_INVIS,
+ NULL);
}
STATIC int
struct inode *inode,
struct file *filp)
{
- vnode_t *vp = vn_from_inode(inode);
- int error;
-
if (!(filp->f_flags & O_LARGEFILE) && i_size_read(inode) > MAX_NON_LFS)
return -EFBIG;
- VOP_OPEN(vp, NULL, error);
- return -error;
+ return -bhv_vop_open(vn_from_inode(inode), NULL);
+}
+
+STATIC int
+xfs_file_close(
+ struct file *filp)
+{
+ return -bhv_vop_close(vn_from_inode(filp->f_dentry->d_inode), 0,
+ file_count(filp) > 1 ? L_FALSE : L_TRUE, NULL);
}
STATIC int
struct inode *inode,
struct file *filp)
{
- vnode_t *vp = vn_from_inode(inode);
- int error = 0;
+ bhv_vnode_t *vp = vn_from_inode(inode);
if (vp)
- VOP_RELEASE(vp, error);
- return -error;
+ return -bhv_vop_release(vp);
+ return 0;
}
STATIC int
struct dentry *dentry,
int datasync)
{
- struct inode *inode = dentry->d_inode;
- vnode_t *vp = vn_from_inode(inode);
- int error;
+ bhv_vnode_t *vp = vn_from_inode(dentry->d_inode);
int flags = FSYNC_WAIT;
if (datasync)
flags |= FSYNC_DATA;
- VOP_FSYNC(vp, flags, NULL, (xfs_off_t)0, (xfs_off_t)-1, error);
- return -error;
+ if (VN_TRUNC(vp))
+ VUNTRUNCATE(vp);
+ return -bhv_vop_fsync(vp, flags, NULL, (xfs_off_t)0, (xfs_off_t)-1);
}
#ifdef CONFIG_XFS_DMAPI
int *type)
{
struct inode *inode = area->vm_file->f_dentry->d_inode;
- vnode_t *vp = vn_from_inode(inode);
- xfs_mount_t *mp = XFS_VFSTOM(vp->v_vfsp);
- int error;
+ bhv_vnode_t *vp = vn_from_inode(inode);
ASSERT_ALWAYS(vp->v_vfsp->vfs_flag & VFS_DMI);
-
- error = XFS_SEND_MMAP(mp, area, 0);
- if (error)
+ if (XFS_SEND_MMAP(XFS_VFSTOM(vp->v_vfsp), area, 0))
return NULL;
-
return filemap_nopage(area, address, type);
}
#endif /* CONFIG_XFS_DMAPI */
filldir_t filldir)
{
int error = 0;
- vnode_t *vp = vn_from_inode(filp->f_dentry->d_inode);
+ bhv_vnode_t *vp = vn_from_inode(filp->f_dentry->d_inode);
uio_t uio;
iovec_t iov;
int eof = 0;
start_offset = uio.uio_offset;
- VOP_READDIR(vp, &uio, NULL, &eof, error);
+ error = bhv_vop_readdir(vp, &uio, NULL, &eof);
if ((uio.uio_offset == start_offset) || error) {
size = 0;
break;
struct file *filp,
struct vm_area_struct *vma)
{
- struct inode *ip = filp->f_dentry->d_inode;
- vnode_t *vp = vn_from_inode(ip);
- vattr_t vattr;
- int error;
-
vma->vm_ops = &xfs_file_vm_ops;
#ifdef CONFIG_XFS_DMAPI
- if (vp->v_vfsp->vfs_flag & VFS_DMI) {
+ if (vn_from_inode(filp->f_dentry->d_inode)->v_vfsp->vfs_flag & VFS_DMI)
vma->vm_ops = &xfs_dmapi_file_vm_ops;
- }
#endif /* CONFIG_XFS_DMAPI */
- vattr.va_mask = XFS_AT_UPDATIME;
- VOP_SETATTR(vp, &vattr, XFS_AT_UPDATIME, NULL, error);
- if (likely(!error))
- __vn_revalidate(vp, &vattr); /* update flags */
+ file_accessed(filp);
return 0;
}
-
STATIC long
xfs_file_ioctl(
struct file *filp,
unsigned int cmd,
- unsigned long arg)
+ unsigned long p)
{
int error;
struct inode *inode = filp->f_dentry->d_inode;
- vnode_t *vp = vn_from_inode(inode);
+ bhv_vnode_t *vp = vn_from_inode(inode);
- VOP_IOCTL(vp, inode, filp, 0, cmd, (void __user *)arg, error);
+ error = bhv_vop_ioctl(vp, inode, filp, 0, cmd, (void __user *)p);
VMODIFY(vp);
/* NOTE: some of the ioctl's return positive #'s as a
xfs_file_ioctl_invis(
struct file *filp,
unsigned int cmd,
- unsigned long arg)
+ unsigned long p)
{
- struct inode *inode = filp->f_dentry->d_inode;
- vnode_t *vp = vn_from_inode(inode);
int error;
+ struct inode *inode = filp->f_dentry->d_inode;
+ bhv_vnode_t *vp = vn_from_inode(inode);
- VOP_IOCTL(vp, inode, filp, IO_INVIS, cmd, (void __user *)arg, error);
+ error = bhv_vop_ioctl(vp, inode, filp, IO_INVIS, cmd, (void __user *)p);
VMODIFY(vp);
/* NOTE: some of the ioctl's return positive #'s as a
struct vm_area_struct *vma,
unsigned int newflags)
{
- vnode_t *vp = vn_from_inode(vma->vm_file->f_dentry->d_inode);
+ bhv_vnode_t *vp = vn_from_inode(vma->vm_file->f_dentry->d_inode);
int error = 0;
if (vp->v_vfsp->vfs_flag & VFS_DMI) {
xfs_file_open_exec(
struct inode *inode)
{
- vnode_t *vp = vn_from_inode(inode);
- xfs_mount_t *mp = XFS_VFSTOM(vp->v_vfsp);
- int error = 0;
- xfs_inode_t *ip;
+ bhv_vnode_t *vp = vn_from_inode(inode);
- if (vp->v_vfsp->vfs_flag & VFS_DMI) {
- ip = xfs_vtoi(vp);
- if (!ip) {
- error = -EINVAL;
- goto open_exec_out;
- }
- if (DM_EVENT_ENABLED(vp->v_vfsp, ip, DM_EVENT_READ)) {
- error = -XFS_SEND_DATA(mp, DM_EVENT_READ, vp,
+ if (unlikely(vp->v_vfsp->vfs_flag & VFS_DMI)) {
+ xfs_mount_t *mp = XFS_VFSTOM(vp->v_vfsp);
+ xfs_inode_t *ip = xfs_vtoi(vp);
+
+ if (!ip)
+ return -EINVAL;
+ if (DM_EVENT_ENABLED(vp->v_vfsp, ip, DM_EVENT_READ))
+ return -XFS_SEND_DATA(mp, DM_EVENT_READ, vp,
0, 0, 0, NULL);
- }
}
-open_exec_out:
- return error;
+ return 0;
}
#endif /* HAVE_FOP_OPEN_EXEC */
#endif
.mmap = xfs_file_mmap,
.open = xfs_file_open,
+ .flush = xfs_file_close,
.release = xfs_file_release,
.fsync = xfs_file_fsync,
#ifdef HAVE_FOP_OPEN_EXEC
#endif
.mmap = xfs_file_mmap,
.open = xfs_file_open,
+ .flush = xfs_file_close,
.release = xfs_file_release,
.fsync = xfs_file_fsync,
};
/*
- * Copyright (c) 2000-2002,2005 Silicon Graphics, Inc.
+ * Copyright (c) 2000-2002,2005-2006 Silicon Graphics, Inc.
* All Rights Reserved.
*
* This program is free software; you can redistribute it and/or
* along with this program; if not, write the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
-
#include "xfs.h"
-/*
- * Stub for no-op vnode operations that return error status.
- */
-int
-fs_noerr(void)
-{
- return 0;
-}
+int fs_noerr(void) { return 0; }
+int fs_nosys(void) { return ENOSYS; }
+void fs_noval(void) { return; }
-/*
- * Operation unsupported under this file system.
- */
-int
-fs_nosys(void)
-{
- return ENOSYS;
-}
-
-/*
- * Stub for inactive, strategy, and read/write lock/unlock. Does nothing.
- */
-/* ARGSUSED */
-void
-fs_noval(void)
-{
-}
-
-/*
- * vnode pcache layer for vnode_tosspages.
- * 'last' parameter unused but left in for IRIX compatibility
- */
void
fs_tosspages(
bhv_desc_t *bdp,
xfs_off_t last,
int fiopt)
{
- vnode_t *vp = BHV_TO_VNODE(bdp);
+ bhv_vnode_t *vp = BHV_TO_VNODE(bdp);
struct inode *ip = vn_to_inode(vp);
if (VN_CACHED(vp))
truncate_inode_pages(ip->i_mapping, first);
}
-
-/*
- * vnode pcache layer for vnode_flushinval_pages.
- * 'last' parameter unused but left in for IRIX compatibility
- */
void
fs_flushinval_pages(
bhv_desc_t *bdp,
xfs_off_t last,
int fiopt)
{
- vnode_t *vp = BHV_TO_VNODE(bdp);
+ bhv_vnode_t *vp = BHV_TO_VNODE(bdp);
struct inode *ip = vn_to_inode(vp);
if (VN_CACHED(vp)) {
+ if (VN_TRUNC(vp))
+ VUNTRUNCATE(vp);
filemap_write_and_wait(ip->i_mapping);
-
truncate_inode_pages(ip->i_mapping, first);
}
}
-/*
- * vnode pcache layer for vnode_flush_pages.
- * 'last' parameter unused but left in for IRIX compatibility
- */
int
fs_flush_pages(
bhv_desc_t *bdp,
uint64_t flags,
int fiopt)
{
- vnode_t *vp = BHV_TO_VNODE(bdp);
+ bhv_vnode_t *vp = BHV_TO_VNODE(bdp);
struct inode *ip = vn_to_inode(vp);
- if (VN_CACHED(vp)) {
+ if (VN_DIRTY(vp)) {
+ if (VN_TRUNC(vp))
+ VUNTRUNCATE(vp);
filemap_fdatawrite(ip->i_mapping);
if (flags & XFS_B_ASYNC)
return 0;
filemap_fdatawait(ip->i_mapping);
}
-
return 0;
}
.xfs_buf_age = { 1*100, 15*100, 7200*100},
.inherit_nosym = { 0, 0, 1 },
.rotorstep = { 1, 1, 255 },
+ .inherit_nodfrg = { 0, 1, 1 },
};
/*
#include "xfs_trans.h"
#include "xfs_sb.h"
#include "xfs_ag.h"
-#include "xfs_dir.h"
#include "xfs_dir2.h"
#include "xfs_alloc.h"
#include "xfs_dmapi.h"
#include "xfs_bmap_btree.h"
#include "xfs_alloc_btree.h"
#include "xfs_ialloc_btree.h"
-#include "xfs_dir_sf.h"
#include "xfs_attr_sf.h"
#include "xfs_dir2_sf.h"
#include "xfs_dinode.h"
xfs_handle_t handle;
xfs_fsop_handlereq_t hreq;
struct inode *inode;
- struct vnode *vp;
+ bhv_vnode_t *vp;
if (copy_from_user(&hreq, arg, sizeof(hreq)))
return -XFS_ERROR(EFAULT);
xfs_mount_t *mp,
struct inode *parinode, /* parent inode pointer */
xfs_fsop_handlereq_t *hreq,
- vnode_t **vp,
+ bhv_vnode_t **vp,
struct inode **inode)
{
void __user *hanp;
xfs_handle_t handle;
xfs_inode_t *ip;
struct inode *inodep;
- vnode_t *vpp;
+ bhv_vnode_t *vpp;
xfs_ino_t ino;
__u32 igen;
int error;
struct file *filp;
struct inode *inode;
struct dentry *dentry;
- vnode_t *vp;
+ bhv_vnode_t *vp;
xfs_fsop_handlereq_t hreq;
if (!capable(CAP_SYS_ADMIN))
struct uio auio;
struct inode *inode;
xfs_fsop_handlereq_t hreq;
- vnode_t *vp;
+ bhv_vnode_t *vp;
__u32 olen;
if (!capable(CAP_SYS_ADMIN))
auio.uio_segflg = UIO_USERSPACE;
auio.uio_resid = olen;
- VOP_READLINK(vp, &auio, IO_INVIS, NULL, error);
-
+ error = bhv_vop_readlink(vp, &auio, IO_INVIS, NULL);
VN_RELE(vp);
+ if (error)
+ return -error;
+
return (olen - auio.uio_resid);
}
xfs_fsop_setdm_handlereq_t dmhreq;
struct inode *inode;
bhv_desc_t *bdp;
- vnode_t *vp;
+ bhv_vnode_t *vp;
if (!capable(CAP_MKNOD))
return -XFS_ERROR(EPERM);
attrlist_cursor_kern_t *cursor;
xfs_fsop_attrlist_handlereq_t al_hreq;
struct inode *inode;
- vnode_t *vp;
+ bhv_vnode_t *vp;
char *kbuf;
if (!capable(CAP_SYS_ADMIN))
goto out_vn_rele;
cursor = (attrlist_cursor_kern_t *)&al_hreq.pos;
- VOP_ATTR_LIST(vp, kbuf, al_hreq.buflen, al_hreq.flags,
- cursor, NULL, error);
+ error = bhv_vop_attr_list(vp, kbuf, al_hreq.buflen, al_hreq.flags,
+ cursor, NULL);
if (error)
goto out_kfree;
STATIC int
xfs_attrmulti_attr_get(
- struct vnode *vp,
+ bhv_vnode_t *vp,
char *name,
char __user *ubuf,
__uint32_t *len,
if (!kbuf)
return ENOMEM;
- VOP_ATTR_GET(vp, name, kbuf, len, flags, NULL, error);
+ error = bhv_vop_attr_get(vp, name, kbuf, len, flags, NULL);
if (error)
goto out_kfree;
STATIC int
xfs_attrmulti_attr_set(
- struct vnode *vp,
+ bhv_vnode_t *vp,
char *name,
const char __user *ubuf,
__uint32_t len,
if (copy_from_user(kbuf, ubuf, len))
goto out_kfree;
- VOP_ATTR_SET(vp, name, kbuf, len, flags, NULL, error);
+ error = bhv_vop_attr_set(vp, name, kbuf, len, flags, NULL);
out_kfree:
kfree(kbuf);
STATIC int
xfs_attrmulti_attr_remove(
- struct vnode *vp,
+ bhv_vnode_t *vp,
char *name,
__uint32_t flags)
{
- int error;
-
-
if (IS_RDONLY(&vp->v_inode))
return -EROFS;
if (IS_IMMUTABLE(&vp->v_inode) || IS_APPEND(&vp->v_inode))
return EPERM;
-
- VOP_ATTR_REMOVE(vp, name, flags, NULL, error);
- return error;
+ return bhv_vop_attr_remove(vp, name, flags, NULL);
}
STATIC int
xfs_attr_multiop_t *ops;
xfs_fsop_attrmulti_handlereq_t am_hreq;
struct inode *inode;
- vnode_t *vp;
+ bhv_vnode_t *vp;
unsigned int i, size;
char *attr_name;
STATIC int
xfs_ioc_space(
bhv_desc_t *bdp,
- vnode_t *vp,
+ bhv_vnode_t *vp,
struct file *filp,
int flags,
unsigned int cmd,
STATIC int
xfs_ioc_xattr(
- vnode_t *vp,
+ bhv_vnode_t *vp,
xfs_inode_t *ip,
struct file *filp,
unsigned int cmd,
void __user *arg)
{
int error;
- vnode_t *vp;
+ bhv_vnode_t *vp;
xfs_inode_t *ip;
xfs_mount_t *mp;
STATIC int
xfs_ioc_space(
bhv_desc_t *bdp,
- vnode_t *vp,
+ bhv_vnode_t *vp,
struct file *filp,
int ioflags,
unsigned int cmd,
STATIC int
xfs_ioc_xattr(
- vnode_t *vp,
+ bhv_vnode_t *vp,
xfs_inode_t *ip,
struct file *filp,
unsigned int cmd,
void __user *arg)
{
struct fsxattr fa;
- struct vattr *vattr;
+ struct bhv_vattr *vattr;
int error = 0;
int attr_flags;
unsigned int flags;
case XFS_IOC_FSGETXATTR: {
vattr->va_mask = XFS_AT_XFLAGS | XFS_AT_EXTSIZE | \
XFS_AT_NEXTENTS | XFS_AT_PROJID;
- VOP_GETATTR(vp, vattr, 0, NULL, error);
+ error = bhv_vop_getattr(vp, vattr, 0, NULL);
if (unlikely(error)) {
error = -error;
break;
vattr->va_extsize = fa.fsx_extsize;
vattr->va_projid = fa.fsx_projid;
- VOP_SETATTR(vp, vattr, attr_flags, NULL, error);
+ error = bhv_vop_setattr(vp, vattr, attr_flags, NULL);
if (likely(!error))
__vn_revalidate(vp, vattr); /* update flags */
error = -error;
case XFS_IOC_FSGETXATTRA: {
vattr->va_mask = XFS_AT_XFLAGS | XFS_AT_EXTSIZE | \
XFS_AT_ANEXTENTS | XFS_AT_PROJID;
- VOP_GETATTR(vp, vattr, 0, NULL, error);
+ error = bhv_vop_getattr(vp, vattr, 0, NULL);
if (unlikely(error)) {
error = -error;
break;
vattr->va_xflags = xfs_merge_ioc_xflags(flags,
xfs_ip2xflags(ip));
- VOP_SETATTR(vp, vattr, attr_flags, NULL, error);
+ error = bhv_vop_setattr(vp, vattr, attr_flags, NULL);
if (likely(!error))
__vn_revalidate(vp, vattr); /* update flags */
error = -error;
unsigned long arg)
{
struct inode *inode = file->f_dentry->d_inode;
- vnode_t *vp = vn_from_inode(inode);
+ bhv_vnode_t *vp = vn_from_inode(inode);
int error;
switch (cmd) {
return -ENOIOCTLCMD;
}
- VOP_IOCTL(vp, inode, file, mode, cmd, (void __user *)arg, error);
+ error = bhv_vop_ioctl(vp, inode, file, mode, cmd, (void __user *)arg);
VMODIFY(vp);
return error;
#include "xfs_trans.h"
#include "xfs_sb.h"
#include "xfs_ag.h"
-#include "xfs_dir.h"
#include "xfs_dir2.h"
#include "xfs_alloc.h"
#include "xfs_dmapi.h"
#include "xfs_bmap_btree.h"
#include "xfs_alloc_btree.h"
#include "xfs_ialloc_btree.h"
-#include "xfs_dir_sf.h"
#include "xfs_dir2_sf.h"
#include "xfs_attr_sf.h"
#include "xfs_dinode.h"
*/
xfs_inode_t *
xfs_vtoi(
- struct vnode *vp)
+ bhv_vnode_t *vp)
{
bhv_desc_t *bdp;
xfs_synchronize_atime(
xfs_inode_t *ip)
{
- vnode_t *vp;
+ bhv_vnode_t *vp;
vp = XFS_ITOV_NULL(ip);
if (vp) {
STATIC void
xfs_validate_fields(
struct inode *ip,
- struct vattr *vattr)
+ bhv_vattr_t *vattr)
{
- vnode_t *vp = vn_from_inode(ip);
- int error;
-
vattr->va_mask = XFS_AT_NLINK|XFS_AT_SIZE|XFS_AT_NBLOCKS;
- VOP_GETATTR(vp, vattr, ATTR_LAZY, NULL, error);
- if (likely(!error)) {
+ if (!bhv_vop_getattr(vn_from_inode(ip), vattr, ATTR_LAZY, NULL)) {
ip->i_nlink = vattr->va_nlink;
ip->i_blocks = vattr->va_nblocks;
*/
STATIC int
xfs_init_security(
- struct vnode *vp,
+ bhv_vnode_t *vp,
struct inode *dir)
{
struct inode *ip = vn_to_inode(vp);
return -error;
}
- VOP_ATTR_SET(vp, name, value, length, ATTR_SECURE, NULL, error);
+ error = bhv_vop_attr_set(vp, name, value, length, ATTR_SECURE, NULL);
if (!error)
VMODIFY(vp);
STATIC inline void
xfs_cleanup_inode(
- vnode_t *dvp,
- vnode_t *vp,
+ bhv_vnode_t *dvp,
+ bhv_vnode_t *vp,
struct dentry *dentry,
int mode)
{
struct dentry teardown = {};
- int error;
/* Oh, the horror.
* If we can't add the ACL or we fail in
teardown.d_name = dentry->d_name;
if (S_ISDIR(mode))
- VOP_RMDIR(dvp, &teardown, NULL, error);
+ bhv_vop_rmdir(dvp, &teardown, NULL);
else
- VOP_REMOVE(dvp, &teardown, NULL, error);
+ bhv_vop_remove(dvp, &teardown, NULL);
VN_RELE(vp);
}
dev_t rdev)
{
struct inode *ip;
- vattr_t vattr = { 0 };
- vnode_t *vp = NULL, *dvp = vn_from_inode(dir);
+ bhv_vattr_t vattr = { 0 };
+ bhv_vnode_t *vp = NULL, *dvp = vn_from_inode(dir);
xfs_acl_t *default_acl = NULL;
attrexists_t test_default_acl = _ACL_DEFAULT_EXISTS;
int error;
vattr.va_mask |= XFS_AT_RDEV;
/*FALLTHROUGH*/
case S_IFREG:
- VOP_CREATE(dvp, dentry, &vattr, &vp, NULL, error);
+ error = bhv_vop_create(dvp, dentry, &vattr, &vp, NULL);
break;
case S_IFDIR:
- VOP_MKDIR(dvp, dentry, &vattr, &vp, NULL, error);
+ error = bhv_vop_mkdir(dvp, dentry, &vattr, &vp, NULL);
break;
default:
error = EINVAL;
struct dentry *dentry,
struct nameidata *nd)
{
- struct vnode *vp = vn_from_inode(dir), *cvp;
+ bhv_vnode_t *vp = vn_from_inode(dir), *cvp;
int error;
if (dentry->d_name.len >= MAXNAMELEN)
return ERR_PTR(-ENAMETOOLONG);
- VOP_LOOKUP(vp, dentry, &cvp, 0, NULL, NULL, error);
- if (error) {
+ error = bhv_vop_lookup(vp, dentry, &cvp, 0, NULL, NULL);
+ if (unlikely(error)) {
if (unlikely(error != ENOENT))
return ERR_PTR(-error);
d_add(dentry, NULL);
struct dentry *dentry)
{
struct inode *ip; /* inode of guy being linked to */
- vnode_t *tdvp; /* target directory for new name/link */
- vnode_t *vp; /* vp of name being linked */
- vattr_t vattr;
+ bhv_vnode_t *tdvp; /* target directory for new name/link */
+ bhv_vnode_t *vp; /* vp of name being linked */
+ bhv_vattr_t vattr;
int error;
ip = old_dentry->d_inode; /* inode being linked to */
tdvp = vn_from_inode(dir);
vp = vn_from_inode(ip);
- VOP_LINK(tdvp, vp, dentry, NULL, error);
+ error = bhv_vop_link(tdvp, vp, dentry, NULL);
if (likely(!error)) {
VMODIFY(tdvp);
VN_HOLD(vp);
struct dentry *dentry)
{
struct inode *inode;
- vnode_t *dvp; /* directory containing name to remove */
- vattr_t vattr;
+ bhv_vnode_t *dvp; /* directory containing name to remove */
+ bhv_vattr_t vattr;
int error;
inode = dentry->d_inode;
dvp = vn_from_inode(dir);
- VOP_REMOVE(dvp, dentry, NULL, error);
+ error = bhv_vop_remove(dvp, dentry, NULL);
if (likely(!error)) {
xfs_validate_fields(dir, &vattr); /* size needs update */
xfs_validate_fields(inode, &vattr);
const char *symname)
{
struct inode *ip;
- vattr_t vattr = { 0 };
- vnode_t *dvp; /* directory containing name of symlink */
- vnode_t *cvp; /* used to lookup symlink to put in dentry */
+ bhv_vattr_t va = { 0 };
+ bhv_vnode_t *dvp; /* directory containing name of symlink */
+ bhv_vnode_t *cvp; /* used to lookup symlink to put in dentry */
int error;
dvp = vn_from_inode(dir);
cvp = NULL;
- vattr.va_mode = S_IFLNK |
+ va.va_mode = S_IFLNK |
(irix_symlink_mode ? 0777 & ~current->fs->umask : S_IRWXUGO);
- vattr.va_mask = XFS_AT_TYPE|XFS_AT_MODE;
+ va.va_mask = XFS_AT_TYPE|XFS_AT_MODE;
- error = 0;
- VOP_SYMLINK(dvp, dentry, &vattr, (char *)symname, &cvp, NULL, error);
+ error = bhv_vop_symlink(dvp, dentry, &va, (char *)symname, &cvp, NULL);
if (likely(!error && cvp)) {
error = xfs_init_security(cvp, dir);
if (likely(!error)) {
ip = vn_to_inode(cvp);
d_instantiate(dentry, ip);
- xfs_validate_fields(dir, &vattr);
- xfs_validate_fields(ip, &vattr);
+ xfs_validate_fields(dir, &va);
+ xfs_validate_fields(ip, &va);
} else {
xfs_cleanup_inode(dvp, cvp, dentry, 0);
}
struct dentry *dentry)
{
struct inode *inode = dentry->d_inode;
- vnode_t *dvp = vn_from_inode(dir);
- vattr_t vattr;
+ bhv_vnode_t *dvp = vn_from_inode(dir);
+ bhv_vattr_t vattr;
int error;
- VOP_RMDIR(dvp, dentry, NULL, error);
+ error = bhv_vop_rmdir(dvp, dentry, NULL);
if (likely(!error)) {
xfs_validate_fields(inode, &vattr);
xfs_validate_fields(dir, &vattr);
struct dentry *ndentry)
{
struct inode *new_inode = ndentry->d_inode;
- vnode_t *fvp; /* from directory */
- vnode_t *tvp; /* target directory */
- vattr_t vattr;
+ bhv_vnode_t *fvp; /* from directory */
+ bhv_vnode_t *tvp; /* target directory */
+ bhv_vattr_t vattr;
int error;
fvp = vn_from_inode(odir);
tvp = vn_from_inode(ndir);
- VOP_RENAME(fvp, odentry, tvp, ndentry, NULL, error);
+ error = bhv_vop_rename(fvp, odentry, tvp, ndentry, NULL);
if (likely(!error)) {
if (new_inode)
xfs_validate_fields(new_inode, &vattr);
struct dentry *dentry,
struct nameidata *nd)
{
- vnode_t *vp;
+ bhv_vnode_t *vp;
uio_t *uio;
iovec_t iov;
int error;
uio->uio_resid = MAXPATHLEN;
uio->uio_iovcnt = 1;
- VOP_READLINK(vp, uio, 0, NULL, error);
- if (error) {
+ error = bhv_vop_readlink(vp, uio, 0, NULL);
+ if (unlikely(error)) {
kfree(link);
link = ERR_PTR(-error);
} else {
int mode,
struct nameidata *nd)
{
- vnode_t *vp = vn_from_inode(inode);
- int error;
-
- mode <<= 6; /* convert from linux to vnode access bits */
- VOP_ACCESS(vp, mode, NULL, error);
- return -error;
+ return -bhv_vop_access(vn_from_inode(inode), mode << 6, NULL);
}
#else
#define xfs_vn_permission NULL
struct kstat *stat)
{
struct inode *inode = dentry->d_inode;
- vnode_t *vp = vn_from_inode(inode);
+ bhv_vnode_t *vp = vn_from_inode(inode);
int error = 0;
if (unlikely(vp->v_flag & VMODIFIED))
error = vn_revalidate(vp);
if (!error)
generic_fillattr(inode, stat);
- return 0;
+ return -error;
}
STATIC int
{
struct inode *inode = dentry->d_inode;
unsigned int ia_valid = attr->ia_valid;
- vnode_t *vp = vn_from_inode(inode);
- vattr_t vattr = { 0 };
+ bhv_vnode_t *vp = vn_from_inode(inode);
+ bhv_vattr_t vattr = { 0 };
int flags = 0;
int error;
flags |= ATTR_NONBLOCK;
#endif
- VOP_SETATTR(vp, &vattr, flags, NULL, error);
+ error = bhv_vop_setattr(vp, &vattr, flags, NULL);
if (likely(!error))
__vn_revalidate(vp, &vattr);
return -error;
size_t size,
int flags)
{
- vnode_t *vp = vn_from_inode(dentry->d_inode);
+ bhv_vnode_t *vp = vn_from_inode(dentry->d_inode);
char *attr = (char *)name;
attrnames_t *namesp;
int xflags = 0;
void *data,
size_t size)
{
- vnode_t *vp = vn_from_inode(dentry->d_inode);
+ bhv_vnode_t *vp = vn_from_inode(dentry->d_inode);
char *attr = (char *)name;
attrnames_t *namesp;
int xflags = 0;
char *data,
size_t size)
{
- vnode_t *vp = vn_from_inode(dentry->d_inode);
+ bhv_vnode_t *vp = vn_from_inode(dentry->d_inode);
int error, xflags = ATTR_KERNAMELS;
ssize_t result;
struct dentry *dentry,
const char *name)
{
- vnode_t *vp = vn_from_inode(dentry->d_inode);
+ bhv_vnode_t *vp = vn_from_inode(dentry->d_inode);
char *attr = (char *)name;
attrnames_t *namesp;
int xflags = 0;
#define xfs_buf_age_centisecs xfs_params.xfs_buf_age.val
#define xfs_inherit_nosymlinks xfs_params.inherit_nosym.val
#define xfs_rotorstep xfs_params.rotorstep.val
+#define xfs_inherit_nodefrag xfs_params.inherit_nodfrg.val
-#ifndef raw_smp_processor_id
-#define raw_smp_processor_id() smp_processor_id()
-#endif
-#define current_cpu() raw_smp_processor_id()
+#define current_cpu() (raw_smp_processor_id())
#define current_pid() (current->pid)
#define current_fsuid(cred) (current->fsuid)
#define current_fsgid(cred) (current->fsgid)
+#define current_set_flags(f) (current->flags |= (f))
+#define current_test_flags(f) (current->flags & (f))
+#define current_clear_flags(f) (current->flags & ~(f))
+#define current_set_flags_nested(sp, f) \
+ (*(sp) = current->flags, current->flags |= (f))
+#define current_clear_flags_nested(sp, f) \
+ (*(sp) = current->flags, current->flags &= ~(f))
+#define current_restore_flags_nested(sp, f) \
+ (current->flags = ((current->flags & ~(f)) | (*(sp) & (f))))
#define NBPP PAGE_SIZE
#define DPPSHFT (PAGE_SHIFT - 9)
/* bytes to clicks */
#define btoc(x) (((__psunsigned_t)(x)+(NBPC-1))>>BPCSHIFT)
-#ifndef ENOATTR
#define ENOATTR ENODATA /* Attribute not found */
-#endif
-
-/* Note: EWRONGFS never visible outside the kernel */
-#define EWRONGFS EINVAL /* Mount with wrong filesystem type */
-
-/*
- * XXX EFSCORRUPTED needs a real value in errno.h. asm-i386/errno.h won't
- * return codes out of its known range in errno.
- * XXX Also note: needs to be < 1000 and fairly unique on Linux (mustn't
- * conflict with any code we use already or any code a driver may use)
- * XXX Some options (currently we do #2):
- * 1/ New error code ["Filesystem is corrupted", _after_ glibc updated]
- * 2/ 990 ["Unknown error 990"]
- * 3/ EUCLEAN ["Structure needs cleaning"]
- * 4/ Convert EFSCORRUPTED to EIO [just prior to return into userspace]
- */
-#define EFSCORRUPTED 990 /* Filesystem is corrupted */
+#define EWRONGFS EINVAL /* Mount with wrong filesystem type */
+#define EFSCORRUPTED EUCLEAN /* Filesystem is corrupted */
#define SYNCHRONIZE() barrier()
#define __return_address __builtin_return_address(0)
#include "xfs_trans.h"
#include "xfs_sb.h"
#include "xfs_ag.h"
-#include "xfs_dir.h"
#include "xfs_dir2.h"
#include "xfs_alloc.h"
#include "xfs_dmapi.h"
#include "xfs_bmap_btree.h"
#include "xfs_alloc_btree.h"
#include "xfs_ialloc_btree.h"
-#include "xfs_dir_sf.h"
#include "xfs_dir2_sf.h"
#include "xfs_attr_sf.h"
#include "xfs_dinode.h"
xfs_fsize_t n;
xfs_inode_t *ip;
xfs_mount_t *mp;
- vnode_t *vp;
+ bhv_vnode_t *vp;
unsigned long seg;
ip = XFS_BHVTOI(bdp);
if (DM_EVENT_ENABLED(vp->v_vfsp, ip, DM_EVENT_READ) &&
!(ioflags & IO_INVIS)) {
- vrwlock_t locktype = VRWLOCK_READ;
+ bhv_vrwlock_t locktype = VRWLOCK_READ;
int dmflags = FILP_DELAY_FLAG(file) | DM_SEM_FLAG_RD(ioflags);
ret = -XFS_SEND_DATA(mp, DM_EVENT_READ,
}
if (unlikely((ioflags & IO_ISDIRECT) && VN_CACHED(vp)))
- VOP_FLUSHINVAL_PAGES(vp, ctooff(offtoct(*offset)),
+ bhv_vop_flushinval_pages(vp, ctooff(offtoct(*offset)),
-1, FI_REMAPF_LOCKED);
xfs_rw_enter_trace(XFS_READ_ENTER, &ip->i_iocore,
if (DM_EVENT_ENABLED(BHV_TO_VNODE(bdp)->v_vfsp, ip, DM_EVENT_READ) &&
(!(ioflags & IO_INVIS))) {
- vrwlock_t locktype = VRWLOCK_READ;
+ bhv_vrwlock_t locktype = VRWLOCK_READ;
int error;
error = XFS_SEND_DATA(mp, DM_EVENT_READ, BHV_TO_VNODE(bdp),
if (DM_EVENT_ENABLED(BHV_TO_VNODE(bdp)->v_vfsp, ip, DM_EVENT_READ) &&
(!(ioflags & IO_INVIS))) {
- vrwlock_t locktype = VRWLOCK_READ;
+ bhv_vrwlock_t locktype = VRWLOCK_READ;
int error;
error = XFS_SEND_DATA(mp, DM_EVENT_READ, BHV_TO_VNODE(bdp),
if (DM_EVENT_ENABLED(BHV_TO_VNODE(bdp)->v_vfsp, ip, DM_EVENT_WRITE) &&
(!(ioflags & IO_INVIS))) {
- vrwlock_t locktype = VRWLOCK_WRITE;
+ bhv_vrwlock_t locktype = VRWLOCK_WRITE;
int error;
error = XFS_SEND_DATA(mp, DM_EVENT_WRITE, BHV_TO_VNODE(bdp),
last_fsb = XFS_B_TO_FSBT(mp, isize);
nimaps = 1;
error = XFS_BMAPI(mp, NULL, io, last_fsb, 1, 0, NULL, 0, &imap,
- &nimaps, NULL);
+ &nimaps, NULL, NULL);
if (error) {
return error;
}
int /* error (positive) */
xfs_zero_eof(
- vnode_t *vp,
+ bhv_vnode_t *vp,
xfs_iocore_t *io,
xfs_off_t offset, /* starting I/O offset */
xfs_fsize_t isize, /* current inode size */
xfs_fileoff_t end_zero_fsb;
xfs_fileoff_t zero_count_fsb;
xfs_fileoff_t last_fsb;
- xfs_extlen_t buf_len_fsb;
xfs_mount_t *mp = io->io_mount;
int nimaps;
int error = 0;
nimaps = 1;
zero_count_fsb = end_zero_fsb - start_zero_fsb + 1;
error = XFS_BMAPI(mp, NULL, io, start_zero_fsb, zero_count_fsb,
- 0, NULL, 0, &imap, &nimaps, NULL);
+ 0, NULL, 0, &imap, &nimaps, NULL, NULL);
if (error) {
ASSERT(ismrlocked(io->io_lock, MR_UPDATE));
ASSERT(ismrlocked(io->io_iolock, MR_UPDATE));
}
/*
- * There are blocks in the range requested.
- * Zero them a single write at a time. We actually
- * don't zero the entire range returned if it is
- * too big and simply loop around to get the rest.
- * That is not the most efficient thing to do, but it
- * is simple and this path should not be exercised often.
- */
- buf_len_fsb = XFS_FILBLKS_MIN(imap.br_blockcount,
- mp->m_writeio_blocks << 8);
- /*
+ * There are blocks we need to zero.
* Drop the inode lock while we're doing the I/O.
* We'll still have the iolock to protect us.
*/
error = xfs_iozero(ip,
XFS_FSB_TO_B(mp, start_zero_fsb),
- XFS_FSB_TO_B(mp, buf_len_fsb),
+ XFS_FSB_TO_B(mp, imap.br_blockcount),
end_size);
-
if (error) {
goto out_lock;
}
- start_zero_fsb = imap.br_startoff + buf_len_fsb;
+ start_zero_fsb = imap.br_startoff + imap.br_blockcount;
ASSERT(start_zero_fsb <= (end_zero_fsb + 1));
XFS_ILOCK(mp, io, XFS_ILOCK_EXCL|XFS_EXTSIZE_RD);
ssize_t ret = 0, error = 0;
xfs_fsize_t isize, new_size;
xfs_iocore_t *io;
- vnode_t *vp;
+ bhv_vnode_t *vp;
unsigned long seg;
int iolock;
int eventsent = 0;
- vrwlock_t locktype;
+ bhv_vrwlock_t locktype;
size_t ocount = 0, count;
loff_t pos;
int need_i_mutex = 1, need_flush = 0;
io = &xip->i_iocore;
mp = io->io_mount;
+ vfs_wait_for_freeze(vp->v_vfsp, SB_FREEZE_WRITE);
+
if (XFS_FORCED_SHUTDOWN(mp))
return -EIO;
- fs_check_frozen(vp->v_vfsp, SB_FREEZE_WRITE);
-
if (ioflags & IO_ISDIRECT) {
xfs_buftarg_t *target =
(xip->i_d.di_flags & XFS_DIFLAG_REALTIME) ?
if (need_flush) {
xfs_inval_cached_trace(io, pos, -1,
ctooff(offtoct(pos)), -1);
- VOP_FLUSHINVAL_PAGES(vp, ctooff(offtoct(pos)),
+ bhv_vop_flushinval_pages(vp, ctooff(offtoct(pos)),
-1, FI_REMAPF_LOCKED);
}
/* Handle various SYNC-type writes */
if ((file->f_flags & O_SYNC) || IS_SYNC(inode)) {
- /*
- * If we're treating this as O_DSYNC and we have not updated the
- * size, force the log.
- */
- if (!(mp->m_flags & XFS_MOUNT_OSYNCISOSYNC) &&
- !(xip->i_update_size)) {
- xfs_inode_log_item_t *iip = xip->i_itemp;
-
- /*
- * If an allocation transaction occurred
- * without extending the size, then we have to force
- * the log up the proper point to ensure that the
- * allocation is permanent. We can't count on
- * the fact that buffered writes lock out direct I/O
- * writes - the direct I/O write could have extended
- * the size nontransactionally, then finished before
- * we started. xfs_write_file will think that the file
- * didn't grow but the update isn't safe unless the
- * size change is logged.
- *
- * Force the log if we've committed a transaction
- * against the inode or if someone else has and
- * the commit record hasn't gone to disk (e.g.
- * the inode is pinned). This guarantees that
- * all changes affecting the inode are permanent
- * when we return.
- */
- if (iip && iip->ili_last_lsn) {
- xfs_log_force(mp, iip->ili_last_lsn,
- XFS_LOG_FORCE | XFS_LOG_SYNC);
- } else if (xfs_ipincount(xip) > 0) {
- xfs_log_force(mp, (xfs_lsn_t)0,
- XFS_LOG_FORCE | XFS_LOG_SYNC);
- }
-
- } else {
- xfs_trans_t *tp;
-
- /*
- * O_SYNC or O_DSYNC _with_ a size update are handled
- * the same way.
- *
- * If the write was synchronous then we need to make
- * sure that the inode modification time is permanent.
- * We'll have updated the timestamp above, so here
- * we use a synchronous transaction to log the inode.
- * It's not fast, but it's necessary.
- *
- * If this a dsync write and the size got changed
- * non-transactionally, then we need to ensure that
- * the size change gets logged in a synchronous
- * transaction.
- */
-
- tp = xfs_trans_alloc(mp, XFS_TRANS_WRITE_SYNC);
- if ((error = xfs_trans_reserve(tp, 0,
- XFS_SWRITE_LOG_RES(mp),
- 0, 0, 0))) {
- /* Transaction reserve failed */
- xfs_trans_cancel(tp, 0);
- } else {
- /* Transaction reserve successful */
- xfs_ilock(xip, XFS_ILOCK_EXCL);
- xfs_trans_ijoin(tp, xip, XFS_ILOCK_EXCL);
- xfs_trans_ihold(tp, xip);
- xfs_trans_log_inode(tp, xip, XFS_ILOG_CORE);
- xfs_trans_set_sync(tp);
- error = xfs_trans_commit(tp, 0, NULL);
- xfs_iunlock(xip, XFS_ILOCK_EXCL);
- }
- if (error)
- goto out_unlock_internal;
- }
+ error = xfs_write_sync_logforce(mp, xip);
+ if (error)
+ goto out_unlock_internal;
xfs_rwunlock(bdp, locktype);
if (need_i_mutex)
#ifndef __XFS_LRW_H__
#define __XFS_LRW_H__
-struct vnode;
struct bhv_desc;
+struct bhv_vnode;
struct xfs_mount;
struct xfs_iocore;
struct xfs_inode;
#define XFS_CTRUNC4 14
#define XFS_CTRUNC5 15
#define XFS_CTRUNC6 16
-#define XFS_BUNMAPI 17
+#define XFS_BUNMAP 17
#define XFS_INVAL_CACHED 18
#define XFS_DIORD_ENTER 19
#define XFS_DIOWR_ENTER 20
extern int xfs_bdstrat_cb(struct xfs_buf *);
extern int xfs_dev_is_read_only(struct xfs_mount *, char *);
-extern int xfs_zero_eof(struct vnode *, struct xfs_iocore *, xfs_off_t,
+extern int xfs_zero_eof(struct bhv_vnode *, struct xfs_iocore *, xfs_off_t,
xfs_fsize_t, xfs_fsize_t);
extern ssize_t xfs_read(struct bhv_desc *, struct kiocb *,
const struct iovec *, unsigned int,
/*
- * Copyright (c) 2000-2005 Silicon Graphics, Inc.
+ * Copyright (c) 2000-2006 Silicon Graphics, Inc.
* All Rights Reserved.
*
* This program is free software; you can redistribute it and/or
#include "xfs_trans.h"
#include "xfs_sb.h"
#include "xfs_ag.h"
-#include "xfs_dir.h"
#include "xfs_dir2.h"
#include "xfs_alloc.h"
#include "xfs_dmapi.h"
#include "xfs_bmap_btree.h"
#include "xfs_alloc_btree.h"
#include "xfs_ialloc_btree.h"
-#include "xfs_dir_sf.h"
#include "xfs_dir2_sf.h"
#include "xfs_attr_sf.h"
#include "xfs_dinode.h"
STATIC __inline__ void
xfs_revalidate_inode(
xfs_mount_t *mp,
- vnode_t *vp,
+ bhv_vnode_t *vp,
xfs_inode_t *ip)
{
struct inode *inode = vn_to_inode(vp);
void
xfs_initialize_vnode(
bhv_desc_t *bdp,
- vnode_t *vp,
+ bhv_vnode_t *vp,
bhv_desc_t *inode_bhv,
int unlock)
{
xfs_fs_alloc_inode(
struct super_block *sb)
{
- vnode_t *vp;
+ bhv_vnode_t *vp;
vp = kmem_zone_alloc(xfs_vnode_zone, KM_SLEEP);
if (unlikely(!vp))
{
if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) ==
SLAB_CTOR_CONSTRUCTOR)
- inode_init_once(vn_to_inode((vnode_t *)vnode));
+ inode_init_once(vn_to_inode((bhv_vnode_t *)vnode));
}
STATIC int
xfs_init_zones(void)
{
- xfs_vnode_zone = kmem_zone_init_flags(sizeof(vnode_t), "xfs_vnode_t",
+ xfs_vnode_zone = kmem_zone_init_flags(sizeof(bhv_vnode_t), "xfs_vnode",
KM_ZONE_HWALIGN | KM_ZONE_RECLAIM |
KM_ZONE_SPREAD,
xfs_fs_inode_init_once);
struct inode *inode,
int sync)
{
- vnode_t *vp = vn_from_inode(inode);
+ bhv_vnode_t *vp = vn_from_inode(inode);
int error = 0, flags = FLUSH_INODE;
if (vp) {
vn_trace_entry(vp, __FUNCTION__, (inst_t *)__return_address);
if (sync)
flags |= FLUSH_SYNC;
- VOP_IFLUSH(vp, flags, error);
- if (error == EAGAIN) {
- if (sync)
- VOP_IFLUSH(vp, flags | FLUSH_LOG, error);
- else
- error = 0;
- }
+ error = bhv_vop_iflush(vp, flags);
+ if (error == EAGAIN)
+ error = sync? bhv_vop_iflush(vp, flags | FLUSH_LOG) : 0;
}
-
return -error;
}
xfs_fs_clear_inode(
struct inode *inode)
{
- vnode_t *vp = vn_from_inode(inode);
- int error, cache;
+ bhv_vnode_t *vp = vn_from_inode(inode);
vn_trace_entry(vp, __FUNCTION__, (inst_t *)__return_address);
* This can happen because xfs_iget_core calls xfs_idestroy if we
* find an inode with di_mode == 0 but without IGET_CREATE set.
*/
- if (vp->v_fbhv)
- VOP_INACTIVE(vp, NULL, cache);
+ if (VNHEAD(vp))
+ bhv_vop_inactive(vp, NULL);
VN_LOCK(vp);
vp->v_flag &= ~VMODIFIED;
VN_UNLOCK(vp, 0);
- if (vp->v_fbhv) {
- VOP_RECLAIM(vp, error);
- if (error)
- panic("vn_purge: cannot reclaim");
- }
+ if (VNHEAD(vp))
+ if (bhv_vop_reclaim(vp))
+ panic("%s: cannot reclaim 0x%p\n", __FUNCTION__, vp);
- ASSERT(vp->v_fbhv == NULL);
+ ASSERT(VNHEAD(vp) == NULL);
#ifdef XFS_VNODE_TRACE
ktrace_free(vp->v_trace);
*/
STATIC void
xfs_syncd_queue_work(
- struct vfs *vfs,
+ struct bhv_vfs *vfs,
void *data,
- void (*syncer)(vfs_t *, void *))
+ void (*syncer)(bhv_vfs_t *, void *))
{
- vfs_sync_work_t *work;
+ struct bhv_vfs_sync_work *work;
- work = kmem_alloc(sizeof(struct vfs_sync_work), KM_SLEEP);
+ work = kmem_alloc(sizeof(struct bhv_vfs_sync_work), KM_SLEEP);
INIT_LIST_HEAD(&work->w_list);
work->w_syncer = syncer;
work->w_data = data;
*/
STATIC void
xfs_flush_inode_work(
- vfs_t *vfs,
+ bhv_vfs_t *vfs,
void *inode)
{
filemap_flush(((struct inode *)inode)->i_mapping);
xfs_inode_t *ip)
{
struct inode *inode = vn_to_inode(XFS_ITOV(ip));
- struct vfs *vfs = XFS_MTOVFS(ip->i_mount);
+ struct bhv_vfs *vfs = XFS_MTOVFS(ip->i_mount);
igrab(inode);
xfs_syncd_queue_work(vfs, inode, xfs_flush_inode_work);
*/
STATIC void
xfs_flush_device_work(
- vfs_t *vfs,
+ bhv_vfs_t *vfs,
void *inode)
{
sync_blockdev(vfs->vfs_super->s_bdev);
xfs_inode_t *ip)
{
struct inode *inode = vn_to_inode(XFS_ITOV(ip));
- struct vfs *vfs = XFS_MTOVFS(ip->i_mount);
+ struct bhv_vfs *vfs = XFS_MTOVFS(ip->i_mount);
igrab(inode);
xfs_syncd_queue_work(vfs, inode, xfs_flush_device_work);
xfs_log_force(ip->i_mount, (xfs_lsn_t)0, XFS_LOG_FORCE|XFS_LOG_SYNC);
}
-#define SYNCD_FLAGS (SYNC_FSDATA|SYNC_BDFLUSH|SYNC_ATTR|SYNC_REFCACHE)
STATIC void
vfs_sync_worker(
- vfs_t *vfsp,
+ bhv_vfs_t *vfsp,
void *unused)
{
int error;
if (!(vfsp->vfs_flag & VFS_RDONLY))
- VFS_SYNC(vfsp, SYNCD_FLAGS, NULL, error);
+ error = bhv_vfs_sync(vfsp, SYNC_FSDATA | SYNC_BDFLUSH | \
+ SYNC_ATTR | SYNC_REFCACHE, NULL);
vfsp->vfs_sync_seq++;
wmb();
wake_up(&vfsp->vfs_wait_single_sync_task);
void *arg)
{
long timeleft;
- vfs_t *vfsp = (vfs_t *) arg;
- struct vfs_sync_work *work, *n;
+ bhv_vfs_t *vfsp = (bhv_vfs_t *) arg;
+ bhv_vfs_sync_work_t *work, *n;
LIST_HEAD (tmp);
timeleft = xfs_syncd_centisecs * msecs_to_jiffies(10);
list_del(&work->w_list);
if (work == &vfsp->vfs_sync_work)
continue;
- kmem_free(work, sizeof(struct vfs_sync_work));
+ kmem_free(work, sizeof(struct bhv_vfs_sync_work));
}
}
STATIC int
xfs_fs_start_syncd(
- vfs_t *vfsp)
+ bhv_vfs_t *vfsp)
{
vfsp->vfs_sync_work.w_syncer = vfs_sync_worker;
vfsp->vfs_sync_work.w_vfs = vfsp;
STATIC void
xfs_fs_stop_syncd(
- vfs_t *vfsp)
+ bhv_vfs_t *vfsp)
{
kthread_stop(vfsp->vfs_sync_task);
}
xfs_fs_put_super(
struct super_block *sb)
{
- vfs_t *vfsp = vfs_from_sb(sb);
+ bhv_vfs_t *vfsp = vfs_from_sb(sb);
int error;
xfs_fs_stop_syncd(vfsp);
- VFS_SYNC(vfsp, SYNC_ATTR|SYNC_DELWRI, NULL, error);
- if (!error)
- VFS_UNMOUNT(vfsp, 0, NULL, error);
+ bhv_vfs_sync(vfsp, SYNC_ATTR | SYNC_DELWRI, NULL);
+ error = bhv_vfs_unmount(vfsp, 0, NULL);
if (error) {
- printk("XFS unmount got error %d\n", error);
- printk("%s: vfsp/0x%p left dangling!\n", __FUNCTION__, vfsp);
- return;
+ printk("XFS: unmount got error=%d\n", error);
+ printk("%s: vfs=0x%p left dangling!\n", __FUNCTION__, vfsp);
+ } else {
+ vfs_deallocate(vfsp);
}
-
- vfs_deallocate(vfsp);
}
STATIC void
xfs_fs_write_super(
struct super_block *sb)
{
- vfs_t *vfsp = vfs_from_sb(sb);
- int error;
-
- if (sb->s_flags & MS_RDONLY) {
- sb->s_dirt = 0; /* paranoia */
- return;
- }
- /* Push the log and superblock a little */
- VFS_SYNC(vfsp, SYNC_FSDATA, NULL, error);
+ if (!(sb->s_flags & MS_RDONLY))
+ bhv_vfs_sync(vfs_from_sb(sb), SYNC_FSDATA, NULL);
sb->s_dirt = 0;
}
struct super_block *sb,
int wait)
{
- vfs_t *vfsp = vfs_from_sb(sb);
- int error;
- int flags = SYNC_FSDATA;
+ bhv_vfs_t *vfsp = vfs_from_sb(sb);
+ int error;
+ int flags;
if (unlikely(sb->s_frozen == SB_FREEZE_WRITE))
flags = SYNC_QUIESCE;
else
flags = SYNC_FSDATA | (wait ? SYNC_WAIT : 0);
- VFS_SYNC(vfsp, flags, NULL, error);
+ error = bhv_vfs_sync(vfsp, flags, NULL);
sb->s_dirt = 0;
if (unlikely(laptop_mode)) {
struct super_block *sb,
struct kstatfs *statp)
{
- vfs_t *vfsp = vfs_from_sb(sb);
- int error;
-
- VFS_STATVFS(vfsp, statp, NULL, error);
- return -error;
+ return -bhv_vfs_statvfs(vfs_from_sb(sb), statp, NULL);
}
STATIC int
int *flags,
char *options)
{
- vfs_t *vfsp = vfs_from_sb(sb);
+ bhv_vfs_t *vfsp = vfs_from_sb(sb);
struct xfs_mount_args *args = xfs_args_allocate(sb, 0);
int error;
- VFS_PARSEARGS(vfsp, options, args, 1, error);
+ error = bhv_vfs_parseargs(vfsp, options, args, 1);
if (!error)
- VFS_MNTUPDATE(vfsp, flags, args, error);
+ error = bhv_vfs_mntupdate(vfsp, flags, args);
kmem_free(args, sizeof(*args));
return -error;
}
xfs_fs_lockfs(
struct super_block *sb)
{
- VFS_FREEZE(vfs_from_sb(sb));
+ bhv_vfs_freeze(vfs_from_sb(sb));
}
STATIC int
struct seq_file *m,
struct vfsmount *mnt)
{
- struct vfs *vfsp = vfs_from_sb(mnt->mnt_sb);
- int error;
-
- VFS_SHOWARGS(vfsp, m, error);
- return error;
+ return -bhv_vfs_showargs(vfs_from_sb(mnt->mnt_sb), m);
}
STATIC int
struct super_block *sb,
int type)
{
- struct vfs *vfsp = vfs_from_sb(sb);
- int error;
-
- VFS_QUOTACTL(vfsp, Q_XQUOTASYNC, 0, (caddr_t)NULL, error);
- return -error;
+ return -bhv_vfs_quotactl(vfs_from_sb(sb), Q_XQUOTASYNC, 0, NULL);
}
STATIC int
struct super_block *sb,
struct fs_quota_stat *fqs)
{
- struct vfs *vfsp = vfs_from_sb(sb);
- int error;
-
- VFS_QUOTACTL(vfsp, Q_XGETQSTAT, 0, (caddr_t)fqs, error);
- return -error;
+ return -bhv_vfs_quotactl(vfs_from_sb(sb), Q_XGETQSTAT, 0, (caddr_t)fqs);
}
STATIC int
unsigned int flags,
int op)
{
- struct vfs *vfsp = vfs_from_sb(sb);
- int error;
-
- VFS_QUOTACTL(vfsp, op, 0, (caddr_t)&flags, error);
- return -error;
+ return -bhv_vfs_quotactl(vfs_from_sb(sb), op, 0, (caddr_t)&flags);
}
STATIC int
qid_t id,
struct fs_disk_quota *fdq)
{
- struct vfs *vfsp = vfs_from_sb(sb);
- int error, getmode;
-
- getmode = (type == USRQUOTA) ? Q_XGETQUOTA :
- ((type == GRPQUOTA) ? Q_XGETGQUOTA : Q_XGETPQUOTA);
- VFS_QUOTACTL(vfsp, getmode, id, (caddr_t)fdq, error);
- return -error;
+ return -bhv_vfs_quotactl(vfs_from_sb(sb),
+ (type == USRQUOTA) ? Q_XGETQUOTA :
+ ((type == GRPQUOTA) ? Q_XGETGQUOTA :
+ Q_XGETPQUOTA), id, (caddr_t)fdq);
}
STATIC int
qid_t id,
struct fs_disk_quota *fdq)
{
- struct vfs *vfsp = vfs_from_sb(sb);
- int error, setmode;
-
- setmode = (type == USRQUOTA) ? Q_XSETQLIM :
- ((type == GRPQUOTA) ? Q_XSETGQLIM : Q_XSETPQLIM);
- VFS_QUOTACTL(vfsp, setmode, id, (caddr_t)fdq, error);
- return -error;
+ return -bhv_vfs_quotactl(vfs_from_sb(sb),
+ (type == USRQUOTA) ? Q_XSETQLIM :
+ ((type == GRPQUOTA) ? Q_XSETGQLIM :
+ Q_XSETPQLIM), id, (caddr_t)fdq);
}
STATIC int
void *data,
int silent)
{
- vnode_t *rootvp;
- struct vfs *vfsp = vfs_allocate(sb);
+ struct bhv_vnode *rootvp;
+ struct bhv_vfs *vfsp = vfs_allocate(sb);
struct xfs_mount_args *args = xfs_args_allocate(sb, silent);
struct kstatfs statvfs;
- int error, error2;
+ int error;
bhv_insert_all_vfsops(vfsp);
- VFS_PARSEARGS(vfsp, (char *)data, args, 0, error);
+ error = bhv_vfs_parseargs(vfsp, (char *)data, args, 0);
if (error) {
bhv_remove_all_vfsops(vfsp, 1);
goto fail_vfsop;
}
sb_min_blocksize(sb, BBSIZE);
-#ifdef CONFIG_XFS_EXPORT
sb->s_export_op = &xfs_export_operations;
-#endif
sb->s_qcop = &xfs_quotactl_operations;
sb->s_op = &xfs_super_operations;
- VFS_MOUNT(vfsp, args, NULL, error);
+ error = bhv_vfs_mount(vfsp, args, NULL);
if (error) {
bhv_remove_all_vfsops(vfsp, 1);
goto fail_vfsop;
}
- VFS_STATVFS(vfsp, &statvfs, NULL, error);
+ error = bhv_vfs_statvfs(vfsp, &statvfs, NULL);
if (error)
goto fail_unmount;
sb->s_time_gran = 1;
set_posix_acl_flag(sb);
- VFS_ROOT(vfsp, &rootvp, error);
+ error = bhv_vfs_root(vfsp, &rootvp);
if (error)
goto fail_unmount;
}
fail_unmount:
- VFS_UNMOUNT(vfsp, 0, NULL, error2);
+ bhv_vfs_unmount(vfsp, 0, NULL);
fail_vfsop:
vfs_deallocate(vfsp);
extern __uint64_t xfs_max_file_offset(unsigned int);
-extern void xfs_initialize_vnode(bhv_desc_t *, vnode_t *, bhv_desc_t *, int);
+extern void xfs_initialize_vnode(bhv_desc_t *, bhv_vnode_t *, bhv_desc_t *, int);
extern void xfs_flush_inode(struct xfs_inode *);
extern void xfs_flush_device(struct xfs_inode *);
&sysctl_intvec, NULL,
&xfs_params.rotorstep.min, &xfs_params.rotorstep.max},
+ {XFS_INHERIT_NODFRG, "inherit_nodefrag", &xfs_params.inherit_nodfrg.val,
+ sizeof(int), 0644, NULL, &proc_dointvec_minmax,
+ &sysctl_intvec, NULL,
+ &xfs_params.inherit_nodfrg.min, &xfs_params.inherit_nodfrg.max},
+
/* please keep this the last entry */
#ifdef CONFIG_PROC_FS
{XFS_STATS_CLEAR, "stats_clear", &xfs_params.stats_clear.val,
xfs_sysctl_val_t xfs_buf_age; /* Metadata buffer age before flush. */
xfs_sysctl_val_t inherit_nosym; /* Inherit the "nosymlinks" flag. */
xfs_sysctl_val_t rotorstep; /* inode32 AG rotoring control knob */
+ xfs_sysctl_val_t inherit_nodfrg;/* Inherit the "nodefrag" inode flag. */
} xfs_param_t;
/*
/* XFS_IO_BYPASS = 18 */
XFS_INHERIT_NOSYM = 19,
XFS_ROTORSTEP = 20,
+ XFS_INHERIT_NODFRG = 21,
};
extern xfs_param_t xfs_params;
#include "xfs_trans.h"
#include "xfs_sb.h"
#include "xfs_ag.h"
-#include "xfs_dir.h"
#include "xfs_dir2.h"
#include "xfs_imap.h"
#include "xfs_alloc.h"
int
vfs_root(
struct bhv_desc *bdp,
- struct vnode **vpp)
+ struct bhv_vnode **vpp)
{
struct bhv_desc *next = bdp;
int
vfs_statvfs(
struct bhv_desc *bdp,
- xfs_statfs_t *sp,
- struct vnode *vp)
+ bhv_statvfs_t *statp,
+ struct bhv_vnode *vp)
{
struct bhv_desc *next = bdp;
ASSERT(next);
while (! (bhvtovfsops(next))->vfs_statvfs)
next = BHV_NEXT(next);
- return ((*bhvtovfsops(next)->vfs_statvfs)(next, sp, vp));
+ return ((*bhvtovfsops(next)->vfs_statvfs)(next, statp, vp));
}
int
int
vfs_vget(
struct bhv_desc *bdp,
- struct vnode **vpp,
+ struct bhv_vnode **vpp,
struct fid *fidp)
{
struct bhv_desc *next = bdp;
void
vfs_init_vnode(
struct bhv_desc *bdp,
- struct vnode *vp,
+ struct bhv_vnode *vp,
struct bhv_desc *bp,
int unlock)
{
((*bhvtovfsops(next)->vfs_freeze)(next));
}
-vfs_t *
+bhv_vfs_t *
vfs_allocate(
struct super_block *sb)
{
- struct vfs *vfsp;
+ struct bhv_vfs *vfsp;
- vfsp = kmem_zalloc(sizeof(vfs_t), KM_SLEEP);
+ vfsp = kmem_zalloc(sizeof(bhv_vfs_t), KM_SLEEP);
bhv_head_init(VFS_BHVHEAD(vfsp), "vfs");
INIT_LIST_HEAD(&vfsp->vfs_sync_list);
spin_lock_init(&vfsp->vfs_sync_lock);
return vfsp;
}
-vfs_t *
+bhv_vfs_t *
vfs_from_sb(
struct super_block *sb)
{
- return (vfs_t *)sb->s_fs_info;
+ return (bhv_vfs_t *)sb->s_fs_info;
}
void
vfs_deallocate(
- struct vfs *vfsp)
+ struct bhv_vfs *vfsp)
{
bhv_head_destroy(VFS_BHVHEAD(vfsp));
- kmem_free(vfsp, sizeof(vfs_t));
+ kmem_free(vfsp, sizeof(bhv_vfs_t));
}
void
vfs_insertops(
- struct vfs *vfsp,
- struct bhv_vfsops *vfsops)
+ struct bhv_vfs *vfsp,
+ struct bhv_module_vfsops *vfsops)
{
struct bhv_desc *bdp;
void
vfs_insertbhv(
- struct vfs *vfsp,
+ struct bhv_vfs *vfsp,
struct bhv_desc *bdp,
- struct vfsops *vfsops,
+ struct bhv_vfsops *vfsops,
void *mount)
{
bhv_desc_init(bdp, mount, vfsp, vfsops);
void
bhv_remove_vfsops(
- struct vfs *vfsp,
+ struct bhv_vfs *vfsp,
int pos)
{
struct bhv_desc *bhv;
void
bhv_remove_all_vfsops(
- struct vfs *vfsp,
+ struct bhv_vfs *vfsp,
int freebase)
{
struct xfs_mount *mp;
void
bhv_insert_all_vfsops(
- struct vfs *vfsp)
+ struct bhv_vfs *vfsp)
{
struct xfs_mount *mp;
/*
- * Copyright (c) 2000-2005 Silicon Graphics, Inc.
+ * Copyright (c) 2000-2006 Silicon Graphics, Inc.
* All Rights Reserved.
*
* This program is free software; you can redistribute it and/or
#include <linux/vfs.h>
#include "xfs_fs.h"
+struct bhv_vfs;
+struct bhv_vnode;
+
struct fid;
-struct vfs;
struct cred;
-struct vnode;
-struct kstatfs;
struct seq_file;
struct super_block;
struct xfs_mount_args;
-typedef struct kstatfs xfs_statfs_t;
+typedef struct kstatfs bhv_statvfs_t;
-typedef struct vfs_sync_work {
+typedef struct bhv_vfs_sync_work {
struct list_head w_list;
- struct vfs *w_vfs;
+ struct bhv_vfs *w_vfs;
void *w_data; /* syncer routine argument */
- void (*w_syncer)(struct vfs *, void *);
-} vfs_sync_work_t;
+ void (*w_syncer)(struct bhv_vfs *, void *);
+} bhv_vfs_sync_work_t;
-typedef struct vfs {
+typedef struct bhv_vfs {
u_int vfs_flag; /* flags */
xfs_fsid_t vfs_fsid; /* file system ID */
xfs_fsid_t *vfs_altfsid; /* An ID fixed for life of FS */
bhv_head_t vfs_bh; /* head of vfs behavior chain */
struct super_block *vfs_super; /* generic superblock pointer */
struct task_struct *vfs_sync_task; /* generalised sync thread */
- vfs_sync_work_t vfs_sync_work; /* work item for VFS_SYNC */
+ bhv_vfs_sync_work_t vfs_sync_work; /* work item for VFS_SYNC */
struct list_head vfs_sync_list; /* sync thread work item list */
spinlock_t vfs_sync_lock; /* work item list lock */
- int vfs_sync_seq; /* sync thread generation no. */
+ int vfs_sync_seq; /* sync thread generation no. */
wait_queue_head_t vfs_wait_single_sync_task;
-} vfs_t;
-
-#define vfs_fbhv vfs_bh.bh_first /* 1st on vfs behavior chain */
+} bhv_vfs_t;
-#define bhvtovfs(bdp) ( (struct vfs *)BHV_VOBJ(bdp) )
-#define bhvtovfsops(bdp) ( (struct vfsops *)BHV_OPS(bdp) )
+#define bhvtovfs(bdp) ( (struct bhv_vfs *)BHV_VOBJ(bdp) )
+#define bhvtovfsops(bdp) ( (struct bhv_vfsops *)BHV_OPS(bdp) )
#define VFS_BHVHEAD(vfs) ( &(vfs)->vfs_bh )
#define VFS_REMOVEBHV(vfs, bdp) ( bhv_remove(VFS_BHVHEAD(vfs), bdp) )
VFS_BHV_QM, /* quota manager */
VFS_BHV_IO, /* IO path */
VFS_BHV_END /* housekeeping end-of-range */
-} vfs_bhv_t;
+} bhv_vfs_type_t;
#define VFS_POSITION_XFS (BHV_POSITION_BASE)
#define VFS_POSITION_DM (VFS_POSITION_BASE+10)
#define VFS_RDONLY 0x0001 /* read-only vfs */
#define VFS_GRPID 0x0002 /* group-ID assigned from directory */
#define VFS_DMI 0x0004 /* filesystem has the DMI enabled */
-#define VFS_32BITINODES 0x0008 /* do not use inums above 32 bits */
-#define VFS_END 0x0008 /* max flag */
+#define VFS_UMOUNT 0x0008 /* unmount in progress */
+#define VFS_32BITINODES 0x0010 /* do not use inums above 32 bits */
+#define VFS_END 0x0010 /* max flag */
#define SYNC_ATTR 0x0001 /* sync attributes */
#define SYNC_CLOSE 0x0002 /* close file system down */
#define SYNC_FSDATA 0x0020 /* flush fs data (e.g. superblocks) */
#define SYNC_REFCACHE 0x0040 /* prune some of the nfs ref cache */
#define SYNC_REMOUNT 0x0080 /* remount readonly, no dummy LRs */
-#define SYNC_QUIESCE 0x0100 /* quiesce filesystem for a snapshot */
+#define SYNC_QUIESCE 0x0100 /* quiesce fileystem for a snapshot */
+
+#define SHUTDOWN_META_IO_ERROR 0x0001 /* write attempt to metadata failed */
+#define SHUTDOWN_LOG_IO_ERROR 0x0002 /* write attempt to the log failed */
+#define SHUTDOWN_FORCE_UMOUNT 0x0004 /* shutdown from a forced unmount */
+#define SHUTDOWN_CORRUPT_INCORE 0x0008 /* corrupt in-memory data structures */
+#define SHUTDOWN_REMOTE_REQ 0x0010 /* shutdown came from remote cell */
+#define SHUTDOWN_DEVICE_REQ 0x0020 /* failed all paths to the device */
typedef int (*vfs_mount_t)(bhv_desc_t *,
struct xfs_mount_args *, struct cred *);
typedef int (*vfs_unmount_t)(bhv_desc_t *, int, struct cred *);
typedef int (*vfs_mntupdate_t)(bhv_desc_t *, int *,
struct xfs_mount_args *);
-typedef int (*vfs_root_t)(bhv_desc_t *, struct vnode **);
-typedef int (*vfs_statvfs_t)(bhv_desc_t *, xfs_statfs_t *, struct vnode *);
+typedef int (*vfs_root_t)(bhv_desc_t *, struct bhv_vnode **);
+typedef int (*vfs_statvfs_t)(bhv_desc_t *, bhv_statvfs_t *,
+ struct bhv_vnode *);
typedef int (*vfs_sync_t)(bhv_desc_t *, int, struct cred *);
-typedef int (*vfs_vget_t)(bhv_desc_t *, struct vnode **, struct fid *);
+typedef int (*vfs_vget_t)(bhv_desc_t *, struct bhv_vnode **, struct fid *);
typedef int (*vfs_dmapiops_t)(bhv_desc_t *, caddr_t);
typedef int (*vfs_quotactl_t)(bhv_desc_t *, int, int, caddr_t);
typedef void (*vfs_init_vnode_t)(bhv_desc_t *,
- struct vnode *, bhv_desc_t *, int);
+ struct bhv_vnode *, bhv_desc_t *, int);
typedef void (*vfs_force_shutdown_t)(bhv_desc_t *, int, char *, int);
typedef void (*vfs_freeze_t)(bhv_desc_t *);
-typedef struct vfsops {
+typedef struct bhv_vfsops {
bhv_position_t vf_position; /* behavior chain position */
vfs_mount_t vfs_mount; /* mount file system */
vfs_parseargs_t vfs_parseargs; /* parse mount options */
vfs_init_vnode_t vfs_init_vnode; /* initialize a new vnode */
vfs_force_shutdown_t vfs_force_shutdown; /* crash and burn */
vfs_freeze_t vfs_freeze; /* freeze fs for snapshot */
-} vfsops_t;
+} bhv_vfsops_t;
/*
- * VFS's. Operates on vfs structure pointers (starts at bhv head).
+ * Virtual filesystem operations, operating from head bhv.
*/
-#define VHEAD(v) ((v)->vfs_fbhv)
-#define VFS_MOUNT(v, ma,cr, rv) ((rv) = vfs_mount(VHEAD(v), ma,cr))
-#define VFS_PARSEARGS(v, o,ma,f, rv) ((rv) = vfs_parseargs(VHEAD(v), o,ma,f))
-#define VFS_SHOWARGS(v, m, rv) ((rv) = vfs_showargs(VHEAD(v), m))
-#define VFS_UNMOUNT(v, f, cr, rv) ((rv) = vfs_unmount(VHEAD(v), f,cr))
-#define VFS_MNTUPDATE(v, fl, args, rv) ((rv) = vfs_mntupdate(VHEAD(v), fl, args))
-#define VFS_ROOT(v, vpp, rv) ((rv) = vfs_root(VHEAD(v), vpp))
-#define VFS_STATVFS(v, sp,vp, rv) ((rv) = vfs_statvfs(VHEAD(v), sp,vp))
-#define VFS_SYNC(v, flag,cr, rv) ((rv) = vfs_sync(VHEAD(v), flag,cr))
-#define VFS_VGET(v, vpp,fidp, rv) ((rv) = vfs_vget(VHEAD(v), vpp,fidp))
-#define VFS_DMAPIOPS(v, p, rv) ((rv) = vfs_dmapiops(VHEAD(v), p))
-#define VFS_QUOTACTL(v, c,id,p, rv) ((rv) = vfs_quotactl(VHEAD(v), c,id,p))
-#define VFS_INIT_VNODE(v, vp,b,ul) ( vfs_init_vnode(VHEAD(v), vp,b,ul) )
-#define VFS_FORCE_SHUTDOWN(v, fl,f,l) ( vfs_force_shutdown(VHEAD(v), fl,f,l) )
-#define VFS_FREEZE(v) ( vfs_freeze(VHEAD(v)) )
+#define VFSHEAD(v) ((v)->vfs_bh.bh_first)
+#define bhv_vfs_mount(v, ma,cr) vfs_mount(VFSHEAD(v), ma,cr)
+#define bhv_vfs_parseargs(v, o,ma,f) vfs_parseargs(VFSHEAD(v), o,ma,f)
+#define bhv_vfs_showargs(v, m) vfs_showargs(VFSHEAD(v), m)
+#define bhv_vfs_unmount(v, f,cr) vfs_unmount(VFSHEAD(v), f,cr)
+#define bhv_vfs_mntupdate(v, fl,args) vfs_mntupdate(VFSHEAD(v), fl,args)
+#define bhv_vfs_root(v, vpp) vfs_root(VFSHEAD(v), vpp)
+#define bhv_vfs_statvfs(v, sp,vp) vfs_statvfs(VFSHEAD(v), sp,vp)
+#define bhv_vfs_sync(v, flag,cr) vfs_sync(VFSHEAD(v), flag,cr)
+#define bhv_vfs_vget(v, vpp,fidp) vfs_vget(VFSHEAD(v), vpp,fidp)
+#define bhv_vfs_dmapiops(v, p) vfs_dmapiops(VFSHEAD(v), p)
+#define bhv_vfs_quotactl(v, c,id,p) vfs_quotactl(VFSHEAD(v), c,id,p)
+#define bhv_vfs_init_vnode(v, vp,b,ul) vfs_init_vnode(VFSHEAD(v), vp,b,ul)
+#define bhv_vfs_force_shutdown(v,u,f,l) vfs_force_shutdown(VFSHEAD(v), u,f,l)
+#define bhv_vfs_freeze(v) vfs_freeze(VFSHEAD(v))
/*
- * PVFS's. Operates on behavior descriptor pointers.
+ * Virtual filesystem operations, operating from next bhv.
*/
-#define PVFS_MOUNT(b, ma,cr, rv) ((rv) = vfs_mount(b, ma,cr))
-#define PVFS_PARSEARGS(b, o,ma,f, rv) ((rv) = vfs_parseargs(b, o,ma,f))
-#define PVFS_SHOWARGS(b, m, rv) ((rv) = vfs_showargs(b, m))
-#define PVFS_UNMOUNT(b, f,cr, rv) ((rv) = vfs_unmount(b, f,cr))
-#define PVFS_MNTUPDATE(b, fl, args, rv) ((rv) = vfs_mntupdate(b, fl, args))
-#define PVFS_ROOT(b, vpp, rv) ((rv) = vfs_root(b, vpp))
-#define PVFS_STATVFS(b, sp,vp, rv) ((rv) = vfs_statvfs(b, sp,vp))
-#define PVFS_SYNC(b, flag,cr, rv) ((rv) = vfs_sync(b, flag,cr))
-#define PVFS_VGET(b, vpp,fidp, rv) ((rv) = vfs_vget(b, vpp,fidp))
-#define PVFS_DMAPIOPS(b, p, rv) ((rv) = vfs_dmapiops(b, p))
-#define PVFS_QUOTACTL(b, c,id,p, rv) ((rv) = vfs_quotactl(b, c,id,p))
-#define PVFS_INIT_VNODE(b, vp,b2,ul) ( vfs_init_vnode(b, vp,b2,ul) )
-#define PVFS_FORCE_SHUTDOWN(b, fl,f,l) ( vfs_force_shutdown(b, fl,f,l) )
-#define PVFS_FREEZE(b) ( vfs_freeze(b) )
+#define bhv_next_vfs_mount(b, ma,cr) vfs_mount(b, ma,cr)
+#define bhv_next_vfs_parseargs(b, o,ma,f) vfs_parseargs(b, o,ma,f)
+#define bhv_next_vfs_showargs(b, m) vfs_showargs(b, m)
+#define bhv_next_vfs_unmount(b, f,cr) vfs_unmount(b, f,cr)
+#define bhv_next_vfs_mntupdate(b, fl,args) vfs_mntupdate(b, fl, args)
+#define bhv_next_vfs_root(b, vpp) vfs_root(b, vpp)
+#define bhv_next_vfs_statvfs(b, sp,vp) vfs_statvfs(b, sp,vp)
+#define bhv_next_vfs_sync(b, flag,cr) vfs_sync(b, flag,cr)
+#define bhv_next_vfs_vget(b, vpp,fidp) vfs_vget(b, vpp,fidp)
+#define bhv_next_vfs_dmapiops(b, p) vfs_dmapiops(b, p)
+#define bhv_next_vfs_quotactl(b, c,id,p) vfs_quotactl(b, c,id,p)
+#define bhv_next_vfs_init_vnode(b, vp,b2,ul) vfs_init_vnode(b, vp,b2,ul)
+#define bhv_next_force_shutdown(b, fl,f,l) vfs_force_shutdown(b, fl,f,l)
+#define bhv_next_vfs_freeze(b) vfs_freeze(b)
extern int vfs_mount(bhv_desc_t *, struct xfs_mount_args *, struct cred *);
extern int vfs_parseargs(bhv_desc_t *, char *, struct xfs_mount_args *, int);
extern int vfs_showargs(bhv_desc_t *, struct seq_file *);
extern int vfs_unmount(bhv_desc_t *, int, struct cred *);
extern int vfs_mntupdate(bhv_desc_t *, int *, struct xfs_mount_args *);
-extern int vfs_root(bhv_desc_t *, struct vnode **);
-extern int vfs_statvfs(bhv_desc_t *, xfs_statfs_t *, struct vnode *);
+extern int vfs_root(bhv_desc_t *, struct bhv_vnode **);
+extern int vfs_statvfs(bhv_desc_t *, bhv_statvfs_t *, struct bhv_vnode *);
extern int vfs_sync(bhv_desc_t *, int, struct cred *);
-extern int vfs_vget(bhv_desc_t *, struct vnode **, struct fid *);
+extern int vfs_vget(bhv_desc_t *, struct bhv_vnode **, struct fid *);
extern int vfs_dmapiops(bhv_desc_t *, caddr_t);
extern int vfs_quotactl(bhv_desc_t *, int, int, caddr_t);
-extern void vfs_init_vnode(bhv_desc_t *, struct vnode *, bhv_desc_t *, int);
+extern void vfs_init_vnode(bhv_desc_t *, struct bhv_vnode *, bhv_desc_t *, int);
extern void vfs_force_shutdown(bhv_desc_t *, int, char *, int);
extern void vfs_freeze(bhv_desc_t *);
-typedef struct bhv_vfsops {
- struct vfsops bhv_common;
+#define vfs_test_for_freeze(vfs) ((vfs)->vfs_super->s_frozen)
+#define vfs_wait_for_freeze(vfs,l) vfs_check_frozen((vfs)->vfs_super, (l))
+
+typedef struct bhv_module_vfsops {
+ struct bhv_vfsops bhv_common;
void * bhv_custom;
-} bhv_vfsops_t;
+} bhv_module_vfsops_t;
-#define vfs_bhv_lookup(v, id) ( bhv_lookup_range(&(v)->vfs_bh, (id), (id)) )
-#define vfs_bhv_custom(b) ( ((bhv_vfsops_t *)BHV_OPS(b))->bhv_custom )
-#define vfs_bhv_set_custom(b,o) ( (b)->bhv_custom = (void *)(o))
-#define vfs_bhv_clr_custom(b) ( (b)->bhv_custom = NULL )
+#define vfs_bhv_lookup(v, id) (bhv_lookup_range(&(v)->vfs_bh, (id), (id)))
+#define vfs_bhv_custom(b) (((bhv_module_vfsops_t*)BHV_OPS(b))->bhv_custom)
+#define vfs_bhv_set_custom(b,o) ((b)->bhv_custom = (void *)(o))
+#define vfs_bhv_clr_custom(b) ((b)->bhv_custom = NULL)
-extern vfs_t *vfs_allocate(struct super_block *);
-extern vfs_t *vfs_from_sb(struct super_block *);
-extern void vfs_deallocate(vfs_t *);
-extern void vfs_insertops(vfs_t *, bhv_vfsops_t *);
-extern void vfs_insertbhv(vfs_t *, bhv_desc_t *, vfsops_t *, void *);
+extern bhv_vfs_t *vfs_allocate(struct super_block *);
+extern bhv_vfs_t *vfs_from_sb(struct super_block *);
+extern void vfs_deallocate(bhv_vfs_t *);
+extern void vfs_insertbhv(bhv_vfs_t *, bhv_desc_t *, bhv_vfsops_t *, void *);
-extern void bhv_insert_all_vfsops(struct vfs *);
-extern void bhv_remove_all_vfsops(struct vfs *, int);
-extern void bhv_remove_vfsops(struct vfs *, int);
+extern void vfs_insertops(bhv_vfs_t *, bhv_module_vfsops_t *);
-#define fs_frozen(vfsp) ((vfsp)->vfs_super->s_frozen)
-#define fs_check_frozen(vfsp, level) \
- vfs_check_frozen(vfsp->vfs_super, level);
+extern void bhv_insert_all_vfsops(struct bhv_vfs *);
+extern void bhv_remove_all_vfsops(struct bhv_vfs *, int);
+extern void bhv_remove_vfsops(struct bhv_vfs *, int);
#endif /* __XFS_VFS_H__ */
void
vn_iowait(
- struct vnode *vp)
+ bhv_vnode_t *vp)
{
wait_queue_head_t *wq = vptosync(vp);
void
vn_iowake(
- struct vnode *vp)
+ bhv_vnode_t *vp)
{
if (atomic_dec_and_test(&vp->v_iocount))
wake_up(vptosync(vp));
}
-struct vnode *
+/*
+ * Volume managers supporting multiple paths can send back ENODEV when the
+ * final path disappears. In this case continuing to fill the page cache
+ * with dirty data which cannot be written out is evil, so prevent that.
+ */
+void
+vn_ioerror(
+ bhv_vnode_t *vp,
+ int error,
+ char *f,
+ int l)
+{
+ if (unlikely(error == -ENODEV))
+ bhv_vfs_force_shutdown(vp->v_vfsp, SHUTDOWN_DEVICE_REQ, f, l);
+}
+
+bhv_vnode_t *
vn_initialize(
struct inode *inode)
{
- struct vnode *vp = vn_from_inode(inode);
+ bhv_vnode_t *vp = vn_from_inode(inode);
XFS_STATS_INC(vn_active);
XFS_STATS_INC(vn_alloc);
*/
void
vn_revalidate_core(
- struct vnode *vp,
- vattr_t *vap)
+ bhv_vnode_t *vp,
+ bhv_vattr_t *vap)
{
struct inode *inode = vn_to_inode(vp);
*/
int
__vn_revalidate(
- struct vnode *vp,
- struct vattr *vattr)
+ bhv_vnode_t *vp,
+ bhv_vattr_t *vattr)
{
int error;
vn_trace_entry(vp, __FUNCTION__, (inst_t *)__return_address);
vattr->va_mask = XFS_AT_STAT | XFS_AT_XFLAGS;
- VOP_GETATTR(vp, vattr, 0, NULL, error);
+ error = bhv_vop_getattr(vp, vattr, 0, NULL);
if (likely(!error)) {
vn_revalidate_core(vp, vattr);
VUNMODIFY(vp);
int
vn_revalidate(
- struct vnode *vp)
+ bhv_vnode_t *vp)
{
- vattr_t vattr;
+ bhv_vattr_t vattr;
return __vn_revalidate(vp, &vattr);
}
/*
* Add a reference to a referenced vnode.
*/
-struct vnode *
+bhv_vnode_t *
vn_hold(
- struct vnode *vp)
+ bhv_vnode_t *vp)
{
struct inode *inode;
* Vnode tracing code.
*/
void
-vn_trace_entry(vnode_t *vp, const char *func, inst_t *ra)
+vn_trace_entry(bhv_vnode_t *vp, const char *func, inst_t *ra)
{
KTRACE_ENTER(vp, VNODE_KTRACE_ENTRY, func, 0, ra);
}
void
-vn_trace_exit(vnode_t *vp, const char *func, inst_t *ra)
+vn_trace_exit(bhv_vnode_t *vp, const char *func, inst_t *ra)
{
KTRACE_ENTER(vp, VNODE_KTRACE_EXIT, func, 0, ra);
}
void
-vn_trace_hold(vnode_t *vp, char *file, int line, inst_t *ra)
+vn_trace_hold(bhv_vnode_t *vp, char *file, int line, inst_t *ra)
{
KTRACE_ENTER(vp, VNODE_KTRACE_HOLD, file, line, ra);
}
void
-vn_trace_ref(vnode_t *vp, char *file, int line, inst_t *ra)
+vn_trace_ref(bhv_vnode_t *vp, char *file, int line, inst_t *ra)
{
KTRACE_ENTER(vp, VNODE_KTRACE_REF, file, line, ra);
}
void
-vn_trace_rele(vnode_t *vp, char *file, int line, inst_t *ra)
+vn_trace_rele(bhv_vnode_t *vp, char *file, int line, inst_t *ra)
{
KTRACE_ENTER(vp, VNODE_KTRACE_RELE, file, line, ra);
}
* You should have received a copy of the GNU General Public License
* along with this program; if not, write the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Portions Copyright (c) 1989, 1993
- * The Regents of the University of California. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the University nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
*/
#ifndef __XFS_VNODE_H__
#define __XFS_VNODE_H__
struct uio;
struct file;
-struct vattr;
+struct bhv_vfs;
+struct bhv_vattr;
struct xfs_iomap;
struct attrlist_cursor_kern;
+typedef struct dentry bhv_vname_t;
+typedef __u64 bhv_vnumber_t;
-typedef xfs_ino_t vnumber_t;
-typedef struct dentry vname_t;
-typedef bhv_head_t vn_bhv_head_t;
+typedef enum bhv_vflags {
+ VMODIFIED = 0x08, /* XFS inode state possibly differs */
+ /* to the Linux inode state. */
+ VTRUNCATED = 0x40, /* truncated down so flush-on-close */
+} bhv_vflags_t;
/*
* MP locking protocols:
* v_flag, v_vfsp VN_LOCK/VN_UNLOCK
*/
-typedef struct vnode {
- __u32 v_flag; /* vnode flags (see below) */
- struct vfs *v_vfsp; /* ptr to containing VFS */
- vnumber_t v_number; /* in-core vnode number */
- vn_bhv_head_t v_bh; /* behavior head */
+typedef struct bhv_vnode {
+ bhv_vflags_t v_flag; /* vnode flags (see above) */
+ bhv_vfs_t *v_vfsp; /* ptr to containing VFS */
+ bhv_vnumber_t v_number; /* in-core vnode number */
+ bhv_head_t v_bh; /* behavior head */
spinlock_t v_lock; /* VN_LOCK/VN_UNLOCK */
atomic_t v_iocount; /* outstanding I/O count */
#ifdef XFS_VNODE_TRACE
#endif
struct inode v_inode; /* Linux inode */
/* inode MUST be last */
-} vnode_t;
+} bhv_vnode_t;
#define VN_ISLNK(vp) S_ISLNK((vp)->v_inode.i_mode)
#define VN_ISREG(vp) S_ISREG((vp)->v_inode.i_mode)
#define VN_ISCHR(vp) S_ISCHR((vp)->v_inode.i_mode)
#define VN_ISBLK(vp) S_ISBLK((vp)->v_inode.i_mode)
-#define v_fbhv v_bh.bh_first /* first behavior */
-#define v_fops v_bh.bh_first->bd_ops /* first behavior ops */
-
#define VNODE_POSITION_BASE BHV_POSITION_BASE /* chain bottom */
#define VNODE_POSITION_TOP BHV_POSITION_TOP /* chain top */
#define VNODE_POSITION_INVALID BHV_POSITION_INVALID /* invalid pos. num */
/*
* Macros for dealing with the behavior descriptor inside of the vnode.
*/
-#define BHV_TO_VNODE(bdp) ((vnode_t *)BHV_VOBJ(bdp))
-#define BHV_TO_VNODE_NULL(bdp) ((vnode_t *)BHV_VOBJNULL(bdp))
+#define BHV_TO_VNODE(bdp) ((bhv_vnode_t *)BHV_VOBJ(bdp))
+#define BHV_TO_VNODE_NULL(bdp) ((bhv_vnode_t *)BHV_VOBJNULL(bdp))
#define VN_BHV_HEAD(vp) ((bhv_head_t *)(&((vp)->v_bh)))
#define vn_bhv_head_init(bhp,name) bhv_head_init(bhp,name)
/*
* Vnode to Linux inode mapping.
*/
-static inline struct vnode *vn_from_inode(struct inode *inode)
+static inline struct bhv_vnode *vn_from_inode(struct inode *inode)
{
- return (vnode_t *)list_entry(inode, vnode_t, v_inode);
+ return (bhv_vnode_t *)list_entry(inode, bhv_vnode_t, v_inode);
}
-static inline struct inode *vn_to_inode(struct vnode *vnode)
+static inline struct inode *vn_to_inode(struct bhv_vnode *vnode)
{
return &vnode->v_inode;
}
/*
- * Vnode flags.
- */
-#define VMODIFIED 0x8 /* XFS inode state possibly differs */
- /* to the Linux inode state. */
-
-/*
- * Values for the VOP_RWLOCK and VOP_RWUNLOCK flags parameter.
+ * Values for the vop_rwlock/rwunlock flags parameter.
*/
-typedef enum vrwlock {
+typedef enum bhv_vrwlock {
VRWLOCK_NONE,
VRWLOCK_READ,
VRWLOCK_WRITE,
VRWLOCK_WRITE_DIRECT,
VRWLOCK_TRY_READ,
VRWLOCK_TRY_WRITE
-} vrwlock_t;
+} bhv_vrwlock_t;
/*
- * Return values for VOP_INACTIVE. A return value of
+ * Return values for bhv_vop_inactive. A return value of
* VN_INACTIVE_NOCACHE implies that the file system behavior
* has disassociated its state and bhv_desc_t from the vnode.
*/
#define VN_INACTIVE_NOCACHE 1
/*
- * Values for the cmd code given to VOP_VNODE_CHANGE.
+ * Values for the cmd code given to vop_vnode_change.
*/
-typedef enum vchange {
+typedef enum bhv_vchange {
VCHANGE_FLAGS_FRLOCKS = 0,
VCHANGE_FLAGS_ENF_LOCKING = 1,
VCHANGE_FLAGS_TRUNCATED = 2,
VCHANGE_FLAGS_PAGE_DIRTY = 3,
VCHANGE_FLAGS_IOEXCL_COUNT = 4
-} vchange_t;
+} bhv_vchange_t;
+typedef enum { L_FALSE, L_TRUE } lastclose_t;
typedef int (*vop_open_t)(bhv_desc_t *, struct cred *);
+typedef int (*vop_close_t)(bhv_desc_t *, int, lastclose_t, struct cred *);
typedef ssize_t (*vop_read_t)(bhv_desc_t *, struct kiocb *,
const struct iovec *, unsigned int,
loff_t *, int, struct cred *);
struct cred *);
typedef int (*vop_ioctl_t)(bhv_desc_t *, struct inode *, struct file *,
int, unsigned int, void __user *);
-typedef int (*vop_getattr_t)(bhv_desc_t *, struct vattr *, int,
+typedef int (*vop_getattr_t)(bhv_desc_t *, struct bhv_vattr *, int,
struct cred *);
-typedef int (*vop_setattr_t)(bhv_desc_t *, struct vattr *, int,
+typedef int (*vop_setattr_t)(bhv_desc_t *, struct bhv_vattr *, int,
struct cred *);
typedef int (*vop_access_t)(bhv_desc_t *, int, struct cred *);
-typedef int (*vop_lookup_t)(bhv_desc_t *, vname_t *, vnode_t **,
- int, vnode_t *, struct cred *);
-typedef int (*vop_create_t)(bhv_desc_t *, vname_t *, struct vattr *,
- vnode_t **, struct cred *);
-typedef int (*vop_remove_t)(bhv_desc_t *, vname_t *, struct cred *);
-typedef int (*vop_link_t)(bhv_desc_t *, vnode_t *, vname_t *,
- struct cred *);
-typedef int (*vop_rename_t)(bhv_desc_t *, vname_t *, vnode_t *, vname_t *,
+typedef int (*vop_lookup_t)(bhv_desc_t *, bhv_vname_t *, bhv_vnode_t **,
+ int, bhv_vnode_t *, struct cred *);
+typedef int (*vop_create_t)(bhv_desc_t *, bhv_vname_t *, struct bhv_vattr *,
+ bhv_vnode_t **, struct cred *);
+typedef int (*vop_remove_t)(bhv_desc_t *, bhv_vname_t *, struct cred *);
+typedef int (*vop_link_t)(bhv_desc_t *, bhv_vnode_t *, bhv_vname_t *,
struct cred *);
-typedef int (*vop_mkdir_t)(bhv_desc_t *, vname_t *, struct vattr *,
- vnode_t **, struct cred *);
-typedef int (*vop_rmdir_t)(bhv_desc_t *, vname_t *, struct cred *);
+typedef int (*vop_rename_t)(bhv_desc_t *, bhv_vname_t *, bhv_vnode_t *,
+ bhv_vname_t *, struct cred *);
+typedef int (*vop_mkdir_t)(bhv_desc_t *, bhv_vname_t *, struct bhv_vattr *,
+ bhv_vnode_t **, struct cred *);
+typedef int (*vop_rmdir_t)(bhv_desc_t *, bhv_vname_t *, struct cred *);
typedef int (*vop_readdir_t)(bhv_desc_t *, struct uio *, struct cred *,
int *);
-typedef int (*vop_symlink_t)(bhv_desc_t *, vname_t *, struct vattr *,
- char *, vnode_t **, struct cred *);
+typedef int (*vop_symlink_t)(bhv_desc_t *, bhv_vname_t *, struct bhv_vattr*,
+ char *, bhv_vnode_t **, struct cred *);
typedef int (*vop_readlink_t)(bhv_desc_t *, struct uio *, int,
struct cred *);
typedef int (*vop_fsync_t)(bhv_desc_t *, int, struct cred *,
typedef int (*vop_inactive_t)(bhv_desc_t *, struct cred *);
typedef int (*vop_fid2_t)(bhv_desc_t *, struct fid *);
typedef int (*vop_release_t)(bhv_desc_t *);
-typedef int (*vop_rwlock_t)(bhv_desc_t *, vrwlock_t);
-typedef void (*vop_rwunlock_t)(bhv_desc_t *, vrwlock_t);
+typedef int (*vop_rwlock_t)(bhv_desc_t *, bhv_vrwlock_t);
+typedef void (*vop_rwunlock_t)(bhv_desc_t *, bhv_vrwlock_t);
typedef int (*vop_bmap_t)(bhv_desc_t *, xfs_off_t, ssize_t, int,
struct xfs_iomap *, int *);
typedef int (*vop_reclaim_t)(bhv_desc_t *);
int, struct cred *);
typedef int (*vop_attr_list_t)(bhv_desc_t *, char *, int, int,
struct attrlist_cursor_kern *, struct cred *);
-typedef void (*vop_link_removed_t)(bhv_desc_t *, vnode_t *, int);
-typedef void (*vop_vnode_change_t)(bhv_desc_t *, vchange_t, __psint_t);
+typedef void (*vop_link_removed_t)(bhv_desc_t *, bhv_vnode_t *, int);
+typedef void (*vop_vnode_change_t)(bhv_desc_t *, bhv_vchange_t, __psint_t);
typedef void (*vop_ptossvp_t)(bhv_desc_t *, xfs_off_t, xfs_off_t, int);
typedef void (*vop_pflushinvalvp_t)(bhv_desc_t *, xfs_off_t, xfs_off_t, int);
typedef int (*vop_pflushvp_t)(bhv_desc_t *, xfs_off_t, xfs_off_t,
typedef int (*vop_iflush_t)(bhv_desc_t *, int);
-typedef struct vnodeops {
+typedef struct bhv_vnodeops {
bhv_position_t vn_position; /* position within behavior chain */
vop_open_t vop_open;
+ vop_close_t vop_close;
vop_read_t vop_read;
vop_write_t vop_write;
vop_sendfile_t vop_sendfile;
vop_pflushvp_t vop_flush_pages;
vop_release_t vop_release;
vop_iflush_t vop_iflush;
-} vnodeops_t;
+} bhv_vnodeops_t;
/*
- * VOP's.
- */
-#define _VOP_(op, vp) (*((vnodeops_t *)(vp)->v_fops)->op)
-
-#define VOP_READ(vp,file,iov,segs,offset,ioflags,cr,rv) \
- rv = _VOP_(vop_read, vp)((vp)->v_fbhv,file,iov,segs,offset,ioflags,cr)
-#define VOP_WRITE(vp,file,iov,segs,offset,ioflags,cr,rv) \
- rv = _VOP_(vop_write, vp)((vp)->v_fbhv,file,iov,segs,offset,ioflags,cr)
-#define VOP_SENDFILE(vp,f,off,ioflags,cnt,act,targ,cr,rv) \
- rv = _VOP_(vop_sendfile, vp)((vp)->v_fbhv,f,off,ioflags,cnt,act,targ,cr)
-#define VOP_SPLICE_READ(vp,f,o,pipe,cnt,fl,iofl,cr,rv) \
- rv = _VOP_(vop_splice_read, vp)((vp)->v_fbhv,f,o,pipe,cnt,fl,iofl,cr)
-#define VOP_SPLICE_WRITE(vp,f,o,pipe,cnt,fl,iofl,cr,rv) \
- rv = _VOP_(vop_splice_write, vp)((vp)->v_fbhv,f,o,pipe,cnt,fl,iofl,cr)
-#define VOP_BMAP(vp,of,sz,rw,b,n,rv) \
- rv = _VOP_(vop_bmap, vp)((vp)->v_fbhv,of,sz,rw,b,n)
-#define VOP_OPEN(vp, cr, rv) \
- rv = _VOP_(vop_open, vp)((vp)->v_fbhv, cr)
-#define VOP_GETATTR(vp, vap, f, cr, rv) \
- rv = _VOP_(vop_getattr, vp)((vp)->v_fbhv, vap, f, cr)
-#define VOP_SETATTR(vp, vap, f, cr, rv) \
- rv = _VOP_(vop_setattr, vp)((vp)->v_fbhv, vap, f, cr)
-#define VOP_ACCESS(vp, mode, cr, rv) \
- rv = _VOP_(vop_access, vp)((vp)->v_fbhv, mode, cr)
-#define VOP_LOOKUP(vp,d,vpp,f,rdir,cr,rv) \
- rv = _VOP_(vop_lookup, vp)((vp)->v_fbhv,d,vpp,f,rdir,cr)
-#define VOP_CREATE(dvp,d,vap,vpp,cr,rv) \
- rv = _VOP_(vop_create, dvp)((dvp)->v_fbhv,d,vap,vpp,cr)
-#define VOP_REMOVE(dvp,d,cr,rv) \
- rv = _VOP_(vop_remove, dvp)((dvp)->v_fbhv,d,cr)
-#define VOP_LINK(tdvp,fvp,d,cr,rv) \
- rv = _VOP_(vop_link, tdvp)((tdvp)->v_fbhv,fvp,d,cr)
-#define VOP_RENAME(fvp,fnm,tdvp,tnm,cr,rv) \
- rv = _VOP_(vop_rename, fvp)((fvp)->v_fbhv,fnm,tdvp,tnm,cr)
-#define VOP_MKDIR(dp,d,vap,vpp,cr,rv) \
- rv = _VOP_(vop_mkdir, dp)((dp)->v_fbhv,d,vap,vpp,cr)
-#define VOP_RMDIR(dp,d,cr,rv) \
- rv = _VOP_(vop_rmdir, dp)((dp)->v_fbhv,d,cr)
-#define VOP_READDIR(vp,uiop,cr,eofp,rv) \
- rv = _VOP_(vop_readdir, vp)((vp)->v_fbhv,uiop,cr,eofp)
-#define VOP_SYMLINK(dvp,d,vap,tnm,vpp,cr,rv) \
- rv = _VOP_(vop_symlink, dvp) ((dvp)->v_fbhv,d,vap,tnm,vpp,cr)
-#define VOP_READLINK(vp,uiop,fl,cr,rv) \
- rv = _VOP_(vop_readlink, vp)((vp)->v_fbhv,uiop,fl,cr)
-#define VOP_FSYNC(vp,f,cr,b,e,rv) \
- rv = _VOP_(vop_fsync, vp)((vp)->v_fbhv,f,cr,b,e)
-#define VOP_INACTIVE(vp, cr, rv) \
- rv = _VOP_(vop_inactive, vp)((vp)->v_fbhv, cr)
-#define VOP_RELEASE(vp, rv) \
- rv = _VOP_(vop_release, vp)((vp)->v_fbhv)
-#define VOP_FID2(vp, fidp, rv) \
- rv = _VOP_(vop_fid2, vp)((vp)->v_fbhv, fidp)
-#define VOP_RWLOCK(vp,i) \
- (void)_VOP_(vop_rwlock, vp)((vp)->v_fbhv, i)
-#define VOP_RWLOCK_TRY(vp,i) \
- _VOP_(vop_rwlock, vp)((vp)->v_fbhv, i)
-#define VOP_RWUNLOCK(vp,i) \
- (void)_VOP_(vop_rwunlock, vp)((vp)->v_fbhv, i)
-#define VOP_FRLOCK(vp,c,fl,flags,offset,fr,rv) \
- rv = _VOP_(vop_frlock, vp)((vp)->v_fbhv,c,fl,flags,offset,fr)
-#define VOP_RECLAIM(vp, rv) \
- rv = _VOP_(vop_reclaim, vp)((vp)->v_fbhv)
-#define VOP_ATTR_GET(vp, name, val, vallenp, fl, cred, rv) \
- rv = _VOP_(vop_attr_get, vp)((vp)->v_fbhv,name,val,vallenp,fl,cred)
-#define VOP_ATTR_SET(vp, name, val, vallen, fl, cred, rv) \
- rv = _VOP_(vop_attr_set, vp)((vp)->v_fbhv,name,val,vallen,fl,cred)
-#define VOP_ATTR_REMOVE(vp, name, flags, cred, rv) \
- rv = _VOP_(vop_attr_remove, vp)((vp)->v_fbhv,name,flags,cred)
-#define VOP_ATTR_LIST(vp, buf, buflen, fl, cursor, cred, rv) \
- rv = _VOP_(vop_attr_list, vp)((vp)->v_fbhv,buf,buflen,fl,cursor,cred)
-#define VOP_LINK_REMOVED(vp, dvp, linkzero) \
- (void)_VOP_(vop_link_removed, vp)((vp)->v_fbhv, dvp, linkzero)
-#define VOP_VNODE_CHANGE(vp, cmd, val) \
- (void)_VOP_(vop_vnode_change, vp)((vp)->v_fbhv,cmd,val)
-/*
- * These are page cache functions that now go thru VOPs.
- * 'last' parameter is unused and left in for IRIX compatibility
+ * Virtual node operations, operating from head bhv.
*/
-#define VOP_TOSS_PAGES(vp, first, last, fiopt) \
- _VOP_(vop_tosspages, vp)((vp)->v_fbhv,first, last, fiopt)
-/*
- * 'last' parameter is unused and left in for IRIX compatibility
- */
-#define VOP_FLUSHINVAL_PAGES(vp, first, last, fiopt) \
- _VOP_(vop_flushinval_pages, vp)((vp)->v_fbhv,first,last,fiopt)
-/*
- * 'last' parameter is unused and left in for IRIX compatibility
- */
-#define VOP_FLUSH_PAGES(vp, first, last, flags, fiopt, rv) \
- rv = _VOP_(vop_flush_pages, vp)((vp)->v_fbhv,first,last,flags,fiopt)
-#define VOP_IOCTL(vp, inode, filp, fl, cmd, arg, rv) \
- rv = _VOP_(vop_ioctl, vp)((vp)->v_fbhv,inode,filp,fl,cmd,arg)
-#define VOP_IFLUSH(vp, flags, rv) \
- rv = _VOP_(vop_iflush, vp)((vp)->v_fbhv, flags)
+#define VNHEAD(vp) ((vp)->v_bh.bh_first)
+#define VOP(op, vp) (*((bhv_vnodeops_t *)VNHEAD(vp)->bd_ops)->op)
+#define bhv_vop_open(vp, cr) VOP(vop_open, vp)(VNHEAD(vp),cr)
+#define bhv_vop_close(vp, f,last,cr) VOP(vop_close, vp)(VNHEAD(vp),f,last,cr)
+#define bhv_vop_read(vp,file,iov,segs,offset,ioflags,cr) \
+ VOP(vop_read, vp)(VNHEAD(vp),file,iov,segs,offset,ioflags,cr)
+#define bhv_vop_write(vp,file,iov,segs,offset,ioflags,cr) \
+ VOP(vop_write, vp)(VNHEAD(vp),file,iov,segs,offset,ioflags,cr)
+#define bhv_vop_sendfile(vp,f,off,ioflags,cnt,act,targ,cr) \
+ VOP(vop_sendfile, vp)(VNHEAD(vp),f,off,ioflags,cnt,act,targ,cr)
+#define bhv_vop_splice_read(vp,f,o,pipe,cnt,fl,iofl,cr) \
+ VOP(vop_splice_read, vp)(VNHEAD(vp),f,o,pipe,cnt,fl,iofl,cr)
+#define bhv_vop_splice_write(vp,f,o,pipe,cnt,fl,iofl,cr) \
+ VOP(vop_splice_write, vp)(VNHEAD(vp),f,o,pipe,cnt,fl,iofl,cr)
+#define bhv_vop_bmap(vp,of,sz,rw,b,n) \
+ VOP(vop_bmap, vp)(VNHEAD(vp),of,sz,rw,b,n)
+#define bhv_vop_getattr(vp, vap,f,cr) \
+ VOP(vop_getattr, vp)(VNHEAD(vp), vap,f,cr)
+#define bhv_vop_setattr(vp, vap,f,cr) \
+ VOP(vop_setattr, vp)(VNHEAD(vp), vap,f,cr)
+#define bhv_vop_access(vp, mode,cr) VOP(vop_access, vp)(VNHEAD(vp), mode,cr)
+#define bhv_vop_lookup(vp,d,vpp,f,rdir,cr) \
+ VOP(vop_lookup, vp)(VNHEAD(vp),d,vpp,f,rdir,cr)
+#define bhv_vop_create(dvp,d,vap,vpp,cr) \
+ VOP(vop_create, dvp)(VNHEAD(dvp),d,vap,vpp,cr)
+#define bhv_vop_remove(dvp,d,cr) VOP(vop_remove, dvp)(VNHEAD(dvp),d,cr)
+#define bhv_vop_link(dvp,fvp,d,cr) VOP(vop_link, dvp)(VNHEAD(dvp),fvp,d,cr)
+#define bhv_vop_rename(fvp,fnm,tdvp,tnm,cr) \
+ VOP(vop_rename, fvp)(VNHEAD(fvp),fnm,tdvp,tnm,cr)
+#define bhv_vop_mkdir(dp,d,vap,vpp,cr) \
+ VOP(vop_mkdir, dp)(VNHEAD(dp),d,vap,vpp,cr)
+#define bhv_vop_rmdir(dp,d,cr) VOP(vop_rmdir, dp)(VNHEAD(dp),d,cr)
+#define bhv_vop_readdir(vp,uiop,cr,eofp) \
+ VOP(vop_readdir, vp)(VNHEAD(vp),uiop,cr,eofp)
+#define bhv_vop_symlink(dvp,d,vap,tnm,vpp,cr) \
+ VOP(vop_symlink, dvp)(VNHEAD(dvp),d,vap,tnm,vpp,cr)
+#define bhv_vop_readlink(vp,uiop,fl,cr) \
+ VOP(vop_readlink, vp)(VNHEAD(vp),uiop,fl,cr)
+#define bhv_vop_fsync(vp,f,cr,b,e) VOP(vop_fsync, vp)(VNHEAD(vp),f,cr,b,e)
+#define bhv_vop_inactive(vp,cr) VOP(vop_inactive, vp)(VNHEAD(vp),cr)
+#define bhv_vop_release(vp) VOP(vop_release, vp)(VNHEAD(vp))
+#define bhv_vop_fid2(vp,fidp) VOP(vop_fid2, vp)(VNHEAD(vp),fidp)
+#define bhv_vop_rwlock(vp,i) VOP(vop_rwlock, vp)(VNHEAD(vp),i)
+#define bhv_vop_rwlock_try(vp,i) VOP(vop_rwlock, vp)(VNHEAD(vp),i)
+#define bhv_vop_rwunlock(vp,i) VOP(vop_rwunlock, vp)(VNHEAD(vp),i)
+#define bhv_vop_frlock(vp,c,fl,flags,offset,fr) \
+ VOP(vop_frlock, vp)(VNHEAD(vp),c,fl,flags,offset,fr)
+#define bhv_vop_reclaim(vp) VOP(vop_reclaim, vp)(VNHEAD(vp))
+#define bhv_vop_attr_get(vp, name, val, vallenp, fl, cred) \
+ VOP(vop_attr_get, vp)(VNHEAD(vp),name,val,vallenp,fl,cred)
+#define bhv_vop_attr_set(vp, name, val, vallen, fl, cred) \
+ VOP(vop_attr_set, vp)(VNHEAD(vp),name,val,vallen,fl,cred)
+#define bhv_vop_attr_remove(vp, name, flags, cred) \
+ VOP(vop_attr_remove, vp)(VNHEAD(vp),name,flags,cred)
+#define bhv_vop_attr_list(vp, buf, buflen, fl, cursor, cred) \
+ VOP(vop_attr_list, vp)(VNHEAD(vp),buf,buflen,fl,cursor,cred)
+#define bhv_vop_link_removed(vp, dvp, linkzero) \
+ VOP(vop_link_removed, vp)(VNHEAD(vp), dvp, linkzero)
+#define bhv_vop_vnode_change(vp, cmd, val) \
+ VOP(vop_vnode_change, vp)(VNHEAD(vp), cmd, val)
+#define bhv_vop_toss_pages(vp, first, last, fiopt) \
+ VOP(vop_tosspages, vp)(VNHEAD(vp), first, last, fiopt)
+#define bhv_vop_flushinval_pages(vp, first, last, fiopt) \
+ VOP(vop_flushinval_pages, vp)(VNHEAD(vp),first,last,fiopt)
+#define bhv_vop_flush_pages(vp, first, last, flags, fiopt) \
+ VOP(vop_flush_pages, vp)(VNHEAD(vp),first,last,flags,fiopt)
+#define bhv_vop_ioctl(vp, inode, filp, fl, cmd, arg) \
+ VOP(vop_ioctl, vp)(VNHEAD(vp),inode,filp,fl,cmd,arg)
+#define bhv_vop_iflush(vp, flags) VOP(vop_iflush, vp)(VNHEAD(vp), flags)
/*
* Flags for read/write calls - same values as IRIX
#define IO_INVIS 0x00020 /* don't update inode timestamps */
/*
- * Flags for VOP_IFLUSH call
+ * Flags for vop_iflush call
*/
#define FLUSH_SYNC 1 /* wait for flush to complete */
#define FLUSH_INODE 2 /* flush the inode itself */
* this inode out to disk */
/*
- * Flush/Invalidate options for VOP_TOSS_PAGES, VOP_FLUSHINVAL_PAGES and
- * VOP_FLUSH_PAGES.
+ * Flush/Invalidate options for vop_toss/flush/flushinval_pages.
*/
#define FI_NONE 0 /* none */
#define FI_REMAPF 1 /* Do a remapf prior to the operation */
* Vnode attributes. va_mask indicates those attributes the caller
* wants to set or extract.
*/
-typedef struct vattr {
+typedef struct bhv_vattr {
int va_mask; /* bit-mask of attributes present */
mode_t va_mode; /* file access mode and type */
xfs_nlink_t va_nlink; /* number of references to file */
u_long va_nextents; /* number of extents in file */
u_long va_anextents; /* number of attr extents in file */
prid_t va_projid; /* project id */
-} vattr_t;
+} bhv_vattr_t;
/*
* setattr or getattr attributes
(VN_ISREG(vp) && ((mode) & (VSGID|(VEXEC>>3))) == VSGID)
extern void vn_init(void);
-extern vnode_t *vn_initialize(struct inode *);
-
-/*
- * vnode_map structures _must_ match vn_epoch and vnode structure sizes.
- */
-typedef struct vnode_map {
- vfs_t *v_vfsp;
- vnumber_t v_number; /* in-core vnode number */
- xfs_ino_t v_ino; /* inode # */
-} vmap_t;
-
-#define VMAP(vp, vmap) {(vmap).v_vfsp = (vp)->v_vfsp, \
- (vmap).v_number = (vp)->v_number, \
- (vmap).v_ino = (vp)->v_inode.i_ino; }
+extern bhv_vnode_t *vn_initialize(struct inode *);
+extern int vn_revalidate(struct bhv_vnode *);
+extern int __vn_revalidate(struct bhv_vnode *, bhv_vattr_t *);
+extern void vn_revalidate_core(struct bhv_vnode *, bhv_vattr_t *);
-extern int vn_revalidate(struct vnode *);
-extern int __vn_revalidate(struct vnode *, vattr_t *);
-extern void vn_revalidate_core(struct vnode *, vattr_t *);
+extern void vn_iowait(struct bhv_vnode *vp);
+extern void vn_iowake(struct bhv_vnode *vp);
-extern void vn_iowait(struct vnode *vp);
-extern void vn_iowake(struct vnode *vp);
+extern void vn_ioerror(struct bhv_vnode *vp, int error, char *f, int l);
-static inline int vn_count(struct vnode *vp)
+static inline int vn_count(struct bhv_vnode *vp)
{
return atomic_read(&vn_to_inode(vp)->i_count);
}
/*
* Vnode reference counting functions (and macros for compatibility).
*/
-extern vnode_t *vn_hold(struct vnode *);
+extern bhv_vnode_t *vn_hold(struct bhv_vnode *);
#if defined(XFS_VNODE_TRACE)
#define VN_HOLD(vp) \
#define VN_RELE(vp) (iput(vn_to_inode(vp)))
#endif
-static inline struct vnode *vn_grab(struct vnode *vp)
+static inline struct bhv_vnode *vn_grab(struct bhv_vnode *vp)
{
struct inode *inode = igrab(vn_to_inode(vp));
return inode ? vn_from_inode(inode) : NULL;
*/
#define VN_LOCK(vp) mutex_spinlock(&(vp)->v_lock)
#define VN_UNLOCK(vp, s) mutex_spinunlock(&(vp)->v_lock, s)
-#define VN_FLAGSET(vp,b) vn_flagset(vp,b)
-#define VN_FLAGCLR(vp,b) vn_flagclr(vp,b)
-static __inline__ void vn_flagset(struct vnode *vp, uint flag)
+static __inline__ void vn_flagset(struct bhv_vnode *vp, uint flag)
{
spin_lock(&vp->v_lock);
vp->v_flag |= flag;
spin_unlock(&vp->v_lock);
}
-static __inline__ void vn_flagclr(struct vnode *vp, uint flag)
+static __inline__ uint vn_flagclr(struct bhv_vnode *vp, uint flag)
{
+ uint cleared;
+
spin_lock(&vp->v_lock);
+ cleared = (vp->v_flag & flag);
vp->v_flag &= ~flag;
spin_unlock(&vp->v_lock);
+ return cleared;
}
+#define VMODIFY(vp) vn_flagset(vp, VMODIFIED)
+#define VUNMODIFY(vp) vn_flagclr(vp, VMODIFIED)
+#define VTRUNCATE(vp) vn_flagset(vp, VTRUNCATED)
+#define VUNTRUNCATE(vp) vn_flagclr(vp, VTRUNCATED)
+
/*
* Dealing with bad inodes
*/
-static inline void vn_mark_bad(struct vnode *vp)
+static inline void vn_mark_bad(struct bhv_vnode *vp)
{
make_bad_inode(vn_to_inode(vp));
}
-static inline int VN_BAD(struct vnode *vp)
+static inline int VN_BAD(struct bhv_vnode *vp)
{
return is_bad_inode(vn_to_inode(vp));
}
/*
* Extracting atime values in various formats
*/
-static inline void vn_atime_to_bstime(struct vnode *vp, xfs_bstime_t *bs_atime)
+static inline void vn_atime_to_bstime(bhv_vnode_t *vp, xfs_bstime_t *bs_atime)
{
bs_atime->tv_sec = vp->v_inode.i_atime.tv_sec;
bs_atime->tv_nsec = vp->v_inode.i_atime.tv_nsec;
}
-static inline void vn_atime_to_timespec(struct vnode *vp, struct timespec *ts)
+static inline void vn_atime_to_timespec(bhv_vnode_t *vp, struct timespec *ts)
{
*ts = vp->v_inode.i_atime;
}
-static inline void vn_atime_to_time_t(struct vnode *vp, time_t *tt)
+static inline void vn_atime_to_time_t(bhv_vnode_t *vp, time_t *tt)
{
*tt = vp->v_inode.i_atime.tv_sec;
}
#define VN_CACHED(vp) (vn_to_inode(vp)->i_mapping->nrpages)
#define VN_DIRTY(vp) mapping_tagged(vn_to_inode(vp)->i_mapping, \
PAGECACHE_TAG_DIRTY)
-#define VMODIFY(vp) VN_FLAGSET(vp, VMODIFIED)
-#define VUNMODIFY(vp) VN_FLAGCLR(vp, VMODIFIED)
+#define VN_TRUNC(vp) ((vp)->v_flag & VTRUNCATED)
/*
- * Flags to VOP_SETATTR/VOP_GETATTR.
+ * Flags to vop_setattr/getattr.
*/
#define ATTR_UTIME 0x01 /* non-default utime(2) request */
#define ATTR_DMI 0x08 /* invocation from a DMI function */
#define ATTR_NOSIZETOK 0x400 /* Don't get the SIZE token */
/*
- * Flags to VOP_FSYNC and VOP_RECLAIM.
+ * Flags to vop_fsync/reclaim.
*/
#define FSYNC_NOWAIT 0 /* asynchronous flush */
#define FSYNC_WAIT 0x1 /* synchronous fsync or forced reclaim */
#define VNODE_KTRACE_REF 4
#define VNODE_KTRACE_RELE 5
-extern void vn_trace_entry(struct vnode *, const char *, inst_t *);
-extern void vn_trace_exit(struct vnode *, const char *, inst_t *);
-extern void vn_trace_hold(struct vnode *, char *, int, inst_t *);
-extern void vn_trace_ref(struct vnode *, char *, int, inst_t *);
-extern void vn_trace_rele(struct vnode *, char *, int, inst_t *);
+extern void vn_trace_entry(struct bhv_vnode *, const char *, inst_t *);
+extern void vn_trace_exit(struct bhv_vnode *, const char *, inst_t *);
+extern void vn_trace_hold(struct bhv_vnode *, char *, int, inst_t *);
+extern void vn_trace_ref(struct bhv_vnode *, char *, int, inst_t *);
+extern void vn_trace_rele(struct bhv_vnode *, char *, int, inst_t *);
#define VN_TRACE(vp) \
vn_trace_ref(vp, __FILE__, __LINE__, (inst_t *)__return_address)
#include "xfs_trans.h"
#include "xfs_sb.h"
#include "xfs_ag.h"
-#include "xfs_dir.h"
#include "xfs_dir2.h"
#include "xfs_alloc.h"
#include "xfs_dmapi.h"
#include "xfs_bmap_btree.h"
#include "xfs_alloc_btree.h"
#include "xfs_ialloc_btree.h"
-#include "xfs_dir_sf.h"
#include "xfs_dir2_sf.h"
#include "xfs_attr_sf.h"
#include "xfs_dinode.h"
XFS_BMAPI_METADATA | XFS_BMAPI_WRITE,
&firstblock,
XFS_QM_DQALLOC_SPACE_RES(mp),
- &map, &nmaps, &flist))) {
+ &map, &nmaps, &flist, NULL))) {
goto error0;
}
ASSERT(map.br_blockcount == XFS_DQUOT_CLUSTER_SIZE_FSB);
error = xfs_bmapi(NULL, quotip, dqp->q_fileoffset,
XFS_DQUOT_CLUSTER_SIZE_FSB,
XFS_BMAPI_METADATA,
- NULL, 0, &map, &nmaps, NULL);
+ NULL, 0, &map, &nmaps, NULL, NULL);
xfs_iunlock(quotip, XFS_ILOCK_SHARED);
if (error)
if (xfs_qm_dqcheck(&dqp->q_core, be32_to_cpu(ddqp->d_id),
0, XFS_QMOPT_DOWARN, "dqflush (incore copy)")) {
- xfs_force_shutdown(dqp->q_mount, XFS_CORRUPT_INCORE);
+ xfs_force_shutdown(dqp->q_mount, SHUTDOWN_CORRUPT_INCORE);
return XFS_ERROR(EIO);
}
*/
#define xfs_dqflock(dqp) { psema(&((dqp)->q_flock), PINOD | PRECALC);\
(dqp)->dq_flags |= XFS_DQ_FLOCKED; }
-#define xfs_dqfunlock(dqp) { ASSERT(valusema(&((dqp)->q_flock)) <= 0); \
+#define xfs_dqfunlock(dqp) { ASSERT(issemalocked(&((dqp)->q_flock))); \
vsema(&((dqp)->q_flock)); \
(dqp)->dq_flags &= ~(XFS_DQ_FLOCKED); }
#define XFS_DQ_PINUNLOCK(dqp, s) mutex_spinunlock( \
&(XFS_DQ_TO_QINF(dqp)->qi_pinlock), s)
-#define XFS_DQ_IS_FLUSH_LOCKED(dqp) (valusema(&((dqp)->q_flock)) <= 0)
+#define XFS_DQ_IS_FLUSH_LOCKED(dqp) (issemalocked(&((dqp)->q_flock)))
#define XFS_DQ_IS_ON_FREELIST(dqp) ((dqp)->dq_flnext != (dqp))
#define XFS_DQ_IS_DIRTY(dqp) ((dqp)->dq_flags & XFS_DQ_DIRTY)
#define XFS_QM_ISUDQ(dqp) ((dqp)->dq_flags & XFS_DQ_USER)
#include "xfs_trans.h"
#include "xfs_sb.h"
#include "xfs_ag.h"
-#include "xfs_dir.h"
#include "xfs_dir2.h"
#include "xfs_alloc.h"
#include "xfs_dmapi.h"
#include "xfs_bmap_btree.h"
#include "xfs_alloc_btree.h"
#include "xfs_ialloc_btree.h"
-#include "xfs_dir_sf.h"
#include "xfs_dir2_sf.h"
#include "xfs_attr_sf.h"
#include "xfs_dinode.h"
* inode flush completed and the inode was taken off the AIL.
* So, just get out.
*/
- if ((valusema(&(dqp->q_flock)) > 0) ||
+ if (!issemalocked(&(dqp->q_flock)) ||
((qip->qli_item.li_flags & XFS_LI_IN_AIL) == 0)) {
qip->qli_pushbuf_flag = 0;
xfs_dqunlock(dqp);
if (bp != NULL) {
if (XFS_BUF_ISDELAYWRITE(bp)) {
dopush = ((qip->qli_item.li_flags & XFS_LI_IN_AIL) &&
- (valusema(&(dqp->q_flock)) <= 0));
+ issemalocked(&(dqp->q_flock)));
qip->qli_pushbuf_flag = 0;
xfs_dqunlock(dqp);
#include "xfs_trans.h"
#include "xfs_sb.h"
#include "xfs_ag.h"
-#include "xfs_dir.h"
#include "xfs_dir2.h"
#include "xfs_alloc.h"
#include "xfs_dmapi.h"
#include "xfs_bmap_btree.h"
#include "xfs_alloc_btree.h"
#include "xfs_ialloc_btree.h"
-#include "xfs_dir_sf.h"
#include "xfs_dir2_sf.h"
#include "xfs_attr_sf.h"
#include "xfs_dinode.h"
maxlblkcnt - lblkno,
XFS_BMAPI_METADATA,
NULL,
- 0, map, &nmaps, NULL);
+ 0, map, &nmaps, NULL, NULL);
xfs_iunlock(qip, XFS_ILOCK_SHARED);
if (error)
break;
*/
if ((error = xfs_bulkstat(mp, &lastino, &count,
xfs_qm_dqusage_adjust, NULL,
- structsz, NULL,
- BULKSTAT_FG_IGET|BULKSTAT_FG_VFSLOCKED,
- &done)))
+ structsz, NULL, BULKSTAT_FG_IGET, &done)))
break;
} while (! done);
/*
- * Copyright (c) 2000-2005 Silicon Graphics, Inc.
+ * Copyright (c) 2000-2006 Silicon Graphics, Inc.
* All Rights Reserved.
*
* This program is free software; you can redistribute it and/or
#include "xfs_trans.h"
#include "xfs_sb.h"
#include "xfs_ag.h"
-#include "xfs_dir.h"
#include "xfs_dir2.h"
#include "xfs_alloc.h"
#include "xfs_dmapi.h"
#include "xfs_bmap_btree.h"
#include "xfs_alloc_btree.h"
#include "xfs_ialloc_btree.h"
-#include "xfs_dir_sf.h"
#include "xfs_dir2_sf.h"
#include "xfs_attr_sf.h"
#include "xfs_dinode.h"
return XFS_ERROR(EINVAL);
}
- PVFS_PARSEARGS(BHV_NEXT(bhv), options, args, update, error);
+ error = bhv_next_vfs_parseargs(BHV_NEXT(bhv), options, args, update);
if (!error && !referenced)
bhv_remove_vfsops(bhvtovfs(bhv), VFS_POSITION_QM);
return error;
struct bhv_desc *bhv,
struct seq_file *m)
{
- struct vfs *vfsp = bhvtovfs(bhv);
+ struct bhv_vfs *vfsp = bhvtovfs(bhv);
struct xfs_mount *mp = XFS_VFSTOM(vfsp);
- int error;
if (mp->m_qflags & XFS_UQUOTA_ACCT) {
(mp->m_qflags & XFS_UQUOTA_ENFD) ?
if (!(mp->m_qflags & XFS_ALL_QUOTA_ACCT))
seq_puts(m, "," MNTOPT_NOQUOTA);
- PVFS_SHOWARGS(BHV_NEXT(bhv), m, error);
- return error;
+ return bhv_next_vfs_showargs(BHV_NEXT(bhv), m);
}
STATIC int
struct xfs_mount_args *args,
struct cred *cr)
{
- struct vfs *vfsp = bhvtovfs(bhv);
+ struct bhv_vfs *vfsp = bhvtovfs(bhv);
struct xfs_mount *mp = XFS_VFSTOM(vfsp);
- int error;
if (args->flags & (XFSMNT_UQUOTA | XFSMNT_GQUOTA | XFSMNT_PQUOTA))
xfs_qm_mount_quotainit(mp, args->flags);
- PVFS_MOUNT(BHV_NEXT(bhv), args, cr, error);
- return error;
+ return bhv_next_vfs_mount(BHV_NEXT(bhv), args, cr);
+}
+
+/*
+ * Directory tree accounting is implemented using project quotas, where
+ * the project identifier is inherited from parent directories.
+ * A statvfs (df, etc.) of a directory that is using project quota should
+ * return a statvfs of the project, not the entire filesystem.
+ * This makes such trees appear as if they are filesystems in themselves.
+ */
+STATIC int
+xfs_qm_statvfs(
+ struct bhv_desc *bhv,
+ bhv_statvfs_t *statp,
+ struct bhv_vnode *vnode)
+{
+ xfs_mount_t *mp;
+ xfs_inode_t *ip;
+ xfs_dquot_t *dqp;
+ xfs_disk_dquot_t *dp;
+ __uint64_t limit;
+ int error;
+
+ error = bhv_next_vfs_statvfs(BHV_NEXT(bhv), statp, vnode);
+ if (error || !vnode)
+ return error;
+
+ mp = XFS_BHVTOM(bhv);
+ ip = xfs_vtoi(vnode);
+
+ if (!(ip->i_d.di_flags & XFS_DIFLAG_PROJINHERIT))
+ return 0;
+ if (!(mp->m_qflags & XFS_PQUOTA_ACCT))
+ return 0;
+ if (!(mp->m_qflags & XFS_OQUOTA_ENFD))
+ return 0;
+
+ if (xfs_qm_dqget(mp, NULL, ip->i_d.di_projid, XFS_DQ_PROJ, 0, &dqp))
+ return 0;
+ dp = &dqp->q_core;
+
+ limit = dp->d_blk_softlimit ? dp->d_blk_softlimit : dp->d_blk_hardlimit;
+ if (limit && statp->f_blocks > limit) {
+ statp->f_blocks = limit;
+ statp->f_bfree = (statp->f_blocks > dp->d_bcount) ?
+ (statp->f_blocks - dp->d_bcount) : 0;
+ }
+ limit = dp->d_ino_softlimit ? dp->d_ino_softlimit : dp->d_ino_hardlimit;
+ if (limit && statp->f_files > limit) {
+ statp->f_files = limit;
+ statp->f_ffree = (statp->f_files > dp->d_icount) ?
+ (statp->f_ffree - dp->d_icount) : 0;
+ }
+
+ xfs_qm_dqput(dqp);
+ return 0;
}
STATIC int
int flags,
cred_t *credp)
{
- struct vfs *vfsp = bhvtovfs(bhv);
+ struct bhv_vfs *vfsp = bhvtovfs(bhv);
struct xfs_mount *mp = XFS_VFSTOM(vfsp);
int error;
}
}
}
- PVFS_SYNC(BHV_NEXT(bhv), flags, credp, error);
- return error;
+ return bhv_next_vfs_sync(BHV_NEXT(bhv), flags, credp);
}
STATIC int
.xfs_dqtrxops = &xfs_trans_dquot_ops,
};
-struct bhv_vfsops xfs_qmops = { {
+struct bhv_module_vfsops xfs_qmops = { {
BHV_IDENTITY_INIT(VFS_BHV_QM, VFS_POSITION_QM),
.vfs_parseargs = xfs_qm_parseargs,
.vfs_showargs = xfs_qm_showargs,
.vfs_mount = xfs_qm_mount,
+ .vfs_statvfs = xfs_qm_statvfs,
.vfs_sync = xfs_qm_syncall,
.vfs_quotactl = xfs_qm_quotactl, },
};
#include "xfs_trans.h"
#include "xfs_sb.h"
#include "xfs_ag.h"
-#include "xfs_dir.h"
#include "xfs_dir2.h"
#include "xfs_alloc.h"
#include "xfs_dmapi.h"
#include "xfs_bmap_btree.h"
#include "xfs_alloc_btree.h"
#include "xfs_ialloc_btree.h"
-#include "xfs_dir_sf.h"
#include "xfs_dir2_sf.h"
#include "xfs_attr_sf.h"
#include "xfs_dinode.h"
#include "xfs_trans.h"
#include "xfs_sb.h"
#include "xfs_ag.h"
-#include "xfs_dir.h"
#include "xfs_dir2.h"
#include "xfs_alloc.h"
#include "xfs_dmapi.h"
#include "xfs_bmap_btree.h"
#include "xfs_alloc_btree.h"
#include "xfs_ialloc_btree.h"
-#include "xfs_dir_sf.h"
#include "xfs_dir2_sf.h"
#include "xfs_attr_sf.h"
#include "xfs_dinode.h"
xfs_caddr_t addr)
{
xfs_mount_t *mp;
+ bhv_vfs_t *vfsp;
int error;
- struct vfs *vfsp;
vfsp = bhvtovfs(bdp);
mp = XFS_VFSTOM(vfsp);
{
xfs_inode_t *ip, *topino;
uint ireclaims;
- vnode_t *vp;
+ bhv_vnode_t *vp;
boolean_t vnode_refd;
ASSERT(mp->m_quotainfo);
#include "xfs_trans.h"
#include "xfs_sb.h"
#include "xfs_ag.h"
-#include "xfs_dir.h"
#include "xfs_dir2.h"
#include "xfs_alloc.h"
#include "xfs_dmapi.h"
#include "xfs_alloc_btree.h"
#include "xfs_ialloc_btree.h"
#include "xfs_attr_sf.h"
-#include "xfs_dir_sf.h"
#include "xfs_dir2_sf.h"
#include "xfs_dinode.h"
#include "xfs_inode.h"
va_start(ap, fmt);
if (*fmt == '!') fp++;
len = vsprintf(message, fp, ap);
- if (message[len-1] != '\n')
+ if (level != CE_DEBUG && message[len-1] != '\n')
strcat(message, "\n");
printk("%s%s", err_level[level], message);
va_end(ap);
level = XFS_MAX_ERR_LEVEL;
spin_lock_irqsave(&xfs_err_lock,flags);
len = vsprintf(message, fmt, ap);
- if (message[len-1] != '\n')
+ if (level != CE_DEBUG && message[len-1] != '\n')
strcat(message, "\n");
spin_unlock_irqrestore(&xfs_err_lock,flags);
printk("%s%s", err_level[level], message);
__attribute__ ((format (printf, 2, 3)));
extern void assfail(char *expr, char *f, int l);
-#define prdev(fmt,targ,args...) \
- printk("Device %s - " fmt "\n", XFS_BUFTARG_NAME(targ), ## args)
-
#define ASSERT_ALWAYS(expr) \
(unlikely((expr) != 0) ? (void)0 : assfail(#expr, __FILE__, __LINE__))
#include "xfs_bit.h"
#include "xfs_inum.h"
#include "xfs_ag.h"
-#include "xfs_dir.h"
#include "xfs_dir2.h"
#include "xfs_bmap_btree.h"
#include "xfs_alloc_btree.h"
#include "xfs_ialloc_btree.h"
-#include "xfs_dir_sf.h"
#include "xfs_dir2_sf.h"
#include "xfs_attr_sf.h"
#include "xfs_dinode.h"
#include <linux/capability.h>
#include <linux/posix_acl_xattr.h>
-STATIC int xfs_acl_setmode(vnode_t *, xfs_acl_t *, int *);
+STATIC int xfs_acl_setmode(bhv_vnode_t *, xfs_acl_t *, int *);
STATIC void xfs_acl_filter_mode(mode_t, xfs_acl_t *);
STATIC void xfs_acl_get_endian(xfs_acl_t *);
STATIC int xfs_acl_access(uid_t, gid_t, xfs_acl_t *, mode_t, cred_t *);
STATIC int xfs_acl_invalid(xfs_acl_t *);
STATIC void xfs_acl_sync_mode(mode_t, xfs_acl_t *);
-STATIC void xfs_acl_get_attr(vnode_t *, xfs_acl_t *, int, int, int *);
-STATIC void xfs_acl_set_attr(vnode_t *, xfs_acl_t *, int, int *);
-STATIC int xfs_acl_allow_set(vnode_t *, int);
+STATIC void xfs_acl_get_attr(bhv_vnode_t *, xfs_acl_t *, int, int, int *);
+STATIC void xfs_acl_set_attr(bhv_vnode_t *, xfs_acl_t *, int, int *);
+STATIC int xfs_acl_allow_set(bhv_vnode_t *, int);
kmem_zone_t *xfs_acl_zone;
*/
int
xfs_acl_vhasacl_access(
- vnode_t *vp)
+ bhv_vnode_t *vp)
{
int error;
*/
int
xfs_acl_vhasacl_default(
- vnode_t *vp)
+ bhv_vnode_t *vp)
{
int error;
int
xfs_acl_vget(
- vnode_t *vp,
+ bhv_vnode_t *vp,
void *acl,
size_t size,
int kind)
goto out;
}
if (kind == _ACL_TYPE_ACCESS) {
- vattr_t va;
+ bhv_vattr_t va;
va.va_mask = XFS_AT_MODE;
- VOP_GETATTR(vp, &va, 0, sys_cred, error);
+ error = bhv_vop_getattr(vp, &va, 0, sys_cred);
if (error)
goto out;
xfs_acl_sync_mode(va.va_mode, xfs_acl);
int
xfs_acl_vremove(
- vnode_t *vp,
+ bhv_vnode_t *vp,
int kind)
{
int error;
VN_HOLD(vp);
error = xfs_acl_allow_set(vp, kind);
if (!error) {
- VOP_ATTR_REMOVE(vp, kind == _ACL_TYPE_DEFAULT?
- SGI_ACL_DEFAULT: SGI_ACL_FILE,
- ATTR_ROOT, sys_cred, error);
+ error = bhv_vop_attr_remove(vp, kind == _ACL_TYPE_DEFAULT?
+ SGI_ACL_DEFAULT: SGI_ACL_FILE,
+ ATTR_ROOT, sys_cred);
if (error == ENOATTR)
error = 0; /* 'scool */
}
int
xfs_acl_vset(
- vnode_t *vp,
+ bhv_vnode_t *vp,
void *acl,
size_t size,
int kind)
STATIC int
xfs_acl_allow_set(
- vnode_t *vp,
+ bhv_vnode_t *vp,
int kind)
{
- vattr_t va;
+ bhv_vattr_t va;
int error;
if (vp->v_inode.i_flags & (S_IMMUTABLE|S_APPEND))
if (vp->v_vfsp->vfs_flag & VFS_RDONLY)
return EROFS;
va.va_mask = XFS_AT_UID;
- VOP_GETATTR(vp, &va, 0, NULL, error);
+ error = bhv_vop_getattr(vp, &va, 0, NULL);
if (error)
return error;
if (va.va_uid != current->fsuid && !capable(CAP_FOWNER))
*/
STATIC void
xfs_acl_get_attr(
- vnode_t *vp,
+ bhv_vnode_t *vp,
xfs_acl_t *aclp,
int kind,
int flags,
ASSERT((flags & ATTR_KERNOVAL) ? (aclp == NULL) : 1);
flags |= ATTR_ROOT;
- VOP_ATTR_GET(vp,
- kind == _ACL_TYPE_ACCESS ? SGI_ACL_FILE : SGI_ACL_DEFAULT,
- (char *)aclp, &len, flags, sys_cred, *error);
+ *error = bhv_vop_attr_get(vp, kind == _ACL_TYPE_ACCESS ?
+ SGI_ACL_FILE : SGI_ACL_DEFAULT,
+ (char *)aclp, &len, flags, sys_cred);
if (*error || (flags & ATTR_KERNOVAL))
return;
xfs_acl_get_endian(aclp);
*/
STATIC void
xfs_acl_set_attr(
- vnode_t *vp,
+ bhv_vnode_t *vp,
xfs_acl_t *aclp,
int kind,
int *error)
INT_SET(newace->ae_perm, ARCH_CONVERT, ace->ae_perm);
}
INT_SET(newacl->acl_cnt, ARCH_CONVERT, aclp->acl_cnt);
- VOP_ATTR_SET(vp,
- kind == _ACL_TYPE_ACCESS ? SGI_ACL_FILE: SGI_ACL_DEFAULT,
- (char *)newacl, len, ATTR_ROOT, sys_cred, *error);
+ *error = bhv_vop_attr_set(vp, kind == _ACL_TYPE_ACCESS ?
+ SGI_ACL_FILE: SGI_ACL_DEFAULT,
+ (char *)newacl, len, ATTR_ROOT, sys_cred);
_ACL_FREE(newacl);
}
int
xfs_acl_vtoacl(
- vnode_t *vp,
+ bhv_vnode_t *vp,
xfs_acl_t *access_acl,
xfs_acl_t *default_acl)
{
- vattr_t va;
+ bhv_vattr_t va;
int error = 0;
if (access_acl) {
if (!error) {
/* Got the ACL, need the mode... */
va.va_mask = XFS_AT_MODE;
- VOP_GETATTR(vp, &va, 0, sys_cred, error);
+ error = bhv_vop_getattr(vp, &va, 0, sys_cred);
}
if (error)
*/
int
xfs_acl_inherit(
- vnode_t *vp,
- vattr_t *vap,
+ bhv_vnode_t *vp,
+ bhv_vattr_t *vap,
xfs_acl_t *pdaclp)
{
xfs_acl_t *cacl;
*/
STATIC int
xfs_acl_setmode(
- vnode_t *vp,
+ bhv_vnode_t *vp,
xfs_acl_t *acl,
int *basicperms)
{
- vattr_t va;
+ bhv_vattr_t va;
xfs_acl_entry_t *ap;
xfs_acl_entry_t *gap = NULL;
int i, error, nomask = 1;
* mode. The m:: bits take precedence over the g:: bits.
*/
va.va_mask = XFS_AT_MODE;
- VOP_GETATTR(vp, &va, 0, sys_cred, error);
+ error = bhv_vop_getattr(vp, &va, 0, sys_cred);
if (error)
return error;
if (gap && nomask)
va.va_mode |= gap->ae_perm << 3;
- VOP_SETATTR(vp, &va, 0, sys_cred, error);
- return error;
+ return bhv_vop_setattr(vp, &va, 0, sys_cred);
}
/*
#ifdef CONFIG_XFS_POSIX_ACL
struct vattr;
-struct vnode;
+struct bhv_vnode;
struct xfs_inode;
extern struct kmem_zone *xfs_acl_zone;
(zone) = kmem_zone_init(sizeof(xfs_acl_t), (name))
#define xfs_acl_zone_destroy(zone) kmem_zone_destroy(zone)
-extern int xfs_acl_inherit(struct vnode *, struct vattr *, xfs_acl_t *);
+extern int xfs_acl_inherit(struct bhv_vnode *, struct bhv_vattr *, xfs_acl_t *);
extern int xfs_acl_iaccess(struct xfs_inode *, mode_t, cred_t *);
-extern int xfs_acl_vtoacl(struct vnode *, xfs_acl_t *, xfs_acl_t *);
-extern int xfs_acl_vhasacl_access(struct vnode *);
-extern int xfs_acl_vhasacl_default(struct vnode *);
-extern int xfs_acl_vset(struct vnode *, void *, size_t, int);
-extern int xfs_acl_vget(struct vnode *, void *, size_t, int);
-extern int xfs_acl_vremove(struct vnode *vp, int);
+extern int xfs_acl_vtoacl(struct bhv_vnode *, xfs_acl_t *, xfs_acl_t *);
+extern int xfs_acl_vhasacl_access(struct bhv_vnode *);
+extern int xfs_acl_vhasacl_default(struct bhv_vnode *);
+extern int xfs_acl_vset(struct bhv_vnode *, void *, size_t, int);
+extern int xfs_acl_vget(struct bhv_vnode *, void *, size_t, int);
+extern int xfs_acl_vremove(struct bhv_vnode *, int);
#define _ACL_TYPE_ACCESS 1
#define _ACL_TYPE_DEFAULT 2
#include "xfs_trans.h"
#include "xfs_sb.h"
#include "xfs_ag.h"
-#include "xfs_dir.h"
#include "xfs_dir2.h"
#include "xfs_dmapi.h"
#include "xfs_mount.h"
#include "xfs_bmap_btree.h"
#include "xfs_alloc_btree.h"
#include "xfs_ialloc_btree.h"
-#include "xfs_dir_sf.h"
#include "xfs_dir2_sf.h"
#include "xfs_attr_sf.h"
#include "xfs_dinode.h"
(pag->pagf_longest - delta) :
(pag->pagf_flcount > 0 || pag->pagf_longest > 0);
if (args->minlen + args->alignment + args->minalignslop - 1 > longest ||
- (args->minleft &&
+ (!(flags & XFS_ALLOC_FLAG_FREEING) &&
(int)(pag->pagf_freeblks + pag->pagf_flcount -
need - args->total) <
(int)args->minleft)) {
longest = (longest > delta) ? (longest - delta) :
(be32_to_cpu(agf->agf_flcount) > 0 || longest > 0);
if (args->minlen + args->alignment + args->minalignslop - 1 > longest ||
- (args->minleft &&
+ (!(flags & XFS_ALLOC_FLAG_FREEING) &&
(int)(be32_to_cpu(agf->agf_freeblks) +
be32_to_cpu(agf->agf_flcount) - need - args->total) <
(int)args->minleft)) {
* the restrictions correctly. Can happen for free calls
* on a completely full ag.
*/
- if (targs.agbno == NULLAGBLOCK)
+ if (targs.agbno == NULLAGBLOCK) {
+ if (!(flags & XFS_ALLOC_FLAG_FREEING)) {
+ xfs_trans_brelse(tp, agflbp);
+ args->agbp = NULL;
+ return 0;
+ }
break;
+ }
/*
* Put each allocated block on the list.
*/
if (args->agno == sagno &&
type == XFS_ALLOCTYPE_START_BNO)
args->type = XFS_ALLOCTYPE_THIS_AG;
- if (++(args->agno) == mp->m_sb.sb_agcount)
- args->agno = 0;
+ /*
+ * For the first allocation, we can try any AG to get
+ * space. However, if we already have allocated a
+ * block, we don't want to try AGs whose number is below
+ * sagno. Otherwise, we may end up with out-of-order
+ * locking of AGF, which might cause deadlock.
+ */
+ if (++(args->agno) == mp->m_sb.sb_agcount) {
+ if (args->firstblock != NULLFSBLOCK)
+ args->agno = sagno;
+ else
+ args->agno = 0;
+ }
/*
* Reached the starting a.g., must either be done
* or switch to non-trylock mode.
args.minlen = args.minleft = args.minalignslop = 0;
down_read(&args.mp->m_peraglock);
args.pag = &args.mp->m_perag[args.agno];
- if ((error = xfs_alloc_fix_freelist(&args, 0)))
+ if ((error = xfs_alloc_fix_freelist(&args, XFS_ALLOC_FLAG_FREEING)))
goto error0;
#ifdef DEBUG
ASSERT(args.agbp != NULL);
* Flags for xfs_alloc_fix_freelist.
*/
#define XFS_ALLOC_FLAG_TRYLOCK 0x00000001 /* use trylock for buffer locking */
+#define XFS_ALLOC_FLAG_FREEING 0x00000002 /* indicate caller is freeing extents*/
/*
* Argument structure for xfs_alloc routines.
char wasfromfl; /* set if allocation is from freelist */
char isfl; /* set if is freelist blocks - !acctg */
char userdata; /* set if this is user data */
+ xfs_fsblock_t firstblock; /* io first block allocated */
} xfs_alloc_arg_t;
/*
#include "xfs_trans.h"
#include "xfs_sb.h"
#include "xfs_ag.h"
-#include "xfs_dir.h"
#include "xfs_dir2.h"
#include "xfs_dmapi.h"
#include "xfs_mount.h"
#include "xfs_bmap_btree.h"
#include "xfs_alloc_btree.h"
#include "xfs_ialloc_btree.h"
-#include "xfs_dir_sf.h"
#include "xfs_dir2_sf.h"
#include "xfs_attr_sf.h"
#include "xfs_dinode.h"
#include "xfs_trans.h"
#include "xfs_sb.h"
#include "xfs_ag.h"
-#include "xfs_dir.h"
#include "xfs_dir2.h"
#include "xfs_dmapi.h"
#include "xfs_mount.h"
#include "xfs_bmap_btree.h"
#include "xfs_alloc_btree.h"
#include "xfs_ialloc_btree.h"
-#include "xfs_dir_sf.h"
#include "xfs_dir2_sf.h"
#include "xfs_attr_sf.h"
#include "xfs_dinode.h"
error = xfs_bmapi(args->trans, args->dp, (xfs_fileoff_t)lblkno,
args->rmtblkcnt,
XFS_BMAPI_ATTRFORK | XFS_BMAPI_METADATA,
- NULL, 0, map, &nmap, NULL);
+ NULL, 0, map, &nmap, NULL, NULL);
if (error)
return(error);
ASSERT(nmap >= 1);
XFS_BMAPI_ATTRFORK | XFS_BMAPI_METADATA |
XFS_BMAPI_WRITE,
args->firstblock, args->total, &map, &nmap,
- args->flist);
+ args->flist, NULL);
if (!error) {
error = xfs_bmap_finish(&args->trans, args->flist,
*args->firstblock, &committed);
error = xfs_bmapi(NULL, dp, (xfs_fileoff_t)lblkno,
args->rmtblkcnt,
XFS_BMAPI_ATTRFORK | XFS_BMAPI_METADATA,
- args->firstblock, 0, &map, &nmap, NULL);
+ args->firstblock, 0, &map, &nmap,
+ NULL, NULL);
if (error) {
return(error);
}
args->rmtblkcnt,
XFS_BMAPI_ATTRFORK | XFS_BMAPI_METADATA,
args->firstblock, 0, &map, &nmap,
- args->flist);
+ args->flist, NULL);
if (error) {
return(error);
}
XFS_BMAP_INIT(args->flist, args->firstblock);
error = xfs_bunmapi(args->trans, args->dp, lblkno, blkcnt,
XFS_BMAPI_ATTRFORK | XFS_BMAPI_METADATA,
- 1, args->firstblock, args->flist, &done);
+ 1, args->firstblock, args->flist,
+ NULL, &done);
if (!error) {
error = xfs_bmap_finish(&args->trans, args->flist,
*args->firstblock, &committed);
STATIC int
posix_acl_access_set(
- vnode_t *vp, char *name, void *data, size_t size, int xflags)
+ bhv_vnode_t *vp, char *name, void *data, size_t size, int xflags)
{
return xfs_acl_vset(vp, data, size, _ACL_TYPE_ACCESS);
}
STATIC int
posix_acl_access_remove(
- struct vnode *vp, char *name, int xflags)
+ bhv_vnode_t *vp, char *name, int xflags)
{
return xfs_acl_vremove(vp, _ACL_TYPE_ACCESS);
}
STATIC int
posix_acl_access_get(
- vnode_t *vp, char *name, void *data, size_t size, int xflags)
+ bhv_vnode_t *vp, char *name, void *data, size_t size, int xflags)
{
return xfs_acl_vget(vp, data, size, _ACL_TYPE_ACCESS);
}
STATIC int
posix_acl_access_exists(
- vnode_t *vp)
+ bhv_vnode_t *vp)
{
return xfs_acl_vhasacl_access(vp);
}
STATIC int
posix_acl_default_set(
- vnode_t *vp, char *name, void *data, size_t size, int xflags)
+ bhv_vnode_t *vp, char *name, void *data, size_t size, int xflags)
{
return xfs_acl_vset(vp, data, size, _ACL_TYPE_DEFAULT);
}
STATIC int
posix_acl_default_get(
- vnode_t *vp, char *name, void *data, size_t size, int xflags)
+ bhv_vnode_t *vp, char *name, void *data, size_t size, int xflags)
{
return xfs_acl_vget(vp, data, size, _ACL_TYPE_DEFAULT);
}
STATIC int
posix_acl_default_remove(
- struct vnode *vp, char *name, int xflags)
+ bhv_vnode_t *vp, char *name, int xflags)
{
return xfs_acl_vremove(vp, _ACL_TYPE_DEFAULT);
}
STATIC int
posix_acl_default_exists(
- vnode_t *vp)
+ bhv_vnode_t *vp)
{
return xfs_acl_vhasacl_default(vp);
}
STATIC int
attr_generic_set(
- struct vnode *vp, char *name, void *data, size_t size, int xflags)
+ bhv_vnode_t *vp, char *name, void *data, size_t size, int xflags)
{
- int error;
-
- VOP_ATTR_SET(vp, name, data, size, xflags, NULL, error);
- return -error;
+ return -bhv_vop_attr_set(vp, name, data, size, xflags, NULL);
}
STATIC int
attr_generic_get(
- struct vnode *vp, char *name, void *data, size_t size, int xflags)
+ bhv_vnode_t *vp, char *name, void *data, size_t size, int xflags)
{
int error, asize = size;
- VOP_ATTR_GET(vp, name, data, &asize, xflags, NULL, error);
+ error = bhv_vop_attr_get(vp, name, data, &asize, xflags, NULL);
if (!error)
return asize;
return -error;
STATIC int
attr_generic_remove(
- struct vnode *vp, char *name, int xflags)
+ bhv_vnode_t *vp, char *name, int xflags)
{
- int error;
-
- VOP_ATTR_REMOVE(vp, name, xflags, NULL, error);
- return -error;
+ return -bhv_vop_attr_remove(vp, name, xflags, NULL);
}
STATIC int
STATIC int
attr_system_list(
- struct vnode *vp,
+ bhv_vnode_t *vp,
void *data,
size_t size,
ssize_t *result)
int
attr_generic_list(
- struct vnode *vp, void *data, size_t size, int xflags, ssize_t *result)
+ bhv_vnode_t *vp, void *data, size_t size, int xflags, ssize_t *result)
{
attrlist_cursor_kern_t cursor = { 0 };
int error;
- VOP_ATTR_LIST(vp, data, size, xflags, &cursor, NULL, error);
+ error = bhv_vop_attr_list(vp, data, size, xflags, &cursor, NULL);
if (error > 0)
return -error;
*result = -error;
*/
STATIC int
attr_user_capable(
- struct vnode *vp,
+ bhv_vnode_t *vp,
cred_t *cred)
{
struct inode *inode = vn_to_inode(vp);
STATIC int
attr_trusted_capable(
- struct vnode *vp,
+ bhv_vnode_t *vp,
cred_t *cred)
{
struct inode *inode = vn_to_inode(vp);
STATIC int
attr_secure_capable(
- struct vnode *vp,
+ bhv_vnode_t *vp,
cred_t *cred)
{
return -ENOSECURITY;
STATIC int
attr_system_set(
- struct vnode *vp, char *name, void *data, size_t size, int xflags)
+ bhv_vnode_t *vp, char *name, void *data, size_t size, int xflags)
{
attrnames_t *namesp;
int error;
STATIC int
attr_system_get(
- struct vnode *vp, char *name, void *data, size_t size, int xflags)
+ bhv_vnode_t *vp, char *name, void *data, size_t size, int xflags)
{
attrnames_t *namesp;
STATIC int
attr_system_remove(
- struct vnode *vp, char *name, int xflags)
+ bhv_vnode_t *vp, char *name, int xflags)
{
attrnames_t *namesp;
*========================================================================*/
struct cred;
-struct vnode;
+struct bhv_vnode;
-typedef int (*attrset_t)(struct vnode *, char *, void *, size_t, int);
-typedef int (*attrget_t)(struct vnode *, char *, void *, size_t, int);
-typedef int (*attrremove_t)(struct vnode *, char *, int);
-typedef int (*attrexists_t)(struct vnode *);
-typedef int (*attrcapable_t)(struct vnode *, struct cred *);
+typedef int (*attrset_t)(struct bhv_vnode *, char *, void *, size_t, int);
+typedef int (*attrget_t)(struct bhv_vnode *, char *, void *, size_t, int);
+typedef int (*attrremove_t)(struct bhv_vnode *, char *, int);
+typedef int (*attrexists_t)(struct bhv_vnode *);
+typedef int (*attrcapable_t)(struct bhv_vnode *, struct cred *);
typedef struct attrnames {
char * attr_name;
extern struct attrnames *attr_namespaces[ATTR_NAMECOUNT];
extern attrnames_t *attr_lookup_namespace(char *, attrnames_t **, int);
-extern int attr_generic_list(struct vnode *, void *, size_t, int, ssize_t *);
+extern int attr_generic_list(struct bhv_vnode *, void *, size_t, int, ssize_t *);
#define ATTR_DONTFOLLOW 0x0001 /* -- unused, from IRIX -- */
#define ATTR_ROOT 0x0002 /* use attrs in root (trusted) namespace */
#include "xfs_trans.h"
#include "xfs_sb.h"
#include "xfs_ag.h"
-#include "xfs_dir.h"
#include "xfs_dir2.h"
#include "xfs_dmapi.h"
#include "xfs_mount.h"
#include "xfs_ialloc_btree.h"
#include "xfs_alloc.h"
#include "xfs_btree.h"
-#include "xfs_dir_sf.h"
#include "xfs_dir2_sf.h"
#include "xfs_attr_sf.h"
#include "xfs_dinode.h"
nmap = 1;
error = xfs_bmapi(*trans, dp, (xfs_fileoff_t)tblkno, tblkcnt,
XFS_BMAPI_ATTRFORK | XFS_BMAPI_METADATA,
- NULL, 0, &map, &nmap, NULL);
+ NULL, 0, &map, &nmap, NULL, NULL);
if (error) {
return(error);
}
/*
- * Copyright (c) 2000-2005 Silicon Graphics, Inc.
+ * Copyright (c) 2000-2006 Silicon Graphics, Inc.
* All Rights Reserved.
*
* This program is free software; you can redistribute it and/or
#include "xfs_trans.h"
#include "xfs_sb.h"
#include "xfs_ag.h"
-#include "xfs_dir.h"
#include "xfs_dir2.h"
#include "xfs_da_btree.h"
#include "xfs_bmap_btree.h"
#include "xfs_alloc_btree.h"
#include "xfs_ialloc_btree.h"
-#include "xfs_dir_sf.h"
#include "xfs_dir2_sf.h"
#include "xfs_attr_sf.h"
#include "xfs_dinode.h"
#include "xfs_mount.h"
#include "xfs_ialloc.h"
#include "xfs_itable.h"
+#include "xfs_dir2_data.h"
+#include "xfs_dir2_leaf.h"
+#include "xfs_dir2_block.h"
#include "xfs_inode_item.h"
#include "xfs_extfree_item.h"
#include "xfs_alloc.h"
#include "xfs_bmap.h"
#include "xfs_rtalloc.h"
#include "xfs_error.h"
-#include "xfs_dir_leaf.h"
#include "xfs_attr_leaf.h"
#include "xfs_rw.h"
#include "xfs_quota.h"
xfs_fsblock_t *first, /* pointer to firstblock variable */
xfs_bmap_free_t *flist, /* list of extents to be freed */
int *logflagsp, /* inode logging flags */
+ xfs_extdelta_t *delta, /* Change made to incore extents */
int whichfork, /* data or attr fork */
int rsvd); /* OK to allocate reserved blocks */
xfs_fsblock_t *first, /* pointer to firstblock variable */
xfs_bmap_free_t *flist, /* list of extents to be freed */
int *logflagsp, /* inode logging flags */
+ xfs_extdelta_t *delta, /* Change made to incore extents */
int rsvd); /* OK to allocate reserved blocks */
/*
xfs_btree_cur_t *cur, /* if null, not a btree */
xfs_bmbt_irec_t *new, /* new data to add to file extents */
int *logflagsp,/* inode logging flags */
+ xfs_extdelta_t *delta, /* Change made to incore extents */
int rsvd); /* OK to allocate reserved blocks */
/*
xfs_btree_cur_t *cur, /* if null, not a btree */
xfs_bmbt_irec_t *new, /* new data to add to file extents */
int *logflagsp, /* inode logging flags */
+ xfs_extdelta_t *delta, /* Change made to incore extents */
int whichfork); /* data or attr fork */
/*
xfs_extnum_t idx, /* extent number to update/insert */
xfs_btree_cur_t **curp, /* if *curp is null, not a btree */
xfs_bmbt_irec_t *new, /* new data to add to file extents */
- int *logflagsp); /* inode logging flags */
+ int *logflagsp, /* inode logging flags */
+ xfs_extdelta_t *delta); /* Change made to incore extents */
/*
* xfs_bmap_alloc is called by xfs_bmapi to allocate an extent for a file.
xfs_btree_cur_t *cur, /* if null, not a btree */
xfs_bmbt_irec_t *new, /* new data to add to file extents */
int *logflagsp,/* inode logging flags */
+ xfs_extdelta_t *delta, /* Change made to incore extents */
int whichfork, /* data or attr fork */
int rsvd); /* OK to allocate reserved blocks */
dargs.total = mp->m_dirblkfsbs;
dargs.whichfork = XFS_DATA_FORK;
dargs.trans = tp;
- error = XFS_DIR_SHORTFORM_TO_SINGLE(mp, &dargs);
+ error = xfs_dir2_sf_to_block(&dargs);
} else
error = xfs_bmap_local_to_extents(tp, ip, firstblock, 1, flags,
XFS_DATA_FORK);
xfs_fsblock_t *first, /* pointer to firstblock variable */
xfs_bmap_free_t *flist, /* list of extents to be freed */
int *logflagsp, /* inode logging flags */
+ xfs_extdelta_t *delta, /* Change made to incore extents */
int whichfork, /* data or attr fork */
int rsvd) /* OK to use reserved data blocks */
{
logflags = XFS_ILOG_CORE | XFS_ILOG_FEXT(whichfork);
} else
logflags = 0;
+ /* DELTA: single new extent */
+ if (delta) {
+ if (delta->xed_startoff > new->br_startoff)
+ delta->xed_startoff = new->br_startoff;
+ if (delta->xed_blockcount <
+ new->br_startoff + new->br_blockcount)
+ delta->xed_blockcount = new->br_startoff +
+ new->br_blockcount;
+ }
}
/*
* Any kind of new delayed allocation goes here.
ASSERT((cur->bc_private.b.flags &
XFS_BTCUR_BPRV_WASDEL) == 0);
if ((error = xfs_bmap_add_extent_hole_delay(ip, idx, cur, new,
- &logflags, rsvd)))
+ &logflags, delta, rsvd)))
goto done;
}
/*
ASSERT((cur->bc_private.b.flags &
XFS_BTCUR_BPRV_WASDEL) == 0);
if ((error = xfs_bmap_add_extent_hole_real(ip, idx, cur, new,
- &logflags, whichfork)))
+ &logflags, delta, whichfork)))
goto done;
} else {
xfs_bmbt_irec_t prev; /* old extent at offset idx */
XFS_BTCUR_BPRV_WASDEL);
if ((error = xfs_bmap_add_extent_delay_real(ip,
idx, &cur, new, &da_new, first, flist,
- &logflags, rsvd)))
+ &logflags, delta, rsvd)))
goto done;
} else if (new->br_state == XFS_EXT_NORM) {
ASSERT(new->br_state == XFS_EXT_NORM);
if ((error = xfs_bmap_add_extent_unwritten_real(
- ip, idx, &cur, new, &logflags)))
+ ip, idx, &cur, new, &logflags, delta)))
goto done;
} else {
ASSERT(new->br_state == XFS_EXT_UNWRITTEN);
if ((error = xfs_bmap_add_extent_unwritten_real(
- ip, idx, &cur, new, &logflags)))
+ ip, idx, &cur, new, &logflags, delta)))
goto done;
}
ASSERT(*curp == cur || *curp == NULL);
ASSERT((cur->bc_private.b.flags &
XFS_BTCUR_BPRV_WASDEL) == 0);
if ((error = xfs_bmap_add_extent_hole_real(ip, idx, cur,
- new, &logflags, whichfork)))
+ new, &logflags, delta, whichfork)))
goto done;
}
}
xfs_fsblock_t *first, /* pointer to firstblock variable */
xfs_bmap_free_t *flist, /* list of extents to be freed */
int *logflagsp, /* inode logging flags */
+ xfs_extdelta_t *delta, /* Change made to incore extents */
int rsvd) /* OK to use reserved data block allocation */
{
xfs_btree_cur_t *cur; /* btree cursor */
/* left is 0, right is 1, prev is 2 */
int rval=0; /* return value (logging flags) */
int state = 0;/* state bits, accessed thru macros */
- xfs_filblks_t temp; /* value for dnew calculations */
- xfs_filblks_t temp2; /* value for dnew calculations */
+ xfs_filblks_t temp=0; /* value for dnew calculations */
+ xfs_filblks_t temp2=0;/* value for dnew calculations */
int tmp_rval; /* partial logging flags */
enum { /* bit number definitions for state */
LEFT_CONTIG, RIGHT_CONTIG,
goto done;
}
*dnew = 0;
+ /* DELTA: Three in-core extents are replaced by one. */
+ temp = LEFT.br_startoff;
+ temp2 = LEFT.br_blockcount +
+ PREV.br_blockcount +
+ RIGHT.br_blockcount;
break;
case MASK3(LEFT_FILLING, RIGHT_FILLING, LEFT_CONTIG):
goto done;
}
*dnew = 0;
+ /* DELTA: Two in-core extents are replaced by one. */
+ temp = LEFT.br_startoff;
+ temp2 = LEFT.br_blockcount +
+ PREV.br_blockcount;
break;
case MASK3(LEFT_FILLING, RIGHT_FILLING, RIGHT_CONTIG):
goto done;
}
*dnew = 0;
+ /* DELTA: Two in-core extents are replaced by one. */
+ temp = PREV.br_startoff;
+ temp2 = PREV.br_blockcount +
+ RIGHT.br_blockcount;
break;
case MASK2(LEFT_FILLING, RIGHT_FILLING):
ASSERT(i == 1);
}
*dnew = 0;
+ /* DELTA: The in-core extent described by new changed type. */
+ temp = new->br_startoff;
+ temp2 = new->br_blockcount;
break;
case MASK2(LEFT_FILLING, LEFT_CONTIG):
xfs_bmap_trace_post_update(fname, "LF|LC", ip, idx,
XFS_DATA_FORK);
*dnew = temp;
+ /* DELTA: The boundary between two in-core extents moved. */
+ temp = LEFT.br_startoff;
+ temp2 = LEFT.br_blockcount +
+ PREV.br_blockcount;
break;
case MASK(LEFT_FILLING):
xfs_bmap_trace_post_update(fname, "LF", ip, idx + 1,
XFS_DATA_FORK);
*dnew = temp;
+ /* DELTA: One in-core extent is split in two. */
+ temp = PREV.br_startoff;
+ temp2 = PREV.br_blockcount;
break;
case MASK2(RIGHT_FILLING, RIGHT_CONTIG):
xfs_bmap_trace_post_update(fname, "RF|RC", ip, idx,
XFS_DATA_FORK);
*dnew = temp;
+ /* DELTA: The boundary between two in-core extents moved. */
+ temp = PREV.br_startoff;
+ temp2 = PREV.br_blockcount +
+ RIGHT.br_blockcount;
break;
case MASK(RIGHT_FILLING):
xfs_bmbt_set_startblock(ep, NULLSTARTBLOCK((int)temp));
xfs_bmap_trace_post_update(fname, "RF", ip, idx, XFS_DATA_FORK);
*dnew = temp;
+ /* DELTA: One in-core extent is split in two. */
+ temp = PREV.br_startoff;
+ temp2 = PREV.br_blockcount;
break;
case 0:
xfs_bmap_trace_post_update(fname, "0", ip, idx + 2,
XFS_DATA_FORK);
*dnew = temp + temp2;
+ /* DELTA: One in-core extent is split in three. */
+ temp = PREV.br_startoff;
+ temp2 = PREV.br_blockcount;
break;
case MASK3(LEFT_FILLING, LEFT_CONTIG, RIGHT_CONTIG):
ASSERT(0);
}
*curp = cur;
+ if (delta) {
+ temp2 += temp;
+ if (delta->xed_startoff > temp)
+ delta->xed_startoff = temp;
+ if (delta->xed_blockcount < temp2)
+ delta->xed_blockcount = temp2;
+ }
done:
*logflagsp = rval;
return error;
xfs_extnum_t idx, /* extent number to update/insert */
xfs_btree_cur_t **curp, /* if *curp is null, not a btree */
xfs_bmbt_irec_t *new, /* new data to add to file extents */
- int *logflagsp) /* inode logging flags */
+ int *logflagsp, /* inode logging flags */
+ xfs_extdelta_t *delta) /* Change made to incore extents */
{
xfs_btree_cur_t *cur; /* btree cursor */
xfs_bmbt_rec_t *ep; /* extent entry for idx */
/* left is 0, right is 1, prev is 2 */
int rval=0; /* return value (logging flags) */
int state = 0;/* state bits, accessed thru macros */
+ xfs_filblks_t temp=0;
+ xfs_filblks_t temp2=0;
enum { /* bit number definitions for state */
LEFT_CONTIG, RIGHT_CONTIG,
LEFT_FILLING, RIGHT_FILLING,
RIGHT.br_blockcount, LEFT.br_state)))
goto done;
}
+ /* DELTA: Three in-core extents are replaced by one. */
+ temp = LEFT.br_startoff;
+ temp2 = LEFT.br_blockcount +
+ PREV.br_blockcount +
+ RIGHT.br_blockcount;
break;
case MASK3(LEFT_FILLING, RIGHT_FILLING, LEFT_CONTIG):
LEFT.br_state)))
goto done;
}
+ /* DELTA: Two in-core extents are replaced by one. */
+ temp = LEFT.br_startoff;
+ temp2 = LEFT.br_blockcount +
+ PREV.br_blockcount;
break;
case MASK3(LEFT_FILLING, RIGHT_FILLING, RIGHT_CONTIG):
newext)))
goto done;
}
+ /* DELTA: Two in-core extents are replaced by one. */
+ temp = PREV.br_startoff;
+ temp2 = PREV.br_blockcount +
+ RIGHT.br_blockcount;
break;
case MASK2(LEFT_FILLING, RIGHT_FILLING):
newext)))
goto done;
}
+ /* DELTA: The in-core extent described by new changed type. */
+ temp = new->br_startoff;
+ temp2 = new->br_blockcount;
break;
case MASK2(LEFT_FILLING, LEFT_CONTIG):
LEFT.br_state))
goto done;
}
+ /* DELTA: The boundary between two in-core extents moved. */
+ temp = LEFT.br_startoff;
+ temp2 = LEFT.br_blockcount +
+ PREV.br_blockcount;
break;
case MASK(LEFT_FILLING):
goto done;
ASSERT(i == 1);
}
+ /* DELTA: One in-core extent is split in two. */
+ temp = PREV.br_startoff;
+ temp2 = PREV.br_blockcount;
break;
case MASK2(RIGHT_FILLING, RIGHT_CONTIG):
newext)))
goto done;
}
+ /* DELTA: The boundary between two in-core extents moved. */
+ temp = PREV.br_startoff;
+ temp2 = PREV.br_blockcount +
+ RIGHT.br_blockcount;
break;
case MASK(RIGHT_FILLING):
goto done;
ASSERT(i == 1);
}
+ /* DELTA: One in-core extent is split in two. */
+ temp = PREV.br_startoff;
+ temp2 = PREV.br_blockcount;
break;
case 0:
goto done;
ASSERT(i == 1);
}
+ /* DELTA: One in-core extent is split in three. */
+ temp = PREV.br_startoff;
+ temp2 = PREV.br_blockcount;
break;
case MASK3(LEFT_FILLING, LEFT_CONTIG, RIGHT_CONTIG):
ASSERT(0);
}
*curp = cur;
+ if (delta) {
+ temp2 += temp;
+ if (delta->xed_startoff > temp)
+ delta->xed_startoff = temp;
+ if (delta->xed_blockcount < temp2)
+ delta->xed_blockcount = temp2;
+ }
done:
*logflagsp = rval;
return error;
xfs_btree_cur_t *cur, /* if null, not a btree */
xfs_bmbt_irec_t *new, /* new data to add to file extents */
int *logflagsp, /* inode logging flags */
+ xfs_extdelta_t *delta, /* Change made to incore extents */
int rsvd) /* OK to allocate reserved blocks */
{
xfs_bmbt_rec_t *ep; /* extent record for idx */
xfs_filblks_t oldlen=0; /* old indirect size */
xfs_bmbt_irec_t right; /* right neighbor extent entry */
int state; /* state bits, accessed thru macros */
- xfs_filblks_t temp; /* temp for indirect calculations */
+ xfs_filblks_t temp=0; /* temp for indirect calculations */
+ xfs_filblks_t temp2=0;
enum { /* bit number definitions for state */
LEFT_CONTIG, RIGHT_CONTIG,
LEFT_DELAY, RIGHT_DELAY,
XFS_DATA_FORK);
xfs_iext_remove(ifp, idx, 1);
ip->i_df.if_lastex = idx - 1;
+ /* DELTA: Two in-core extents were replaced by one. */
+ temp2 = temp;
+ temp = left.br_startoff;
break;
case MASK(LEFT_CONTIG):
xfs_bmap_trace_post_update(fname, "LC", ip, idx - 1,
XFS_DATA_FORK);
ip->i_df.if_lastex = idx - 1;
+ /* DELTA: One in-core extent grew into a hole. */
+ temp2 = temp;
+ temp = left.br_startoff;
break;
case MASK(RIGHT_CONTIG):
NULLSTARTBLOCK((int)newlen), temp, right.br_state);
xfs_bmap_trace_post_update(fname, "RC", ip, idx, XFS_DATA_FORK);
ip->i_df.if_lastex = idx;
+ /* DELTA: One in-core extent grew into a hole. */
+ temp2 = temp;
+ temp = new->br_startoff;
break;
case 0:
XFS_DATA_FORK);
xfs_iext_insert(ifp, idx, 1, new);
ip->i_df.if_lastex = idx;
+ /* DELTA: A new in-core extent was added in a hole. */
+ temp2 = new->br_blockcount;
+ temp = new->br_startoff;
break;
}
if (oldlen != newlen) {
* Nothing to do for disk quota accounting here.
*/
}
+ if (delta) {
+ temp2 += temp;
+ if (delta->xed_startoff > temp)
+ delta->xed_startoff = temp;
+ if (delta->xed_blockcount < temp2)
+ delta->xed_blockcount = temp2;
+ }
*logflagsp = 0;
return 0;
#undef MASK
xfs_btree_cur_t *cur, /* if null, not a btree */
xfs_bmbt_irec_t *new, /* new data to add to file extents */
int *logflagsp, /* inode logging flags */
+ xfs_extdelta_t *delta, /* Change made to incore extents */
int whichfork) /* data or attr fork */
{
xfs_bmbt_rec_t *ep; /* pointer to extent entry ins. point */
xfs_ifork_t *ifp; /* inode fork pointer */
xfs_bmbt_irec_t left; /* left neighbor extent entry */
xfs_bmbt_irec_t right; /* right neighbor extent entry */
+ int rval=0; /* return value (logging flags) */
int state; /* state bits, accessed thru macros */
+ xfs_filblks_t temp=0;
+ xfs_filblks_t temp2=0;
enum { /* bit number definitions for state */
LEFT_CONTIG, RIGHT_CONTIG,
LEFT_DELAY, RIGHT_DELAY,
left.br_blockcount + new->br_blockcount +
right.br_blockcount <= MAXEXTLEN));
+ error = 0;
/*
* Select which case we're in here, and implement it.
*/
XFS_IFORK_NEXT_SET(ip, whichfork,
XFS_IFORK_NEXTENTS(ip, whichfork) - 1);
if (cur == NULL) {
- *logflagsp = XFS_ILOG_CORE | XFS_ILOG_FEXT(whichfork);
- return 0;
+ rval = XFS_ILOG_CORE | XFS_ILOG_FEXT(whichfork);
+ } else {
+ rval = XFS_ILOG_CORE;
+ if ((error = xfs_bmbt_lookup_eq(cur,
+ right.br_startoff,
+ right.br_startblock,
+ right.br_blockcount, &i)))
+ goto done;
+ ASSERT(i == 1);
+ if ((error = xfs_bmbt_delete(cur, &i)))
+ goto done;
+ ASSERT(i == 1);
+ if ((error = xfs_bmbt_decrement(cur, 0, &i)))
+ goto done;
+ ASSERT(i == 1);
+ if ((error = xfs_bmbt_update(cur, left.br_startoff,
+ left.br_startblock,
+ left.br_blockcount +
+ new->br_blockcount +
+ right.br_blockcount,
+ left.br_state)))
+ goto done;
}
- *logflagsp = XFS_ILOG_CORE;
- if ((error = xfs_bmbt_lookup_eq(cur, right.br_startoff,
- right.br_startblock, right.br_blockcount, &i)))
- return error;
- ASSERT(i == 1);
- if ((error = xfs_bmbt_delete(cur, &i)))
- return error;
- ASSERT(i == 1);
- if ((error = xfs_bmbt_decrement(cur, 0, &i)))
- return error;
- ASSERT(i == 1);
- error = xfs_bmbt_update(cur, left.br_startoff,
- left.br_startblock,
- left.br_blockcount + new->br_blockcount +
- right.br_blockcount, left.br_state);
- return error;
+ /* DELTA: Two in-core extents were replaced by one. */
+ temp = left.br_startoff;
+ temp2 = left.br_blockcount +
+ new->br_blockcount +
+ right.br_blockcount;
+ break;
case MASK(LEFT_CONTIG):
/*
xfs_bmap_trace_post_update(fname, "LC", ip, idx - 1, whichfork);
ifp->if_lastex = idx - 1;
if (cur == NULL) {
- *logflagsp = XFS_ILOG_FEXT(whichfork);
- return 0;
+ rval = XFS_ILOG_FEXT(whichfork);
+ } else {
+ rval = 0;
+ if ((error = xfs_bmbt_lookup_eq(cur,
+ left.br_startoff,
+ left.br_startblock,
+ left.br_blockcount, &i)))
+ goto done;
+ ASSERT(i == 1);
+ if ((error = xfs_bmbt_update(cur, left.br_startoff,
+ left.br_startblock,
+ left.br_blockcount +
+ new->br_blockcount,
+ left.br_state)))
+ goto done;
}
- *logflagsp = 0;
- if ((error = xfs_bmbt_lookup_eq(cur, left.br_startoff,
- left.br_startblock, left.br_blockcount, &i)))
- return error;
- ASSERT(i == 1);
- error = xfs_bmbt_update(cur, left.br_startoff,
- left.br_startblock,
- left.br_blockcount + new->br_blockcount,
- left.br_state);
- return error;
+ /* DELTA: One in-core extent grew. */
+ temp = left.br_startoff;
+ temp2 = left.br_blockcount +
+ new->br_blockcount;
+ break;
case MASK(RIGHT_CONTIG):
/*
xfs_bmap_trace_post_update(fname, "RC", ip, idx, whichfork);
ifp->if_lastex = idx;
if (cur == NULL) {
- *logflagsp = XFS_ILOG_FEXT(whichfork);
- return 0;
+ rval = XFS_ILOG_FEXT(whichfork);
+ } else {
+ rval = 0;
+ if ((error = xfs_bmbt_lookup_eq(cur,
+ right.br_startoff,
+ right.br_startblock,
+ right.br_blockcount, &i)))
+ goto done;
+ ASSERT(i == 1);
+ if ((error = xfs_bmbt_update(cur, new->br_startoff,
+ new->br_startblock,
+ new->br_blockcount +
+ right.br_blockcount,
+ right.br_state)))
+ goto done;
}
- *logflagsp = 0;
- if ((error = xfs_bmbt_lookup_eq(cur, right.br_startoff,
- right.br_startblock, right.br_blockcount, &i)))
- return error;
- ASSERT(i == 1);
- error = xfs_bmbt_update(cur, new->br_startoff,
- new->br_startblock,
- new->br_blockcount + right.br_blockcount,
- right.br_state);
- return error;
+ /* DELTA: One in-core extent grew. */
+ temp = new->br_startoff;
+ temp2 = new->br_blockcount +
+ right.br_blockcount;
+ break;
case 0:
/*
XFS_IFORK_NEXT_SET(ip, whichfork,
XFS_IFORK_NEXTENTS(ip, whichfork) + 1);
if (cur == NULL) {
- *logflagsp = XFS_ILOG_CORE | XFS_ILOG_FEXT(whichfork);
- return 0;
+ rval = XFS_ILOG_CORE | XFS_ILOG_FEXT(whichfork);
+ } else {
+ rval = XFS_ILOG_CORE;
+ if ((error = xfs_bmbt_lookup_eq(cur,
+ new->br_startoff,
+ new->br_startblock,
+ new->br_blockcount, &i)))
+ goto done;
+ ASSERT(i == 0);
+ cur->bc_rec.b.br_state = new->br_state;
+ if ((error = xfs_bmbt_insert(cur, &i)))
+ goto done;
+ ASSERT(i == 1);
}
- *logflagsp = XFS_ILOG_CORE;
- if ((error = xfs_bmbt_lookup_eq(cur, new->br_startoff,
- new->br_startblock, new->br_blockcount, &i)))
- return error;
- ASSERT(i == 0);
- cur->bc_rec.b.br_state = new->br_state;
- if ((error = xfs_bmbt_insert(cur, &i)))
- return error;
- ASSERT(i == 1);
- return 0;
+ /* DELTA: A new extent was added in a hole. */
+ temp = new->br_startoff;
+ temp2 = new->br_blockcount;
+ break;
+ }
+ if (delta) {
+ temp2 += temp;
+ if (delta->xed_startoff > temp)
+ delta->xed_startoff = temp;
+ if (delta->xed_blockcount < temp2)
+ delta->xed_blockcount = temp2;
}
+done:
+ *logflagsp = rval;
+ return error;
#undef MASK
#undef MASK2
#undef STATE_SET
#undef STATE_TEST
#undef STATE_SET_TEST
#undef SWITCH_STATE
- /* NOTREACHED */
- ASSERT(0);
- return 0; /* keep gcc quite */
}
/*
args.mp = mp;
args.fsbno = ap->rval;
args.maxlen = MIN(ap->alen, mp->m_sb.sb_agblocks);
+ args.firstblock = ap->firstblock;
blen = 0;
if (nullfb) {
args.type = XFS_ALLOCTYPE_START_BNO;
else
args.minlen = ap->alen;
} else if (ap->low) {
- args.type = XFS_ALLOCTYPE_FIRST_AG;
+ args.type = XFS_ALLOCTYPE_START_BNO;
args.total = args.minlen = ap->minlen;
} else {
args.type = XFS_ALLOCTYPE_NEAR_BNO;
args.prod = ap->ip->i_d.di_extsize;
if ((args.mod = (xfs_extlen_t)do_mod(ap->off, args.prod)))
args.mod = (xfs_extlen_t)(args.prod - args.mod);
- } else if (unlikely(mp->m_sb.sb_blocksize >= NBPP)) {
+ } else if (mp->m_sb.sb_blocksize >= NBPP) {
args.prod = 1;
args.mod = 0;
} else {
xfs_btree_cur_t *cur, /* if null, not a btree */
xfs_bmbt_irec_t *del, /* data to remove from extents */
int *logflagsp, /* inode logging flags */
+ xfs_extdelta_t *delta, /* Change made to incore extents */
int whichfork, /* data or attr fork */
int rsvd) /* OK to allocate reserved blocks */
{
if (da_old > da_new)
xfs_mod_incore_sb(mp, XFS_SBS_FDBLOCKS, (int)(da_old - da_new),
rsvd);
+ if (delta) {
+ /* DELTA: report the original extent. */
+ if (delta->xed_startoff > got.br_startoff)
+ delta->xed_startoff = got.br_startoff;
+ if (delta->xed_blockcount < got.br_startoff+got.br_blockcount)
+ delta->xed_blockcount = got.br_startoff +
+ got.br_blockcount;
+ }
done:
*logflagsp = flags;
return error;
XFS_IFORK_FMT_SET(ip, whichfork, XFS_DINODE_FMT_BTREE);
args.tp = tp;
args.mp = mp;
+ args.firstblock = *firstblock;
if (*firstblock == NULLFSBLOCK) {
args.type = XFS_ALLOCTYPE_START_BNO;
args.fsbno = XFS_INO_TO_FSB(mp, ip->i_ino);
args.tp = tp;
args.mp = ip->i_mount;
+ args.firstblock = *firstblock;
ASSERT((ifp->if_flags &
(XFS_IFINLINE|XFS_IFEXTENTS|XFS_IFEXTIREC)) == XFS_IFINLINE);
/*
if (ip->i_rwtrace == NULL)
return;
ktrace_enter(ip->i_rwtrace,
- (void *)(__psint_t)XFS_BUNMAPI,
+ (void *)(__psint_t)XFS_BUNMAP,
(void *)ip,
(void *)(__psint_t)((ip->i_d.di_size >> 32) & 0xffffffff),
(void *)(__psint_t)(ip->i_d.di_size & 0xffffffff),
if (!XFS_FORCED_SHUTDOWN(mp))
xfs_force_shutdown(mp,
(error == EFSCORRUPTED) ?
- XFS_CORRUPT_INCORE :
- XFS_METADATA_IO_ERROR);
+ SHUTDOWN_CORRUPT_INCORE :
+ SHUTDOWN_META_IO_ERROR);
return error;
}
xfs_trans_log_efd_extent(ntp, efd, free->xbfi_startblock,
xfs_extlen_t total, /* total blocks needed */
xfs_bmbt_irec_t *mval, /* output: map values */
int *nmap, /* i/o: mval size/count */
- xfs_bmap_free_t *flist) /* i/o: list extents to free */
+ xfs_bmap_free_t *flist, /* i/o: list extents to free */
+ xfs_extdelta_t *delta) /* o: change made to incore extents */
{
xfs_fsblock_t abno; /* allocated block number */
xfs_extlen_t alen; /* allocated extent length */
end = bno + len;
obno = bno;
bma.ip = NULL;
+ if (delta) {
+ delta->xed_startoff = NULLFILEOFF;
+ delta->xed_blockcount = 0;
+ }
while (bno < end && n < *nmap) {
/*
* Reading past eof, act as though there's a hole
got.br_state = XFS_EXT_UNWRITTEN;
}
error = xfs_bmap_add_extent(ip, lastx, &cur, &got,
- firstblock, flist, &tmp_logflags, whichfork,
- (flags & XFS_BMAPI_RSVBLOCKS));
+ firstblock, flist, &tmp_logflags, delta,
+ whichfork, (flags & XFS_BMAPI_RSVBLOCKS));
logflags |= tmp_logflags;
if (error)
goto error0;
}
mval->br_state = XFS_EXT_NORM;
error = xfs_bmap_add_extent(ip, lastx, &cur, mval,
- firstblock, flist, &tmp_logflags, whichfork,
- (flags & XFS_BMAPI_RSVBLOCKS));
+ firstblock, flist, &tmp_logflags, delta,
+ whichfork, (flags & XFS_BMAPI_RSVBLOCKS));
logflags |= tmp_logflags;
if (error)
goto error0;
ASSERT(XFS_IFORK_FORMAT(ip, whichfork) != XFS_DINODE_FMT_BTREE ||
XFS_IFORK_NEXTENTS(ip, whichfork) > ifp->if_ext_max);
error = 0;
-
+ if (delta && delta->xed_startoff != NULLFILEOFF) {
+ /* A change was actually made.
+ * Note that delta->xed_blockount is an offset at this
+ * point and needs to be converted to a block count.
+ */
+ ASSERT(delta->xed_blockcount > delta->xed_startoff);
+ delta->xed_blockcount -= delta->xed_startoff;
+ }
error0:
/*
* Log everything. Do this after conversion, there's no point in
xfs_fsblock_t *firstblock, /* first allocated block
controls a.g. for allocs */
xfs_bmap_free_t *flist, /* i/o: list extents to free */
+ xfs_extdelta_t *delta, /* o: change made to incore
+ extents */
int *done) /* set if not done yet */
{
xfs_btree_cur_t *cur; /* bmap btree cursor */
bno = start + len - 1;
ep = xfs_bmap_search_extents(ip, bno, whichfork, &eof, &lastx, &got,
&prev);
+ if (delta) {
+ delta->xed_startoff = NULLFILEOFF;
+ delta->xed_blockcount = 0;
+ }
/*
* Check to see if the given block number is past the end of the
* file, back up to the last block if so...
}
del.br_state = XFS_EXT_UNWRITTEN;
error = xfs_bmap_add_extent(ip, lastx, &cur, &del,
- firstblock, flist, &logflags, XFS_DATA_FORK, 0);
+ firstblock, flist, &logflags, delta,
+ XFS_DATA_FORK, 0);
if (error)
goto error0;
goto nodelete;
prev.br_state = XFS_EXT_UNWRITTEN;
error = xfs_bmap_add_extent(ip, lastx - 1, &cur,
&prev, firstblock, flist, &logflags,
- XFS_DATA_FORK, 0);
+ delta, XFS_DATA_FORK, 0);
if (error)
goto error0;
goto nodelete;
del.br_state = XFS_EXT_UNWRITTEN;
error = xfs_bmap_add_extent(ip, lastx, &cur,
&del, firstblock, flist, &logflags,
- XFS_DATA_FORK, 0);
+ delta, XFS_DATA_FORK, 0);
if (error)
goto error0;
goto nodelete;
goto error0;
}
error = xfs_bmap_del_extent(ip, tp, lastx, flist, cur, &del,
- &tmp_logflags, whichfork, rsvd);
+ &tmp_logflags, delta, whichfork, rsvd);
logflags |= tmp_logflags;
if (error)
goto error0;
ASSERT(ifp->if_ext_max ==
XFS_IFORK_SIZE(ip, whichfork) / (uint)sizeof(xfs_bmbt_rec_t));
error = 0;
+ if (delta && delta->xed_startoff != NULLFILEOFF) {
+ /* A change was actually made.
+ * Note that delta->xed_blockount is an offset at this
+ * point and needs to be converted to a block count.
+ */
+ ASSERT(delta->xed_blockcount > delta->xed_startoff);
+ delta->xed_blockcount -= delta->xed_startoff;
+ }
error0:
/*
* Log everything. Do this after conversion, there's no point in
__int64_t fixlen; /* length for -1 case */
int i; /* extent number */
xfs_inode_t *ip; /* xfs incore inode pointer */
- vnode_t *vp; /* corresponding vnode */
+ bhv_vnode_t *vp; /* corresponding vnode */
int lock; /* lock state */
xfs_bmbt_irec_t *map; /* buffer for user's data */
xfs_mount_t *mp; /* file system mount point */
if (whichfork == XFS_DATA_FORK && ip->i_delayed_blks) {
/* xfs_fsize_t last_byte = xfs_file_last_byte(ip); */
- VOP_FLUSH_PAGES(vp, (xfs_off_t)0, -1, 0, FI_REMAPF, error);
+ error = bhv_vop_flush_pages(vp, (xfs_off_t)0, -1, 0, FI_REMAPF);
}
ASSERT(whichfork == XFS_ATTR_FORK || ip->i_delayed_blks == 0);
nmap = (nexleft > subnex) ? subnex : nexleft;
error = xfs_bmapi(NULL, ip, XFS_BB_TO_FSBT(mp, bmv->bmv_offset),
XFS_BB_TO_FSB(mp, bmv->bmv_length),
- bmapi_flags, NULL, 0, map, &nmap, NULL);
+ bmapi_flags, NULL, 0, map, &nmap,
+ NULL, NULL);
if (error)
goto unlock_and_return;
ASSERT(nmap <= subnex);
/*
- * Copyright (c) 2000-2003,2005 Silicon Graphics, Inc.
+ * Copyright (c) 2000-2006 Silicon Graphics, Inc.
* All Rights Reserved.
*
* This program is free software; you can redistribute it and/or
struct xfs_mount;
struct xfs_trans;
+/*
+ * DELTA: describe a change to the in-core extent list.
+ *
+ * Internally the use of xed_blockount is somewhat funky.
+ * xed_blockcount contains an offset much of the time because this
+ * makes merging changes easier. (xfs_fileoff_t and xfs_filblks_t are
+ * the same underlying type).
+ */
+typedef struct xfs_extdelta
+{
+ xfs_fileoff_t xed_startoff; /* offset of range */
+ xfs_filblks_t xed_blockcount; /* blocks in range */
+} xfs_extdelta_t;
+
/*
* List of extents to be free "later".
* The list is kept sorted on xbf_startblock.
xfs_extlen_t total, /* total blocks needed */
struct xfs_bmbt_irec *mval, /* output: map values */
int *nmap, /* i/o: mval size/count */
- xfs_bmap_free_t *flist); /* i/o: list extents to free */
+ xfs_bmap_free_t *flist, /* i/o: list extents to free */
+ xfs_extdelta_t *delta); /* o: change made to incore
+ extents */
/*
* Map file blocks to filesystem blocks, simple version.
xfs_fsblock_t *firstblock, /* first allocated block
controls a.g. for allocs */
xfs_bmap_free_t *flist, /* i/o: list extents to free */
+ xfs_extdelta_t *delta, /* o: change made to incore
+ extents */
int *done); /* set if not done yet */
/*
#include "xfs_trans.h"
#include "xfs_sb.h"
#include "xfs_ag.h"
-#include "xfs_dir.h"
#include "xfs_dir2.h"
#include "xfs_dmapi.h"
#include "xfs_mount.h"
#include "xfs_bmap_btree.h"
#include "xfs_alloc_btree.h"
#include "xfs_ialloc_btree.h"
-#include "xfs_dir_sf.h"
#include "xfs_dir2_sf.h"
#include "xfs_attr_sf.h"
#include "xfs_dinode.h"
lbno = XFS_DADDR_TO_FSB(args.mp, XFS_BUF_ADDR(lbp));
left = XFS_BUF_TO_BMBT_BLOCK(lbp);
args.fsbno = cur->bc_private.b.firstblock;
+ args.firstblock = args.fsbno;
if (args.fsbno == NULLFSBLOCK) {
args.fsbno = lbno;
args.type = XFS_ALLOCTYPE_START_BNO;
- } else if (cur->bc_private.b.flist->xbf_low)
- args.type = XFS_ALLOCTYPE_FIRST_AG;
- else
+ } else
args.type = XFS_ALLOCTYPE_NEAR_BNO;
args.mod = args.minleft = args.alignment = args.total = args.isfl =
args.userdata = args.minalignslop = 0;
args.userdata = args.minalignslop = 0;
args.minlen = args.maxlen = args.prod = 1;
args.wasdel = cur->bc_private.b.flags & XFS_BTCUR_BPRV_WASDEL;
+ args.firstblock = args.fsbno;
if (args.fsbno == NULLFSBLOCK) {
#ifdef DEBUG
if ((error = xfs_btree_check_lptr(cur, INT_GET(*pp, ARCH_CONVERT), level))) {
#endif
args.fsbno = INT_GET(*pp, ARCH_CONVERT);
args.type = XFS_ALLOCTYPE_START_BNO;
- } else if (args.wasdel)
- args.type = XFS_ALLOCTYPE_FIRST_AG;
- else
+ } else
args.type = XFS_ALLOCTYPE_NEAR_BNO;
if ((error = xfs_alloc_vextent(&args))) {
XFS_BMBT_TRACE_CURSOR(cur, ERROR);
#include "xfs_trans.h"
#include "xfs_sb.h"
#include "xfs_ag.h"
-#include "xfs_dir.h"
#include "xfs_dir2.h"
#include "xfs_dmapi.h"
#include "xfs_mount.h"
#include "xfs_bmap_btree.h"
#include "xfs_alloc_btree.h"
#include "xfs_ialloc_btree.h"
-#include "xfs_dir_sf.h"
#include "xfs_dir2_sf.h"
#include "xfs_attr_sf.h"
#include "xfs_dinode.h"
#include "xfs_inum.h"
#include "xfs_trans.h"
#include "xfs_sb.h"
-#include "xfs_dir.h"
#include "xfs_dmapi.h"
#include "xfs_mount.h"
#include "xfs_buf_item.h"
if ((XFS_BUF_TARGET(bp) != lasttarg) ||
(time_after(jiffies, (lasttime + 5*HZ)))) {
lasttime = jiffies;
- prdev("XFS write error in file system meta-data "
- "block 0x%llx in %s",
- XFS_BUF_TARGET(bp),
+ cmn_err(CE_ALERT, "Device %s, XFS metadata write error"
+ " block 0x%llx in %s",
+ XFS_BUFTARG_NAME(XFS_BUF_TARGET(bp)),
(__uint64_t)XFS_BUF_ADDR(bp), mp->m_fsname);
}
lasttarg = XFS_BUF_TARGET(bp);
XFS_BUF_ERROR(bp,0);
xfs_buftrace("BUF_ERROR_RELSE", bp);
if (! XFS_FORCED_SHUTDOWN(mp))
- xfs_force_shutdown(mp, XFS_METADATA_IO_ERROR);
+ xfs_force_shutdown(mp, SHUTDOWN_META_IO_ERROR);
/*
* We have to unpin the pinned buffers so do the
* callbacks.
#include <linux/posix_cap_xattr.h>
-struct vnode;
+struct bhv_vnode;
-extern int xfs_cap_vhascap(struct vnode *);
-extern int xfs_cap_vset(struct vnode *, void *, size_t);
-extern int xfs_cap_vget(struct vnode *, void *, size_t);
-extern int xfs_cap_vremove(struct vnode *vp);
+extern int xfs_cap_vhascap(struct bhv_vnode *);
+extern int xfs_cap_vset(struct bhv_vnode *, void *, size_t);
+extern int xfs_cap_vget(struct bhv_vnode *, void *, size_t);
+extern int xfs_cap_vremove(struct bhv_vnode *);
#define _CAP_EXISTS xfs_cap_vhascap
#include "xfs_trans.h"
#include "xfs_sb.h"
#include "xfs_ag.h"
-#include "xfs_dir.h"
#include "xfs_dir2.h"
#include "xfs_dmapi.h"
#include "xfs_mount.h"
#include "xfs_bmap_btree.h"
#include "xfs_alloc_btree.h"
#include "xfs_ialloc_btree.h"
-#include "xfs_dir_sf.h"
#include "xfs_dir2_sf.h"
#include "xfs_attr_sf.h"
#include "xfs_dinode.h"
#include "xfs_bmap.h"
#include "xfs_attr.h"
#include "xfs_attr_leaf.h"
-#include "xfs_dir_leaf.h"
#include "xfs_dir2_data.h"
#include "xfs_dir2_leaf.h"
#include "xfs_dir2_block.h"
max = state->path.active - 1;
ASSERT((max >= 0) && (max < XFS_DA_NODE_MAXDEPTH));
ASSERT(state->path.blk[max].magic == XFS_ATTR_LEAF_MAGIC ||
- state->path.blk[max].magic == XFS_DIRX_LEAF_MAGIC(state->mp));
+ state->path.blk[max].magic == XFS_DIR2_LEAFN_MAGIC);
addblk = &state->path.blk[max]; /* initial dummy value */
for (i = max; (i >= 0) && addblk; state->path.active--, i--) {
return(error); /* GROT: attr inconsistent */
addblk = newblk;
break;
- case XFS_DIR_LEAF_MAGIC:
- ASSERT(XFS_DIR_IS_V1(state->mp));
- error = xfs_dir_leaf_split(state, oldblk, newblk);
- if ((error != 0) && (error != ENOSPC)) {
- return(error); /* GROT: dir is inconsistent */
- }
- if (!error) {
- addblk = newblk;
- break;
- }
- /*
- * Entry wouldn't fit, split the leaf again.
- */
- state->extravalid = 1;
- if (state->inleaf) {
- state->extraafter = 0; /* before newblk */
- error = xfs_dir_leaf_split(state, oldblk,
- &state->extrablk);
- if (error)
- return(error); /* GROT: dir incon. */
- addblk = newblk;
- } else {
- state->extraafter = 1; /* after newblk */
- error = xfs_dir_leaf_split(state, newblk,
- &state->extrablk);
- if (error)
- return(error); /* GROT: dir incon. */
- addblk = newblk;
- }
- break;
case XFS_DIR2_LEAFN_MAGIC:
- ASSERT(XFS_DIR_IS_V2(state->mp));
error = xfs_dir2_leafn_split(state, oldblk, newblk);
if (error)
return error;
size = (int)((char *)&oldroot->btree[be16_to_cpu(oldroot->hdr.count)] -
(char *)oldroot);
} else {
- ASSERT(XFS_DIR_IS_V2(mp));
ASSERT(be16_to_cpu(oldroot->hdr.info.magic) == XFS_DIR2_LEAFN_MAGIC);
leaf = (xfs_dir2_leaf_t *)oldroot;
size = (int)((char *)&leaf->ents[be16_to_cpu(leaf->hdr.count)] -
* Set up the new root node.
*/
error = xfs_da_node_create(args,
- args->whichfork == XFS_DATA_FORK &&
- XFS_DIR_IS_V2(mp) ? mp->m_dirleafblk : 0,
+ (args->whichfork == XFS_DATA_FORK) ? mp->m_dirleafblk : 0,
be16_to_cpu(node->hdr.level) + 1, &bp, args->whichfork);
if (error)
return(error);
ASSERT(be16_to_cpu(node->hdr.info.magic) == XFS_DA_NODE_MAGIC);
/*
- * With V2 the extra block is data or freespace.
+ * With V2 dirs the extra block is data or freespace.
*/
- useextra = state->extravalid && (XFS_DIR_IS_V1(state->mp) ||
- state->args->whichfork == XFS_ATTR_FORK);
+ useextra = state->extravalid && state->args->whichfork == XFS_ATTR_FORK;
newcount = 1 + useextra;
/*
* Do we have to split the node?
ASSERT(be16_to_cpu(node->hdr.info.magic) == XFS_DA_NODE_MAGIC);
ASSERT((oldblk->index >= 0) && (oldblk->index <= be16_to_cpu(node->hdr.count)));
ASSERT(newblk->blkno != 0);
- if (state->args->whichfork == XFS_DATA_FORK && XFS_DIR_IS_V2(mp))
+ if (state->args->whichfork == XFS_DATA_FORK)
ASSERT(newblk->blkno >= mp->m_dirleafblk &&
newblk->blkno < mp->m_dirfreeblk);
save_blk = &state->altpath.blk[ state->path.active-1 ];
ASSERT(state->path.blk[0].magic == XFS_DA_NODE_MAGIC);
ASSERT(drop_blk->magic == XFS_ATTR_LEAF_MAGIC ||
- drop_blk->magic == XFS_DIRX_LEAF_MAGIC(state->mp));
+ drop_blk->magic == XFS_DIR2_LEAFN_MAGIC);
/*
* Walk back up the tree joining/deallocating as necessary.
return(0);
xfs_attr_leaf_unbalance(state, drop_blk, save_blk);
break;
- case XFS_DIR_LEAF_MAGIC:
- ASSERT(XFS_DIR_IS_V1(state->mp));
- error = xfs_dir_leaf_toosmall(state, &action);
- if (error)
- return(error);
- if (action == 0)
- return(0);
- xfs_dir_leaf_unbalance(state, drop_blk, save_blk);
- break;
case XFS_DIR2_LEAFN_MAGIC:
- ASSERT(XFS_DIR_IS_V2(state->mp));
error = xfs_dir2_leafn_toosmall(state, &action);
if (error)
return error;
ASSERT(bp != NULL);
blkinfo = bp->data;
if (be16_to_cpu(oldroot->hdr.level) == 1) {
- ASSERT(be16_to_cpu(blkinfo->magic) == XFS_DIRX_LEAF_MAGIC(state->mp) ||
+ ASSERT(be16_to_cpu(blkinfo->magic) == XFS_DIR2_LEAFN_MAGIC ||
be16_to_cpu(blkinfo->magic) == XFS_ATTR_LEAF_MAGIC);
} else {
ASSERT(be16_to_cpu(blkinfo->magic) == XFS_DA_NODE_MAGIC);
if (count == 0)
return;
break;
- case XFS_DIR_LEAF_MAGIC:
- ASSERT(XFS_DIR_IS_V1(state->mp));
- lasthash = xfs_dir_leaf_lasthash(blk->bp, &count);
- if (count == 0)
- return;
- break;
case XFS_DIR2_LEAFN_MAGIC:
- ASSERT(XFS_DIR_IS_V2(state->mp));
lasthash = xfs_dir2_leafn_lasthash(blk->bp, &count);
if (count == 0)
return;
* Descend thru the B-tree searching each level for the right
* node to use, until the right hashval is found.
*/
- if (args->whichfork == XFS_DATA_FORK && XFS_DIR_IS_V2(state->mp))
- blkno = state->mp->m_dirleafblk;
- else
- blkno = 0;
+ blkno = (args->whichfork == XFS_DATA_FORK)? state->mp->m_dirleafblk : 0;
for (blk = &state->path.blk[0], state->path.active = 1;
state->path.active <= XFS_DA_NODE_MAXDEPTH;
blk++, state->path.active++) {
}
curr = blk->bp->data;
ASSERT(be16_to_cpu(curr->magic) == XFS_DA_NODE_MAGIC ||
- be16_to_cpu(curr->magic) == XFS_DIRX_LEAF_MAGIC(state->mp) ||
+ be16_to_cpu(curr->magic) == XFS_DIR2_LEAFN_MAGIC ||
be16_to_cpu(curr->magic) == XFS_ATTR_LEAF_MAGIC);
/*
blk->index = probe;
blkno = be32_to_cpu(btree->before);
}
- }
- else if (be16_to_cpu(curr->magic) == XFS_ATTR_LEAF_MAGIC) {
+ } else if (be16_to_cpu(curr->magic) == XFS_ATTR_LEAF_MAGIC) {
blk->hashval = xfs_attr_leaf_lasthash(blk->bp, NULL);
break;
- }
- else if (be16_to_cpu(curr->magic) == XFS_DIR_LEAF_MAGIC) {
- blk->hashval = xfs_dir_leaf_lasthash(blk->bp, NULL);
- break;
- }
- else if (be16_to_cpu(curr->magic) == XFS_DIR2_LEAFN_MAGIC) {
+ } else if (be16_to_cpu(curr->magic) == XFS_DIR2_LEAFN_MAGIC) {
blk->hashval = xfs_dir2_leafn_lasthash(blk->bp, NULL);
break;
}
* next leaf and keep searching.
*/
for (;;) {
- if (blk->magic == XFS_DIR_LEAF_MAGIC) {
- ASSERT(XFS_DIR_IS_V1(state->mp));
- retval = xfs_dir_leaf_lookup_int(blk->bp, args,
- &blk->index);
- } else if (blk->magic == XFS_DIR2_LEAFN_MAGIC) {
- ASSERT(XFS_DIR_IS_V2(state->mp));
+ if (blk->magic == XFS_DIR2_LEAFN_MAGIC) {
retval = xfs_dir2_leafn_lookup_int(blk->bp, args,
&blk->index, state);
}
old_info = old_blk->bp->data;
new_info = new_blk->bp->data;
ASSERT(old_blk->magic == XFS_DA_NODE_MAGIC ||
- old_blk->magic == XFS_DIRX_LEAF_MAGIC(state->mp) ||
+ old_blk->magic == XFS_DIR2_LEAFN_MAGIC ||
old_blk->magic == XFS_ATTR_LEAF_MAGIC);
ASSERT(old_blk->magic == be16_to_cpu(old_info->magic));
ASSERT(new_blk->magic == be16_to_cpu(new_info->magic));
case XFS_ATTR_LEAF_MAGIC:
before = xfs_attr_leaf_order(old_blk->bp, new_blk->bp);
break;
- case XFS_DIR_LEAF_MAGIC:
- ASSERT(XFS_DIR_IS_V1(state->mp));
- before = xfs_dir_leaf_order(old_blk->bp, new_blk->bp);
- break;
case XFS_DIR2_LEAFN_MAGIC:
- ASSERT(XFS_DIR_IS_V2(state->mp));
before = xfs_dir2_leafn_order(old_blk->bp, new_blk->bp);
break;
case XFS_DA_NODE_MAGIC:
save_info = save_blk->bp->data;
drop_info = drop_blk->bp->data;
ASSERT(save_blk->magic == XFS_DA_NODE_MAGIC ||
- save_blk->magic == XFS_DIRX_LEAF_MAGIC(state->mp) ||
+ save_blk->magic == XFS_DIR2_LEAFN_MAGIC ||
save_blk->magic == XFS_ATTR_LEAF_MAGIC);
ASSERT(save_blk->magic == be16_to_cpu(save_info->magic));
ASSERT(drop_blk->magic == be16_to_cpu(drop_info->magic));
ASSERT(blk->bp != NULL);
info = blk->bp->data;
ASSERT(be16_to_cpu(info->magic) == XFS_DA_NODE_MAGIC ||
- be16_to_cpu(info->magic) == XFS_DIRX_LEAF_MAGIC(state->mp) ||
+ be16_to_cpu(info->magic) == XFS_DIR2_LEAFN_MAGIC ||
be16_to_cpu(info->magic) == XFS_ATTR_LEAF_MAGIC);
blk->magic = be16_to_cpu(info->magic);
if (blk->magic == XFS_DA_NODE_MAGIC) {
blk->hashval = xfs_attr_leaf_lasthash(blk->bp,
NULL);
break;
- case XFS_DIR_LEAF_MAGIC:
- ASSERT(XFS_DIR_IS_V1(state->mp));
- blk->hashval = xfs_dir_leaf_lasthash(blk->bp,
- NULL);
- break;
case XFS_DIR2_LEAFN_MAGIC:
- ASSERT(XFS_DIR_IS_V2(state->mp));
blk->hashval = xfs_dir2_leafn_lasthash(blk->bp,
NULL);
break;
default:
ASSERT(blk->magic == XFS_ATTR_LEAF_MAGIC ||
- blk->magic ==
- XFS_DIRX_LEAF_MAGIC(state->mp));
+ blk->magic == XFS_DIR2_LEAFN_MAGIC);
break;
}
}
xfs_bmbt_irec_t *mapp;
xfs_inode_t *dp;
int nmap, error, w, count, c, got, i, mapi;
- xfs_fsize_t size;
xfs_trans_t *tp;
xfs_mount_t *mp;
/*
* For new directories adjust the file offset and block count.
*/
- if (w == XFS_DATA_FORK && XFS_DIR_IS_V2(mp)) {
+ if (w == XFS_DATA_FORK) {
bno = mp->m_dirleafblk;
count = mp->m_dirblkfsbs;
} else {
/*
* Find a spot in the file space to put the new block.
*/
- if ((error = xfs_bmap_first_unused(tp, dp, count, &bno, w))) {
+ if ((error = xfs_bmap_first_unused(tp, dp, count, &bno, w)))
return error;
- }
- if (w == XFS_DATA_FORK && XFS_DIR_IS_V2(mp))
+ if (w == XFS_DATA_FORK)
ASSERT(bno >= mp->m_dirleafblk && bno < mp->m_dirfreeblk);
/*
* Try mapping it in one filesystem block.
XFS_BMAPI_AFLAG(w)|XFS_BMAPI_WRITE|XFS_BMAPI_METADATA|
XFS_BMAPI_CONTIG,
args->firstblock, args->total, &map, &nmap,
- args->flist))) {
+ args->flist, NULL))) {
return error;
}
ASSERT(nmap <= 1);
XFS_BMAPI_AFLAG(w)|XFS_BMAPI_WRITE|
XFS_BMAPI_METADATA,
args->firstblock, args->total,
- &mapp[mapi], &nmap, args->flist))) {
+ &mapp[mapi], &nmap, args->flist,
+ NULL))) {
kmem_free(mapp, sizeof(*mapp) * count);
return error;
}
if (mapp != &map)
kmem_free(mapp, sizeof(*mapp) * count);
*new_blkno = (xfs_dablk_t)bno;
- /*
- * For version 1 directories, adjust the file size if it changed.
- */
- if (w == XFS_DATA_FORK && XFS_DIR_IS_V1(mp)) {
- ASSERT(mapi == 1);
- if ((error = xfs_bmap_last_offset(tp, dp, &bno, w)))
- return error;
- size = XFS_FSB_TO_B(mp, bno);
- if (size != dp->i_d.di_size) {
- dp->i_d.di_size = size;
- xfs_trans_log_inode(tp, dp, XFS_ILOG_CORE);
- }
- }
return 0;
}
int error, w, entno, level, dead_level;
xfs_da_blkinfo_t *dead_info, *sib_info;
xfs_da_intnode_t *par_node, *dead_node;
- xfs_dir_leafblock_t *dead_leaf;
xfs_dir2_leaf_t *dead_leaf2;
xfs_dahash_t dead_hash;
w = args->whichfork;
ASSERT(w == XFS_DATA_FORK);
mp = ip->i_mount;
- if (XFS_DIR_IS_V2(mp)) {
- lastoff = mp->m_dirfreeblk;
- error = xfs_bmap_last_before(tp, ip, &lastoff, w);
- } else
- error = xfs_bmap_last_offset(tp, ip, &lastoff, w);
+ lastoff = mp->m_dirfreeblk;
+ error = xfs_bmap_last_before(tp, ip, &lastoff, w);
if (error)
return error;
if (unlikely(lastoff == 0)) {
/*
* Get values from the moved block.
*/
- if (be16_to_cpu(dead_info->magic) == XFS_DIR_LEAF_MAGIC) {
- ASSERT(XFS_DIR_IS_V1(mp));
- dead_leaf = (xfs_dir_leafblock_t *)dead_info;
- dead_level = 0;
- dead_hash =
- INT_GET(dead_leaf->entries[INT_GET(dead_leaf->hdr.count, ARCH_CONVERT) - 1].hashval, ARCH_CONVERT);
- } else if (be16_to_cpu(dead_info->magic) == XFS_DIR2_LEAFN_MAGIC) {
- ASSERT(XFS_DIR_IS_V2(mp));
+ if (be16_to_cpu(dead_info->magic) == XFS_DIR2_LEAFN_MAGIC) {
dead_leaf2 = (xfs_dir2_leaf_t *)dead_info;
dead_level = 0;
dead_hash = be32_to_cpu(dead_leaf2->ents[be16_to_cpu(dead_leaf2->hdr.count) - 1].hashval);
xfs_da_buf_done(sib_buf);
sib_buf = NULL;
}
- par_blkno = XFS_DIR_IS_V1(mp) ? 0 : mp->m_dirleafblk;
+ par_blkno = mp->m_dirleafblk;
level = -1;
/*
* Walk down the tree looking for the parent of the moved block.
{
xfs_inode_t *dp;
int done, error, w, count;
- xfs_fileoff_t bno;
- xfs_fsize_t size;
xfs_trans_t *tp;
xfs_mount_t *mp;
w = args->whichfork;
tp = args->trans;
mp = dp->i_mount;
- if (w == XFS_DATA_FORK && XFS_DIR_IS_V2(mp))
+ if (w == XFS_DATA_FORK)
count = mp->m_dirblkfsbs;
else
count = 1;
*/
if ((error = xfs_bunmapi(tp, dp, dead_blkno, count,
XFS_BMAPI_AFLAG(w)|XFS_BMAPI_METADATA,
- 0, args->firstblock, args->flist,
+ 0, args->firstblock, args->flist, NULL,
&done)) == ENOSPC) {
if (w != XFS_DATA_FORK)
- goto done;
+ break;
if ((error = xfs_da_swap_lastblock(args, &dead_blkno,
&dead_buf)))
- goto done;
- } else if (error)
- goto done;
- else
+ break;
+ } else {
break;
- }
- ASSERT(done);
- xfs_da_binval(tp, dead_buf);
- /*
- * Adjust the directory size for version 1.
- */
- if (w == XFS_DATA_FORK && XFS_DIR_IS_V1(mp)) {
- if ((error = xfs_bmap_last_offset(tp, dp, &bno, w)))
- return error;
- size = XFS_FSB_TO_B(dp->i_mount, bno);
- if (size != dp->i_d.di_size) {
- dp->i_d.di_size = size;
- xfs_trans_log_inode(tp, dp, XFS_ILOG_CORE);
}
}
- return 0;
-done:
xfs_da_binval(tp, dead_buf);
return error;
}
xfs_dabuf_t *rbp;
mp = dp->i_mount;
- if (whichfork == XFS_DATA_FORK && XFS_DIR_IS_V2(mp))
- nfsb = mp->m_dirblkfsbs;
- else
- nfsb = 1;
+ nfsb = (whichfork == XFS_DATA_FORK) ? mp->m_dirblkfsbs : 1;
mappedbno = *mappedbnop;
/*
* Caller doesn't have a mapping. -2 means don't complain
nfsb,
XFS_BMAPI_METADATA |
XFS_BMAPI_AFLAG(whichfork),
- NULL, 0, mapp, &nmap, NULL)))
+ NULL, 0, mapp, &nmap, NULL, NULL)))
goto exit0;
}
} else {
magic1 = be32_to_cpu(data->hdr.magic);
if (unlikely(
XFS_TEST_ERROR((magic != XFS_DA_NODE_MAGIC) &&
- (magic != XFS_DIR_LEAF_MAGIC) &&
(magic != XFS_ATTR_LEAF_MAGIC) &&
(magic != XFS_DIR2_LEAF1_MAGIC) &&
(magic != XFS_DIR2_LEAFN_MAGIC) &&
* level in the Btree, and to identify which type of block this is.
*/
#define XFS_DA_NODE_MAGIC 0xfebe /* magic number: non-leaf blocks */
-#define XFS_DIR_LEAF_MAGIC 0xfeeb /* magic number: directory leaf blks */
#define XFS_ATTR_LEAF_MAGIC 0xfbee /* magic number: attribute leaf blks */
#define XFS_DIR2_LEAF1_MAGIC 0xd2f1 /* magic number: v2 dirlf single blks */
#define XFS_DIR2_LEAFN_MAGIC 0xd2ff /* magic number: v2 dirlf multi blks */
-#define XFS_DIRX_LEAF_MAGIC(mp) \
- (XFS_DIR_IS_V1(mp) ? XFS_DIR_LEAF_MAGIC : XFS_DIR2_LEAFN_MAGIC)
-
typedef struct xfs_da_blkinfo {
__be32 forw; /* previous block in list */
__be32 back; /* following block in list */
/*
- * Copyright (c) 2000-2002,2005 Silicon Graphics, Inc.
+ * Copyright (c) 2000-2006 Silicon Graphics, Inc.
* All Rights Reserved.
*
* This program is free software; you can redistribute it and/or
#include "xfs_trans.h"
#include "xfs_sb.h"
#include "xfs_ag.h"
-#include "xfs_dir.h"
#include "xfs_dir2.h"
#include "xfs_dmapi.h"
#include "xfs_mount.h"
#include "xfs_bmap_btree.h"
#include "xfs_alloc_btree.h"
#include "xfs_ialloc_btree.h"
-#include "xfs_dir_sf.h"
#include "xfs_dir2_sf.h"
#include "xfs_attr_sf.h"
#include "xfs_dinode.h"
xfs_swapext_t __user *sxu)
{
xfs_swapext_t *sxp;
- xfs_inode_t *ip=NULL, *tip=NULL, *ips[2];
- xfs_trans_t *tp;
+ xfs_inode_t *ip=NULL, *tip=NULL;
xfs_mount_t *mp;
- xfs_bstat_t *sbp;
struct file *fp = NULL, *tfp = NULL;
- vnode_t *vp, *tvp;
- static uint lock_flags = XFS_ILOCK_EXCL | XFS_IOLOCK_EXCL;
- int ilf_fields, tilf_fields;
+ bhv_vnode_t *vp, *tvp;
int error = 0;
- xfs_ifork_t *tempifp, *ifp, *tifp;
- __uint64_t tmp;
- int aforkblks = 0;
- int taforkblks = 0;
- char locked = 0;
sxp = kmem_alloc(sizeof(xfs_swapext_t), KM_MAYFAIL);
- tempifp = kmem_alloc(sizeof(xfs_ifork_t), KM_MAYFAIL);
- if (!sxp || !tempifp) {
+ if (!sxp) {
error = XFS_ERROR(ENOMEM);
goto error0;
}
mp = ip->i_mount;
- sbp = &sxp->sx_stat;
-
if (XFS_FORCED_SHUTDOWN(mp)) {
error = XFS_ERROR(EIO);
goto error0;
}
- locked = 1;
+ error = XFS_SWAP_EXTENTS(mp, &ip->i_iocore, &tip->i_iocore, sxp);
+
+ error0:
+ if (fp != NULL)
+ fput(fp);
+ if (tfp != NULL)
+ fput(tfp);
+
+ if (sxp != NULL)
+ kmem_free(sxp, sizeof(xfs_swapext_t));
+
+ return error;
+}
+
+int
+xfs_swap_extents(
+ xfs_inode_t *ip,
+ xfs_inode_t *tip,
+ xfs_swapext_t *sxp)
+{
+ xfs_mount_t *mp;
+ xfs_inode_t *ips[2];
+ xfs_trans_t *tp;
+ xfs_bstat_t *sbp = &sxp->sx_stat;
+ bhv_vnode_t *vp, *tvp;
+ xfs_ifork_t *tempifp, *ifp, *tifp;
+ int ilf_fields, tilf_fields;
+ static uint lock_flags = XFS_ILOCK_EXCL | XFS_IOLOCK_EXCL;
+ int error = 0;
+ int aforkblks = 0;
+ int taforkblks = 0;
+ __uint64_t tmp;
+ char locked = 0;
+
+ mp = ip->i_mount;
+
+ tempifp = kmem_alloc(sizeof(xfs_ifork_t), KM_MAYFAIL);
+ if (!tempifp) {
+ error = XFS_ERROR(ENOMEM);
+ goto error0;
+ }
+
+ sbp = &sxp->sx_stat;
+ vp = XFS_ITOV(ip);
+ tvp = XFS_ITOV(tip);
/* Lock in i_ino order */
if (ip->i_ino < tip->i_ino) {
}
xfs_lock_inodes(ips, 2, 0, lock_flags);
+ locked = 1;
/* Check permissions */
error = xfs_iaccess(ip, S_IWUSR, NULL);
if (VN_CACHED(tvp) != 0) {
xfs_inval_cached_trace(&tip->i_iocore, 0, -1, 0, -1);
- VOP_FLUSHINVAL_PAGES(tvp, 0, -1, FI_REMAPF_LOCKED);
+ bhv_vop_flushinval_pages(tvp, 0, -1, FI_REMAPF_LOCKED);
}
/* Verify O_DIRECT for ftmp */
/* We need to fail if the file is memory mapped. Once we have tossed
* all existing pages, the page fault will have no option
* but to go to the filesystem for pages. By making the page fault call
- * VOP_READ (or write in the case of autogrow) they block on the iolock
+ * vop_read (or write in the case of autogrow) they block on the iolock
* until we have switched the extents.
*/
if (VN_MAPPED(vp)) {
* fields change.
*/
- VOP_TOSS_PAGES(vp, 0, -1, FI_REMAPF);
+ bhv_vop_toss_pages(vp, 0, -1, FI_REMAPF);
tp = xfs_trans_alloc(mp, XFS_TRANS_SWAPEXT);
if ((error = xfs_trans_reserve(tp, 0,
xfs_iunlock(ip, lock_flags);
xfs_iunlock(tip, lock_flags);
}
-
- if (fp != NULL)
- fput(fp);
- if (tfp != NULL)
- fput(tfp);
-
- if (sxp != NULL)
- kmem_free(sxp, sizeof(xfs_swapext_t));
if (tempifp != NULL)
kmem_free(tempifp, sizeof(xfs_ifork_t));
-
return error;
}
*/
int xfs_swapext(struct xfs_swapext __user *sx);
+int xfs_swap_extents(struct xfs_inode *ip, struct xfs_inode *tip,
+ struct xfs_swapext *sxp);
+
#endif /* __KERNEL__ */
#endif /* __XFS_DFRAG_H__ */
union {
xfs_bmdr_block_t di_bmbt; /* btree root block */
xfs_bmbt_rec_32_t di_bmx[1]; /* extent list */
- xfs_dir_shortform_t di_dirsf; /* shortform directory */
xfs_dir2_sf_t di_dir2sf; /* shortform directory v2 */
char di_c[1]; /* local contents */
xfs_dev_t di_dev; /* device for S_IFCHR/S_IFBLK */
#define XFS_DIFLAG_NOSYMLINKS_BIT 10 /* disallow symlink creation */
#define XFS_DIFLAG_EXTSIZE_BIT 11 /* inode extent size allocator hint */
#define XFS_DIFLAG_EXTSZINHERIT_BIT 12 /* inherit inode extent size */
+#define XFS_DIFLAG_NODEFRAG_BIT 13 /* do not reorganize/defragment */
#define XFS_DIFLAG_REALTIME (1 << XFS_DIFLAG_REALTIME_BIT)
#define XFS_DIFLAG_PREALLOC (1 << XFS_DIFLAG_PREALLOC_BIT)
#define XFS_DIFLAG_NEWRTBM (1 << XFS_DIFLAG_NEWRTBM_BIT)
#define XFS_DIFLAG_NOSYMLINKS (1 << XFS_DIFLAG_NOSYMLINKS_BIT)
#define XFS_DIFLAG_EXTSIZE (1 << XFS_DIFLAG_EXTSIZE_BIT)
#define XFS_DIFLAG_EXTSZINHERIT (1 << XFS_DIFLAG_EXTSZINHERIT_BIT)
+#define XFS_DIFLAG_NODEFRAG (1 << XFS_DIFLAG_NODEFRAG_BIT)
#define XFS_DIFLAG_ANY \
(XFS_DIFLAG_REALTIME | XFS_DIFLAG_PREALLOC | XFS_DIFLAG_NEWRTBM | \
XFS_DIFLAG_IMMUTABLE | XFS_DIFLAG_APPEND | XFS_DIFLAG_SYNC | \
XFS_DIFLAG_NOATIME | XFS_DIFLAG_NODUMP | XFS_DIFLAG_RTINHERIT | \
XFS_DIFLAG_PROJINHERIT | XFS_DIFLAG_NOSYMLINKS | XFS_DIFLAG_EXTSIZE | \
- XFS_DIFLAG_EXTSZINHERIT)
+ XFS_DIFLAG_EXTSZINHERIT | XFS_DIFLAG_NODEFRAG)
#endif /* __XFS_DINODE_H__ */
+++ /dev/null
-/*
- * Copyright (c) 2000-2001,2005 Silicon Graphics, Inc.
- * 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.
- *
- * This program is distributed in the hope that it would 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 the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-#include "xfs.h"
-#include "xfs_fs.h"
-#include "xfs_types.h"
-#include "xfs_log.h"
-#include "xfs_inum.h"
-#include "xfs_trans.h"
-#include "xfs_sb.h"
-#include "xfs_dir.h"
-#include "xfs_dir2.h"
-#include "xfs_dmapi.h"
-#include "xfs_mount.h"
-#include "xfs_da_btree.h"
-#include "xfs_bmap_btree.h"
-#include "xfs_alloc_btree.h"
-#include "xfs_ialloc_btree.h"
-#include "xfs_alloc.h"
-#include "xfs_btree.h"
-#include "xfs_dir_sf.h"
-#include "xfs_dir2_sf.h"
-#include "xfs_attr_sf.h"
-#include "xfs_dinode.h"
-#include "xfs_inode.h"
-#include "xfs_bmap.h"
-#include "xfs_dir_leaf.h"
-#include "xfs_error.h"
-
-/*
- * xfs_dir.c
- *
- * Provide the external interfaces to manage directories.
- */
-
-/*========================================================================
- * Function prototypes for the kernel.
- *========================================================================*/
-
-/*
- * Functions for the dirops interfaces.
- */
-static void xfs_dir_mount(struct xfs_mount *mp);
-
-static int xfs_dir_isempty(struct xfs_inode *dp);
-
-static int xfs_dir_init(struct xfs_trans *trans,
- struct xfs_inode *dir,
- struct xfs_inode *parent_dir);
-
-static int xfs_dir_createname(struct xfs_trans *trans,
- struct xfs_inode *dp,
- char *name_string,
- int name_len,
- xfs_ino_t inode_number,
- xfs_fsblock_t *firstblock,
- xfs_bmap_free_t *flist,
- xfs_extlen_t total);
-
-static int xfs_dir_lookup(struct xfs_trans *tp,
- struct xfs_inode *dp,
- char *name_string,
- int name_length,
- xfs_ino_t *inode_number);
-
-static int xfs_dir_removename(struct xfs_trans *trans,
- struct xfs_inode *dp,
- char *name_string,
- int name_length,
- xfs_ino_t ino,
- xfs_fsblock_t *firstblock,
- xfs_bmap_free_t *flist,
- xfs_extlen_t total);
-
-static int xfs_dir_getdents(struct xfs_trans *tp,
- struct xfs_inode *dp,
- struct uio *uiop,
- int *eofp);
-
-static int xfs_dir_replace(struct xfs_trans *tp,
- struct xfs_inode *dp,
- char *name_string,
- int name_length,
- xfs_ino_t inode_number,
- xfs_fsblock_t *firstblock,
- xfs_bmap_free_t *flist,
- xfs_extlen_t total);
-
-static int xfs_dir_canenter(struct xfs_trans *tp,
- struct xfs_inode *dp,
- char *name_string,
- int name_length);
-
-static int xfs_dir_shortform_validate_ondisk(xfs_mount_t *mp,
- xfs_dinode_t *dip);
-
-xfs_dirops_t xfsv1_dirops = {
- .xd_mount = xfs_dir_mount,
- .xd_isempty = xfs_dir_isempty,
- .xd_init = xfs_dir_init,
- .xd_createname = xfs_dir_createname,
- .xd_lookup = xfs_dir_lookup,
- .xd_removename = xfs_dir_removename,
- .xd_getdents = xfs_dir_getdents,
- .xd_replace = xfs_dir_replace,
- .xd_canenter = xfs_dir_canenter,
- .xd_shortform_validate_ondisk = xfs_dir_shortform_validate_ondisk,
- .xd_shortform_to_single = xfs_dir_shortform_to_leaf,
-};
-
-/*
- * Internal routines when dirsize == XFS_LBSIZE(mp).
- */
-STATIC int xfs_dir_leaf_lookup(xfs_da_args_t *args);
-STATIC int xfs_dir_leaf_removename(xfs_da_args_t *args, int *number_entries,
- int *total_namebytes);
-STATIC int xfs_dir_leaf_getdents(xfs_trans_t *trans, xfs_inode_t *dp,
- uio_t *uio, int *eofp,
- xfs_dirent_t *dbp,
- xfs_dir_put_t put);
-STATIC int xfs_dir_leaf_replace(xfs_da_args_t *args);
-
-/*
- * Internal routines when dirsize > XFS_LBSIZE(mp).
- */
-STATIC int xfs_dir_node_addname(xfs_da_args_t *args);
-STATIC int xfs_dir_node_lookup(xfs_da_args_t *args);
-STATIC int xfs_dir_node_removename(xfs_da_args_t *args);
-STATIC int xfs_dir_node_getdents(xfs_trans_t *trans, xfs_inode_t *dp,
- uio_t *uio, int *eofp,
- xfs_dirent_t *dbp,
- xfs_dir_put_t put);
-STATIC int xfs_dir_node_replace(xfs_da_args_t *args);
-
-#if defined(XFS_DIR_TRACE)
-ktrace_t *xfs_dir_trace_buf;
-#endif
-
-
-/*========================================================================
- * Overall external interface routines.
- *========================================================================*/
-
-xfs_dahash_t xfs_dir_hash_dot, xfs_dir_hash_dotdot;
-
-/*
- * One-time startup routine called from xfs_init().
- */
-void
-xfs_dir_startup(void)
-{
- xfs_dir_hash_dot = xfs_da_hashname(".", 1);
- xfs_dir_hash_dotdot = xfs_da_hashname("..", 2);
-}
-
-/*
- * Initialize directory-related fields in the mount structure.
- */
-static void
-xfs_dir_mount(xfs_mount_t *mp)
-{
- uint shortcount, leafcount, count;
-
- mp->m_dirversion = 1;
- if (!(mp->m_flags & XFS_MOUNT_ATTR2)) {
- shortcount = (mp->m_attroffset -
- (uint)sizeof(xfs_dir_sf_hdr_t)) /
- (uint)sizeof(xfs_dir_sf_entry_t);
- leafcount = (XFS_LBSIZE(mp) -
- (uint)sizeof(xfs_dir_leaf_hdr_t)) /
- ((uint)sizeof(xfs_dir_leaf_entry_t) +
- (uint)sizeof(xfs_dir_leaf_name_t));
- } else {
- shortcount = (XFS_BMDR_SPACE_CALC(MINABTPTRS) -
- (uint)sizeof(xfs_dir_sf_hdr_t)) /
- (uint)sizeof(xfs_dir_sf_entry_t);
- leafcount = (XFS_LBSIZE(mp) -
- (uint)sizeof(xfs_dir_leaf_hdr_t)) /
- ((uint)sizeof(xfs_dir_leaf_entry_t) +
- (uint)sizeof(xfs_dir_leaf_name_t));
- }
- count = shortcount > leafcount ? shortcount : leafcount;
- mp->m_dircook_elog = xfs_da_log2_roundup(count + 1);
- ASSERT(mp->m_dircook_elog <= mp->m_sb.sb_blocklog);
- mp->m_dir_node_ents = mp->m_attr_node_ents =
- (XFS_LBSIZE(mp) - (uint)sizeof(xfs_da_node_hdr_t)) /
- (uint)sizeof(xfs_da_node_entry_t);
- mp->m_dir_magicpct = (XFS_LBSIZE(mp) * 37) / 100;
- mp->m_dirblksize = mp->m_sb.sb_blocksize;
- mp->m_dirblkfsbs = 1;
-}
-
-/*
- * Return 1 if directory contains only "." and "..".
- */
-static int
-xfs_dir_isempty(xfs_inode_t *dp)
-{
- xfs_dir_sf_hdr_t *hdr;
-
- ASSERT((dp->i_d.di_mode & S_IFMT) == S_IFDIR);
- if (dp->i_d.di_size == 0)
- return(1);
- if (dp->i_d.di_size > XFS_IFORK_DSIZE(dp))
- return(0);
- hdr = (xfs_dir_sf_hdr_t *)dp->i_df.if_u1.if_data;
- return(hdr->count == 0);
-}
-
-/*
- * Initialize a directory with its "." and ".." entries.
- */
-static int
-xfs_dir_init(xfs_trans_t *trans, xfs_inode_t *dir, xfs_inode_t *parent_dir)
-{
- xfs_da_args_t args;
- int error;
-
- memset((char *)&args, 0, sizeof(args));
- args.dp = dir;
- args.trans = trans;
-
- ASSERT((dir->i_d.di_mode & S_IFMT) == S_IFDIR);
- if ((error = xfs_dir_ino_validate(trans->t_mountp, parent_dir->i_ino)))
- return error;
-
- return(xfs_dir_shortform_create(&args, parent_dir->i_ino));
-}
-
-/*
- * Generic handler routine to add a name to a directory.
- * Transitions directory from shortform to Btree as necessary.
- */
-static int /* error */
-xfs_dir_createname(xfs_trans_t *trans, xfs_inode_t *dp, char *name,
- int namelen, xfs_ino_t inum, xfs_fsblock_t *firstblock,
- xfs_bmap_free_t *flist, xfs_extlen_t total)
-{
- xfs_da_args_t args;
- int retval, newsize, done;
-
- ASSERT((dp->i_d.di_mode & S_IFMT) == S_IFDIR);
-
- if ((retval = xfs_dir_ino_validate(trans->t_mountp, inum)))
- return (retval);
-
- XFS_STATS_INC(xs_dir_create);
- /*
- * Fill in the arg structure for this request.
- */
- args.name = name;
- args.namelen = namelen;
- args.hashval = xfs_da_hashname(name, namelen);
- args.inumber = inum;
- args.dp = dp;
- args.firstblock = firstblock;
- args.flist = flist;
- args.total = total;
- args.whichfork = XFS_DATA_FORK;
- args.trans = trans;
- args.justcheck = 0;
- args.addname = args.oknoent = 1;
-
- /*
- * Decide on what work routines to call based on the inode size.
- */
- done = 0;
- if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL) {
- newsize = XFS_DIR_SF_ENTSIZE_BYNAME(args.namelen);
- if ((dp->i_d.di_size + newsize) <= XFS_IFORK_DSIZE(dp)) {
- retval = xfs_dir_shortform_addname(&args);
- done = 1;
- } else {
- if (total == 0)
- return XFS_ERROR(ENOSPC);
- retval = xfs_dir_shortform_to_leaf(&args);
- done = retval != 0;
- }
- }
- if (!done && xfs_bmap_one_block(dp, XFS_DATA_FORK)) {
- retval = xfs_dir_leaf_addname(&args);
- done = retval != ENOSPC;
- if (!done) {
- if (total == 0)
- return XFS_ERROR(ENOSPC);
- retval = xfs_dir_leaf_to_node(&args);
- done = retval != 0;
- }
- }
- if (!done) {
- retval = xfs_dir_node_addname(&args);
- }
- return(retval);
-}
-
-/*
- * Generic handler routine to check if a name can be added to a directory,
- * without adding any blocks to the directory.
- */
-static int /* error */
-xfs_dir_canenter(xfs_trans_t *trans, xfs_inode_t *dp, char *name, int namelen)
-{
- xfs_da_args_t args;
- int retval, newsize;
-
- ASSERT((dp->i_d.di_mode & S_IFMT) == S_IFDIR);
- /*
- * Fill in the arg structure for this request.
- */
- args.name = name;
- args.namelen = namelen;
- args.hashval = xfs_da_hashname(name, namelen);
- args.inumber = 0;
- args.dp = dp;
- args.firstblock = NULL;
- args.flist = NULL;
- args.total = 0;
- args.whichfork = XFS_DATA_FORK;
- args.trans = trans;
- args.justcheck = args.addname = args.oknoent = 1;
-
- /*
- * Decide on what work routines to call based on the inode size.
- */
- if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL) {
- newsize = XFS_DIR_SF_ENTSIZE_BYNAME(args.namelen);
- if ((dp->i_d.di_size + newsize) <= XFS_IFORK_DSIZE(dp))
- retval = 0;
- else
- retval = XFS_ERROR(ENOSPC);
- } else if (xfs_bmap_one_block(dp, XFS_DATA_FORK)) {
- retval = xfs_dir_leaf_addname(&args);
- } else {
- retval = xfs_dir_node_addname(&args);
- }
- return(retval);
-}
-
-/*
- * Generic handler routine to remove a name from a directory.
- * Transitions directory from Btree to shortform as necessary.
- */
-static int /* error */
-xfs_dir_removename(xfs_trans_t *trans, xfs_inode_t *dp, char *name,
- int namelen, xfs_ino_t ino, xfs_fsblock_t *firstblock,
- xfs_bmap_free_t *flist, xfs_extlen_t total)
-{
- xfs_da_args_t args;
- int count, totallen, newsize, retval;
-
- ASSERT((dp->i_d.di_mode & S_IFMT) == S_IFDIR);
- XFS_STATS_INC(xs_dir_remove);
- /*
- * Fill in the arg structure for this request.
- */
- args.name = name;
- args.namelen = namelen;
- args.hashval = xfs_da_hashname(name, namelen);
- args.inumber = ino;
- args.dp = dp;
- args.firstblock = firstblock;
- args.flist = flist;
- args.total = total;
- args.whichfork = XFS_DATA_FORK;
- args.trans = trans;
- args.justcheck = args.addname = args.oknoent = 0;
-
- /*
- * Decide on what work routines to call based on the inode size.
- */
- if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL) {
- retval = xfs_dir_shortform_removename(&args);
- } else if (xfs_bmap_one_block(dp, XFS_DATA_FORK)) {
- retval = xfs_dir_leaf_removename(&args, &count, &totallen);
- if (retval == 0) {
- newsize = XFS_DIR_SF_ALLFIT(count, totallen);
- if (newsize <= XFS_IFORK_DSIZE(dp)) {
- retval = xfs_dir_leaf_to_shortform(&args);
- }
- }
- } else {
- retval = xfs_dir_node_removename(&args);
- }
- return(retval);
-}
-
-static int /* error */
-xfs_dir_lookup(xfs_trans_t *trans, xfs_inode_t *dp, char *name, int namelen,
- xfs_ino_t *inum)
-{
- xfs_da_args_t args;
- int retval;
-
- ASSERT((dp->i_d.di_mode & S_IFMT) == S_IFDIR);
-
- XFS_STATS_INC(xs_dir_lookup);
- /*
- * Fill in the arg structure for this request.
- */
- args.name = name;
- args.namelen = namelen;
- args.hashval = xfs_da_hashname(name, namelen);
- args.inumber = 0;
- args.dp = dp;
- args.firstblock = NULL;
- args.flist = NULL;
- args.total = 0;
- args.whichfork = XFS_DATA_FORK;
- args.trans = trans;
- args.justcheck = args.addname = 0;
- args.oknoent = 1;
-
- /*
- * Decide on what work routines to call based on the inode size.
- */
- if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL) {
- retval = xfs_dir_shortform_lookup(&args);
- } else if (xfs_bmap_one_block(dp, XFS_DATA_FORK)) {
- retval = xfs_dir_leaf_lookup(&args);
- } else {
- retval = xfs_dir_node_lookup(&args);
- }
- if (retval == EEXIST)
- retval = 0;
- *inum = args.inumber;
- return(retval);
-}
-
-/*
- * Implement readdir.
- */
-static int /* error */
-xfs_dir_getdents(xfs_trans_t *trans, xfs_inode_t *dp, uio_t *uio, int *eofp)
-{
- xfs_dirent_t *dbp;
- int alignment, retval;
- xfs_dir_put_t put;
-
- XFS_STATS_INC(xs_dir_getdents);
- ASSERT((dp->i_d.di_mode & S_IFMT) == S_IFDIR);
-
- /*
- * If our caller has given us a single contiguous memory buffer,
- * just work directly within that buffer. If it's in user memory,
- * lock it down first.
- */
- alignment = sizeof(xfs_off_t) - 1;
- if ((uio->uio_iovcnt == 1) &&
- (((__psint_t)uio->uio_iov[0].iov_base & alignment) == 0) &&
- ((uio->uio_iov[0].iov_len & alignment) == 0)) {
- dbp = NULL;
- put = xfs_dir_put_dirent64_direct;
- } else {
- dbp = kmem_alloc(sizeof(*dbp) + MAXNAMELEN, KM_SLEEP);
- put = xfs_dir_put_dirent64_uio;
- }
-
- /*
- * Decide on what work routines to call based on the inode size.
- */
- *eofp = 0;
-
- if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL) {
- retval = xfs_dir_shortform_getdents(dp, uio, eofp, dbp, put);
- } else if (xfs_bmap_one_block(dp, XFS_DATA_FORK)) {
- retval = xfs_dir_leaf_getdents(trans, dp, uio, eofp, dbp, put);
- } else {
- retval = xfs_dir_node_getdents(trans, dp, uio, eofp, dbp, put);
- }
- if (dbp != NULL)
- kmem_free(dbp, sizeof(*dbp) + MAXNAMELEN);
-
- return(retval);
-}
-
-static int /* error */
-xfs_dir_replace(xfs_trans_t *trans, xfs_inode_t *dp, char *name, int namelen,
- xfs_ino_t inum, xfs_fsblock_t *firstblock,
- xfs_bmap_free_t *flist, xfs_extlen_t total)
-{
- xfs_da_args_t args;
- int retval;
-
- ASSERT((dp->i_d.di_mode & S_IFMT) == S_IFDIR);
-
- if ((retval = xfs_dir_ino_validate(trans->t_mountp, inum)))
- return retval;
-
- /*
- * Fill in the arg structure for this request.
- */
- args.name = name;
- args.namelen = namelen;
- args.hashval = xfs_da_hashname(name, namelen);
- args.inumber = inum;
- args.dp = dp;
- args.firstblock = firstblock;
- args.flist = flist;
- args.total = total;
- args.whichfork = XFS_DATA_FORK;
- args.trans = trans;
- args.justcheck = args.addname = args.oknoent = 0;
-
- /*
- * Decide on what work routines to call based on the inode size.
- */
- if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL) {
- retval = xfs_dir_shortform_replace(&args);
- } else if (xfs_bmap_one_block(dp, XFS_DATA_FORK)) {
- retval = xfs_dir_leaf_replace(&args);
- } else {
- retval = xfs_dir_node_replace(&args);
- }
-
- return(retval);
-}
-
-static int
-xfs_dir_shortform_validate_ondisk(xfs_mount_t *mp, xfs_dinode_t *dp)
-{
- xfs_ino_t ino;
- int namelen_sum;
- int count;
- xfs_dir_shortform_t *sf;
- xfs_dir_sf_entry_t *sfe;
- int i;
-
-
-
- if ((INT_GET(dp->di_core.di_mode, ARCH_CONVERT) & S_IFMT) != S_IFDIR) {
- return 0;
- }
- if (INT_GET(dp->di_core.di_format, ARCH_CONVERT) != XFS_DINODE_FMT_LOCAL) {
- return 0;
- }
- if (INT_GET(dp->di_core.di_size, ARCH_CONVERT) < sizeof(sf->hdr)) {
- xfs_fs_cmn_err(CE_WARN, mp, "Invalid shortform size: dp 0x%p",
- dp);
- return 1;
- }
- sf = (xfs_dir_shortform_t *)(&dp->di_u.di_dirsf);
- ino = XFS_GET_DIR_INO8(sf->hdr.parent);
- if (xfs_dir_ino_validate(mp, ino))
- return 1;
-
- count = sf->hdr.count;
- if ((count < 0) || ((count * 10) > XFS_LITINO(mp))) {
- xfs_fs_cmn_err(CE_WARN, mp,
- "Invalid shortform count: dp 0x%p", dp);
- return(1);
- }
-
- if (count == 0) {
- return 0;
- }
-
- namelen_sum = 0;
- sfe = &sf->list[0];
- for (i = sf->hdr.count - 1; i >= 0; i--) {
- ino = XFS_GET_DIR_INO8(sfe->inumber);
- xfs_dir_ino_validate(mp, ino);
- if (sfe->namelen >= XFS_LITINO(mp)) {
- xfs_fs_cmn_err(CE_WARN, mp,
- "Invalid shortform namelen: dp 0x%p", dp);
- return 1;
- }
- namelen_sum += sfe->namelen;
- sfe = XFS_DIR_SF_NEXTENTRY(sfe);
- }
- if (namelen_sum >= XFS_LITINO(mp)) {
- xfs_fs_cmn_err(CE_WARN, mp,
- "Invalid shortform namelen: dp 0x%p", dp);
- return 1;
- }
-
- return 0;
-}
-
-/*========================================================================
- * External routines when dirsize == XFS_LBSIZE(dp->i_mount).
- *========================================================================*/
-
-/*
- * Add a name to the leaf directory structure
- * This is the external routine.
- */
-int
-xfs_dir_leaf_addname(xfs_da_args_t *args)
-{
- int index, retval;
- xfs_dabuf_t *bp;
-
- retval = xfs_da_read_buf(args->trans, args->dp, 0, -1, &bp,
- XFS_DATA_FORK);
- if (retval)
- return(retval);
- ASSERT(bp != NULL);
-
- retval = xfs_dir_leaf_lookup_int(bp, args, &index);
- if (retval == ENOENT)
- retval = xfs_dir_leaf_add(bp, args, index);
- xfs_da_buf_done(bp);
- return(retval);
-}
-
-/*
- * Remove a name from the leaf directory structure
- * This is the external routine.
- */
-STATIC int
-xfs_dir_leaf_removename(xfs_da_args_t *args, int *count, int *totallen)
-{
- xfs_dir_leafblock_t *leaf;
- int index, retval;
- xfs_dabuf_t *bp;
-
- retval = xfs_da_read_buf(args->trans, args->dp, 0, -1, &bp,
- XFS_DATA_FORK);
- if (retval)
- return(retval);
- ASSERT(bp != NULL);
- leaf = bp->data;
- ASSERT(be16_to_cpu(leaf->hdr.info.magic) == XFS_DIR_LEAF_MAGIC);
- retval = xfs_dir_leaf_lookup_int(bp, args, &index);
- if (retval == EEXIST) {
- (void)xfs_dir_leaf_remove(args->trans, bp, index);
- *count = INT_GET(leaf->hdr.count, ARCH_CONVERT);
- *totallen = INT_GET(leaf->hdr.namebytes, ARCH_CONVERT);
- retval = 0;
- }
- xfs_da_buf_done(bp);
- return(retval);
-}
-
-/*
- * Look up a name in a leaf directory structure.
- * This is the external routine.
- */
-STATIC int
-xfs_dir_leaf_lookup(xfs_da_args_t *args)
-{
- int index, retval;
- xfs_dabuf_t *bp;
-
- retval = xfs_da_read_buf(args->trans, args->dp, 0, -1, &bp,
- XFS_DATA_FORK);
- if (retval)
- return(retval);
- ASSERT(bp != NULL);
- retval = xfs_dir_leaf_lookup_int(bp, args, &index);
- xfs_da_brelse(args->trans, bp);
- return(retval);
-}
-
-/*
- * Copy out directory entries for getdents(), for leaf directories.
- */
-STATIC int
-xfs_dir_leaf_getdents(xfs_trans_t *trans, xfs_inode_t *dp, uio_t *uio,
- int *eofp, xfs_dirent_t *dbp, xfs_dir_put_t put)
-{
- xfs_dabuf_t *bp;
- int retval, eob;
-
- retval = xfs_da_read_buf(dp->i_transp, dp, 0, -1, &bp, XFS_DATA_FORK);
- if (retval)
- return(retval);
- ASSERT(bp != NULL);
- retval = xfs_dir_leaf_getdents_int(bp, dp, 0, uio, &eob, dbp, put, -1);
- xfs_da_brelse(trans, bp);
- *eofp = (eob == 0);
- return(retval);
-}
-
-/*
- * Look up a name in a leaf directory structure, replace the inode number.
- * This is the external routine.
- */
-STATIC int
-xfs_dir_leaf_replace(xfs_da_args_t *args)
-{
- int index, retval;
- xfs_dabuf_t *bp;
- xfs_ino_t inum;
- xfs_dir_leafblock_t *leaf;
- xfs_dir_leaf_entry_t *entry;
- xfs_dir_leaf_name_t *namest;
-
- inum = args->inumber;
- retval = xfs_da_read_buf(args->trans, args->dp, 0, -1, &bp,
- XFS_DATA_FORK);
- if (retval)
- return(retval);
- ASSERT(bp != NULL);
- retval = xfs_dir_leaf_lookup_int(bp, args, &index);
- if (retval == EEXIST) {
- leaf = bp->data;
- entry = &leaf->entries[index];
- namest = XFS_DIR_LEAF_NAMESTRUCT(leaf, INT_GET(entry->nameidx, ARCH_CONVERT));
- /* XXX - replace assert? */
- XFS_DIR_SF_PUT_DIRINO(&inum, &namest->inumber);
- xfs_da_log_buf(args->trans, bp,
- XFS_DA_LOGRANGE(leaf, namest, sizeof(namest->inumber)));
- xfs_da_buf_done(bp);
- retval = 0;
- } else
- xfs_da_brelse(args->trans, bp);
- return(retval);
-}
-
-
-/*========================================================================
- * External routines when dirsize > XFS_LBSIZE(mp).
- *========================================================================*/
-
-/*
- * Add a name to a Btree-format directory.
- *
- * This will involve walking down the Btree, and may involve splitting
- * leaf nodes and even splitting intermediate nodes up to and including
- * the root node (a special case of an intermediate node).
- */
-STATIC int
-xfs_dir_node_addname(xfs_da_args_t *args)
-{
- xfs_da_state_t *state;
- xfs_da_state_blk_t *blk;
- int retval, error;
-
- /*
- * Fill in bucket of arguments/results/context to carry around.
- */
- state = xfs_da_state_alloc();
- state->args = args;
- state->mp = args->dp->i_mount;
- state->blocksize = state->mp->m_sb.sb_blocksize;
- state->node_ents = state->mp->m_dir_node_ents;
-
- /*
- * Search to see if name already exists, and get back a pointer
- * to where it should go.
- */
- error = xfs_da_node_lookup_int(state, &retval);
- if (error)
- retval = error;
- if (retval != ENOENT)
- goto error;
- blk = &state->path.blk[ state->path.active-1 ];
- ASSERT(blk->magic == XFS_DIR_LEAF_MAGIC);
- retval = xfs_dir_leaf_add(blk->bp, args, blk->index);
- if (retval == 0) {
- /*
- * Addition succeeded, update Btree hashvals.
- */
- if (!args->justcheck)
- xfs_da_fixhashpath(state, &state->path);
- } else {
- /*
- * Addition failed, split as many Btree elements as required.
- */
- if (args->total == 0) {
- ASSERT(retval == ENOSPC);
- goto error;
- }
- retval = xfs_da_split(state);
- }
-error:
- xfs_da_state_free(state);
-
- return(retval);
-}
-
-/*
- * Remove a name from a B-tree directory.
- *
- * This will involve walking down the Btree, and may involve joining
- * leaf nodes and even joining intermediate nodes up to and including
- * the root node (a special case of an intermediate node).
- */
-STATIC int
-xfs_dir_node_removename(xfs_da_args_t *args)
-{
- xfs_da_state_t *state;
- xfs_da_state_blk_t *blk;
- int retval, error;
-
- state = xfs_da_state_alloc();
- state->args = args;
- state->mp = args->dp->i_mount;
- state->blocksize = state->mp->m_sb.sb_blocksize;
- state->node_ents = state->mp->m_dir_node_ents;
-
- /*
- * Search to see if name exists, and get back a pointer to it.
- */
- error = xfs_da_node_lookup_int(state, &retval);
- if (error)
- retval = error;
- if (retval != EEXIST) {
- xfs_da_state_free(state);
- return(retval);
- }
-
- /*
- * Remove the name and update the hashvals in the tree.
- */
- blk = &state->path.blk[ state->path.active-1 ];
- ASSERT(blk->magic == XFS_DIR_LEAF_MAGIC);
- retval = xfs_dir_leaf_remove(args->trans, blk->bp, blk->index);
- xfs_da_fixhashpath(state, &state->path);
-
- /*
- * Check to see if the tree needs to be collapsed.
- */
- error = 0;
- if (retval) {
- error = xfs_da_join(state);
- }
-
- xfs_da_state_free(state);
- if (error)
- return(error);
- return(0);
-}
-
-/*
- * Look up a filename in a int directory.
- * Use an internal routine to actually do all the work.
- */
-STATIC int
-xfs_dir_node_lookup(xfs_da_args_t *args)
-{
- xfs_da_state_t *state;
- int retval, error, i;
-
- state = xfs_da_state_alloc();
- state->args = args;
- state->mp = args->dp->i_mount;
- state->blocksize = state->mp->m_sb.sb_blocksize;
- state->node_ents = state->mp->m_dir_node_ents;
-
- /*
- * Search to see if name exists,
- * and get back a pointer to it.
- */
- error = xfs_da_node_lookup_int(state, &retval);
- if (error) {
- retval = error;
- }
-
- /*
- * If not in a transaction, we have to release all the buffers.
- */
- for (i = 0; i < state->path.active; i++) {
- xfs_da_brelse(args->trans, state->path.blk[i].bp);
- state->path.blk[i].bp = NULL;
- }
-
- xfs_da_state_free(state);
- return(retval);
-}
-
-STATIC int
-xfs_dir_node_getdents(xfs_trans_t *trans, xfs_inode_t *dp, uio_t *uio,
- int *eofp, xfs_dirent_t *dbp, xfs_dir_put_t put)
-{
- xfs_da_intnode_t *node;
- xfs_da_node_entry_t *btree;
- xfs_dir_leafblock_t *leaf = NULL;
- xfs_dablk_t bno, nextbno;
- xfs_dahash_t cookhash;
- xfs_mount_t *mp;
- int error, eob, i;
- xfs_dabuf_t *bp;
- xfs_daddr_t nextda;
-
- /*
- * Pick up our context.
- */
- mp = dp->i_mount;
- bp = NULL;
- bno = XFS_DA_COOKIE_BNO(mp, uio->uio_offset);
- cookhash = XFS_DA_COOKIE_HASH(mp, uio->uio_offset);
-
- xfs_dir_trace_g_du("node: start", dp, uio);
-
- /*
- * Re-find our place, even if we're confused about what our place is.
- *
- * First we check the block number from the magic cookie, it is a
- * cache of where we ended last time. If we find a leaf block, and
- * the starting hashval in that block is less than our desired
- * hashval, then we run with it.
- */
- if (bno > 0) {
- error = xfs_da_read_buf(trans, dp, bno, -2, &bp, XFS_DATA_FORK);
- if ((error != 0) && (error != EFSCORRUPTED))
- return(error);
- if (bp)
- leaf = bp->data;
- if (bp && be16_to_cpu(leaf->hdr.info.magic) != XFS_DIR_LEAF_MAGIC) {
- xfs_dir_trace_g_dub("node: block not a leaf",
- dp, uio, bno);
- xfs_da_brelse(trans, bp);
- bp = NULL;
- }
- if (bp && INT_GET(leaf->entries[0].hashval, ARCH_CONVERT) > cookhash) {
- xfs_dir_trace_g_dub("node: leaf hash too large",
- dp, uio, bno);
- xfs_da_brelse(trans, bp);
- bp = NULL;
- }
- if (bp &&
- cookhash > INT_GET(leaf->entries[INT_GET(leaf->hdr.count, ARCH_CONVERT) - 1].hashval, ARCH_CONVERT)) {
- xfs_dir_trace_g_dub("node: leaf hash too small",
- dp, uio, bno);
- xfs_da_brelse(trans, bp);
- bp = NULL;
- }
- }
-
- /*
- * If we did not find a leaf block from the blockno in the cookie,
- * or we there was no blockno in the cookie (eg: first time thru),
- * the we start at the top of the Btree and re-find our hashval.
- */
- if (bp == NULL) {
- xfs_dir_trace_g_du("node: start at root" , dp, uio);
- bno = 0;
- for (;;) {
- error = xfs_da_read_buf(trans, dp, bno, -1, &bp,
- XFS_DATA_FORK);
- if (error)
- return(error);
- if (bp == NULL)
- return(XFS_ERROR(EFSCORRUPTED));
- node = bp->data;
- if (be16_to_cpu(node->hdr.info.magic) != XFS_DA_NODE_MAGIC)
- break;
- btree = &node->btree[0];
- xfs_dir_trace_g_dun("node: node detail", dp, uio, node);
- for (i = 0; i < be16_to_cpu(node->hdr.count); btree++, i++) {
- if (be32_to_cpu(btree->hashval) >= cookhash) {
- bno = be32_to_cpu(btree->before);
- break;
- }
- }
- if (i == be16_to_cpu(node->hdr.count)) {
- xfs_da_brelse(trans, bp);
- xfs_dir_trace_g_du("node: hash beyond EOF",
- dp, uio);
- uio->uio_offset = XFS_DA_MAKE_COOKIE(mp, 0, 0,
- XFS_DA_MAXHASH);
- *eofp = 1;
- return(0);
- }
- xfs_dir_trace_g_dub("node: going to block",
- dp, uio, bno);
- xfs_da_brelse(trans, bp);
- }
- }
- ASSERT(cookhash != XFS_DA_MAXHASH);
-
- /*
- * We've dropped down to the (first) leaf block that contains the
- * hashval we are interested in. Continue rolling upward thru the
- * leaf blocks until we fill up our buffer.
- */
- for (;;) {
- leaf = bp->data;
- if (unlikely(be16_to_cpu(leaf->hdr.info.magic) != XFS_DIR_LEAF_MAGIC)) {
- xfs_dir_trace_g_dul("node: not a leaf", dp, uio, leaf);
- xfs_da_brelse(trans, bp);
- XFS_CORRUPTION_ERROR("xfs_dir_node_getdents(1)",
- XFS_ERRLEVEL_LOW, mp, leaf);
- return XFS_ERROR(EFSCORRUPTED);
- }
- xfs_dir_trace_g_dul("node: leaf detail", dp, uio, leaf);
- if ((nextbno = be32_to_cpu(leaf->hdr.info.forw))) {
- nextda = xfs_da_reada_buf(trans, dp, nextbno,
- XFS_DATA_FORK);
- } else
- nextda = -1;
- error = xfs_dir_leaf_getdents_int(bp, dp, bno, uio, &eob, dbp,
- put, nextda);
- xfs_da_brelse(trans, bp);
- bno = nextbno;
- if (eob) {
- xfs_dir_trace_g_dub("node: E-O-B", dp, uio, bno);
- *eofp = 0;
- return(error);
- }
- if (bno == 0)
- break;
- error = xfs_da_read_buf(trans, dp, bno, nextda, &bp,
- XFS_DATA_FORK);
- if (error)
- return(error);
- if (unlikely(bp == NULL)) {
- XFS_ERROR_REPORT("xfs_dir_node_getdents(2)",
- XFS_ERRLEVEL_LOW, mp);
- return(XFS_ERROR(EFSCORRUPTED));
- }
- }
- *eofp = 1;
- xfs_dir_trace_g_du("node: E-O-F", dp, uio);
- return(0);
-}
-
-/*
- * Look up a filename in an int directory, replace the inode number.
- * Use an internal routine to actually do the lookup.
- */
-STATIC int
-xfs_dir_node_replace(xfs_da_args_t *args)
-{
- xfs_da_state_t *state;
- xfs_da_state_blk_t *blk;
- xfs_dir_leafblock_t *leaf;
- xfs_dir_leaf_entry_t *entry;
- xfs_dir_leaf_name_t *namest;
- xfs_ino_t inum;
- int retval, error, i;
- xfs_dabuf_t *bp;
-
- state = xfs_da_state_alloc();
- state->args = args;
- state->mp = args->dp->i_mount;
- state->blocksize = state->mp->m_sb.sb_blocksize;
- state->node_ents = state->mp->m_dir_node_ents;
- inum = args->inumber;
-
- /*
- * Search to see if name exists,
- * and get back a pointer to it.
- */
- error = xfs_da_node_lookup_int(state, &retval);
- if (error) {
- retval = error;
- }
-
- if (retval == EEXIST) {
- blk = &state->path.blk[state->path.active - 1];
- ASSERT(blk->magic == XFS_DIR_LEAF_MAGIC);
- bp = blk->bp;
- leaf = bp->data;
- entry = &leaf->entries[blk->index];
- namest = XFS_DIR_LEAF_NAMESTRUCT(leaf, INT_GET(entry->nameidx, ARCH_CONVERT));
- /* XXX - replace assert ? */
- XFS_DIR_SF_PUT_DIRINO(&inum, &namest->inumber);
- xfs_da_log_buf(args->trans, bp,
- XFS_DA_LOGRANGE(leaf, namest, sizeof(namest->inumber)));
- xfs_da_buf_done(bp);
- blk->bp = NULL;
- retval = 0;
- } else {
- i = state->path.active - 1;
- xfs_da_brelse(args->trans, state->path.blk[i].bp);
- state->path.blk[i].bp = NULL;
- }
- for (i = 0; i < state->path.active - 1; i++) {
- xfs_da_brelse(args->trans, state->path.blk[i].bp);
- state->path.blk[i].bp = NULL;
- }
-
- xfs_da_state_free(state);
- return(retval);
-}
-
-#if defined(XFS_DIR_TRACE)
-/*
- * Add a trace buffer entry for an inode and a uio.
- */
-void
-xfs_dir_trace_g_du(char *where, xfs_inode_t *dp, uio_t *uio)
-{
- xfs_dir_trace_enter(XFS_DIR_KTRACE_G_DU, where,
- (void *)dp, (void *)dp->i_mount,
- (void *)((unsigned long)(uio->uio_offset >> 32)),
- (void *)((unsigned long)(uio->uio_offset & 0xFFFFFFFF)),
- (void *)(unsigned long)uio->uio_resid,
- NULL, NULL, NULL, NULL, NULL, NULL, NULL);
-}
-
-/*
- * Add a trace buffer entry for an inode and a uio.
- */
-void
-xfs_dir_trace_g_dub(char *where, xfs_inode_t *dp, uio_t *uio, xfs_dablk_t bno)
-{
- xfs_dir_trace_enter(XFS_DIR_KTRACE_G_DUB, where,
- (void *)dp, (void *)dp->i_mount,
- (void *)((unsigned long)(uio->uio_offset >> 32)),
- (void *)((unsigned long)(uio->uio_offset & 0xFFFFFFFF)),
- (void *)(unsigned long)uio->uio_resid,
- (void *)(unsigned long)bno,
- NULL, NULL, NULL, NULL, NULL, NULL);
-}
-
-/*
- * Add a trace buffer entry for an inode and a uio.
- */
-void
-xfs_dir_trace_g_dun(char *where, xfs_inode_t *dp, uio_t *uio,
- xfs_da_intnode_t *node)
-{
- int last = be16_to_cpu(node->hdr.count) - 1;
-
- xfs_dir_trace_enter(XFS_DIR_KTRACE_G_DUN, where,
- (void *)dp, (void *)dp->i_mount,
- (void *)((unsigned long)(uio->uio_offset >> 32)),
- (void *)((unsigned long)(uio->uio_offset & 0xFFFFFFFF)),
- (void *)(unsigned long)uio->uio_resid,
- (void *)(unsigned long)be32_to_cpu(node->hdr.info.forw),
- (void *)(unsigned long)
- be16_to_cpu(node->hdr.count),
- (void *)(unsigned long)
- be32_to_cpu(node->btree[0].hashval),
- (void *)(unsigned long)
- be32_to_cpu(node->btree[last].hashval),
- NULL, NULL, NULL);
-}
-
-/*
- * Add a trace buffer entry for an inode and a uio.
- */
-void
-xfs_dir_trace_g_dul(char *where, xfs_inode_t *dp, uio_t *uio,
- xfs_dir_leafblock_t *leaf)
-{
- int last = INT_GET(leaf->hdr.count, ARCH_CONVERT) - 1;
-
- xfs_dir_trace_enter(XFS_DIR_KTRACE_G_DUL, where,
- (void *)dp, (void *)dp->i_mount,
- (void *)((unsigned long)(uio->uio_offset >> 32)),
- (void *)((unsigned long)(uio->uio_offset & 0xFFFFFFFF)),
- (void *)(unsigned long)uio->uio_resid,
- (void *)(unsigned long)be32_to_cpu(leaf->hdr.info.forw),
- (void *)(unsigned long)
- INT_GET(leaf->hdr.count, ARCH_CONVERT),
- (void *)(unsigned long)
- INT_GET(leaf->entries[0].hashval, ARCH_CONVERT),
- (void *)(unsigned long)
- INT_GET(leaf->entries[last].hashval, ARCH_CONVERT),
- NULL, NULL, NULL);
-}
-
-/*
- * Add a trace buffer entry for an inode and a uio.
- */
-void
-xfs_dir_trace_g_due(char *where, xfs_inode_t *dp, uio_t *uio,
- xfs_dir_leaf_entry_t *entry)
-{
- xfs_dir_trace_enter(XFS_DIR_KTRACE_G_DUE, where,
- (void *)dp, (void *)dp->i_mount,
- (void *)((unsigned long)(uio->uio_offset >> 32)),
- (void *)((unsigned long)(uio->uio_offset & 0xFFFFFFFF)),
- (void *)(unsigned long)uio->uio_resid,
- (void *)(unsigned long)
- INT_GET(entry->hashval, ARCH_CONVERT),
- NULL, NULL, NULL, NULL, NULL, NULL);
-}
-
-/*
- * Add a trace buffer entry for an inode and a uio.
- */
-void
-xfs_dir_trace_g_duc(char *where, xfs_inode_t *dp, uio_t *uio, xfs_off_t cookie)
-{
- xfs_dir_trace_enter(XFS_DIR_KTRACE_G_DUC, where,
- (void *)dp, (void *)dp->i_mount,
- (void *)((unsigned long)(uio->uio_offset >> 32)),
- (void *)((unsigned long)(uio->uio_offset & 0xFFFFFFFF)),
- (void *)(unsigned long)uio->uio_resid,
- (void *)((unsigned long)(cookie >> 32)),
- (void *)((unsigned long)(cookie & 0xFFFFFFFF)),
- NULL, NULL, NULL, NULL, NULL);
-}
-
-/*
- * Add a trace buffer entry for the arguments given to the routine,
- * generic form.
- */
-void
-xfs_dir_trace_enter(int type, char *where,
- void * a0, void * a1,
- void * a2, void * a3,
- void * a4, void * a5,
- void * a6, void * a7,
- void * a8, void * a9,
- void * a10, void * a11)
-{
- ASSERT(xfs_dir_trace_buf);
- ktrace_enter(xfs_dir_trace_buf, (void *)(unsigned long)type,
- (void *)where,
- (void *)a0, (void *)a1, (void *)a2,
- (void *)a3, (void *)a4, (void *)a5,
- (void *)a6, (void *)a7, (void *)a8,
- (void *)a9, (void *)a10, (void *)a11,
- NULL, NULL);
-}
-#endif /* XFS_DIR_TRACE */
+++ /dev/null
-/*
- * Copyright (c) 2000,2005 Silicon Graphics, Inc.
- * 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.
- *
- * This program is distributed in the hope that it would 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 the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-#ifndef __XFS_DIR_H__
-#define __XFS_DIR_H__
-
-/*
- * Large directories are structured around Btrees where all the data
- * elements are in the leaf nodes. Filenames are hashed into an int,
- * then that int is used as the index into the Btree. Since the hashval
- * of a filename may not be unique, we may have duplicate keys. The
- * internal links in the Btree are logical block offsets into the file.
- *
- * Small directories use a different format and are packed as tightly
- * as possible so as to fit into the literal area of the inode.
- */
-
-/*========================================================================
- * Function prototypes for the kernel.
- *========================================================================*/
-
-struct uio;
-struct xfs_bmap_free;
-struct xfs_da_args;
-struct xfs_dinode;
-struct xfs_inode;
-struct xfs_mount;
-struct xfs_trans;
-
-/*
- * Directory function types.
- * Put in structures (xfs_dirops_t) for v1 and v2 directories.
- */
-typedef void (*xfs_dir_mount_t)(struct xfs_mount *mp);
-typedef int (*xfs_dir_isempty_t)(struct xfs_inode *dp);
-typedef int (*xfs_dir_init_t)(struct xfs_trans *tp,
- struct xfs_inode *dp,
- struct xfs_inode *pdp);
-typedef int (*xfs_dir_createname_t)(struct xfs_trans *tp,
- struct xfs_inode *dp,
- char *name,
- int namelen,
- xfs_ino_t inum,
- xfs_fsblock_t *first,
- struct xfs_bmap_free *flist,
- xfs_extlen_t total);
-typedef int (*xfs_dir_lookup_t)(struct xfs_trans *tp,
- struct xfs_inode *dp,
- char *name,
- int namelen,
- xfs_ino_t *inum);
-typedef int (*xfs_dir_removename_t)(struct xfs_trans *tp,
- struct xfs_inode *dp,
- char *name,
- int namelen,
- xfs_ino_t ino,
- xfs_fsblock_t *first,
- struct xfs_bmap_free *flist,
- xfs_extlen_t total);
-typedef int (*xfs_dir_getdents_t)(struct xfs_trans *tp,
- struct xfs_inode *dp,
- struct uio *uio,
- int *eofp);
-typedef int (*xfs_dir_replace_t)(struct xfs_trans *tp,
- struct xfs_inode *dp,
- char *name,
- int namelen,
- xfs_ino_t inum,
- xfs_fsblock_t *first,
- struct xfs_bmap_free *flist,
- xfs_extlen_t total);
-typedef int (*xfs_dir_canenter_t)(struct xfs_trans *tp,
- struct xfs_inode *dp,
- char *name,
- int namelen);
-typedef int (*xfs_dir_shortform_validate_ondisk_t)(struct xfs_mount *mp,
- struct xfs_dinode *dip);
-typedef int (*xfs_dir_shortform_to_single_t)(struct xfs_da_args *args);
-
-typedef struct xfs_dirops {
- xfs_dir_mount_t xd_mount;
- xfs_dir_isempty_t xd_isempty;
- xfs_dir_init_t xd_init;
- xfs_dir_createname_t xd_createname;
- xfs_dir_lookup_t xd_lookup;
- xfs_dir_removename_t xd_removename;
- xfs_dir_getdents_t xd_getdents;
- xfs_dir_replace_t xd_replace;
- xfs_dir_canenter_t xd_canenter;
- xfs_dir_shortform_validate_ondisk_t xd_shortform_validate_ondisk;
- xfs_dir_shortform_to_single_t xd_shortform_to_single;
-} xfs_dirops_t;
-
-/*
- * Overall external interface routines.
- */
-void xfs_dir_startup(void); /* called exactly once */
-
-#define XFS_DIR_MOUNT(mp) \
- ((mp)->m_dirops.xd_mount(mp))
-#define XFS_DIR_ISEMPTY(mp,dp) \
- ((mp)->m_dirops.xd_isempty(dp))
-#define XFS_DIR_INIT(mp,tp,dp,pdp) \
- ((mp)->m_dirops.xd_init(tp,dp,pdp))
-#define XFS_DIR_CREATENAME(mp,tp,dp,name,namelen,inum,first,flist,total) \
- ((mp)->m_dirops.xd_createname(tp,dp,name,namelen,inum,first,flist,\
- total))
-#define XFS_DIR_LOOKUP(mp,tp,dp,name,namelen,inum) \
- ((mp)->m_dirops.xd_lookup(tp,dp,name,namelen,inum))
-#define XFS_DIR_REMOVENAME(mp,tp,dp,name,namelen,ino,first,flist,total) \
- ((mp)->m_dirops.xd_removename(tp,dp,name,namelen,ino,first,flist,total))
-#define XFS_DIR_GETDENTS(mp,tp,dp,uio,eofp) \
- ((mp)->m_dirops.xd_getdents(tp,dp,uio,eofp))
-#define XFS_DIR_REPLACE(mp,tp,dp,name,namelen,inum,first,flist,total) \
- ((mp)->m_dirops.xd_replace(tp,dp,name,namelen,inum,first,flist,total))
-#define XFS_DIR_CANENTER(mp,tp,dp,name,namelen) \
- ((mp)->m_dirops.xd_canenter(tp,dp,name,namelen))
-#define XFS_DIR_SHORTFORM_VALIDATE_ONDISK(mp,dip) \
- ((mp)->m_dirops.xd_shortform_validate_ondisk(mp,dip))
-#define XFS_DIR_SHORTFORM_TO_SINGLE(mp,args) \
- ((mp)->m_dirops.xd_shortform_to_single(args))
-
-#define XFS_DIR_IS_V1(mp) ((mp)->m_dirversion == 1)
-#define XFS_DIR_IS_V2(mp) ((mp)->m_dirversion == 2)
-extern xfs_dirops_t xfsv1_dirops;
-extern xfs_dirops_t xfsv2_dirops;
-
-#endif /* __XFS_DIR_H__ */
#include "xfs_trans.h"
#include "xfs_sb.h"
#include "xfs_ag.h"
-#include "xfs_dir.h"
#include "xfs_dir2.h"
#include "xfs_dmapi.h"
#include "xfs_mount.h"
#include "xfs_da_btree.h"
#include "xfs_bmap_btree.h"
#include "xfs_alloc_btree.h"
-#include "xfs_dir_sf.h"
#include "xfs_dir2_sf.h"
#include "xfs_attr_sf.h"
#include "xfs_dinode.h"
#include "xfs_inode.h"
#include "xfs_inode_item.h"
#include "xfs_bmap.h"
-#include "xfs_dir_leaf.h"
#include "xfs_dir2_data.h"
#include "xfs_dir2_leaf.h"
#include "xfs_dir2_block.h"
#include "xfs_dir2_trace.h"
#include "xfs_error.h"
-/*
- * Declarations for interface routines.
- */
-static void xfs_dir2_mount(xfs_mount_t *mp);
-static int xfs_dir2_isempty(xfs_inode_t *dp);
-static int xfs_dir2_init(xfs_trans_t *tp, xfs_inode_t *dp,
- xfs_inode_t *pdp);
-static int xfs_dir2_createname(xfs_trans_t *tp, xfs_inode_t *dp,
- char *name, int namelen, xfs_ino_t inum,
- xfs_fsblock_t *first,
- xfs_bmap_free_t *flist, xfs_extlen_t total);
-static int xfs_dir2_lookup(xfs_trans_t *tp, xfs_inode_t *dp, char *name,
- int namelen, xfs_ino_t *inum);
-static int xfs_dir2_removename(xfs_trans_t *tp, xfs_inode_t *dp,
- char *name, int namelen, xfs_ino_t ino,
- xfs_fsblock_t *first,
- xfs_bmap_free_t *flist, xfs_extlen_t total);
-static int xfs_dir2_getdents(xfs_trans_t *tp, xfs_inode_t *dp, uio_t *uio,
- int *eofp);
-static int xfs_dir2_replace(xfs_trans_t *tp, xfs_inode_t *dp, char *name,
- int namelen, xfs_ino_t inum,
- xfs_fsblock_t *first, xfs_bmap_free_t *flist,
- xfs_extlen_t total);
-static int xfs_dir2_canenter(xfs_trans_t *tp, xfs_inode_t *dp, char *name,
- int namelen);
-static int xfs_dir2_shortform_validate_ondisk(xfs_mount_t *mp,
- xfs_dinode_t *dip);
-
-/*
- * Utility routine declarations.
- */
static int xfs_dir2_put_dirent64_direct(xfs_dir2_put_args_t *pa);
static int xfs_dir2_put_dirent64_uio(xfs_dir2_put_args_t *pa);
-/*
- * Directory operations vector.
- */
-xfs_dirops_t xfsv2_dirops = {
- .xd_mount = xfs_dir2_mount,
- .xd_isempty = xfs_dir2_isempty,
- .xd_init = xfs_dir2_init,
- .xd_createname = xfs_dir2_createname,
- .xd_lookup = xfs_dir2_lookup,
- .xd_removename = xfs_dir2_removename,
- .xd_getdents = xfs_dir2_getdents,
- .xd_replace = xfs_dir2_replace,
- .xd_canenter = xfs_dir2_canenter,
- .xd_shortform_validate_ondisk = xfs_dir2_shortform_validate_ondisk,
- .xd_shortform_to_single = xfs_dir2_sf_to_block,
-};
-
-/*
- * Interface routines.
- */
-
-/*
- * Initialize directory-related fields in the mount structure.
- */
-static void
-xfs_dir2_mount(
- xfs_mount_t *mp) /* filesystem mount point */
+void
+xfs_dir_mount(
+ xfs_mount_t *mp)
{
- mp->m_dirversion = 2;
+ ASSERT(XFS_SB_VERSION_HASDIRV2(&mp->m_sb));
ASSERT((1 << (mp->m_sb.sb_blocklog + mp->m_sb.sb_dirblklog)) <=
XFS_MAX_BLOCKSIZE);
mp->m_dirblksize = 1 << (mp->m_sb.sb_blocklog + mp->m_sb.sb_dirblklog);
/*
* Return 1 if directory contains only "." and "..".
*/
-static int /* return code */
-xfs_dir2_isempty(
- xfs_inode_t *dp) /* incore inode structure */
+int
+xfs_dir_isempty(
+ xfs_inode_t *dp)
{
- xfs_dir2_sf_t *sfp; /* shortform directory structure */
+ xfs_dir2_sf_t *sfp;
ASSERT((dp->i_d.di_mode & S_IFMT) == S_IFDIR);
- /*
- * Might happen during shutdown.
- */
- if (dp->i_d.di_size == 0) {
+ if (dp->i_d.di_size == 0) /* might happen during shutdown. */
return 1;
- }
if (dp->i_d.di_size > XFS_IFORK_DSIZE(dp))
return 0;
sfp = (xfs_dir2_sf_t *)dp->i_df.if_u1.if_data;
return !sfp->hdr.count;
}
+/*
+ * Validate a given inode number.
+ */
+int
+xfs_dir_ino_validate(
+ xfs_mount_t *mp,
+ xfs_ino_t ino)
+{
+ xfs_agblock_t agblkno;
+ xfs_agino_t agino;
+ xfs_agnumber_t agno;
+ int ino_ok;
+ int ioff;
+
+ agno = XFS_INO_TO_AGNO(mp, ino);
+ agblkno = XFS_INO_TO_AGBNO(mp, ino);
+ ioff = XFS_INO_TO_OFFSET(mp, ino);
+ agino = XFS_OFFBNO_TO_AGINO(mp, agblkno, ioff);
+ ino_ok =
+ agno < mp->m_sb.sb_agcount &&
+ agblkno < mp->m_sb.sb_agblocks &&
+ agblkno != 0 &&
+ ioff < (1 << mp->m_sb.sb_inopblog) &&
+ XFS_AGINO_TO_INO(mp, agno, agino) == ino;
+ if (unlikely(XFS_TEST_ERROR(!ino_ok, mp, XFS_ERRTAG_DIR_INO_VALIDATE,
+ XFS_RANDOM_DIR_INO_VALIDATE))) {
+ xfs_fs_cmn_err(CE_WARN, mp, "Invalid inode number 0x%Lx",
+ (unsigned long long) ino);
+ XFS_ERROR_REPORT("xfs_dir_ino_validate", XFS_ERRLEVEL_LOW, mp);
+ return XFS_ERROR(EFSCORRUPTED);
+ }
+ return 0;
+}
+
/*
* Initialize a directory with its "." and ".." entries.
*/
-static int /* error */
-xfs_dir2_init(
- xfs_trans_t *tp, /* transaction pointer */
- xfs_inode_t *dp, /* incore directory inode */
- xfs_inode_t *pdp) /* incore parent directory inode */
+int
+xfs_dir_init(
+ xfs_trans_t *tp,
+ xfs_inode_t *dp,
+ xfs_inode_t *pdp)
{
- xfs_da_args_t args; /* operation arguments */
- int error; /* error return value */
+ xfs_da_args_t args;
+ int error;
memset((char *)&args, 0, sizeof(args));
args.dp = dp;
args.trans = tp;
ASSERT((dp->i_d.di_mode & S_IFMT) == S_IFDIR);
- if ((error = xfs_dir_ino_validate(tp->t_mountp, pdp->i_ino))) {
+ if ((error = xfs_dir_ino_validate(tp->t_mountp, pdp->i_ino)))
return error;
- }
return xfs_dir2_sf_create(&args, pdp->i_ino);
}
/*
Enter a name in a directory.
*/
-static int /* error */
-xfs_dir2_createname(
- xfs_trans_t *tp, /* transaction pointer */
- xfs_inode_t *dp, /* incore directory inode */
- char *name, /* new entry name */
- int namelen, /* new entry name length */
+int
+xfs_dir_createname(
+ xfs_trans_t *tp,
+ xfs_inode_t *dp,
+ char *name,
+ int namelen,
xfs_ino_t inum, /* new entry inode number */
xfs_fsblock_t *first, /* bmap's firstblock */
xfs_bmap_free_t *flist, /* bmap's freeblock list */
xfs_extlen_t total) /* bmap's total block count */
{
- xfs_da_args_t args; /* operation arguments */
- int rval; /* return value */
+ xfs_da_args_t args;
+ int rval;
int v; /* type-checking value */
ASSERT((dp->i_d.di_mode & S_IFMT) == S_IFDIR);
- if ((rval = xfs_dir_ino_validate(tp->t_mountp, inum))) {
+ if ((rval = xfs_dir_ino_validate(tp->t_mountp, inum)))
return rval;
- }
XFS_STATS_INC(xs_dir_create);
- /*
- * Fill in the arg structure for this request.
- */
+
args.name = name;
args.namelen = namelen;
args.hashval = xfs_da_hashname(name, namelen);
args.trans = tp;
args.justcheck = 0;
args.addname = args.oknoent = 1;
- /*
- * Decide on what work routines to call based on the inode size.
- */
+
if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL)
rval = xfs_dir2_sf_addname(&args);
- else if ((rval = xfs_dir2_isblock(tp, dp, &v))) {
+ else if ((rval = xfs_dir2_isblock(tp, dp, &v)))
return rval;
- } else if (v)
+ else if (v)
rval = xfs_dir2_block_addname(&args);
- else if ((rval = xfs_dir2_isleaf(tp, dp, &v))) {
+ else if ((rval = xfs_dir2_isleaf(tp, dp, &v)))
return rval;
- } else if (v)
+ else if (v)
rval = xfs_dir2_leaf_addname(&args);
else
rval = xfs_dir2_node_addname(&args);
/*
* Lookup a name in a directory, give back the inode number.
*/
-static int /* error */
-xfs_dir2_lookup(
- xfs_trans_t *tp, /* transaction pointer */
- xfs_inode_t *dp, /* incore directory inode */
- char *name, /* lookup name */
- int namelen, /* lookup name length */
+int
+xfs_dir_lookup(
+ xfs_trans_t *tp,
+ xfs_inode_t *dp,
+ char *name,
+ int namelen,
xfs_ino_t *inum) /* out: inode number */
{
- xfs_da_args_t args; /* operation arguments */
- int rval; /* return value */
+ xfs_da_args_t args;
+ int rval;
int v; /* type-checking value */
ASSERT((dp->i_d.di_mode & S_IFMT) == S_IFDIR);
XFS_STATS_INC(xs_dir_lookup);
- /*
- * Fill in the arg structure for this request.
- */
args.name = name;
args.namelen = namelen;
args.hashval = xfs_da_hashname(name, namelen);
args.trans = tp;
args.justcheck = args.addname = 0;
args.oknoent = 1;
- /*
- * Decide on what work routines to call based on the inode size.
- */
+
if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL)
rval = xfs_dir2_sf_lookup(&args);
- else if ((rval = xfs_dir2_isblock(tp, dp, &v))) {
+ else if ((rval = xfs_dir2_isblock(tp, dp, &v)))
return rval;
- } else if (v)
+ else if (v)
rval = xfs_dir2_block_lookup(&args);
- else if ((rval = xfs_dir2_isleaf(tp, dp, &v))) {
+ else if ((rval = xfs_dir2_isleaf(tp, dp, &v)))
return rval;
- } else if (v)
+ else if (v)
rval = xfs_dir2_leaf_lookup(&args);
else
rval = xfs_dir2_node_lookup(&args);
/*
* Remove an entry from a directory.
*/
-static int /* error */
-xfs_dir2_removename(
- xfs_trans_t *tp, /* transaction pointer */
- xfs_inode_t *dp, /* incore directory inode */
- char *name, /* name of entry to remove */
- int namelen, /* name length of entry to remove */
- xfs_ino_t ino, /* inode number of entry to remove */
+int
+xfs_dir_removename(
+ xfs_trans_t *tp,
+ xfs_inode_t *dp,
+ char *name,
+ int namelen,
+ xfs_ino_t ino,
xfs_fsblock_t *first, /* bmap's firstblock */
xfs_bmap_free_t *flist, /* bmap's freeblock list */
xfs_extlen_t total) /* bmap's total block count */
{
- xfs_da_args_t args; /* operation arguments */
- int rval; /* return value */
+ xfs_da_args_t args;
+ int rval;
int v; /* type-checking value */
ASSERT((dp->i_d.di_mode & S_IFMT) == S_IFDIR);
XFS_STATS_INC(xs_dir_remove);
- /*
- * Fill in the arg structure for this request.
- */
+
args.name = name;
args.namelen = namelen;
args.hashval = xfs_da_hashname(name, namelen);
args.whichfork = XFS_DATA_FORK;
args.trans = tp;
args.justcheck = args.addname = args.oknoent = 0;
- /*
- * Decide on what work routines to call based on the inode size.
- */
+
if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL)
rval = xfs_dir2_sf_removename(&args);
- else if ((rval = xfs_dir2_isblock(tp, dp, &v))) {
+ else if ((rval = xfs_dir2_isblock(tp, dp, &v)))
return rval;
- } else if (v)
+ else if (v)
rval = xfs_dir2_block_removename(&args);
- else if ((rval = xfs_dir2_isleaf(tp, dp, &v))) {
+ else if ((rval = xfs_dir2_isleaf(tp, dp, &v)))
return rval;
- } else if (v)
+ else if (v)
rval = xfs_dir2_leaf_removename(&args);
else
rval = xfs_dir2_node_removename(&args);
/*
* Read a directory.
*/
-static int /* error */
-xfs_dir2_getdents(
- xfs_trans_t *tp, /* transaction pointer */
- xfs_inode_t *dp, /* incore directory inode */
+int
+xfs_dir_getdents(
+ xfs_trans_t *tp,
+ xfs_inode_t *dp,
uio_t *uio, /* caller's buffer control */
int *eofp) /* out: eof reached */
{
}
*eofp = 0;
- /*
- * Decide on what work routines to call based on the inode size.
- */
if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL)
rval = xfs_dir2_sf_getdents(dp, uio, eofp, dbp, put);
- else if ((rval = xfs_dir2_isblock(tp, dp, &v))) {
+ else if ((rval = xfs_dir2_isblock(tp, dp, &v)))
;
- } else if (v)
+ else if (v)
rval = xfs_dir2_block_getdents(tp, dp, uio, eofp, dbp, put);
else
rval = xfs_dir2_leaf_getdents(tp, dp, uio, eofp, dbp, put);
/*
* Replace the inode number of a directory entry.
*/
-static int /* error */
-xfs_dir2_replace(
- xfs_trans_t *tp, /* transaction pointer */
- xfs_inode_t *dp, /* incore directory inode */
+int
+xfs_dir_replace(
+ xfs_trans_t *tp,
+ xfs_inode_t *dp,
char *name, /* name of entry to replace */
- int namelen, /* name length of entry to replace */
+ int namelen,
xfs_ino_t inum, /* new inode number */
xfs_fsblock_t *first, /* bmap's firstblock */
xfs_bmap_free_t *flist, /* bmap's freeblock list */
xfs_extlen_t total) /* bmap's total block count */
{
- xfs_da_args_t args; /* operation arguments */
- int rval; /* return value */
+ xfs_da_args_t args;
+ int rval;
int v; /* type-checking value */
ASSERT((dp->i_d.di_mode & S_IFMT) == S_IFDIR);
- if ((rval = xfs_dir_ino_validate(tp->t_mountp, inum))) {
+ if ((rval = xfs_dir_ino_validate(tp->t_mountp, inum)))
return rval;
- }
- /*
- * Fill in the arg structure for this request.
- */
+
args.name = name;
args.namelen = namelen;
args.hashval = xfs_da_hashname(name, namelen);
args.whichfork = XFS_DATA_FORK;
args.trans = tp;
args.justcheck = args.addname = args.oknoent = 0;
- /*
- * Decide on what work routines to call based on the inode size.
- */
+
if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL)
rval = xfs_dir2_sf_replace(&args);
- else if ((rval = xfs_dir2_isblock(tp, dp, &v))) {
+ else if ((rval = xfs_dir2_isblock(tp, dp, &v)))
return rval;
- } else if (v)
+ else if (v)
rval = xfs_dir2_block_replace(&args);
- else if ((rval = xfs_dir2_isleaf(tp, dp, &v))) {
+ else if ((rval = xfs_dir2_isleaf(tp, dp, &v)))
return rval;
- } else if (v)
+ else if (v)
rval = xfs_dir2_leaf_replace(&args);
else
rval = xfs_dir2_node_replace(&args);
/*
* See if this entry can be added to the directory without allocating space.
*/
-static int /* error */
-xfs_dir2_canenter(
- xfs_trans_t *tp, /* transaction pointer */
- xfs_inode_t *dp, /* incore directory inode */
+int
+xfs_dir_canenter(
+ xfs_trans_t *tp,
+ xfs_inode_t *dp,
char *name, /* name of entry to add */
- int namelen) /* name length of entry to add */
+ int namelen)
{
- xfs_da_args_t args; /* operation arguments */
- int rval; /* return value */
+ xfs_da_args_t args;
+ int rval;
int v; /* type-checking value */
ASSERT((dp->i_d.di_mode & S_IFMT) == S_IFDIR);
- /*
- * Fill in the arg structure for this request.
- */
+
args.name = name;
args.namelen = namelen;
args.hashval = xfs_da_hashname(name, namelen);
args.whichfork = XFS_DATA_FORK;
args.trans = tp;
args.justcheck = args.addname = args.oknoent = 1;
- /*
- * Decide on what work routines to call based on the inode size.
- */
+
if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL)
rval = xfs_dir2_sf_addname(&args);
- else if ((rval = xfs_dir2_isblock(tp, dp, &v))) {
+ else if ((rval = xfs_dir2_isblock(tp, dp, &v)))
return rval;
- } else if (v)
+ else if (v)
rval = xfs_dir2_block_addname(&args);
- else if ((rval = xfs_dir2_isleaf(tp, dp, &v))) {
+ else if ((rval = xfs_dir2_isleaf(tp, dp, &v)))
return rval;
- } else if (v)
+ else if (v)
rval = xfs_dir2_leaf_addname(&args);
else
rval = xfs_dir2_node_addname(&args);
return rval;
}
-/*
- * Dummy routine for shortform inode validation.
- * Can't really do this.
- */
-/* ARGSUSED */
-static int /* error */
-xfs_dir2_shortform_validate_ondisk(
- xfs_mount_t *mp, /* filesystem mount point */
- xfs_dinode_t *dip) /* ondisk inode */
-{
- return 0;
-}
-
/*
* Utility routines.
*/
* This routine is for data and free blocks, not leaf/node blocks
* which are handled by xfs_da_grow_inode.
*/
-int /* error */
+int
xfs_dir2_grow_inode(
- xfs_da_args_t *args, /* operation arguments */
+ xfs_da_args_t *args,
int space, /* v2 dir's space XFS_DIR2_xxx_SPACE */
xfs_dir2_db_t *dbp) /* out: block number added */
{
xfs_fileoff_t bno; /* directory offset of new block */
int count; /* count of filesystem blocks */
xfs_inode_t *dp; /* incore directory inode */
- int error; /* error return value */
+ int error;
int got; /* blocks actually mapped */
- int i; /* temp mapping index */
+ int i;
xfs_bmbt_irec_t map; /* single structure for bmap */
int mapi; /* mapping index */
xfs_bmbt_irec_t *mapp; /* bmap mapping structure(s) */
- xfs_mount_t *mp; /* filesystem mount point */
+ xfs_mount_t *mp;
int nmap; /* number of bmap entries */
- xfs_trans_t *tp; /* transaction pointer */
+ xfs_trans_t *tp;
xfs_dir2_trace_args_s("grow_inode", args, space);
dp = args->dp;
/*
* Find the first hole for our block.
*/
- if ((error = xfs_bmap_first_unused(tp, dp, count, &bno, XFS_DATA_FORK))) {
+ if ((error = xfs_bmap_first_unused(tp, dp, count, &bno, XFS_DATA_FORK)))
return error;
- }
nmap = 1;
ASSERT(args->firstblock != NULL);
/*
if ((error = xfs_bmapi(tp, dp, bno, count,
XFS_BMAPI_WRITE|XFS_BMAPI_METADATA|XFS_BMAPI_CONTIG,
args->firstblock, args->total, &map, &nmap,
- args->flist))) {
+ args->flist, NULL)))
return error;
- }
ASSERT(nmap <= 1);
- /*
- * Got it in 1.
- */
if (nmap == 1) {
mapp = ↦
mapi = 1;
if ((error = xfs_bmapi(tp, dp, b, c,
XFS_BMAPI_WRITE|XFS_BMAPI_METADATA,
args->firstblock, args->total,
- &mapp[mapi], &nmap, args->flist))) {
+ &mapp[mapi], &nmap, args->flist,
+ NULL))) {
kmem_free(mapp, sizeof(*mapp) * count);
return error;
}
/*
* See if the directory is a single-block form directory.
*/
-int /* error */
+int
xfs_dir2_isblock(
- xfs_trans_t *tp, /* transaction pointer */
- xfs_inode_t *dp, /* incore directory inode */
+ xfs_trans_t *tp,
+ xfs_inode_t *dp,
int *vp) /* out: 1 is block, 0 is not block */
{
xfs_fileoff_t last; /* last file offset */
- xfs_mount_t *mp; /* filesystem mount point */
- int rval; /* return value */
+ xfs_mount_t *mp;
+ int rval;
mp = dp->i_mount;
- if ((rval = xfs_bmap_last_offset(tp, dp, &last, XFS_DATA_FORK))) {
+ if ((rval = xfs_bmap_last_offset(tp, dp, &last, XFS_DATA_FORK)))
return rval;
- }
rval = XFS_FSB_TO_B(mp, last) == mp->m_dirblksize;
ASSERT(rval == 0 || dp->i_d.di_size == mp->m_dirblksize);
*vp = rval;
/*
* See if the directory is a single-leaf form directory.
*/
-int /* error */
+int
xfs_dir2_isleaf(
- xfs_trans_t *tp, /* transaction pointer */
- xfs_inode_t *dp, /* incore directory inode */
+ xfs_trans_t *tp,
+ xfs_inode_t *dp,
int *vp) /* out: 1 is leaf, 0 is not leaf */
{
xfs_fileoff_t last; /* last file offset */
- xfs_mount_t *mp; /* filesystem mount point */
- int rval; /* return value */
+ xfs_mount_t *mp;
+ int rval;
mp = dp->i_mount;
- if ((rval = xfs_bmap_last_offset(tp, dp, &last, XFS_DATA_FORK))) {
+ if ((rval = xfs_bmap_last_offset(tp, dp, &last, XFS_DATA_FORK)))
return rval;
- }
*vp = last == mp->m_dirleafblk + (1 << mp->m_sb.sb_dirblklog);
return 0;
}
/*
* Getdents put routine for 64-bit ABI, direct form.
*/
-static int /* error */
+static int
xfs_dir2_put_dirent64_direct(
- xfs_dir2_put_args_t *pa) /* argument bundle */
+ xfs_dir2_put_args_t *pa)
{
xfs_dirent_t *idbp; /* dirent pointer */
iovec_t *iovp; /* io vector */
/*
* Getdents put routine for 64-bit ABI, uio form.
*/
-static int /* error */
+static int
xfs_dir2_put_dirent64_uio(
- xfs_dir2_put_args_t *pa) /* argument bundle */
+ xfs_dir2_put_args_t *pa)
{
xfs_dirent_t *idbp; /* dirent pointer */
int namelen; /* entry name length */
*/
int
xfs_dir2_shrink_inode(
- xfs_da_args_t *args, /* operation arguments */
- xfs_dir2_db_t db, /* directory block number */
- xfs_dabuf_t *bp) /* block's buffer */
+ xfs_da_args_t *args,
+ xfs_dir2_db_t db,
+ xfs_dabuf_t *bp)
{
xfs_fileoff_t bno; /* directory file offset */
xfs_dablk_t da; /* directory file offset */
int done; /* bunmap is finished */
- xfs_inode_t *dp; /* incore directory inode */
- int error; /* error return value */
- xfs_mount_t *mp; /* filesystem mount point */
- xfs_trans_t *tp; /* transaction pointer */
+ xfs_inode_t *dp;
+ int error;
+ xfs_mount_t *mp;
+ xfs_trans_t *tp;
xfs_dir2_trace_args_db("shrink_inode", args, db, bp);
dp = args->dp;
*/
if ((error = xfs_bunmapi(tp, dp, da, mp->m_dirblkfsbs,
XFS_BMAPI_METADATA, 0, args->firstblock, args->flist,
- &done))) {
+ NULL, &done))) {
/*
* ENOSPC actually can happen if we're in a removename with
* no space reservation, and the resulting block removal
struct xfs_dabuf;
struct xfs_da_args;
struct xfs_dir2_put_args;
+struct xfs_bmap_free;
struct xfs_inode;
+struct xfs_mount;
struct xfs_trans;
/*
} xfs_dir2_put_args_t;
/*
- * Other interfaces used by the rest of the dir v2 code.
+ * Generic directory interface routines
+ */
+extern void xfs_dir_startup(void);
+extern void xfs_dir_mount(struct xfs_mount *mp);
+extern int xfs_dir_isempty(struct xfs_inode *dp);
+extern int xfs_dir_init(struct xfs_trans *tp, struct xfs_inode *dp,
+ struct xfs_inode *pdp);
+extern int xfs_dir_createname(struct xfs_trans *tp, struct xfs_inode *dp,
+ char *name, int namelen, xfs_ino_t inum,
+ xfs_fsblock_t *first,
+ struct xfs_bmap_free *flist, xfs_extlen_t tot);
+extern int xfs_dir_lookup(struct xfs_trans *tp, struct xfs_inode *dp,
+ char *name, int namelen, xfs_ino_t *inum);
+extern int xfs_dir_removename(struct xfs_trans *tp, struct xfs_inode *dp,
+ char *name, int namelen, xfs_ino_t ino,
+ xfs_fsblock_t *first,
+ struct xfs_bmap_free *flist, xfs_extlen_t tot);
+extern int xfs_dir_getdents(struct xfs_trans *tp, struct xfs_inode *dp,
+ uio_t *uio, int *eofp);
+extern int xfs_dir_replace(struct xfs_trans *tp, struct xfs_inode *dp,
+ char *name, int namelen, xfs_ino_t inum,
+ xfs_fsblock_t *first,
+ struct xfs_bmap_free *flist, xfs_extlen_t tot);
+extern int xfs_dir_canenter(struct xfs_trans *tp, struct xfs_inode *dp,
+ char *name, int namelen);
+extern int xfs_dir_ino_validate(struct xfs_mount *mp, xfs_ino_t ino);
+
+/*
+ * Utility routines for v2 directories.
*/
extern int xfs_dir2_grow_inode(struct xfs_da_args *args, int space,
xfs_dir2_db_t *dbp);
#include "xfs_inum.h"
#include "xfs_trans.h"
#include "xfs_sb.h"
-#include "xfs_dir.h"
#include "xfs_dir2.h"
#include "xfs_dmapi.h"
#include "xfs_mount.h"
#include "xfs_da_btree.h"
#include "xfs_bmap_btree.h"
-#include "xfs_dir_sf.h"
#include "xfs_dir2_sf.h"
#include "xfs_attr_sf.h"
#include "xfs_dinode.h"
#include "xfs_inode.h"
#include "xfs_inode_item.h"
-#include "xfs_dir_leaf.h"
#include "xfs_dir2_data.h"
#include "xfs_dir2_leaf.h"
#include "xfs_dir2_block.h"
int *entno);
static int xfs_dir2_block_sort(const void *a, const void *b);
+static xfs_dahash_t xfs_dir_hash_dot, xfs_dir_hash_dotdot;
+
+/*
+ * One-time startup routine called from xfs_init().
+ */
+void
+xfs_dir_startup(void)
+{
+ xfs_dir_hash_dot = xfs_da_hashname(".", 1);
+ xfs_dir_hash_dotdot = xfs_da_hashname("..", 2);
+}
+
/*
* Add an entry to a block directory.
*/
/*
* Create the new data entry.
*/
- INT_SET(dep->inumber, ARCH_CONVERT, args->inumber);
+ dep->inumber = cpu_to_be64(args->inumber);
dep->namelen = args->namelen;
memcpy(dep->name, args->name, args->namelen);
tagp = XFS_DIR2_DATA_ENTRY_TAG_P(dep);
p.cook = XFS_DIR2_DB_OFF_TO_DATAPTR(mp, mp->m_dirdatablk,
ptr - (char *)block);
- p.ino = INT_GET(dep->inumber, ARCH_CONVERT);
+ p.ino = be64_to_cpu(dep->inumber);
#if XFS_BIG_INUMS
p.ino += mp->m_inoadd;
#endif
/*
* Fill in inode number, release the block.
*/
- args->inumber = INT_GET(dep->inumber, ARCH_CONVERT);
+ args->inumber = be64_to_cpu(dep->inumber);
xfs_da_brelse(args->trans, bp);
return XFS_ERROR(EEXIST);
}
*/
dep = (xfs_dir2_data_entry_t *)
((char *)block + XFS_DIR2_DATAPTR_TO_OFF(mp, be32_to_cpu(blp[ent].address)));
- ASSERT(INT_GET(dep->inumber, ARCH_CONVERT) != args->inumber);
+ ASSERT(be64_to_cpu(dep->inumber) != args->inumber);
/*
* Change the inode number to the new value.
*/
- INT_SET(dep->inumber, ARCH_CONVERT, args->inumber);
+ dep->inumber = cpu_to_be64(args->inumber);
xfs_dir2_data_log_entry(args->trans, bp, dep);
xfs_dir2_data_check(dp, bp);
xfs_da_buf_done(bp);
*/
dep = (xfs_dir2_data_entry_t *)
((char *)block + XFS_DIR2_DATA_DOT_OFFSET);
- INT_SET(dep->inumber, ARCH_CONVERT, dp->i_ino);
+ dep->inumber = cpu_to_be64(dp->i_ino);
dep->namelen = 1;
dep->name[0] = '.';
tagp = XFS_DIR2_DATA_ENTRY_TAG_P(dep);
*/
dep = (xfs_dir2_data_entry_t *)
((char *)block + XFS_DIR2_DATA_DOTDOT_OFFSET);
- INT_SET(dep->inumber, ARCH_CONVERT, XFS_DIR2_SF_GET_INUMBER(sfp, &sfp->hdr.parent));
+ dep->inumber = cpu_to_be64(XFS_DIR2_SF_GET_INUMBER(sfp, &sfp->hdr.parent));
dep->namelen = 2;
dep->name[0] = dep->name[1] = '.';
tagp = XFS_DIR2_DATA_ENTRY_TAG_P(dep);
* Copy a real entry.
*/
dep = (xfs_dir2_data_entry_t *)((char *)block + newoffset);
- INT_SET(dep->inumber, ARCH_CONVERT, XFS_DIR2_SF_GET_INUMBER(sfp,
+ dep->inumber = cpu_to_be64(XFS_DIR2_SF_GET_INUMBER(sfp,
XFS_DIR2_SF_INUMBERP(sfep)));
dep->namelen = sfep->namelen;
memcpy(dep->name, sfep->name, dep->namelen);
#include "xfs_inum.h"
#include "xfs_trans.h"
#include "xfs_sb.h"
-#include "xfs_dir.h"
#include "xfs_dir2.h"
#include "xfs_dmapi.h"
#include "xfs_mount.h"
#include "xfs_da_btree.h"
#include "xfs_bmap_btree.h"
-#include "xfs_dir_sf.h"
#include "xfs_dir2_sf.h"
#include "xfs_attr_sf.h"
#include "xfs_dinode.h"
#include "xfs_inode.h"
-#include "xfs_dir_leaf.h"
#include "xfs_dir2_data.h"
#include "xfs_dir2_leaf.h"
#include "xfs_dir2_block.h"
*/
dep = (xfs_dir2_data_entry_t *)p;
ASSERT(dep->namelen != 0);
- ASSERT(xfs_dir_ino_validate(mp, INT_GET(dep->inumber, ARCH_CONVERT)) == 0);
+ ASSERT(xfs_dir_ino_validate(mp, be64_to_cpu(dep->inumber)) == 0);
ASSERT(be16_to_cpu(*XFS_DIR2_DATA_ENTRY_TAG_P(dep)) ==
(char *)dep - (char *)d);
count++;
* Tag appears as the last 2 bytes.
*/
typedef struct xfs_dir2_data_entry {
- xfs_ino_t inumber; /* inode number */
- __uint8_t namelen; /* name length */
- __uint8_t name[1]; /* name bytes, no null */
+ __be64 inumber; /* inode number */
+ __u8 namelen; /* name length */
+ __u8 name[1]; /* name bytes, no null */
/* variable offset */
- xfs_dir2_data_off_t tag; /* starting offset of us */
+ __be16 tag; /* starting offset of us */
} xfs_dir2_data_entry_t;
/*
#include "xfs_trans.h"
#include "xfs_sb.h"
#include "xfs_ag.h"
-#include "xfs_dir.h"
#include "xfs_dir2.h"
#include "xfs_dmapi.h"
#include "xfs_mount.h"
#include "xfs_da_btree.h"
#include "xfs_bmap_btree.h"
#include "xfs_attr_sf.h"
-#include "xfs_dir_sf.h"
#include "xfs_dir2_sf.h"
#include "xfs_dinode.h"
#include "xfs_inode.h"
* Initialize our new entry (at last).
*/
dep = (xfs_dir2_data_entry_t *)dup;
- INT_SET(dep->inumber, ARCH_CONVERT, args->inumber);
+ dep->inumber = cpu_to_be64(args->inumber);
dep->namelen = args->namelen;
memcpy(dep->name, args->name, dep->namelen);
tagp = XFS_DIR2_DATA_ENTRY_TAG_P(dep);
XFS_DIR2_BYTE_TO_DA(mp,
XFS_DIR2_LEAF_OFFSET) - map_off,
XFS_BMAPI_METADATA, NULL, 0,
- &map[map_valid], &nmap, NULL);
+ &map[map_valid], &nmap, NULL, NULL);
/*
* Don't know if we should ignore this or
* try to return an error.
p->cook = XFS_DIR2_BYTE_TO_DATAPTR(mp, curoff + length);
- p->ino = INT_GET(dep->inumber, ARCH_CONVERT);
+ p->ino = be64_to_cpu(dep->inumber);
#if XFS_BIG_INUMS
p->ino += mp->m_inoadd;
#endif
/*
* Return the found inode number.
*/
- args->inumber = INT_GET(dep->inumber, ARCH_CONVERT);
+ args->inumber = be64_to_cpu(dep->inumber);
xfs_da_brelse(tp, dbp);
xfs_da_brelse(tp, lbp);
return XFS_ERROR(EEXIST);
dep = (xfs_dir2_data_entry_t *)
((char *)dbp->data +
XFS_DIR2_DATAPTR_TO_OFF(dp->i_mount, be32_to_cpu(lep->address)));
- ASSERT(args->inumber != INT_GET(dep->inumber, ARCH_CONVERT));
+ ASSERT(args->inumber != be64_to_cpu(dep->inumber));
/*
* Put the new inode number in, log it.
*/
- INT_SET(dep->inumber, ARCH_CONVERT, args->inumber);
+ dep->inumber = cpu_to_be64(args->inumber);
tp = args->trans;
xfs_dir2_data_log_entry(tp, dbp, dep);
xfs_da_buf_done(dbp);
#include "xfs_inum.h"
#include "xfs_trans.h"
#include "xfs_sb.h"
-#include "xfs_dir.h"
#include "xfs_dir2.h"
#include "xfs_dmapi.h"
#include "xfs_mount.h"
#include "xfs_da_btree.h"
#include "xfs_bmap_btree.h"
-#include "xfs_dir_sf.h"
#include "xfs_dir2_sf.h"
#include "xfs_attr_sf.h"
#include "xfs_dinode.h"
XFS_DATA_FORK))) {
return error;
}
- curfdb = newfdb;
free = curbp->data;
ASSERT(be32_to_cpu(free->hdr.magic) ==
XFS_DIR2_FREE_MAGIC);
if (unlikely(be16_to_cpu(free->bests[fi]) == NULLDATAOFF)) {
XFS_ERROR_REPORT("xfs_dir2_leafn_lookup_int",
XFS_ERRLEVEL_LOW, mp);
+ if (curfdb != newfdb)
+ xfs_da_brelse(tp, curbp);
return XFS_ERROR(EFSCORRUPTED);
}
+ curfdb = newfdb;
if (be16_to_cpu(free->bests[fi]) >= length) {
*indexp = index;
state->extravalid = 1;
if (dep->namelen == args->namelen &&
dep->name[0] == args->name[0] &&
memcmp(dep->name, args->name, args->namelen) == 0) {
- args->inumber = INT_GET(dep->inumber, ARCH_CONVERT);
+ args->inumber = be64_to_cpu(dep->inumber);
*indexp = index;
state->extravalid = 1;
state->extrablk.bp = curbp;
/*
* One less used entry in the free table.
*/
- free->hdr.nused = cpu_to_be32(-1);
+ be32_add(&free->hdr.nused, -1);
xfs_dir2_free_log_header(tp, fbp);
/*
* If this was the last entry in the table, we can
* Fill in the new entry and log it.
*/
dep = (xfs_dir2_data_entry_t *)dup;
- INT_SET(dep->inumber, ARCH_CONVERT, args->inumber);
+ dep->inumber = cpu_to_be64(args->inumber);
dep->namelen = args->namelen;
memcpy(dep->name, args->name, dep->namelen);
tagp = XFS_DIR2_DATA_ENTRY_TAG_P(dep);
dep = (xfs_dir2_data_entry_t *)
((char *)data +
XFS_DIR2_DATAPTR_TO_OFF(state->mp, be32_to_cpu(lep->address)));
- ASSERT(inum != INT_GET(dep->inumber, ARCH_CONVERT));
+ ASSERT(inum != be64_to_cpu(dep->inumber));
/*
* Fill in the new inode number and log the entry.
*/
- INT_SET(dep->inumber, ARCH_CONVERT, inum);
+ dep->inumber = cpu_to_be64(inum);
xfs_dir2_data_log_entry(args->trans, state->extrablk.bp, dep);
rval = 0;
}
#include "xfs_inum.h"
#include "xfs_trans.h"
#include "xfs_sb.h"
-#include "xfs_dir.h"
#include "xfs_dir2.h"
#include "xfs_dmapi.h"
#include "xfs_mount.h"
#include "xfs_da_btree.h"
#include "xfs_bmap_btree.h"
-#include "xfs_dir_sf.h"
#include "xfs_dir2_sf.h"
#include "xfs_attr_sf.h"
#include "xfs_dinode.h"
#include "xfs_inode.h"
#include "xfs_inode_item.h"
-#include "xfs_dir_leaf.h"
#include "xfs_error.h"
#include "xfs_dir2_data.h"
#include "xfs_dir2_leaf.h"
dep->name[0] == '.' && dep->name[1] == '.';
#if XFS_BIG_INUMS
if (!isdot)
- i8count += INT_GET(dep->inumber, ARCH_CONVERT) > XFS_DIR2_MAX_SHORT_INUM;
+ i8count += be64_to_cpu(dep->inumber) > XFS_DIR2_MAX_SHORT_INUM;
#endif
if (!isdot && !isdotdot) {
count++;
namelen += dep->namelen;
} else if (isdotdot)
- parent = INT_GET(dep->inumber, ARCH_CONVERT);
+ parent = be64_to_cpu(dep->inumber);
/*
* Calculate the new size, see if we should give up yet.
*/
* Skip .
*/
if (dep->namelen == 1 && dep->name[0] == '.')
- ASSERT(INT_GET(dep->inumber, ARCH_CONVERT) == dp->i_ino);
+ ASSERT(be64_to_cpu(dep->inumber) == dp->i_ino);
/*
* Skip .., but make sure the inode number is right.
*/
else if (dep->namelen == 2 &&
dep->name[0] == '.' && dep->name[1] == '.')
- ASSERT(INT_GET(dep->inumber, ARCH_CONVERT) ==
+ ASSERT(be64_to_cpu(dep->inumber) ==
XFS_DIR2_SF_GET_INUMBER(sfp, &sfp->hdr.parent));
/*
* Normal entry, copy it into shortform.
(xfs_dir2_data_aoff_t)
((char *)dep - (char *)block));
memcpy(sfep->name, dep->name, dep->namelen);
- temp=INT_GET(dep->inumber, ARCH_CONVERT);
+ temp = be64_to_cpu(dep->inumber);
XFS_DIR2_SF_PUT_INUMBER(sfp, &temp,
XFS_DIR2_SF_INUMBERP(sfep));
sfep = XFS_DIR2_SF_NEXTENTRY(sfp, sfep);
#include "xfs_fs.h"
#include "xfs_types.h"
#include "xfs_inum.h"
-#include "xfs_dir.h"
#include "xfs_dir2.h"
#include "xfs_da_btree.h"
#include "xfs_bmap_btree.h"
-#include "xfs_dir_sf.h"
#include "xfs_dir2_sf.h"
#include "xfs_attr_sf.h"
#include "xfs_dinode.h"
+++ /dev/null
-/*
- * Copyright (c) 2000-2003,2005 Silicon Graphics, Inc.
- * 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.
- *
- * This program is distributed in the hope that it would 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 the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-#include "xfs.h"
-#include "xfs_fs.h"
-#include "xfs_types.h"
-#include "xfs_log.h"
-#include "xfs_inum.h"
-#include "xfs_trans.h"
-#include "xfs_sb.h"
-#include "xfs_dir.h"
-#include "xfs_dir2.h"
-#include "xfs_dmapi.h"
-#include "xfs_mount.h"
-#include "xfs_da_btree.h"
-#include "xfs_bmap_btree.h"
-#include "xfs_alloc_btree.h"
-#include "xfs_ialloc_btree.h"
-#include "xfs_dir_sf.h"
-#include "xfs_dir2_sf.h"
-#include "xfs_attr_sf.h"
-#include "xfs_dinode.h"
-#include "xfs_inode.h"
-#include "xfs_inode_item.h"
-#include "xfs_alloc.h"
-#include "xfs_btree.h"
-#include "xfs_bmap.h"
-#include "xfs_dir_leaf.h"
-#include "xfs_error.h"
-
-/*
- * xfs_dir_leaf.c
- *
- * Routines to implement leaf blocks of directories as Btrees of hashed names.
- */
-
-/*========================================================================
- * Function prototypes for the kernel.
- *========================================================================*/
-
-/*
- * Routines used for growing the Btree.
- */
-STATIC void xfs_dir_leaf_add_work(xfs_dabuf_t *leaf_buffer, xfs_da_args_t *args,
- int insertion_index,
- int freemap_index);
-STATIC int xfs_dir_leaf_compact(xfs_trans_t *trans, xfs_dabuf_t *leaf_buffer,
- int musthave, int justcheck);
-STATIC void xfs_dir_leaf_rebalance(xfs_da_state_t *state,
- xfs_da_state_blk_t *blk1,
- xfs_da_state_blk_t *blk2);
-STATIC int xfs_dir_leaf_figure_balance(xfs_da_state_t *state,
- xfs_da_state_blk_t *leaf_blk_1,
- xfs_da_state_blk_t *leaf_blk_2,
- int *number_entries_in_blk1,
- int *number_namebytes_in_blk1);
-
-STATIC int xfs_dir_leaf_create(struct xfs_da_args *args,
- xfs_dablk_t which_block,
- struct xfs_dabuf **bpp);
-
-/*
- * Utility routines.
- */
-STATIC void xfs_dir_leaf_moveents(xfs_dir_leafblock_t *src_leaf,
- int src_start,
- xfs_dir_leafblock_t *dst_leaf,
- int dst_start, int move_count,
- xfs_mount_t *mp);
-
-
-/*========================================================================
- * External routines when dirsize < XFS_IFORK_DSIZE(dp).
- *========================================================================*/
-
-
-/*
- * Validate a given inode number.
- */
-int
-xfs_dir_ino_validate(xfs_mount_t *mp, xfs_ino_t ino)
-{
- xfs_agblock_t agblkno;
- xfs_agino_t agino;
- xfs_agnumber_t agno;
- int ino_ok;
- int ioff;
-
- agno = XFS_INO_TO_AGNO(mp, ino);
- agblkno = XFS_INO_TO_AGBNO(mp, ino);
- ioff = XFS_INO_TO_OFFSET(mp, ino);
- agino = XFS_OFFBNO_TO_AGINO(mp, agblkno, ioff);
- ino_ok =
- agno < mp->m_sb.sb_agcount &&
- agblkno < mp->m_sb.sb_agblocks &&
- agblkno != 0 &&
- ioff < (1 << mp->m_sb.sb_inopblog) &&
- XFS_AGINO_TO_INO(mp, agno, agino) == ino;
- if (unlikely(XFS_TEST_ERROR(!ino_ok, mp, XFS_ERRTAG_DIR_INO_VALIDATE,
- XFS_RANDOM_DIR_INO_VALIDATE))) {
- xfs_fs_cmn_err(CE_WARN, mp, "Invalid inode number 0x%Lx",
- (unsigned long long) ino);
- XFS_ERROR_REPORT("xfs_dir_ino_validate", XFS_ERRLEVEL_LOW, mp);
- return XFS_ERROR(EFSCORRUPTED);
- }
- return 0;
-}
-
-/*
- * Create the initial contents of a shortform directory.
- */
-int
-xfs_dir_shortform_create(xfs_da_args_t *args, xfs_ino_t parent)
-{
- xfs_dir_sf_hdr_t *hdr;
- xfs_inode_t *dp;
-
- dp = args->dp;
- ASSERT(dp != NULL);
- ASSERT(dp->i_d.di_size == 0);
- if (dp->i_d.di_format == XFS_DINODE_FMT_EXTENTS) {
- dp->i_df.if_flags &= ~XFS_IFEXTENTS; /* just in case */
- dp->i_d.di_format = XFS_DINODE_FMT_LOCAL;
- xfs_trans_log_inode(args->trans, dp, XFS_ILOG_CORE);
- dp->i_df.if_flags |= XFS_IFINLINE;
- }
- ASSERT(dp->i_df.if_flags & XFS_IFINLINE);
- ASSERT(dp->i_df.if_bytes == 0);
- xfs_idata_realloc(dp, sizeof(*hdr), XFS_DATA_FORK);
- hdr = (xfs_dir_sf_hdr_t *)dp->i_df.if_u1.if_data;
- XFS_DIR_SF_PUT_DIRINO(&parent, &hdr->parent);
-
- hdr->count = 0;
- dp->i_d.di_size = sizeof(*hdr);
- xfs_trans_log_inode(args->trans, dp, XFS_ILOG_CORE | XFS_ILOG_DDATA);
- return 0;
-}
-
-/*
- * Add a name to the shortform directory structure.
- * Overflow from the inode has already been checked for.
- */
-int
-xfs_dir_shortform_addname(xfs_da_args_t *args)
-{
- xfs_dir_shortform_t *sf;
- xfs_dir_sf_entry_t *sfe;
- int i, offset, size;
- xfs_inode_t *dp;
-
- dp = args->dp;
- ASSERT(dp->i_df.if_flags & XFS_IFINLINE);
- /*
- * Catch the case where the conversion from shortform to leaf
- * failed part way through.
- */
- if (dp->i_d.di_size < sizeof(xfs_dir_sf_hdr_t)) {
- ASSERT(XFS_FORCED_SHUTDOWN(dp->i_mount));
- return XFS_ERROR(EIO);
- }
- ASSERT(dp->i_df.if_bytes == dp->i_d.di_size);
- ASSERT(dp->i_df.if_u1.if_data != NULL);
- sf = (xfs_dir_shortform_t *)dp->i_df.if_u1.if_data;
- sfe = &sf->list[0];
- for (i = sf->hdr.count-1; i >= 0; i--) {
- if (sfe->namelen == args->namelen &&
- args->name[0] == sfe->name[0] &&
- memcmp(args->name, sfe->name, args->namelen) == 0)
- return XFS_ERROR(EEXIST);
- sfe = XFS_DIR_SF_NEXTENTRY(sfe);
- }
-
- offset = (int)((char *)sfe - (char *)sf);
- size = XFS_DIR_SF_ENTSIZE_BYNAME(args->namelen);
- xfs_idata_realloc(dp, size, XFS_DATA_FORK);
- sf = (xfs_dir_shortform_t *)dp->i_df.if_u1.if_data;
- sfe = (xfs_dir_sf_entry_t *)((char *)sf + offset);
-
- XFS_DIR_SF_PUT_DIRINO(&args->inumber, &sfe->inumber);
- sfe->namelen = args->namelen;
- memcpy(sfe->name, args->name, sfe->namelen);
- sf->hdr.count++;
-
- dp->i_d.di_size += size;
- xfs_trans_log_inode(args->trans, dp, XFS_ILOG_CORE | XFS_ILOG_DDATA);
-
- return 0;
-}
-
-/*
- * Remove a name from the shortform directory structure.
- */
-int
-xfs_dir_shortform_removename(xfs_da_args_t *args)
-{
- xfs_dir_shortform_t *sf;
- xfs_dir_sf_entry_t *sfe;
- int base, size = 0, i;
- xfs_inode_t *dp;
-
- dp = args->dp;
- ASSERT(dp->i_df.if_flags & XFS_IFINLINE);
- /*
- * Catch the case where the conversion from shortform to leaf
- * failed part way through.
- */
- if (dp->i_d.di_size < sizeof(xfs_dir_sf_hdr_t)) {
- ASSERT(XFS_FORCED_SHUTDOWN(dp->i_mount));
- return XFS_ERROR(EIO);
- }
- ASSERT(dp->i_df.if_bytes == dp->i_d.di_size);
- ASSERT(dp->i_df.if_u1.if_data != NULL);
- base = sizeof(xfs_dir_sf_hdr_t);
- sf = (xfs_dir_shortform_t *)dp->i_df.if_u1.if_data;
- sfe = &sf->list[0];
- for (i = sf->hdr.count-1; i >= 0; i--) {
- size = XFS_DIR_SF_ENTSIZE_BYENTRY(sfe);
- if (sfe->namelen == args->namelen &&
- sfe->name[0] == args->name[0] &&
- memcmp(sfe->name, args->name, args->namelen) == 0)
- break;
- base += size;
- sfe = XFS_DIR_SF_NEXTENTRY(sfe);
- }
- if (i < 0) {
- ASSERT(args->oknoent);
- return XFS_ERROR(ENOENT);
- }
-
- if ((base + size) != dp->i_d.di_size) {
- memmove(&((char *)sf)[base], &((char *)sf)[base+size],
- dp->i_d.di_size - (base+size));
- }
- sf->hdr.count--;
-
- xfs_idata_realloc(dp, -size, XFS_DATA_FORK);
- dp->i_d.di_size -= size;
- xfs_trans_log_inode(args->trans, dp, XFS_ILOG_CORE | XFS_ILOG_DDATA);
-
- return 0;
-}
-
-/*
- * Look up a name in a shortform directory structure.
- */
-int
-xfs_dir_shortform_lookup(xfs_da_args_t *args)
-{
- xfs_dir_shortform_t *sf;
- xfs_dir_sf_entry_t *sfe;
- int i;
- xfs_inode_t *dp;
-
- dp = args->dp;
- ASSERT(dp->i_df.if_flags & XFS_IFINLINE);
- /*
- * Catch the case where the conversion from shortform to leaf
- * failed part way through.
- */
- if (dp->i_d.di_size < sizeof(xfs_dir_sf_hdr_t)) {
- ASSERT(XFS_FORCED_SHUTDOWN(dp->i_mount));
- return XFS_ERROR(EIO);
- }
- ASSERT(dp->i_df.if_bytes == dp->i_d.di_size);
- ASSERT(dp->i_df.if_u1.if_data != NULL);
- sf = (xfs_dir_shortform_t *)dp->i_df.if_u1.if_data;
- if (args->namelen == 2 &&
- args->name[0] == '.' && args->name[1] == '.') {
- XFS_DIR_SF_GET_DIRINO(&sf->hdr.parent, &args->inumber);
- return(XFS_ERROR(EEXIST));
- }
- if (args->namelen == 1 && args->name[0] == '.') {
- args->inumber = dp->i_ino;
- return(XFS_ERROR(EEXIST));
- }
- sfe = &sf->list[0];
- for (i = sf->hdr.count-1; i >= 0; i--) {
- if (sfe->namelen == args->namelen &&
- sfe->name[0] == args->name[0] &&
- memcmp(args->name, sfe->name, args->namelen) == 0) {
- XFS_DIR_SF_GET_DIRINO(&sfe->inumber, &args->inumber);
- return(XFS_ERROR(EEXIST));
- }
- sfe = XFS_DIR_SF_NEXTENTRY(sfe);
- }
- ASSERT(args->oknoent);
- return(XFS_ERROR(ENOENT));
-}
-
-/*
- * Convert from using the shortform to the leaf.
- */
-int
-xfs_dir_shortform_to_leaf(xfs_da_args_t *iargs)
-{
- xfs_inode_t *dp;
- xfs_dir_shortform_t *sf;
- xfs_dir_sf_entry_t *sfe;
- xfs_da_args_t args;
- xfs_ino_t inumber;
- char *tmpbuffer;
- int retval, i, size;
- xfs_dablk_t blkno;
- xfs_dabuf_t *bp;
-
- dp = iargs->dp;
- /*
- * Catch the case where the conversion from shortform to leaf
- * failed part way through.
- */
- if (dp->i_d.di_size < sizeof(xfs_dir_sf_hdr_t)) {
- ASSERT(XFS_FORCED_SHUTDOWN(dp->i_mount));
- return XFS_ERROR(EIO);
- }
- ASSERT(dp->i_df.if_bytes == dp->i_d.di_size);
- ASSERT(dp->i_df.if_u1.if_data != NULL);
- size = dp->i_df.if_bytes;
- tmpbuffer = kmem_alloc(size, KM_SLEEP);
- ASSERT(tmpbuffer != NULL);
-
- memcpy(tmpbuffer, dp->i_df.if_u1.if_data, size);
-
- sf = (xfs_dir_shortform_t *)tmpbuffer;
- XFS_DIR_SF_GET_DIRINO(&sf->hdr.parent, &inumber);
-
- xfs_idata_realloc(dp, -size, XFS_DATA_FORK);
- dp->i_d.di_size = 0;
- xfs_trans_log_inode(iargs->trans, dp, XFS_ILOG_CORE);
- retval = xfs_da_grow_inode(iargs, &blkno);
- if (retval)
- goto out;
-
- ASSERT(blkno == 0);
- retval = xfs_dir_leaf_create(iargs, blkno, &bp);
- if (retval)
- goto out;
- xfs_da_buf_done(bp);
-
- args.name = ".";
- args.namelen = 1;
- args.hashval = xfs_dir_hash_dot;
- args.inumber = dp->i_ino;
- args.dp = dp;
- args.firstblock = iargs->firstblock;
- args.flist = iargs->flist;
- args.total = iargs->total;
- args.whichfork = XFS_DATA_FORK;
- args.trans = iargs->trans;
- args.justcheck = 0;
- args.addname = args.oknoent = 1;
- retval = xfs_dir_leaf_addname(&args);
- if (retval)
- goto out;
-
- args.name = "..";
- args.namelen = 2;
- args.hashval = xfs_dir_hash_dotdot;
- args.inumber = inumber;
- retval = xfs_dir_leaf_addname(&args);
- if (retval)
- goto out;
-
- sfe = &sf->list[0];
- for (i = 0; i < sf->hdr.count; i++) {
- args.name = (char *)(sfe->name);
- args.namelen = sfe->namelen;
- args.hashval = xfs_da_hashname((char *)(sfe->name),
- sfe->namelen);
- XFS_DIR_SF_GET_DIRINO(&sfe->inumber, &args.inumber);
- retval = xfs_dir_leaf_addname(&args);
- if (retval)
- goto out;
- sfe = XFS_DIR_SF_NEXTENTRY(sfe);
- }
- retval = 0;
-
-out:
- kmem_free(tmpbuffer, size);
- return retval;
-}
-
-STATIC int
-xfs_dir_shortform_compare(const void *a, const void *b)
-{
- xfs_dir_sf_sort_t *sa, *sb;
-
- sa = (xfs_dir_sf_sort_t *)a;
- sb = (xfs_dir_sf_sort_t *)b;
- if (sa->hash < sb->hash)
- return -1;
- else if (sa->hash > sb->hash)
- return 1;
- else
- return sa->entno - sb->entno;
-}
-
-/*
- * Copy out directory entries for getdents(), for shortform directories.
- */
-/*ARGSUSED*/
-int
-xfs_dir_shortform_getdents(xfs_inode_t *dp, uio_t *uio, int *eofp,
- xfs_dirent_t *dbp, xfs_dir_put_t put)
-{
- xfs_dir_shortform_t *sf;
- xfs_dir_sf_entry_t *sfe;
- int retval, i, sbsize, nsbuf, lastresid=0, want_entno;
- xfs_mount_t *mp;
- xfs_dahash_t cookhash, hash;
- xfs_dir_put_args_t p;
- xfs_dir_sf_sort_t *sbuf, *sbp;
-
- mp = dp->i_mount;
- sf = (xfs_dir_shortform_t *)dp->i_df.if_u1.if_data;
- cookhash = XFS_DA_COOKIE_HASH(mp, uio->uio_offset);
- want_entno = XFS_DA_COOKIE_ENTRY(mp, uio->uio_offset);
- nsbuf = sf->hdr.count + 2;
- sbsize = (nsbuf + 1) * sizeof(*sbuf);
- sbp = sbuf = kmem_alloc(sbsize, KM_SLEEP);
-
- xfs_dir_trace_g_du("sf: start", dp, uio);
-
- /*
- * Collect all the entries into the buffer.
- * Entry 0 is .
- */
- sbp->entno = 0;
- sbp->seqno = 0;
- sbp->hash = xfs_dir_hash_dot;
- sbp->ino = dp->i_ino;
- sbp->name = ".";
- sbp->namelen = 1;
- sbp++;
-
- /*
- * Entry 1 is ..
- */
- sbp->entno = 1;
- sbp->seqno = 0;
- sbp->hash = xfs_dir_hash_dotdot;
- sbp->ino = XFS_GET_DIR_INO8(sf->hdr.parent);
- sbp->name = "..";
- sbp->namelen = 2;
- sbp++;
-
- /*
- * Scan the directory data for the rest of the entries.
- */
- for (i = 0, sfe = &sf->list[0]; i < sf->hdr.count; i++) {
-
- if (unlikely(
- ((char *)sfe < (char *)sf) ||
- ((char *)sfe >= ((char *)sf + dp->i_df.if_bytes)))) {
- xfs_dir_trace_g_du("sf: corrupted", dp, uio);
- XFS_CORRUPTION_ERROR("xfs_dir_shortform_getdents",
- XFS_ERRLEVEL_LOW, mp, sfe);
- kmem_free(sbuf, sbsize);
- return XFS_ERROR(EFSCORRUPTED);
- }
-
- sbp->entno = i + 2;
- sbp->seqno = 0;
- sbp->hash = xfs_da_hashname((char *)sfe->name, sfe->namelen);
- sbp->ino = XFS_GET_DIR_INO8(sfe->inumber);
- sbp->name = (char *)sfe->name;
- sbp->namelen = sfe->namelen;
- sfe = XFS_DIR_SF_NEXTENTRY(sfe);
- sbp++;
- }
-
- /*
- * Sort the entries on hash then entno.
- */
- xfs_sort(sbuf, nsbuf, sizeof(*sbuf), xfs_dir_shortform_compare);
- /*
- * Stuff in last entry.
- */
- sbp->entno = nsbuf;
- sbp->hash = XFS_DA_MAXHASH;
- sbp->seqno = 0;
- /*
- * Figure out the sequence numbers in case there's a hash duplicate.
- */
- for (hash = sbuf->hash, sbp = sbuf + 1;
- sbp < &sbuf[nsbuf + 1]; sbp++) {
- if (sbp->hash == hash)
- sbp->seqno = sbp[-1].seqno + 1;
- else
- hash = sbp->hash;
- }
-
- /*
- * Set up put routine.
- */
- p.dbp = dbp;
- p.put = put;
- p.uio = uio;
-
- /*
- * Find our place.
- */
- for (sbp = sbuf; sbp < &sbuf[nsbuf + 1]; sbp++) {
- if (sbp->hash > cookhash ||
- (sbp->hash == cookhash && sbp->seqno >= want_entno))
- break;
- }
-
- /*
- * Did we fail to find anything? We stop at the last entry,
- * the one we put maxhash into.
- */
- if (sbp == &sbuf[nsbuf]) {
- kmem_free(sbuf, sbsize);
- xfs_dir_trace_g_du("sf: hash beyond end", dp, uio);
- uio->uio_offset = XFS_DA_MAKE_COOKIE(mp, 0, 0, XFS_DA_MAXHASH);
- *eofp = 1;
- return 0;
- }
-
- /*
- * Loop putting entries into the user buffer.
- */
- while (sbp < &sbuf[nsbuf]) {
- /*
- * Save the first resid in a run of equal-hashval entries
- * so that we can back them out if they don't all fit.
- */
- if (sbp->seqno == 0 || sbp == sbuf)
- lastresid = uio->uio_resid;
- XFS_PUT_COOKIE(p.cook, mp, 0, sbp[1].seqno, sbp[1].hash);
- p.ino = sbp->ino;
-#if XFS_BIG_INUMS
- p.ino += mp->m_inoadd;
-#endif
- p.name = sbp->name;
- p.namelen = sbp->namelen;
- retval = p.put(&p);
- if (!p.done) {
- uio->uio_offset =
- XFS_DA_MAKE_COOKIE(mp, 0, 0, sbp->hash);
- kmem_free(sbuf, sbsize);
- uio->uio_resid = lastresid;
- xfs_dir_trace_g_du("sf: E-O-B", dp, uio);
- return retval;
- }
- sbp++;
- }
- kmem_free(sbuf, sbsize);
- uio->uio_offset = p.cook.o;
- *eofp = 1;
- xfs_dir_trace_g_du("sf: E-O-F", dp, uio);
- return 0;
-}
-
-/*
- * Look up a name in a shortform directory structure, replace the inode number.
- */
-int
-xfs_dir_shortform_replace(xfs_da_args_t *args)
-{
- xfs_dir_shortform_t *sf;
- xfs_dir_sf_entry_t *sfe;
- xfs_inode_t *dp;
- int i;
-
- dp = args->dp;
- ASSERT(dp->i_df.if_flags & XFS_IFINLINE);
- /*
- * Catch the case where the conversion from shortform to leaf
- * failed part way through.
- */
- if (dp->i_d.di_size < sizeof(xfs_dir_sf_hdr_t)) {
- ASSERT(XFS_FORCED_SHUTDOWN(dp->i_mount));
- return XFS_ERROR(EIO);
- }
- ASSERT(dp->i_df.if_bytes == dp->i_d.di_size);
- ASSERT(dp->i_df.if_u1.if_data != NULL);
- sf = (xfs_dir_shortform_t *)dp->i_df.if_u1.if_data;
- if (args->namelen == 2 &&
- args->name[0] == '.' && args->name[1] == '.') {
- /* XXX - replace assert? */
- XFS_DIR_SF_PUT_DIRINO(&args->inumber, &sf->hdr.parent);
- xfs_trans_log_inode(args->trans, dp, XFS_ILOG_DDATA);
- return 0;
- }
- ASSERT(args->namelen != 1 || args->name[0] != '.');
- sfe = &sf->list[0];
- for (i = sf->hdr.count-1; i >= 0; i--) {
- if (sfe->namelen == args->namelen &&
- sfe->name[0] == args->name[0] &&
- memcmp(args->name, sfe->name, args->namelen) == 0) {
- ASSERT(memcmp((char *)&args->inumber,
- (char *)&sfe->inumber, sizeof(xfs_ino_t)));
- XFS_DIR_SF_PUT_DIRINO(&args->inumber, &sfe->inumber);
- xfs_trans_log_inode(args->trans, dp, XFS_ILOG_DDATA);
- return 0;
- }
- sfe = XFS_DIR_SF_NEXTENTRY(sfe);
- }
- ASSERT(args->oknoent);
- return XFS_ERROR(ENOENT);
-}
-
-/*
- * Convert a leaf directory to shortform structure
- */
-int
-xfs_dir_leaf_to_shortform(xfs_da_args_t *iargs)
-{
- xfs_dir_leafblock_t *leaf;
- xfs_dir_leaf_hdr_t *hdr;
- xfs_dir_leaf_entry_t *entry;
- xfs_dir_leaf_name_t *namest;
- xfs_da_args_t args;
- xfs_inode_t *dp;
- xfs_ino_t parent = 0;
- char *tmpbuffer;
- int retval, i;
- xfs_dabuf_t *bp;
-
- dp = iargs->dp;
- tmpbuffer = kmem_alloc(XFS_LBSIZE(dp->i_mount), KM_SLEEP);
- ASSERT(tmpbuffer != NULL);
-
- retval = xfs_da_read_buf(iargs->trans, iargs->dp, 0, -1, &bp,
- XFS_DATA_FORK);
- if (retval)
- goto out;
- ASSERT(bp != NULL);
- memcpy(tmpbuffer, bp->data, XFS_LBSIZE(dp->i_mount));
- leaf = (xfs_dir_leafblock_t *)tmpbuffer;
- ASSERT(be16_to_cpu(leaf->hdr.info.magic) == XFS_DIR_LEAF_MAGIC);
- memset(bp->data, 0, XFS_LBSIZE(dp->i_mount));
-
- /*
- * Find and special case the parent inode number
- */
- hdr = &leaf->hdr;
- entry = &leaf->entries[0];
- for (i = INT_GET(hdr->count, ARCH_CONVERT)-1; i >= 0; entry++, i--) {
- namest = XFS_DIR_LEAF_NAMESTRUCT(leaf, INT_GET(entry->nameidx, ARCH_CONVERT));
- if ((entry->namelen == 2) &&
- (namest->name[0] == '.') &&
- (namest->name[1] == '.')) {
- XFS_DIR_SF_GET_DIRINO(&namest->inumber, &parent);
- entry->nameidx = 0;
- } else if ((entry->namelen == 1) && (namest->name[0] == '.')) {
- entry->nameidx = 0;
- }
- }
- retval = xfs_da_shrink_inode(iargs, 0, bp);
- if (retval)
- goto out;
- retval = xfs_dir_shortform_create(iargs, parent);
- if (retval)
- goto out;
-
- /*
- * Copy the rest of the filenames
- */
- entry = &leaf->entries[0];
- args.dp = dp;
- args.firstblock = iargs->firstblock;
- args.flist = iargs->flist;
- args.total = iargs->total;
- args.whichfork = XFS_DATA_FORK;
- args.trans = iargs->trans;
- args.justcheck = 0;
- args.addname = args.oknoent = 1;
- for (i = 0; i < INT_GET(hdr->count, ARCH_CONVERT); entry++, i++) {
- if (!entry->nameidx)
- continue;
- namest = XFS_DIR_LEAF_NAMESTRUCT(leaf, INT_GET(entry->nameidx, ARCH_CONVERT));
- args.name = (char *)(namest->name);
- args.namelen = entry->namelen;
- args.hashval = INT_GET(entry->hashval, ARCH_CONVERT);
- XFS_DIR_SF_GET_DIRINO(&namest->inumber, &args.inumber);
- xfs_dir_shortform_addname(&args);
- }
-
-out:
- kmem_free(tmpbuffer, XFS_LBSIZE(dp->i_mount));
- return retval;
-}
-
-/*
- * Convert from using a single leaf to a root node and a leaf.
- */
-int
-xfs_dir_leaf_to_node(xfs_da_args_t *args)
-{
- xfs_dir_leafblock_t *leaf;
- xfs_da_intnode_t *node;
- xfs_inode_t *dp;
- xfs_dabuf_t *bp1, *bp2;
- xfs_dablk_t blkno;
- int retval;
-
- dp = args->dp;
- retval = xfs_da_grow_inode(args, &blkno);
- ASSERT(blkno == 1);
- if (retval)
- return retval;
- retval = xfs_da_read_buf(args->trans, args->dp, 0, -1, &bp1,
- XFS_DATA_FORK);
- if (retval)
- return retval;
- ASSERT(bp1 != NULL);
- retval = xfs_da_get_buf(args->trans, args->dp, 1, -1, &bp2,
- XFS_DATA_FORK);
- if (retval) {
- xfs_da_buf_done(bp1);
- return retval;
- }
- ASSERT(bp2 != NULL);
- memcpy(bp2->data, bp1->data, XFS_LBSIZE(dp->i_mount));
- xfs_da_buf_done(bp1);
- xfs_da_log_buf(args->trans, bp2, 0, XFS_LBSIZE(dp->i_mount) - 1);
-
- /*
- * Set up the new root node.
- */
- retval = xfs_da_node_create(args, 0, 1, &bp1, XFS_DATA_FORK);
- if (retval) {
- xfs_da_buf_done(bp2);
- return retval;
- }
- node = bp1->data;
- leaf = bp2->data;
- ASSERT(be16_to_cpu(leaf->hdr.info.magic) == XFS_DIR_LEAF_MAGIC);
- node->btree[0].hashval = cpu_to_be32(
- INT_GET(leaf->entries[
- INT_GET(leaf->hdr.count, ARCH_CONVERT)-1].hashval, ARCH_CONVERT));
- xfs_da_buf_done(bp2);
- node->btree[0].before = cpu_to_be32(blkno);
- node->hdr.count = cpu_to_be16(1);
- xfs_da_log_buf(args->trans, bp1,
- XFS_DA_LOGRANGE(node, &node->btree[0], sizeof(node->btree[0])));
- xfs_da_buf_done(bp1);
-
- return retval;
-}
-
-
-/*========================================================================
- * Routines used for growing the Btree.
- *========================================================================*/
-
-/*
- * Create the initial contents of a leaf directory
- * or a leaf in a node directory.
- */
-STATIC int
-xfs_dir_leaf_create(xfs_da_args_t *args, xfs_dablk_t blkno, xfs_dabuf_t **bpp)
-{
- xfs_dir_leafblock_t *leaf;
- xfs_dir_leaf_hdr_t *hdr;
- xfs_inode_t *dp;
- xfs_dabuf_t *bp;
- int retval;
-
- dp = args->dp;
- ASSERT(dp != NULL);
- retval = xfs_da_get_buf(args->trans, dp, blkno, -1, &bp, XFS_DATA_FORK);
- if (retval)
- return retval;
- ASSERT(bp != NULL);
- leaf = bp->data;
- memset((char *)leaf, 0, XFS_LBSIZE(dp->i_mount));
- hdr = &leaf->hdr;
- hdr->info.magic = cpu_to_be16(XFS_DIR_LEAF_MAGIC);
- INT_SET(hdr->firstused, ARCH_CONVERT, XFS_LBSIZE(dp->i_mount));
- if (!hdr->firstused)
- INT_SET(hdr->firstused, ARCH_CONVERT, XFS_LBSIZE(dp->i_mount) - 1);
- INT_SET(hdr->freemap[0].base, ARCH_CONVERT, sizeof(xfs_dir_leaf_hdr_t));
- INT_SET(hdr->freemap[0].size, ARCH_CONVERT, INT_GET(hdr->firstused, ARCH_CONVERT) - INT_GET(hdr->freemap[0].base, ARCH_CONVERT));
-
- xfs_da_log_buf(args->trans, bp, 0, XFS_LBSIZE(dp->i_mount) - 1);
-
- *bpp = bp;
- return 0;
-}
-
-/*
- * Split the leaf node, rebalance, then add the new entry.
- */
-int
-xfs_dir_leaf_split(xfs_da_state_t *state, xfs_da_state_blk_t *oldblk,
- xfs_da_state_blk_t *newblk)
-{
- xfs_dablk_t blkno;
- xfs_da_args_t *args;
- int error;
-
- /*
- * Allocate space for a new leaf node.
- */
- args = state->args;
- ASSERT(args != NULL);
- ASSERT(oldblk->magic == XFS_DIR_LEAF_MAGIC);
- error = xfs_da_grow_inode(args, &blkno);
- if (error)
- return error;
- error = xfs_dir_leaf_create(args, blkno, &newblk->bp);
- if (error)
- return error;
- newblk->blkno = blkno;
- newblk->magic = XFS_DIR_LEAF_MAGIC;
-
- /*
- * Rebalance the entries across the two leaves.
- */
- xfs_dir_leaf_rebalance(state, oldblk, newblk);
- error = xfs_da_blk_link(state, oldblk, newblk);
- if (error)
- return error;
-
- /*
- * Insert the new entry in the correct block.
- */
- if (state->inleaf) {
- error = xfs_dir_leaf_add(oldblk->bp, args, oldblk->index);
- } else {
- error = xfs_dir_leaf_add(newblk->bp, args, newblk->index);
- }
-
- /*
- * Update last hashval in each block since we added the name.
- */
- oldblk->hashval = xfs_dir_leaf_lasthash(oldblk->bp, NULL);
- newblk->hashval = xfs_dir_leaf_lasthash(newblk->bp, NULL);
- return error;
-}
-
-/*
- * Add a name to the leaf directory structure.
- *
- * Must take into account fragmented leaves and leaves where spacemap has
- * lost some freespace information (ie: holes).
- */
-int
-xfs_dir_leaf_add(xfs_dabuf_t *bp, xfs_da_args_t *args, int index)
-{
- xfs_dir_leafblock_t *leaf;
- xfs_dir_leaf_hdr_t *hdr;
- xfs_dir_leaf_map_t *map;
- int tablesize, entsize, sum, i, tmp, error;
-
- leaf = bp->data;
- ASSERT(be16_to_cpu(leaf->hdr.info.magic) == XFS_DIR_LEAF_MAGIC);
- ASSERT((index >= 0) && (index <= INT_GET(leaf->hdr.count, ARCH_CONVERT)));
- hdr = &leaf->hdr;
- entsize = XFS_DIR_LEAF_ENTSIZE_BYNAME(args->namelen);
-
- /*
- * Search through freemap for first-fit on new name length.
- * (may need to figure in size of entry struct too)
- */
- tablesize = (INT_GET(hdr->count, ARCH_CONVERT) + 1) * (uint)sizeof(xfs_dir_leaf_entry_t)
- + (uint)sizeof(xfs_dir_leaf_hdr_t);
- map = &hdr->freemap[XFS_DIR_LEAF_MAPSIZE-1];
- for (sum = 0, i = XFS_DIR_LEAF_MAPSIZE-1; i >= 0; map--, i--) {
- if (tablesize > INT_GET(hdr->firstused, ARCH_CONVERT)) {
- sum += INT_GET(map->size, ARCH_CONVERT);
- continue;
- }
- if (!map->size)
- continue; /* no space in this map */
- tmp = entsize;
- if (INT_GET(map->base, ARCH_CONVERT) < INT_GET(hdr->firstused, ARCH_CONVERT))
- tmp += (uint)sizeof(xfs_dir_leaf_entry_t);
- if (INT_GET(map->size, ARCH_CONVERT) >= tmp) {
- if (!args->justcheck)
- xfs_dir_leaf_add_work(bp, args, index, i);
- return 0;
- }
- sum += INT_GET(map->size, ARCH_CONVERT);
- }
-
- /*
- * If there are no holes in the address space of the block,
- * and we don't have enough freespace, then compaction will do us
- * no good and we should just give up.
- */
- if (!hdr->holes && (sum < entsize))
- return XFS_ERROR(ENOSPC);
-
- /*
- * Compact the entries to coalesce free space.
- * Pass the justcheck flag so the checking pass can return
- * an error, without changing anything, if it won't fit.
- */
- error = xfs_dir_leaf_compact(args->trans, bp,
- args->total == 0 ?
- entsize +
- (uint)sizeof(xfs_dir_leaf_entry_t) : 0,
- args->justcheck);
- if (error)
- return error;
- /*
- * After compaction, the block is guaranteed to have only one
- * free region, in freemap[0]. If it is not big enough, give up.
- */
- if (INT_GET(hdr->freemap[0].size, ARCH_CONVERT) <
- (entsize + (uint)sizeof(xfs_dir_leaf_entry_t)))
- return XFS_ERROR(ENOSPC);
-
- if (!args->justcheck)
- xfs_dir_leaf_add_work(bp, args, index, 0);
- return 0;
-}
-
-/*
- * Add a name to a leaf directory structure.
- */
-STATIC void
-xfs_dir_leaf_add_work(xfs_dabuf_t *bp, xfs_da_args_t *args, int index,
- int mapindex)
-{
- xfs_dir_leafblock_t *leaf;
- xfs_dir_leaf_hdr_t *hdr;
- xfs_dir_leaf_entry_t *entry;
- xfs_dir_leaf_name_t *namest;
- xfs_dir_leaf_map_t *map;
- /* REFERENCED */
- xfs_mount_t *mp;
- int tmp, i;
-
- leaf = bp->data;
- ASSERT(be16_to_cpu(leaf->hdr.info.magic) == XFS_DIR_LEAF_MAGIC);
- hdr = &leaf->hdr;
- ASSERT((mapindex >= 0) && (mapindex < XFS_DIR_LEAF_MAPSIZE));
- ASSERT((index >= 0) && (index <= INT_GET(hdr->count, ARCH_CONVERT)));
-
- /*
- * Force open some space in the entry array and fill it in.
- */
- entry = &leaf->entries[index];
- if (index < INT_GET(hdr->count, ARCH_CONVERT)) {
- tmp = INT_GET(hdr->count, ARCH_CONVERT) - index;
- tmp *= (uint)sizeof(xfs_dir_leaf_entry_t);
- memmove(entry + 1, entry, tmp);
- xfs_da_log_buf(args->trans, bp,
- XFS_DA_LOGRANGE(leaf, entry, tmp + (uint)sizeof(*entry)));
- }
- INT_MOD(hdr->count, ARCH_CONVERT, +1);
-
- /*
- * Allocate space for the new string (at the end of the run).
- */
- map = &hdr->freemap[mapindex];
- mp = args->trans->t_mountp;
- ASSERT(INT_GET(map->base, ARCH_CONVERT) < XFS_LBSIZE(mp));
- ASSERT(INT_GET(map->size, ARCH_CONVERT) >= XFS_DIR_LEAF_ENTSIZE_BYNAME(args->namelen));
- ASSERT(INT_GET(map->size, ARCH_CONVERT) < XFS_LBSIZE(mp));
- INT_MOD(map->size, ARCH_CONVERT, -(XFS_DIR_LEAF_ENTSIZE_BYNAME(args->namelen)));
- INT_SET(entry->nameidx, ARCH_CONVERT, INT_GET(map->base, ARCH_CONVERT) + INT_GET(map->size, ARCH_CONVERT));
- INT_SET(entry->hashval, ARCH_CONVERT, args->hashval);
- entry->namelen = args->namelen;
- xfs_da_log_buf(args->trans, bp,
- XFS_DA_LOGRANGE(leaf, entry, sizeof(*entry)));
-
- /*
- * Copy the string and inode number into the new space.
- */
- namest = XFS_DIR_LEAF_NAMESTRUCT(leaf, INT_GET(entry->nameidx, ARCH_CONVERT));
- XFS_DIR_SF_PUT_DIRINO(&args->inumber, &namest->inumber);
- memcpy(namest->name, args->name, args->namelen);
- xfs_da_log_buf(args->trans, bp,
- XFS_DA_LOGRANGE(leaf, namest, XFS_DIR_LEAF_ENTSIZE_BYENTRY(entry)));
-
- /*
- * Update the control info for this leaf node
- */
- if (INT_GET(entry->nameidx, ARCH_CONVERT) < INT_GET(hdr->firstused, ARCH_CONVERT))
- INT_COPY(hdr->firstused, entry->nameidx, ARCH_CONVERT);
- ASSERT(INT_GET(hdr->firstused, ARCH_CONVERT) >= ((INT_GET(hdr->count, ARCH_CONVERT)*sizeof(*entry))+sizeof(*hdr)));
- tmp = (INT_GET(hdr->count, ARCH_CONVERT)-1) * (uint)sizeof(xfs_dir_leaf_entry_t)
- + (uint)sizeof(xfs_dir_leaf_hdr_t);
- map = &hdr->freemap[0];
- for (i = 0; i < XFS_DIR_LEAF_MAPSIZE; map++, i++) {
- if (INT_GET(map->base, ARCH_CONVERT) == tmp) {
- INT_MOD(map->base, ARCH_CONVERT, (uint)sizeof(xfs_dir_leaf_entry_t));
- INT_MOD(map->size, ARCH_CONVERT, -((uint)sizeof(xfs_dir_leaf_entry_t)));
- }
- }
- INT_MOD(hdr->namebytes, ARCH_CONVERT, args->namelen);
- xfs_da_log_buf(args->trans, bp,
- XFS_DA_LOGRANGE(leaf, hdr, sizeof(*hdr)));
-}
-
-/*
- * Garbage collect a leaf directory block by copying it to a new buffer.
- */
-STATIC int
-xfs_dir_leaf_compact(xfs_trans_t *trans, xfs_dabuf_t *bp, int musthave,
- int justcheck)
-{
- xfs_dir_leafblock_t *leaf_s, *leaf_d;
- xfs_dir_leaf_hdr_t *hdr_s, *hdr_d;
- xfs_mount_t *mp;
- char *tmpbuffer;
- char *tmpbuffer2=NULL;
- int rval;
- int lbsize;
-
- mp = trans->t_mountp;
- lbsize = XFS_LBSIZE(mp);
- tmpbuffer = kmem_alloc(lbsize, KM_SLEEP);
- ASSERT(tmpbuffer != NULL);
- memcpy(tmpbuffer, bp->data, lbsize);
-
- /*
- * Make a second copy in case xfs_dir_leaf_moveents()
- * below destroys the original.
- */
- if (musthave || justcheck) {
- tmpbuffer2 = kmem_alloc(lbsize, KM_SLEEP);
- memcpy(tmpbuffer2, bp->data, lbsize);
- }
- memset(bp->data, 0, lbsize);
-
- /*
- * Copy basic information
- */
- leaf_s = (xfs_dir_leafblock_t *)tmpbuffer;
- leaf_d = bp->data;
- hdr_s = &leaf_s->hdr;
- hdr_d = &leaf_d->hdr;
- hdr_d->info = hdr_s->info; /* struct copy */
- INT_SET(hdr_d->firstused, ARCH_CONVERT, lbsize);
- if (!hdr_d->firstused)
- INT_SET(hdr_d->firstused, ARCH_CONVERT, lbsize - 1);
- hdr_d->namebytes = 0;
- hdr_d->count = 0;
- hdr_d->holes = 0;
- INT_SET(hdr_d->freemap[0].base, ARCH_CONVERT, sizeof(xfs_dir_leaf_hdr_t));
- INT_SET(hdr_d->freemap[0].size, ARCH_CONVERT, INT_GET(hdr_d->firstused, ARCH_CONVERT) - INT_GET(hdr_d->freemap[0].base, ARCH_CONVERT));
-
- /*
- * Copy all entry's in the same (sorted) order,
- * but allocate filenames packed and in sequence.
- * This changes the source (leaf_s) as well.
- */
- xfs_dir_leaf_moveents(leaf_s, 0, leaf_d, 0, (int)INT_GET(hdr_s->count, ARCH_CONVERT), mp);
-
- if (musthave && INT_GET(hdr_d->freemap[0].size, ARCH_CONVERT) < musthave)
- rval = XFS_ERROR(ENOSPC);
- else
- rval = 0;
-
- if (justcheck || rval == ENOSPC) {
- ASSERT(tmpbuffer2);
- memcpy(bp->data, tmpbuffer2, lbsize);
- } else {
- xfs_da_log_buf(trans, bp, 0, lbsize - 1);
- }
-
- kmem_free(tmpbuffer, lbsize);
- if (musthave || justcheck)
- kmem_free(tmpbuffer2, lbsize);
- return rval;
-}
-
-/*
- * Redistribute the directory entries between two leaf nodes,
- * taking into account the size of the new entry.
- *
- * NOTE: if new block is empty, then it will get the upper half of old block.
- */
-STATIC void
-xfs_dir_leaf_rebalance(xfs_da_state_t *state, xfs_da_state_blk_t *blk1,
- xfs_da_state_blk_t *blk2)
-{
- xfs_da_state_blk_t *tmp_blk;
- xfs_dir_leafblock_t *leaf1, *leaf2;
- xfs_dir_leaf_hdr_t *hdr1, *hdr2;
- int count, totallen, max, space, swap;
-
- /*
- * Set up environment.
- */
- ASSERT(blk1->magic == XFS_DIR_LEAF_MAGIC);
- ASSERT(blk2->magic == XFS_DIR_LEAF_MAGIC);
- leaf1 = blk1->bp->data;
- leaf2 = blk2->bp->data;
- ASSERT(be16_to_cpu(leaf1->hdr.info.magic) == XFS_DIR_LEAF_MAGIC);
- ASSERT(be16_to_cpu(leaf2->hdr.info.magic) == XFS_DIR_LEAF_MAGIC);
-
- /*
- * Check ordering of blocks, reverse if it makes things simpler.
- */
- swap = 0;
- if (xfs_dir_leaf_order(blk1->bp, blk2->bp)) {
- tmp_blk = blk1;
- blk1 = blk2;
- blk2 = tmp_blk;
- leaf1 = blk1->bp->data;
- leaf2 = blk2->bp->data;
- swap = 1;
- }
- hdr1 = &leaf1->hdr;
- hdr2 = &leaf2->hdr;
-
- /*
- * Examine entries until we reduce the absolute difference in
- * byte usage between the two blocks to a minimum. Then get
- * the direction to copy and the number of elements to move.
- */
- state->inleaf = xfs_dir_leaf_figure_balance(state, blk1, blk2,
- &count, &totallen);
- if (swap)
- state->inleaf = !state->inleaf;
-
- /*
- * Move any entries required from leaf to leaf:
- */
- if (count < INT_GET(hdr1->count, ARCH_CONVERT)) {
- /*
- * Figure the total bytes to be added to the destination leaf.
- */
- count = INT_GET(hdr1->count, ARCH_CONVERT) - count; /* number entries being moved */
- space = INT_GET(hdr1->namebytes, ARCH_CONVERT) - totallen;
- space += count * ((uint)sizeof(xfs_dir_leaf_name_t)-1);
- space += count * (uint)sizeof(xfs_dir_leaf_entry_t);
-
- /*
- * leaf2 is the destination, compact it if it looks tight.
- */
- max = INT_GET(hdr2->firstused, ARCH_CONVERT) - (uint)sizeof(xfs_dir_leaf_hdr_t);
- max -= INT_GET(hdr2->count, ARCH_CONVERT) * (uint)sizeof(xfs_dir_leaf_entry_t);
- if (space > max) {
- xfs_dir_leaf_compact(state->args->trans, blk2->bp,
- 0, 0);
- }
-
- /*
- * Move high entries from leaf1 to low end of leaf2.
- */
- xfs_dir_leaf_moveents(leaf1, INT_GET(hdr1->count, ARCH_CONVERT) - count,
- leaf2, 0, count, state->mp);
-
- xfs_da_log_buf(state->args->trans, blk1->bp, 0,
- state->blocksize-1);
- xfs_da_log_buf(state->args->trans, blk2->bp, 0,
- state->blocksize-1);
-
- } else if (count > INT_GET(hdr1->count, ARCH_CONVERT)) {
- /*
- * Figure the total bytes to be added to the destination leaf.
- */
- count -= INT_GET(hdr1->count, ARCH_CONVERT); /* number entries being moved */
- space = totallen - INT_GET(hdr1->namebytes, ARCH_CONVERT);
- space += count * ((uint)sizeof(xfs_dir_leaf_name_t)-1);
- space += count * (uint)sizeof(xfs_dir_leaf_entry_t);
-
- /*
- * leaf1 is the destination, compact it if it looks tight.
- */
- max = INT_GET(hdr1->firstused, ARCH_CONVERT) - (uint)sizeof(xfs_dir_leaf_hdr_t);
- max -= INT_GET(hdr1->count, ARCH_CONVERT) * (uint)sizeof(xfs_dir_leaf_entry_t);
- if (space > max) {
- xfs_dir_leaf_compact(state->args->trans, blk1->bp,
- 0, 0);
- }
-
- /*
- * Move low entries from leaf2 to high end of leaf1.
- */
- xfs_dir_leaf_moveents(leaf2, 0, leaf1, (int)INT_GET(hdr1->count, ARCH_CONVERT),
- count, state->mp);
-
- xfs_da_log_buf(state->args->trans, blk1->bp, 0,
- state->blocksize-1);
- xfs_da_log_buf(state->args->trans, blk2->bp, 0,
- state->blocksize-1);
- }
-
- /*
- * Copy out last hashval in each block for B-tree code.
- */
- blk1->hashval = INT_GET(leaf1->entries[ INT_GET(leaf1->hdr.count, ARCH_CONVERT)-1 ].hashval, ARCH_CONVERT);
- blk2->hashval = INT_GET(leaf2->entries[ INT_GET(leaf2->hdr.count, ARCH_CONVERT)-1 ].hashval, ARCH_CONVERT);
-
- /*
- * Adjust the expected index for insertion.
- * GROT: this doesn't work unless blk2 was originally empty.
- */
- if (!state->inleaf) {
- blk2->index = blk1->index - INT_GET(leaf1->hdr.count, ARCH_CONVERT);
- }
-}
-
-/*
- * Examine entries until we reduce the absolute difference in
- * byte usage between the two blocks to a minimum.
- * GROT: Is this really necessary? With other than a 512 byte blocksize,
- * GROT: there will always be enough room in either block for a new entry.
- * GROT: Do a double-split for this case?
- */
-STATIC int
-xfs_dir_leaf_figure_balance(xfs_da_state_t *state,
- xfs_da_state_blk_t *blk1,
- xfs_da_state_blk_t *blk2,
- int *countarg, int *namebytesarg)
-{
- xfs_dir_leafblock_t *leaf1, *leaf2;
- xfs_dir_leaf_hdr_t *hdr1, *hdr2;
- xfs_dir_leaf_entry_t *entry;
- int count, max, totallen, half;
- int lastdelta, foundit, tmp;
-
- /*
- * Set up environment.
- */
- leaf1 = blk1->bp->data;
- leaf2 = blk2->bp->data;
- hdr1 = &leaf1->hdr;
- hdr2 = &leaf2->hdr;
- foundit = 0;
- totallen = 0;
-
- /*
- * Examine entries until we reduce the absolute difference in
- * byte usage between the two blocks to a minimum.
- */
- max = INT_GET(hdr1->count, ARCH_CONVERT) + INT_GET(hdr2->count, ARCH_CONVERT);
- half = (max+1) * (uint)(sizeof(*entry)+sizeof(xfs_dir_leaf_entry_t)-1);
- half += INT_GET(hdr1->namebytes, ARCH_CONVERT) + INT_GET(hdr2->namebytes, ARCH_CONVERT) + state->args->namelen;
- half /= 2;
- lastdelta = state->blocksize;
- entry = &leaf1->entries[0];
- for (count = 0; count < max; entry++, count++) {
-
-#define XFS_DIR_ABS(A) (((A) < 0) ? -(A) : (A))
- /*
- * The new entry is in the first block, account for it.
- */
- if (count == blk1->index) {
- tmp = totallen + (uint)sizeof(*entry)
- + XFS_DIR_LEAF_ENTSIZE_BYNAME(state->args->namelen);
- if (XFS_DIR_ABS(half - tmp) > lastdelta)
- break;
- lastdelta = XFS_DIR_ABS(half - tmp);
- totallen = tmp;
- foundit = 1;
- }
-
- /*
- * Wrap around into the second block if necessary.
- */
- if (count == INT_GET(hdr1->count, ARCH_CONVERT)) {
- leaf1 = leaf2;
- entry = &leaf1->entries[0];
- }
-
- /*
- * Figure out if next leaf entry would be too much.
- */
- tmp = totallen + (uint)sizeof(*entry)
- + XFS_DIR_LEAF_ENTSIZE_BYENTRY(entry);
- if (XFS_DIR_ABS(half - tmp) > lastdelta)
- break;
- lastdelta = XFS_DIR_ABS(half - tmp);
- totallen = tmp;
-#undef XFS_DIR_ABS
- }
-
- /*
- * Calculate the number of namebytes that will end up in lower block.
- * If new entry not in lower block, fix up the count.
- */
- totallen -=
- count * (uint)(sizeof(*entry)+sizeof(xfs_dir_leaf_entry_t)-1);
- if (foundit) {
- totallen -= (sizeof(*entry)+sizeof(xfs_dir_leaf_entry_t)-1) +
- state->args->namelen;
- }
-
- *countarg = count;
- *namebytesarg = totallen;
- return foundit;
-}
-
-/*========================================================================
- * Routines used for shrinking the Btree.
- *========================================================================*/
-
-/*
- * Check a leaf block and its neighbors to see if the block should be
- * collapsed into one or the other neighbor. Always keep the block
- * with the smaller block number.
- * If the current block is over 50% full, don't try to join it, return 0.
- * If the block is empty, fill in the state structure and return 2.
- * If it can be collapsed, fill in the state structure and return 1.
- * If nothing can be done, return 0.
- */
-int
-xfs_dir_leaf_toosmall(xfs_da_state_t *state, int *action)
-{
- xfs_dir_leafblock_t *leaf;
- xfs_da_state_blk_t *blk;
- xfs_da_blkinfo_t *info;
- int count, bytes, forward, error, retval, i;
- xfs_dablk_t blkno;
- xfs_dabuf_t *bp;
-
- /*
- * Check for the degenerate case of the block being over 50% full.
- * If so, it's not worth even looking to see if we might be able
- * to coalesce with a sibling.
- */
- blk = &state->path.blk[ state->path.active-1 ];
- info = blk->bp->data;
- ASSERT(be16_to_cpu(info->magic) == XFS_DIR_LEAF_MAGIC);
- leaf = (xfs_dir_leafblock_t *)info;
- count = INT_GET(leaf->hdr.count, ARCH_CONVERT);
- bytes = (uint)sizeof(xfs_dir_leaf_hdr_t) +
- count * (uint)sizeof(xfs_dir_leaf_entry_t) +
- count * ((uint)sizeof(xfs_dir_leaf_name_t)-1) +
- INT_GET(leaf->hdr.namebytes, ARCH_CONVERT);
- if (bytes > (state->blocksize >> 1)) {
- *action = 0; /* blk over 50%, don't try to join */
- return 0;
- }
-
- /*
- * Check for the degenerate case of the block being empty.
- * If the block is empty, we'll simply delete it, no need to
- * coalesce it with a sibling block. We choose (arbitrarily)
- * to merge with the forward block unless it is NULL.
- */
- if (count == 0) {
- /*
- * Make altpath point to the block we want to keep and
- * path point to the block we want to drop (this one).
- */
- forward = (info->forw != 0);
- memcpy(&state->altpath, &state->path, sizeof(state->path));
- error = xfs_da_path_shift(state, &state->altpath, forward,
- 0, &retval);
- if (error)
- return error;
- if (retval) {
- *action = 0;
- } else {
- *action = 2;
- }
- return 0;
- }
-
- /*
- * Examine each sibling block to see if we can coalesce with
- * at least 25% free space to spare. We need to figure out
- * whether to merge with the forward or the backward block.
- * We prefer coalescing with the lower numbered sibling so as
- * to shrink a directory over time.
- */
- forward = (be32_to_cpu(info->forw) < be32_to_cpu(info->back)); /* start with smaller blk num */
- for (i = 0; i < 2; forward = !forward, i++) {
- if (forward)
- blkno = be32_to_cpu(info->forw);
- else
- blkno = be32_to_cpu(info->back);
- if (blkno == 0)
- continue;
- error = xfs_da_read_buf(state->args->trans, state->args->dp,
- blkno, -1, &bp,
- XFS_DATA_FORK);
- if (error)
- return error;
- ASSERT(bp != NULL);
-
- leaf = (xfs_dir_leafblock_t *)info;
- count = INT_GET(leaf->hdr.count, ARCH_CONVERT);
- bytes = state->blocksize - (state->blocksize>>2);
- bytes -= INT_GET(leaf->hdr.namebytes, ARCH_CONVERT);
- leaf = bp->data;
- ASSERT(be16_to_cpu(leaf->hdr.info.magic) == XFS_DIR_LEAF_MAGIC);
- count += INT_GET(leaf->hdr.count, ARCH_CONVERT);
- bytes -= INT_GET(leaf->hdr.namebytes, ARCH_CONVERT);
- bytes -= count * ((uint)sizeof(xfs_dir_leaf_name_t) - 1);
- bytes -= count * (uint)sizeof(xfs_dir_leaf_entry_t);
- bytes -= (uint)sizeof(xfs_dir_leaf_hdr_t);
- if (bytes >= 0)
- break; /* fits with at least 25% to spare */
-
- xfs_da_brelse(state->args->trans, bp);
- }
- if (i >= 2) {
- *action = 0;
- return 0;
- }
- xfs_da_buf_done(bp);
-
- /*
- * Make altpath point to the block we want to keep (the lower
- * numbered block) and path point to the block we want to drop.
- */
- memcpy(&state->altpath, &state->path, sizeof(state->path));
- if (blkno < blk->blkno) {
- error = xfs_da_path_shift(state, &state->altpath, forward,
- 0, &retval);
- } else {
- error = xfs_da_path_shift(state, &state->path, forward,
- 0, &retval);
- }
- if (error)
- return error;
- if (retval) {
- *action = 0;
- } else {
- *action = 1;
- }
- return 0;
-}
-
-/*
- * Remove a name from the leaf directory structure.
- *
- * Return 1 if leaf is less than 37% full, 0 if >= 37% full.
- * If two leaves are 37% full, when combined they will leave 25% free.
- */
-int
-xfs_dir_leaf_remove(xfs_trans_t *trans, xfs_dabuf_t *bp, int index)
-{
- xfs_dir_leafblock_t *leaf;
- xfs_dir_leaf_hdr_t *hdr;
- xfs_dir_leaf_map_t *map;
- xfs_dir_leaf_entry_t *entry;
- xfs_dir_leaf_name_t *namest;
- int before, after, smallest, entsize;
- int tablesize, tmp, i;
- xfs_mount_t *mp;
-
- leaf = bp->data;
- ASSERT(be16_to_cpu(leaf->hdr.info.magic) == XFS_DIR_LEAF_MAGIC);
- hdr = &leaf->hdr;
- mp = trans->t_mountp;
- ASSERT((INT_GET(hdr->count, ARCH_CONVERT) > 0) && (INT_GET(hdr->count, ARCH_CONVERT) < (XFS_LBSIZE(mp)/8)));
- ASSERT((index >= 0) && (index < INT_GET(hdr->count, ARCH_CONVERT)));
- ASSERT(INT_GET(hdr->firstused, ARCH_CONVERT) >= ((INT_GET(hdr->count, ARCH_CONVERT)*sizeof(*entry))+sizeof(*hdr)));
- entry = &leaf->entries[index];
- ASSERT(INT_GET(entry->nameidx, ARCH_CONVERT) >= INT_GET(hdr->firstused, ARCH_CONVERT));
- ASSERT(INT_GET(entry->nameidx, ARCH_CONVERT) < XFS_LBSIZE(mp));
-
- /*
- * Scan through free region table:
- * check for adjacency of free'd entry with an existing one,
- * find smallest free region in case we need to replace it,
- * adjust any map that borders the entry table,
- */
- tablesize = INT_GET(hdr->count, ARCH_CONVERT) * (uint)sizeof(xfs_dir_leaf_entry_t)
- + (uint)sizeof(xfs_dir_leaf_hdr_t);
- map = &hdr->freemap[0];
- tmp = INT_GET(map->size, ARCH_CONVERT);
- before = after = -1;
- smallest = XFS_DIR_LEAF_MAPSIZE - 1;
- entsize = XFS_DIR_LEAF_ENTSIZE_BYENTRY(entry);
- for (i = 0; i < XFS_DIR_LEAF_MAPSIZE; map++, i++) {
- ASSERT(INT_GET(map->base, ARCH_CONVERT) < XFS_LBSIZE(mp));
- ASSERT(INT_GET(map->size, ARCH_CONVERT) < XFS_LBSIZE(mp));
- if (INT_GET(map->base, ARCH_CONVERT) == tablesize) {
- INT_MOD(map->base, ARCH_CONVERT, -((uint)sizeof(xfs_dir_leaf_entry_t)));
- INT_MOD(map->size, ARCH_CONVERT, (uint)sizeof(xfs_dir_leaf_entry_t));
- }
-
- if ((INT_GET(map->base, ARCH_CONVERT) + INT_GET(map->size, ARCH_CONVERT)) == INT_GET(entry->nameidx, ARCH_CONVERT)) {
- before = i;
- } else if (INT_GET(map->base, ARCH_CONVERT) == (INT_GET(entry->nameidx, ARCH_CONVERT) + entsize)) {
- after = i;
- } else if (INT_GET(map->size, ARCH_CONVERT) < tmp) {
- tmp = INT_GET(map->size, ARCH_CONVERT);
- smallest = i;
- }
- }
-
- /*
- * Coalesce adjacent freemap regions,
- * or replace the smallest region.
- */
- if ((before >= 0) || (after >= 0)) {
- if ((before >= 0) && (after >= 0)) {
- map = &hdr->freemap[before];
- INT_MOD(map->size, ARCH_CONVERT, entsize);
- INT_MOD(map->size, ARCH_CONVERT, INT_GET(hdr->freemap[after].size, ARCH_CONVERT));
- hdr->freemap[after].base = 0;
- hdr->freemap[after].size = 0;
- } else if (before >= 0) {
- map = &hdr->freemap[before];
- INT_MOD(map->size, ARCH_CONVERT, entsize);
- } else {
- map = &hdr->freemap[after];
- INT_COPY(map->base, entry->nameidx, ARCH_CONVERT);
- INT_MOD(map->size, ARCH_CONVERT, entsize);
- }
- } else {
- /*
- * Replace smallest region (if it is smaller than free'd entry)
- */
- map = &hdr->freemap[smallest];
- if (INT_GET(map->size, ARCH_CONVERT) < entsize) {
- INT_COPY(map->base, entry->nameidx, ARCH_CONVERT);
- INT_SET(map->size, ARCH_CONVERT, entsize);
- }
- }
-
- /*
- * Did we remove the first entry?
- */
- if (INT_GET(entry->nameidx, ARCH_CONVERT) == INT_GET(hdr->firstused, ARCH_CONVERT))
- smallest = 1;
- else
- smallest = 0;
-
- /*
- * Compress the remaining entries and zero out the removed stuff.
- */
- namest = XFS_DIR_LEAF_NAMESTRUCT(leaf, INT_GET(entry->nameidx, ARCH_CONVERT));
- memset((char *)namest, 0, entsize);
- xfs_da_log_buf(trans, bp, XFS_DA_LOGRANGE(leaf, namest, entsize));
-
- INT_MOD(hdr->namebytes, ARCH_CONVERT, -(entry->namelen));
- tmp = (INT_GET(hdr->count, ARCH_CONVERT) - index) * (uint)sizeof(xfs_dir_leaf_entry_t);
- memmove(entry, entry + 1, tmp);
- INT_MOD(hdr->count, ARCH_CONVERT, -1);
- xfs_da_log_buf(trans, bp,
- XFS_DA_LOGRANGE(leaf, entry, tmp + (uint)sizeof(*entry)));
- entry = &leaf->entries[INT_GET(hdr->count, ARCH_CONVERT)];
- memset((char *)entry, 0, sizeof(xfs_dir_leaf_entry_t));
-
- /*
- * If we removed the first entry, re-find the first used byte
- * in the name area. Note that if the entry was the "firstused",
- * then we don't have a "hole" in our block resulting from
- * removing the name.
- */
- if (smallest) {
- tmp = XFS_LBSIZE(mp);
- entry = &leaf->entries[0];
- for (i = INT_GET(hdr->count, ARCH_CONVERT)-1; i >= 0; entry++, i--) {
- ASSERT(INT_GET(entry->nameidx, ARCH_CONVERT) >= INT_GET(hdr->firstused, ARCH_CONVERT));
- ASSERT(INT_GET(entry->nameidx, ARCH_CONVERT) < XFS_LBSIZE(mp));
- if (INT_GET(entry->nameidx, ARCH_CONVERT) < tmp)
- tmp = INT_GET(entry->nameidx, ARCH_CONVERT);
- }
- INT_SET(hdr->firstused, ARCH_CONVERT, tmp);
- if (!hdr->firstused)
- INT_SET(hdr->firstused, ARCH_CONVERT, tmp - 1);
- } else {
- hdr->holes = 1; /* mark as needing compaction */
- }
-
- xfs_da_log_buf(trans, bp, XFS_DA_LOGRANGE(leaf, hdr, sizeof(*hdr)));
-
- /*
- * Check if leaf is less than 50% full, caller may want to
- * "join" the leaf with a sibling if so.
- */
- tmp = (uint)sizeof(xfs_dir_leaf_hdr_t);
- tmp += INT_GET(leaf->hdr.count, ARCH_CONVERT) * (uint)sizeof(xfs_dir_leaf_entry_t);
- tmp += INT_GET(leaf->hdr.count, ARCH_CONVERT) * ((uint)sizeof(xfs_dir_leaf_name_t) - 1);
- tmp += INT_GET(leaf->hdr.namebytes, ARCH_CONVERT);
- if (tmp < mp->m_dir_magicpct)
- return 1; /* leaf is < 37% full */
- return 0;
-}
-
-/*
- * Move all the directory entries from drop_leaf into save_leaf.
- */
-void
-xfs_dir_leaf_unbalance(xfs_da_state_t *state, xfs_da_state_blk_t *drop_blk,
- xfs_da_state_blk_t *save_blk)
-{
- xfs_dir_leafblock_t *drop_leaf, *save_leaf, *tmp_leaf;
- xfs_dir_leaf_hdr_t *drop_hdr, *save_hdr, *tmp_hdr;
- xfs_mount_t *mp;
- char *tmpbuffer;
-
- /*
- * Set up environment.
- */
- mp = state->mp;
- ASSERT(drop_blk->magic == XFS_DIR_LEAF_MAGIC);
- ASSERT(save_blk->magic == XFS_DIR_LEAF_MAGIC);
- drop_leaf = drop_blk->bp->data;
- save_leaf = save_blk->bp->data;
- ASSERT(be16_to_cpu(drop_leaf->hdr.info.magic) == XFS_DIR_LEAF_MAGIC);
- ASSERT(be16_to_cpu(save_leaf->hdr.info.magic) == XFS_DIR_LEAF_MAGIC);
- drop_hdr = &drop_leaf->hdr;
- save_hdr = &save_leaf->hdr;
-
- /*
- * Save last hashval from dying block for later Btree fixup.
- */
- drop_blk->hashval = INT_GET(drop_leaf->entries[ drop_leaf->hdr.count-1 ].hashval, ARCH_CONVERT);
-
- /*
- * Check if we need a temp buffer, or can we do it in place.
- * Note that we don't check "leaf" for holes because we will
- * always be dropping it, toosmall() decided that for us already.
- */
- if (save_hdr->holes == 0) {
- /*
- * dest leaf has no holes, so we add there. May need
- * to make some room in the entry array.
- */
- if (xfs_dir_leaf_order(save_blk->bp, drop_blk->bp)) {
- xfs_dir_leaf_moveents(drop_leaf, 0, save_leaf, 0,
- (int)INT_GET(drop_hdr->count, ARCH_CONVERT), mp);
- } else {
- xfs_dir_leaf_moveents(drop_leaf, 0,
- save_leaf, INT_GET(save_hdr->count, ARCH_CONVERT),
- (int)INT_GET(drop_hdr->count, ARCH_CONVERT), mp);
- }
- } else {
- /*
- * Destination has holes, so we make a temporary copy
- * of the leaf and add them both to that.
- */
- tmpbuffer = kmem_alloc(state->blocksize, KM_SLEEP);
- ASSERT(tmpbuffer != NULL);
- memset(tmpbuffer, 0, state->blocksize);
- tmp_leaf = (xfs_dir_leafblock_t *)tmpbuffer;
- tmp_hdr = &tmp_leaf->hdr;
- tmp_hdr->info = save_hdr->info; /* struct copy */
- tmp_hdr->count = 0;
- INT_SET(tmp_hdr->firstused, ARCH_CONVERT, state->blocksize);
- if (!tmp_hdr->firstused)
- INT_SET(tmp_hdr->firstused, ARCH_CONVERT, state->blocksize - 1);
- tmp_hdr->namebytes = 0;
- if (xfs_dir_leaf_order(save_blk->bp, drop_blk->bp)) {
- xfs_dir_leaf_moveents(drop_leaf, 0, tmp_leaf, 0,
- (int)INT_GET(drop_hdr->count, ARCH_CONVERT), mp);
- xfs_dir_leaf_moveents(save_leaf, 0,
- tmp_leaf, INT_GET(tmp_leaf->hdr.count, ARCH_CONVERT),
- (int)INT_GET(save_hdr->count, ARCH_CONVERT), mp);
- } else {
- xfs_dir_leaf_moveents(save_leaf, 0, tmp_leaf, 0,
- (int)INT_GET(save_hdr->count, ARCH_CONVERT), mp);
- xfs_dir_leaf_moveents(drop_leaf, 0,
- tmp_leaf, INT_GET(tmp_leaf->hdr.count, ARCH_CONVERT),
- (int)INT_GET(drop_hdr->count, ARCH_CONVERT), mp);
- }
- memcpy(save_leaf, tmp_leaf, state->blocksize);
- kmem_free(tmpbuffer, state->blocksize);
- }
-
- xfs_da_log_buf(state->args->trans, save_blk->bp, 0,
- state->blocksize - 1);
-
- /*
- * Copy out last hashval in each block for B-tree code.
- */
- save_blk->hashval = INT_GET(save_leaf->entries[ INT_GET(save_leaf->hdr.count, ARCH_CONVERT)-1 ].hashval, ARCH_CONVERT);
-}
-
-/*========================================================================
- * Routines used for finding things in the Btree.
- *========================================================================*/
-
-/*
- * Look up a name in a leaf directory structure.
- * This is the internal routine, it uses the caller's buffer.
- *
- * Note that duplicate keys are allowed, but only check within the
- * current leaf node. The Btree code must check in adjacent leaf nodes.
- *
- * Return in *index the index into the entry[] array of either the found
- * entry, or where the entry should have been (insert before that entry).
- *
- * Don't change the args->inumber unless we find the filename.
- */
-int
-xfs_dir_leaf_lookup_int(xfs_dabuf_t *bp, xfs_da_args_t *args, int *index)
-{
- xfs_dir_leafblock_t *leaf;
- xfs_dir_leaf_entry_t *entry;
- xfs_dir_leaf_name_t *namest;
- int probe, span;
- xfs_dahash_t hashval;
-
- leaf = bp->data;
- ASSERT(be16_to_cpu(leaf->hdr.info.magic) == XFS_DIR_LEAF_MAGIC);
- ASSERT(INT_GET(leaf->hdr.count, ARCH_CONVERT) < (XFS_LBSIZE(args->dp->i_mount)/8));
-
- /*
- * Binary search. (note: small blocks will skip this loop)
- */
- hashval = args->hashval;
- probe = span = INT_GET(leaf->hdr.count, ARCH_CONVERT) / 2;
- for (entry = &leaf->entries[probe]; span > 4;
- entry = &leaf->entries[probe]) {
- span /= 2;
- if (INT_GET(entry->hashval, ARCH_CONVERT) < hashval)
- probe += span;
- else if (INT_GET(entry->hashval, ARCH_CONVERT) > hashval)
- probe -= span;
- else
- break;
- }
- ASSERT((probe >= 0) && \
- ((!leaf->hdr.count) || (probe < INT_GET(leaf->hdr.count, ARCH_CONVERT))));
- ASSERT((span <= 4) || (INT_GET(entry->hashval, ARCH_CONVERT) == hashval));
-
- /*
- * Since we may have duplicate hashval's, find the first matching
- * hashval in the leaf.
- */
- while ((probe > 0) && (INT_GET(entry->hashval, ARCH_CONVERT) >= hashval)) {
- entry--;
- probe--;
- }
- while ((probe < INT_GET(leaf->hdr.count, ARCH_CONVERT)) && (INT_GET(entry->hashval, ARCH_CONVERT) < hashval)) {
- entry++;
- probe++;
- }
- if ((probe == INT_GET(leaf->hdr.count, ARCH_CONVERT)) || (INT_GET(entry->hashval, ARCH_CONVERT) != hashval)) {
- *index = probe;
- ASSERT(args->oknoent);
- return XFS_ERROR(ENOENT);
- }
-
- /*
- * Duplicate keys may be present, so search all of them for a match.
- */
- while ((probe < INT_GET(leaf->hdr.count, ARCH_CONVERT)) && (INT_GET(entry->hashval, ARCH_CONVERT) == hashval)) {
- namest = XFS_DIR_LEAF_NAMESTRUCT(leaf, INT_GET(entry->nameidx, ARCH_CONVERT));
- if (entry->namelen == args->namelen &&
- namest->name[0] == args->name[0] &&
- memcmp(args->name, namest->name, args->namelen) == 0) {
- XFS_DIR_SF_GET_DIRINO(&namest->inumber, &args->inumber);
- *index = probe;
- return XFS_ERROR(EEXIST);
- }
- entry++;
- probe++;
- }
- *index = probe;
- ASSERT(probe == INT_GET(leaf->hdr.count, ARCH_CONVERT) || args->oknoent);
- return XFS_ERROR(ENOENT);
-}
-
-/*========================================================================
- * Utility routines.
- *========================================================================*/
-
-/*
- * Move the indicated entries from one leaf to another.
- * NOTE: this routine modifies both source and destination leaves.
- */
-/* ARGSUSED */
-STATIC void
-xfs_dir_leaf_moveents(xfs_dir_leafblock_t *leaf_s, int start_s,
- xfs_dir_leafblock_t *leaf_d, int start_d,
- int count, xfs_mount_t *mp)
-{
- xfs_dir_leaf_hdr_t *hdr_s, *hdr_d;
- xfs_dir_leaf_entry_t *entry_s, *entry_d;
- int tmp, i;
-
- /*
- * Check for nothing to do.
- */
- if (count == 0)
- return;
-
- /*
- * Set up environment.
- */
- ASSERT(be16_to_cpu(leaf_s->hdr.info.magic) == XFS_DIR_LEAF_MAGIC);
- ASSERT(be16_to_cpu(leaf_d->hdr.info.magic) == XFS_DIR_LEAF_MAGIC);
- hdr_s = &leaf_s->hdr;
- hdr_d = &leaf_d->hdr;
- ASSERT((INT_GET(hdr_s->count, ARCH_CONVERT) > 0) && (INT_GET(hdr_s->count, ARCH_CONVERT) < (XFS_LBSIZE(mp)/8)));
- ASSERT(INT_GET(hdr_s->firstused, ARCH_CONVERT) >=
- ((INT_GET(hdr_s->count, ARCH_CONVERT)*sizeof(*entry_s))+sizeof(*hdr_s)));
- ASSERT(INT_GET(hdr_d->count, ARCH_CONVERT) < (XFS_LBSIZE(mp)/8));
- ASSERT(INT_GET(hdr_d->firstused, ARCH_CONVERT) >=
- ((INT_GET(hdr_d->count, ARCH_CONVERT)*sizeof(*entry_d))+sizeof(*hdr_d)));
-
- ASSERT(start_s < INT_GET(hdr_s->count, ARCH_CONVERT));
- ASSERT(start_d <= INT_GET(hdr_d->count, ARCH_CONVERT));
- ASSERT(count <= INT_GET(hdr_s->count, ARCH_CONVERT));
-
- /*
- * Move the entries in the destination leaf up to make a hole?
- */
- if (start_d < INT_GET(hdr_d->count, ARCH_CONVERT)) {
- tmp = INT_GET(hdr_d->count, ARCH_CONVERT) - start_d;
- tmp *= (uint)sizeof(xfs_dir_leaf_entry_t);
- entry_s = &leaf_d->entries[start_d];
- entry_d = &leaf_d->entries[start_d + count];
- memcpy(entry_d, entry_s, tmp);
- }
-
- /*
- * Copy all entry's in the same (sorted) order,
- * but allocate filenames packed and in sequence.
- */
- entry_s = &leaf_s->entries[start_s];
- entry_d = &leaf_d->entries[start_d];
- for (i = 0; i < count; entry_s++, entry_d++, i++) {
- ASSERT(INT_GET(entry_s->nameidx, ARCH_CONVERT) >= INT_GET(hdr_s->firstused, ARCH_CONVERT));
- tmp = XFS_DIR_LEAF_ENTSIZE_BYENTRY(entry_s);
- INT_MOD(hdr_d->firstused, ARCH_CONVERT, -(tmp));
- entry_d->hashval = entry_s->hashval; /* INT_: direct copy */
- INT_COPY(entry_d->nameidx, hdr_d->firstused, ARCH_CONVERT);
- entry_d->namelen = entry_s->namelen;
- ASSERT(INT_GET(entry_d->nameidx, ARCH_CONVERT) + tmp <= XFS_LBSIZE(mp));
- memcpy(XFS_DIR_LEAF_NAMESTRUCT(leaf_d, INT_GET(entry_d->nameidx, ARCH_CONVERT)),
- XFS_DIR_LEAF_NAMESTRUCT(leaf_s, INT_GET(entry_s->nameidx, ARCH_CONVERT)), tmp);
- ASSERT(INT_GET(entry_s->nameidx, ARCH_CONVERT) + tmp <= XFS_LBSIZE(mp));
- memset((char *)XFS_DIR_LEAF_NAMESTRUCT(leaf_s, INT_GET(entry_s->nameidx, ARCH_CONVERT)),
- 0, tmp);
- INT_MOD(hdr_s->namebytes, ARCH_CONVERT, -(entry_d->namelen));
- INT_MOD(hdr_d->namebytes, ARCH_CONVERT, entry_d->namelen);
- INT_MOD(hdr_s->count, ARCH_CONVERT, -1);
- INT_MOD(hdr_d->count, ARCH_CONVERT, +1);
- tmp = INT_GET(hdr_d->count, ARCH_CONVERT) * (uint)sizeof(xfs_dir_leaf_entry_t)
- + (uint)sizeof(xfs_dir_leaf_hdr_t);
- ASSERT(INT_GET(hdr_d->firstused, ARCH_CONVERT) >= tmp);
-
- }
-
- /*
- * Zero out the entries we just copied.
- */
- if (start_s == INT_GET(hdr_s->count, ARCH_CONVERT)) {
- tmp = count * (uint)sizeof(xfs_dir_leaf_entry_t);
- entry_s = &leaf_s->entries[start_s];
- ASSERT((char *)entry_s + tmp <= (char *)leaf_s + XFS_LBSIZE(mp));
- memset((char *)entry_s, 0, tmp);
- } else {
- /*
- * Move the remaining entries down to fill the hole,
- * then zero the entries at the top.
- */
- tmp = INT_GET(hdr_s->count, ARCH_CONVERT) - count;
- tmp *= (uint)sizeof(xfs_dir_leaf_entry_t);
- entry_s = &leaf_s->entries[start_s + count];
- entry_d = &leaf_s->entries[start_s];
- memcpy(entry_d, entry_s, tmp);
-
- tmp = count * (uint)sizeof(xfs_dir_leaf_entry_t);
- entry_s = &leaf_s->entries[INT_GET(hdr_s->count, ARCH_CONVERT)];
- ASSERT((char *)entry_s + tmp <= (char *)leaf_s + XFS_LBSIZE(mp));
- memset((char *)entry_s, 0, tmp);
- }
-
- /*
- * Fill in the freemap information
- */
- INT_SET(hdr_d->freemap[0].base, ARCH_CONVERT, (uint)sizeof(xfs_dir_leaf_hdr_t));
- INT_MOD(hdr_d->freemap[0].base, ARCH_CONVERT, INT_GET(hdr_d->count, ARCH_CONVERT) * (uint)sizeof(xfs_dir_leaf_entry_t));
- INT_SET(hdr_d->freemap[0].size, ARCH_CONVERT, INT_GET(hdr_d->firstused, ARCH_CONVERT) - INT_GET(hdr_d->freemap[0].base, ARCH_CONVERT));
- INT_SET(hdr_d->freemap[1].base, ARCH_CONVERT, (hdr_d->freemap[2].base = 0));
- INT_SET(hdr_d->freemap[1].size, ARCH_CONVERT, (hdr_d->freemap[2].size = 0));
- hdr_s->holes = 1; /* leaf may not be compact */
-}
-
-/*
- * Compare two leaf blocks "order".
- */
-int
-xfs_dir_leaf_order(xfs_dabuf_t *leaf1_bp, xfs_dabuf_t *leaf2_bp)
-{
- xfs_dir_leafblock_t *leaf1, *leaf2;
-
- leaf1 = leaf1_bp->data;
- leaf2 = leaf2_bp->data;
- ASSERT((be16_to_cpu(leaf1->hdr.info.magic) == XFS_DIR_LEAF_MAGIC) &&
- (be16_to_cpu(leaf2->hdr.info.magic) == XFS_DIR_LEAF_MAGIC));
- if ((INT_GET(leaf1->hdr.count, ARCH_CONVERT) > 0) && (INT_GET(leaf2->hdr.count, ARCH_CONVERT) > 0) &&
- ((INT_GET(leaf2->entries[ 0 ].hashval, ARCH_CONVERT) <
- INT_GET(leaf1->entries[ 0 ].hashval, ARCH_CONVERT)) ||
- (INT_GET(leaf2->entries[ INT_GET(leaf2->hdr.count, ARCH_CONVERT)-1 ].hashval, ARCH_CONVERT) <
- INT_GET(leaf1->entries[ INT_GET(leaf1->hdr.count, ARCH_CONVERT)-1 ].hashval, ARCH_CONVERT)))) {
- return 1;
- }
- return 0;
-}
-
-/*
- * Pick up the last hashvalue from a leaf block.
- */
-xfs_dahash_t
-xfs_dir_leaf_lasthash(xfs_dabuf_t *bp, int *count)
-{
- xfs_dir_leafblock_t *leaf;
-
- leaf = bp->data;
- ASSERT(be16_to_cpu(leaf->hdr.info.magic) == XFS_DIR_LEAF_MAGIC);
- if (count)
- *count = INT_GET(leaf->hdr.count, ARCH_CONVERT);
- if (!leaf->hdr.count)
- return(0);
- return(INT_GET(leaf->entries[ INT_GET(leaf->hdr.count, ARCH_CONVERT)-1 ].hashval, ARCH_CONVERT));
-}
-
-/*
- * Copy out directory entries for getdents(), for leaf directories.
- */
-int
-xfs_dir_leaf_getdents_int(
- xfs_dabuf_t *bp,
- xfs_inode_t *dp,
- xfs_dablk_t bno,
- uio_t *uio,
- int *eobp,
- xfs_dirent_t *dbp,
- xfs_dir_put_t put,
- xfs_daddr_t nextda)
-{
- xfs_dir_leafblock_t *leaf;
- xfs_dir_leaf_entry_t *entry;
- xfs_dir_leaf_name_t *namest;
- int entno, want_entno, i, nextentno;
- xfs_mount_t *mp;
- xfs_dahash_t cookhash;
- xfs_dahash_t nexthash = 0;
-#if (BITS_PER_LONG == 32)
- xfs_dahash_t lasthash = XFS_DA_MAXHASH;
-#endif
- xfs_dir_put_args_t p;
-
- mp = dp->i_mount;
- leaf = bp->data;
- if (be16_to_cpu(leaf->hdr.info.magic) != XFS_DIR_LEAF_MAGIC) {
- *eobp = 1;
- return XFS_ERROR(ENOENT); /* XXX wrong code */
- }
-
- want_entno = XFS_DA_COOKIE_ENTRY(mp, uio->uio_offset);
-
- cookhash = XFS_DA_COOKIE_HASH(mp, uio->uio_offset);
-
- xfs_dir_trace_g_dul("leaf: start", dp, uio, leaf);
-
- /*
- * Re-find our place.
- */
- for (i = entno = 0, entry = &leaf->entries[0];
- i < INT_GET(leaf->hdr.count, ARCH_CONVERT);
- entry++, i++) {
-
- namest = XFS_DIR_LEAF_NAMESTRUCT(leaf,
- INT_GET(entry->nameidx, ARCH_CONVERT));
-
- if (unlikely(
- ((char *)namest < (char *)leaf) ||
- ((char *)namest >= (char *)leaf + XFS_LBSIZE(mp)))) {
- XFS_CORRUPTION_ERROR("xfs_dir_leaf_getdents_int(1)",
- XFS_ERRLEVEL_LOW, mp, leaf);
- xfs_dir_trace_g_du("leaf: corrupted", dp, uio);
- return XFS_ERROR(EFSCORRUPTED);
- }
- if (INT_GET(entry->hashval, ARCH_CONVERT) >= cookhash) {
- if ( entno < want_entno
- && INT_GET(entry->hashval, ARCH_CONVERT)
- == cookhash) {
- /*
- * Trying to get to a particular offset in a
- * run of equal-hashval entries.
- */
- entno++;
- } else if ( want_entno > 0
- && entno == want_entno
- && INT_GET(entry->hashval, ARCH_CONVERT)
- == cookhash) {
- break;
- } else {
- entno = 0;
- break;
- }
- }
- }
-
- if (i == INT_GET(leaf->hdr.count, ARCH_CONVERT)) {
- xfs_dir_trace_g_du("leaf: hash not found", dp, uio);
- if (!leaf->hdr.info.forw)
- uio->uio_offset =
- XFS_DA_MAKE_COOKIE(mp, 0, 0, XFS_DA_MAXHASH);
- /*
- * Don't set uio_offset if there's another block:
- * the node code will be setting uio_offset anyway.
- */
- *eobp = 0;
- return 0;
- }
- xfs_dir_trace_g_due("leaf: hash found", dp, uio, entry);
-
- p.dbp = dbp;
- p.put = put;
- p.uio = uio;
-
- /*
- * We're synchronized, start copying entries out to the user.
- */
- for (; entno >= 0 && i < INT_GET(leaf->hdr.count, ARCH_CONVERT);
- entry++, i++, (entno = nextentno)) {
- int lastresid=0, retval;
- xfs_dircook_t lastoffset;
- xfs_dahash_t thishash;
-
- /*
- * Check for a damaged directory leaf block and pick up
- * the inode number from this entry.
- */
- namest = XFS_DIR_LEAF_NAMESTRUCT(leaf,
- INT_GET(entry->nameidx, ARCH_CONVERT));
-
- if (unlikely(
- ((char *)namest < (char *)leaf) ||
- ((char *)namest >= (char *)leaf + XFS_LBSIZE(mp)))) {
- XFS_CORRUPTION_ERROR("xfs_dir_leaf_getdents_int(2)",
- XFS_ERRLEVEL_LOW, mp, leaf);
- xfs_dir_trace_g_du("leaf: corrupted", dp, uio);
- return XFS_ERROR(EFSCORRUPTED);
- }
-
- xfs_dir_trace_g_duc("leaf: middle cookie ",
- dp, uio, p.cook.o);
-
- if (i < (INT_GET(leaf->hdr.count, ARCH_CONVERT) - 1)) {
- nexthash = INT_GET(entry[1].hashval, ARCH_CONVERT);
-
- if (nexthash == INT_GET(entry->hashval, ARCH_CONVERT))
- nextentno = entno + 1;
- else
- nextentno = 0;
- XFS_PUT_COOKIE(p.cook, mp, bno, nextentno, nexthash);
- xfs_dir_trace_g_duc("leaf: middle cookie ",
- dp, uio, p.cook.o);
-
- } else if ((thishash = be32_to_cpu(leaf->hdr.info.forw))) {
- xfs_dabuf_t *bp2;
- xfs_dir_leafblock_t *leaf2;
-
- ASSERT(nextda != -1);
-
- retval = xfs_da_read_buf(dp->i_transp, dp, thishash,
- nextda, &bp2, XFS_DATA_FORK);
- if (retval)
- return retval;
-
- ASSERT(bp2 != NULL);
-
- leaf2 = bp2->data;
-
- if (unlikely(
- (be16_to_cpu(leaf2->hdr.info.magic)
- != XFS_DIR_LEAF_MAGIC)
- || (be32_to_cpu(leaf2->hdr.info.back)
- != bno))) { /* GROT */
- XFS_CORRUPTION_ERROR("xfs_dir_leaf_getdents_int(3)",
- XFS_ERRLEVEL_LOW, mp,
- leaf2);
- xfs_da_brelse(dp->i_transp, bp2);
-
- return XFS_ERROR(EFSCORRUPTED);
- }
-
- nexthash = INT_GET(leaf2->entries[0].hashval,
- ARCH_CONVERT);
- nextentno = -1;
- XFS_PUT_COOKIE(p.cook, mp, thishash, 0, nexthash);
- xfs_da_brelse(dp->i_transp, bp2);
- xfs_dir_trace_g_duc("leaf: next blk cookie",
- dp, uio, p.cook.o);
- } else {
- nextentno = -1;
- XFS_PUT_COOKIE(p.cook, mp, 0, 0, XFS_DA_MAXHASH);
- }
-
- /*
- * Save off the cookie so we can fall back should the
- * 'put' into the outgoing buffer fails. To handle a run
- * of equal-hashvals, the off_t structure on 64bit
- * builds has entno built into the cookie to ID the
- * entry. On 32bit builds, we only have space for the
- * hashval so we can't ID specific entries within a group
- * of same hashval entries. For this, lastoffset is set
- * to the first in the run of equal hashvals so we don't
- * include any entries unless we can include all entries
- * that share the same hashval. Hopefully the buffer
- * provided is big enough to handle it (see pv763517).
- */
-#if (BITS_PER_LONG == 32)
- if ((thishash = INT_GET(entry->hashval, ARCH_CONVERT))
- != lasthash) {
- XFS_PUT_COOKIE(lastoffset, mp, bno, entno, thishash);
- lastresid = uio->uio_resid;
- lasthash = thishash;
- } else {
- xfs_dir_trace_g_duc("leaf: DUP COOKIES, skipped",
- dp, uio, p.cook.o);
- }
-#else
- thishash = INT_GET(entry->hashval, ARCH_CONVERT);
- XFS_PUT_COOKIE(lastoffset, mp, bno, entno, thishash);
- lastresid = uio->uio_resid;
-#endif /* BITS_PER_LONG == 32 */
-
- /*
- * Put the current entry into the outgoing buffer. If we fail
- * then restore the UIO to the first entry in the current
- * run of equal-hashval entries (probably one 1 entry long).
- */
- p.ino = XFS_GET_DIR_INO8(namest->inumber);
-#if XFS_BIG_INUMS
- p.ino += mp->m_inoadd;
-#endif
- p.name = (char *)namest->name;
- p.namelen = entry->namelen;
-
- retval = p.put(&p);
-
- if (!p.done) {
- uio->uio_offset = lastoffset.o;
- uio->uio_resid = lastresid;
-
- *eobp = 1;
-
- xfs_dir_trace_g_du("leaf: E-O-B", dp, uio);
-
- return retval;
- }
- }
-
- uio->uio_offset = p.cook.o;
-
- *eobp = 0;
-
- xfs_dir_trace_g_du("leaf: E-O-F", dp, uio);
-
- return 0;
-}
-
-/*
- * Format a dirent64 structure and copy it out the the user's buffer.
- */
-int
-xfs_dir_put_dirent64_direct(xfs_dir_put_args_t *pa)
-{
- iovec_t *iovp;
- int reclen, namelen;
- xfs_dirent_t *idbp;
- uio_t *uio;
-
- namelen = pa->namelen;
- reclen = DIRENTSIZE(namelen);
- uio = pa->uio;
- if (reclen > uio->uio_resid) {
- pa->done = 0;
- return 0;
- }
- iovp = uio->uio_iov;
- idbp = (xfs_dirent_t *)iovp->iov_base;
- iovp->iov_base = (char *)idbp + reclen;
- iovp->iov_len -= reclen;
- uio->uio_resid -= reclen;
- idbp->d_reclen = reclen;
- idbp->d_ino = pa->ino;
- idbp->d_off = pa->cook.o;
- idbp->d_name[namelen] = '\0';
- pa->done = 1;
- memcpy(idbp->d_name, pa->name, namelen);
- return 0;
-}
-
-/*
- * Format a dirent64 structure and copy it out the the user's buffer.
- */
-int
-xfs_dir_put_dirent64_uio(xfs_dir_put_args_t *pa)
-{
- int retval, reclen, namelen;
- xfs_dirent_t *idbp;
- uio_t *uio;
-
- namelen = pa->namelen;
- reclen = DIRENTSIZE(namelen);
- uio = pa->uio;
- if (reclen > uio->uio_resid) {
- pa->done = 0;
- return 0;
- }
- idbp = pa->dbp;
- idbp->d_reclen = reclen;
- idbp->d_ino = pa->ino;
- idbp->d_off = pa->cook.o;
- idbp->d_name[namelen] = '\0';
- memcpy(idbp->d_name, pa->name, namelen);
- retval = uio_read((caddr_t)idbp, reclen, uio);
- pa->done = (retval == 0);
- return retval;
-}
+++ /dev/null
-/*
- * Copyright (c) 2000-2001,2005 Silicon Graphics, Inc.
- * 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.
- *
- * This program is distributed in the hope that it would 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 the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-#ifndef __XFS_DIR_LEAF_H__
-#define __XFS_DIR_LEAF_H__
-
-/*
- * Directory layout, internal structure, access macros, etc.
- *
- * Large directories are structured around Btrees where all the data
- * elements are in the leaf nodes. Filenames are hashed into an int,
- * then that int is used as the index into the Btree. Since the hashval
- * of a filename may not be unique, we may have duplicate keys. The
- * internal links in the Btree are logical block offsets into the file.
- */
-
-struct uio;
-struct xfs_bmap_free;
-struct xfs_dabuf;
-struct xfs_da_args;
-struct xfs_da_state;
-struct xfs_da_state_blk;
-struct xfs_dir_put_args;
-struct xfs_inode;
-struct xfs_mount;
-struct xfs_trans;
-
-/*========================================================================
- * Directory Structure when equal to XFS_LBSIZE(mp) bytes.
- *========================================================================*/
-
-/*
- * This is the structure of the leaf nodes in the Btree.
- *
- * Struct leaf_entry's are packed from the top. Names grow from the bottom
- * but are not packed. The freemap contains run-length-encoded entries
- * for the free bytes after the leaf_entry's, but only the N largest such,
- * smaller runs are dropped. When the freemap doesn't show enough space
- * for an allocation, we compact the namelist area and try again. If we
- * still don't have enough space, then we have to split the block.
- *
- * Since we have duplicate hash keys, for each key that matches, compare
- * the actual string. The root and intermediate node search always takes
- * the first-in-the-block key match found, so we should only have to work
- * "forw"ard. If none matches, continue with the "forw"ard leaf nodes
- * until the hash key changes or the filename is found.
- *
- * The parent directory and the self-pointer are explicitly represented
- * (ie: there are entries for "." and "..").
- *
- * Note that the count being a __uint16_t limits us to something like a
- * blocksize of 1.3MB in the face of worst case (short) filenames.
- */
-#define XFS_DIR_LEAF_MAPSIZE 3 /* how many freespace slots */
-
-typedef struct xfs_dir_leaf_map { /* RLE map of free bytes */
- __uint16_t base; /* base of free region */
- __uint16_t size; /* run length of free region */
-} xfs_dir_leaf_map_t;
-
-typedef struct xfs_dir_leaf_hdr { /* constant-structure header block */
- xfs_da_blkinfo_t info; /* block type, links, etc. */
- __uint16_t count; /* count of active leaf_entry's */
- __uint16_t namebytes; /* num bytes of name strings stored */
- __uint16_t firstused; /* first used byte in name area */
- __uint8_t holes; /* != 0 if blk needs compaction */
- __uint8_t pad1;
- xfs_dir_leaf_map_t freemap[XFS_DIR_LEAF_MAPSIZE];
-} xfs_dir_leaf_hdr_t;
-
-typedef struct xfs_dir_leaf_entry { /* sorted on key, not name */
- xfs_dahash_t hashval; /* hash value of name */
- __uint16_t nameidx; /* index into buffer of name */
- __uint8_t namelen; /* length of name string */
- __uint8_t pad2;
-} xfs_dir_leaf_entry_t;
-
-typedef struct xfs_dir_leaf_name {
- xfs_dir_ino_t inumber; /* inode number for this key */
- __uint8_t name[1]; /* name string itself */
-} xfs_dir_leaf_name_t;
-
-typedef struct xfs_dir_leafblock {
- xfs_dir_leaf_hdr_t hdr; /* constant-structure header block */
- xfs_dir_leaf_entry_t entries[1]; /* var sized array */
- xfs_dir_leaf_name_t namelist[1]; /* grows from bottom of buf */
-} xfs_dir_leafblock_t;
-
-/*
- * Length of name for which a 512-byte block filesystem
- * can get a double split.
- */
-#define XFS_DIR_LEAF_CAN_DOUBLE_SPLIT_LEN \
- (512 - (uint)sizeof(xfs_dir_leaf_hdr_t) - \
- (uint)sizeof(xfs_dir_leaf_entry_t) * 2 - \
- (uint)sizeof(xfs_dir_leaf_name_t) * 2 - (MAXNAMELEN - 2) + 1 + 1)
-
-typedef int (*xfs_dir_put_t)(struct xfs_dir_put_args *pa);
-
-typedef union {
- xfs_off_t o; /* offset (cookie) */
- /*
- * Watch the order here (endian-ness dependent).
- */
- struct {
-#ifndef XFS_NATIVE_HOST
- xfs_dahash_t h; /* hash value */
- __uint32_t be; /* block and entry */
-#else
- __uint32_t be; /* block and entry */
- xfs_dahash_t h; /* hash value */
-#endif /* XFS_NATIVE_HOST */
- } s;
-} xfs_dircook_t;
-
-#define XFS_PUT_COOKIE(c,mp,bno,entry,hash) \
- ((c).s.be = XFS_DA_MAKE_BNOENTRY(mp, bno, entry), (c).s.h = (hash))
-
-typedef struct xfs_dir_put_args {
- xfs_dircook_t cook; /* cookie of (next) entry */
- xfs_intino_t ino; /* inode number */
- struct xfs_dirent *dbp; /* buffer pointer */
- char *name; /* directory entry name */
- int namelen; /* length of name */
- int done; /* output: set if value was stored */
- xfs_dir_put_t put; /* put function ptr (i/o) */
- struct uio *uio; /* uio control structure */
-} xfs_dir_put_args_t;
-
-#define XFS_DIR_LEAF_ENTSIZE_BYNAME(len) \
- xfs_dir_leaf_entsize_byname(len)
-static inline int xfs_dir_leaf_entsize_byname(int len)
-{
- return (uint)sizeof(xfs_dir_leaf_name_t)-1 + len;
-}
-
-#define XFS_DIR_LEAF_ENTSIZE_BYENTRY(entry) \
- xfs_dir_leaf_entsize_byentry(entry)
-static inline int xfs_dir_leaf_entsize_byentry(xfs_dir_leaf_entry_t *entry)
-{
- return (uint)sizeof(xfs_dir_leaf_name_t)-1 + (entry)->namelen;
-}
-
-#define XFS_DIR_LEAF_NAMESTRUCT(leafp,offset) \
- xfs_dir_leaf_namestruct(leafp,offset)
-static inline xfs_dir_leaf_name_t *
-xfs_dir_leaf_namestruct(xfs_dir_leafblock_t *leafp, int offset)
-{
- return (xfs_dir_leaf_name_t *)&((char *)(leafp))[offset];
-}
-
-/*========================================================================
- * Function prototypes for the kernel.
- *========================================================================*/
-
-/*
- * Internal routines when dirsize < XFS_LITINO(mp).
- */
-int xfs_dir_shortform_create(struct xfs_da_args *args, xfs_ino_t parent);
-int xfs_dir_shortform_addname(struct xfs_da_args *args);
-int xfs_dir_shortform_lookup(struct xfs_da_args *args);
-int xfs_dir_shortform_to_leaf(struct xfs_da_args *args);
-int xfs_dir_shortform_removename(struct xfs_da_args *args);
-int xfs_dir_shortform_getdents(struct xfs_inode *dp, struct uio *uio, int *eofp,
- struct xfs_dirent *dbp, xfs_dir_put_t put);
-int xfs_dir_shortform_replace(struct xfs_da_args *args);
-
-/*
- * Internal routines when dirsize == XFS_LBSIZE(mp).
- */
-int xfs_dir_leaf_to_node(struct xfs_da_args *args);
-int xfs_dir_leaf_to_shortform(struct xfs_da_args *args);
-
-/*
- * Routines used for growing the Btree.
- */
-int xfs_dir_leaf_split(struct xfs_da_state *state,
- struct xfs_da_state_blk *oldblk,
- struct xfs_da_state_blk *newblk);
-int xfs_dir_leaf_add(struct xfs_dabuf *leaf_buffer,
- struct xfs_da_args *args, int insertion_index);
-int xfs_dir_leaf_addname(struct xfs_da_args *args);
-int xfs_dir_leaf_lookup_int(struct xfs_dabuf *leaf_buffer,
- struct xfs_da_args *args,
- int *index_found_at);
-int xfs_dir_leaf_remove(struct xfs_trans *trans,
- struct xfs_dabuf *leaf_buffer,
- int index_to_remove);
-int xfs_dir_leaf_getdents_int(struct xfs_dabuf *bp, struct xfs_inode *dp,
- xfs_dablk_t bno, struct uio *uio,
- int *eobp, struct xfs_dirent *dbp,
- xfs_dir_put_t put, xfs_daddr_t nextda);
-
-/*
- * Routines used for shrinking the Btree.
- */
-int xfs_dir_leaf_toosmall(struct xfs_da_state *state, int *retval);
-void xfs_dir_leaf_unbalance(struct xfs_da_state *state,
- struct xfs_da_state_blk *drop_blk,
- struct xfs_da_state_blk *save_blk);
-
-/*
- * Utility routines.
- */
-uint xfs_dir_leaf_lasthash(struct xfs_dabuf *bp, int *count);
-int xfs_dir_leaf_order(struct xfs_dabuf *leaf1_bp,
- struct xfs_dabuf *leaf2_bp);
-int xfs_dir_put_dirent64_direct(xfs_dir_put_args_t *pa);
-int xfs_dir_put_dirent64_uio(xfs_dir_put_args_t *pa);
-int xfs_dir_ino_validate(struct xfs_mount *mp, xfs_ino_t ino);
-
-/*
- * Global data.
- */
-extern xfs_dahash_t xfs_dir_hash_dot, xfs_dir_hash_dotdot;
-
-#endif /* __XFS_DIR_LEAF_H__ */
+++ /dev/null
-/*
- * Copyright (c) 2000,2005 Silicon Graphics, Inc.
- * 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.
- *
- * This program is distributed in the hope that it would 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 the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-#ifndef __XFS_DIR_SF_H__
-#define __XFS_DIR_SF_H__
-
-/*
- * Directory layout when stored internal to an inode.
- *
- * Small directories are packed as tightly as possible so as to
- * fit into the literal area of the inode.
- */
-
-typedef struct { __uint8_t i[sizeof(xfs_ino_t)]; } xfs_dir_ino_t;
-
-/*
- * The parent directory has a dedicated field, and the self-pointer must
- * be calculated on the fly.
- *
- * Entries are packed toward the top as tight as possible. The header
- * and the elements much be memcpy'd out into a work area to get correct
- * alignment for the inode number fields.
- */
-typedef struct xfs_dir_sf_hdr { /* constant-structure header block */
- xfs_dir_ino_t parent; /* parent dir inode number */
- __uint8_t count; /* count of active entries */
-} xfs_dir_sf_hdr_t;
-
-typedef struct xfs_dir_sf_entry {
- xfs_dir_ino_t inumber; /* referenced inode number */
- __uint8_t namelen; /* actual length of name (no NULL) */
- __uint8_t name[1]; /* name */
-} xfs_dir_sf_entry_t;
-
-typedef struct xfs_dir_shortform {
- xfs_dir_sf_hdr_t hdr;
- xfs_dir_sf_entry_t list[1]; /* variable sized array */
-} xfs_dir_shortform_t;
-
-/*
- * We generate this then sort it, so that readdirs are returned in
- * hash-order. Else seekdir won't work.
- */
-typedef struct xfs_dir_sf_sort {
- __uint8_t entno; /* .=0, ..=1, else entry# + 2 */
- __uint8_t seqno; /* sequence # with same hash value */
- __uint8_t namelen; /* length of name value (no null) */
- xfs_dahash_t hash; /* this entry's hash value */
- xfs_intino_t ino; /* this entry's inode number */
- char *name; /* name value, pointer into buffer */
-} xfs_dir_sf_sort_t;
-
-#define XFS_DIR_SF_GET_DIRINO(from,to) xfs_dir_sf_get_dirino(from, to)
-static inline void xfs_dir_sf_get_dirino(xfs_dir_ino_t *from, xfs_ino_t *to)
-{
- *(to) = XFS_GET_DIR_INO8(*from);
-}
-
-#define XFS_DIR_SF_PUT_DIRINO(from,to) xfs_dir_sf_put_dirino(from, to)
-static inline void xfs_dir_sf_put_dirino(xfs_ino_t *from, xfs_dir_ino_t *to)
-{
- XFS_PUT_DIR_INO8(*(from), *(to));
-}
-
-#define XFS_DIR_SF_ENTSIZE_BYNAME(len) xfs_dir_sf_entsize_byname(len)
-static inline int xfs_dir_sf_entsize_byname(int len)
-{
- return (uint)sizeof(xfs_dir_sf_entry_t)-1 + (len);
-}
-
-#define XFS_DIR_SF_ENTSIZE_BYENTRY(sfep) xfs_dir_sf_entsize_byentry(sfep)
-static inline int xfs_dir_sf_entsize_byentry(xfs_dir_sf_entry_t *sfep)
-{
- return (uint)sizeof(xfs_dir_sf_entry_t)-1 + (sfep)->namelen;
-}
-
-#define XFS_DIR_SF_NEXTENTRY(sfep) xfs_dir_sf_nextentry(sfep)
-static inline xfs_dir_sf_entry_t *xfs_dir_sf_nextentry(xfs_dir_sf_entry_t *sfep)
-{
- return (xfs_dir_sf_entry_t *) \
- ((char *)(sfep) + XFS_DIR_SF_ENTSIZE_BYENTRY(sfep));
-}
-
-#define XFS_DIR_SF_ALLFIT(count,totallen) \
- xfs_dir_sf_allfit(count,totallen)
-static inline int xfs_dir_sf_allfit(int count, int totallen)
-{
- return ((uint)sizeof(xfs_dir_sf_hdr_t) + \
- ((uint)sizeof(xfs_dir_sf_entry_t)-1)*(count) + (totallen));
-}
-
-#if defined(XFS_DIR_TRACE)
-
-/*
- * Kernel tracing support for directories.
- */
-struct uio;
-struct xfs_inode;
-struct xfs_da_intnode;
-struct xfs_dinode;
-struct xfs_dir_leafblock;
-struct xfs_dir_leaf_entry;
-
-#define XFS_DIR_TRACE_SIZE 4096 /* size of global trace buffer */
-extern ktrace_t *xfs_dir_trace_buf;
-
-/*
- * Trace record types.
- */
-#define XFS_DIR_KTRACE_G_DU 1 /* dp, uio */
-#define XFS_DIR_KTRACE_G_DUB 2 /* dp, uio, bno */
-#define XFS_DIR_KTRACE_G_DUN 3 /* dp, uio, node */
-#define XFS_DIR_KTRACE_G_DUL 4 /* dp, uio, leaf */
-#define XFS_DIR_KTRACE_G_DUE 5 /* dp, uio, leaf entry */
-#define XFS_DIR_KTRACE_G_DUC 6 /* dp, uio, cookie */
-
-void xfs_dir_trace_g_du(char *where, struct xfs_inode *dp, struct uio *uio);
-void xfs_dir_trace_g_dub(char *where, struct xfs_inode *dp, struct uio *uio,
- xfs_dablk_t bno);
-void xfs_dir_trace_g_dun(char *where, struct xfs_inode *dp, struct uio *uio,
- struct xfs_da_intnode *node);
-void xfs_dir_trace_g_dul(char *where, struct xfs_inode *dp, struct uio *uio,
- struct xfs_dir_leafblock *leaf);
-void xfs_dir_trace_g_due(char *where, struct xfs_inode *dp, struct uio *uio,
- struct xfs_dir_leaf_entry *entry);
-void xfs_dir_trace_g_duc(char *where, struct xfs_inode *dp, struct uio *uio,
- xfs_off_t cookie);
-void xfs_dir_trace_enter(int type, char *where,
- void *a0, void *a1, void *a2, void *a3,
- void *a4, void *a5, void *a6, void *a7,
- void *a8, void *a9, void *a10, void *a11);
-#else
-#define xfs_dir_trace_g_du(w,d,u)
-#define xfs_dir_trace_g_dub(w,d,u,b)
-#define xfs_dir_trace_g_dun(w,d,u,n)
-#define xfs_dir_trace_g_dul(w,d,u,l)
-#define xfs_dir_trace_g_due(w,d,u,e)
-#define xfs_dir_trace_g_duc(w,d,u,c)
-#endif /* DEBUG */
-
-#endif /* __XFS_DIR_SF_H__ */
#define AT_DELAY_FLAG(f) ((f&ATTR_NONBLOCK) ? DM_FLAGS_NDELAY : 0)
-extern struct bhv_vfsops xfs_dmops;
+extern struct bhv_module_vfsops xfs_dmops;
#endif /* __XFS_DMAPI_H__ */
#include "xfs_trans.h"
#include "xfs_sb.h"
#include "xfs_ag.h"
-#include "xfs_dir.h"
#include "xfs_dir2.h"
#include "xfs_dmapi.h"
#include "xfs_mount.h"
#include "xfs_inum.h"
#include "xfs_trans.h"
#include "xfs_sb.h"
-#include "xfs_dir.h"
#include "xfs_dir2.h"
#include "xfs_dmapi.h"
#include "xfs_mount.h"
#include "xfs_bmap_btree.h"
-#include "xfs_dir_sf.h"
#include "xfs_dir2_sf.h"
#include "xfs_attr_sf.h"
#include "xfs_dinode.h"
#include "xfs_trans.h"
#include "xfs_buf_item.h"
#include "xfs_sb.h"
-#include "xfs_dir.h"
#include "xfs_dmapi.h"
#include "xfs_mount.h"
#include "xfs_trans_priv.h"
return (efip);
}
+/*
+ * Copy an EFI format buffer from the given buf, and into the destination
+ * EFI format structure.
+ * The given buffer can be in 32 bit or 64 bit form (which has different padding),
+ * one of which will be the native format for this kernel.
+ * It will handle the conversion of formats if necessary.
+ */
+int
+xfs_efi_copy_format(xfs_log_iovec_t *buf, xfs_efi_log_format_t *dst_efi_fmt)
+{
+ xfs_efi_log_format_t *src_efi_fmt = (xfs_efi_log_format_t *)buf->i_addr;
+ uint i;
+ uint len = sizeof(xfs_efi_log_format_t) +
+ (src_efi_fmt->efi_nextents - 1) * sizeof(xfs_extent_t);
+ uint len32 = sizeof(xfs_efi_log_format_32_t) +
+ (src_efi_fmt->efi_nextents - 1) * sizeof(xfs_extent_32_t);
+ uint len64 = sizeof(xfs_efi_log_format_64_t) +
+ (src_efi_fmt->efi_nextents - 1) * sizeof(xfs_extent_64_t);
+
+ if (buf->i_len == len) {
+ memcpy((char *)dst_efi_fmt, (char*)src_efi_fmt, len);
+ return 0;
+ } else if (buf->i_len == len32) {
+ xfs_efi_log_format_32_t *src_efi_fmt_32 =
+ (xfs_efi_log_format_32_t *)buf->i_addr;
+
+ dst_efi_fmt->efi_type = src_efi_fmt_32->efi_type;
+ dst_efi_fmt->efi_size = src_efi_fmt_32->efi_size;
+ dst_efi_fmt->efi_nextents = src_efi_fmt_32->efi_nextents;
+ dst_efi_fmt->efi_id = src_efi_fmt_32->efi_id;
+ for (i = 0; i < dst_efi_fmt->efi_nextents; i++) {
+ dst_efi_fmt->efi_extents[i].ext_start =
+ src_efi_fmt_32->efi_extents[i].ext_start;
+ dst_efi_fmt->efi_extents[i].ext_len =
+ src_efi_fmt_32->efi_extents[i].ext_len;
+ }
+ return 0;
+ } else if (buf->i_len == len64) {
+ xfs_efi_log_format_64_t *src_efi_fmt_64 =
+ (xfs_efi_log_format_64_t *)buf->i_addr;
+
+ dst_efi_fmt->efi_type = src_efi_fmt_64->efi_type;
+ dst_efi_fmt->efi_size = src_efi_fmt_64->efi_size;
+ dst_efi_fmt->efi_nextents = src_efi_fmt_64->efi_nextents;
+ dst_efi_fmt->efi_id = src_efi_fmt_64->efi_id;
+ for (i = 0; i < dst_efi_fmt->efi_nextents; i++) {
+ dst_efi_fmt->efi_extents[i].ext_start =
+ src_efi_fmt_64->efi_extents[i].ext_start;
+ dst_efi_fmt->efi_extents[i].ext_len =
+ src_efi_fmt_64->efi_extents[i].ext_len;
+ }
+ return 0;
+ }
+ return EFSCORRUPTED;
+}
+
/*
* This is called by the efd item code below to release references to
* the given efi item. Each efd calls this with the number of
xfs_extlen_t ext_len;
} xfs_extent_t;
+/*
+ * Since an xfs_extent_t has types (start:64, len: 32)
+ * there are different alignments on 32 bit and 64 bit kernels.
+ * So we provide the different variants for use by a
+ * conversion routine.
+ */
+
+typedef struct xfs_extent_32 {
+ xfs_dfsbno_t ext_start;
+ xfs_extlen_t ext_len;
+} __attribute__((packed)) xfs_extent_32_t;
+
+typedef struct xfs_extent_64 {
+ xfs_dfsbno_t ext_start;
+ xfs_extlen_t ext_len;
+ __uint32_t ext_pad;
+} xfs_extent_64_t;
+
/*
* This is the structure used to lay out an efi log item in the
* log. The efi_extents field is a variable size array whose
xfs_extent_t efi_extents[1]; /* array of extents to free */
} xfs_efi_log_format_t;
+typedef struct xfs_efi_log_format_32 {
+ unsigned short efi_type; /* efi log item type */
+ unsigned short efi_size; /* size of this item */
+ uint efi_nextents; /* # extents to free */
+ __uint64_t efi_id; /* efi identifier */
+ xfs_extent_32_t efi_extents[1]; /* array of extents to free */
+} __attribute__((packed)) xfs_efi_log_format_32_t;
+
+typedef struct xfs_efi_log_format_64 {
+ unsigned short efi_type; /* efi log item type */
+ unsigned short efi_size; /* size of this item */
+ uint efi_nextents; /* # extents to free */
+ __uint64_t efi_id; /* efi identifier */
+ xfs_extent_64_t efi_extents[1]; /* array of extents to free */
+} xfs_efi_log_format_64_t;
+
/*
* This is the structure used to lay out an efd log item in the
* log. The efd_extents array is a variable size array whose
xfs_extent_t efd_extents[1]; /* array of extents freed */
} xfs_efd_log_format_t;
+typedef struct xfs_efd_log_format_32 {
+ unsigned short efd_type; /* efd log item type */
+ unsigned short efd_size; /* size of this item */
+ uint efd_nextents; /* # of extents freed */
+ __uint64_t efd_efi_id; /* id of corresponding efi */
+ xfs_extent_32_t efd_extents[1]; /* array of extents freed */
+} __attribute__((packed)) xfs_efd_log_format_32_t;
+
+typedef struct xfs_efd_log_format_64 {
+ unsigned short efd_type; /* efd log item type */
+ unsigned short efd_size; /* size of this item */
+ uint efd_nextents; /* # of extents freed */
+ __uint64_t efd_efi_id; /* id of corresponding efi */
+ xfs_extent_64_t efd_extents[1]; /* array of extents freed */
+} xfs_efd_log_format_64_t;
+
#ifdef __KERNEL__
xfs_efi_log_item_t *xfs_efi_init(struct xfs_mount *, uint);
xfs_efd_log_item_t *xfs_efd_init(struct xfs_mount *, xfs_efi_log_item_t *,
uint);
-
+int xfs_efi_copy_format(xfs_log_iovec_t *buf,
+ xfs_efi_log_format_t *dst_efi_fmt);
void xfs_efi_item_free(xfs_efi_log_item_t *);
#endif /* __KERNEL__ */
#define XFS_XFLAG_NOSYMLINKS 0x00000400 /* disallow symlink creation */
#define XFS_XFLAG_EXTSIZE 0x00000800 /* extent size allocator hint */
#define XFS_XFLAG_EXTSZINHERIT 0x00001000 /* inherit inode extent size */
+#define XFS_XFLAG_NODEFRAG 0x00002000 /* do not defragment */
#define XFS_XFLAG_HASATTR 0x80000000 /* no DIFLAG for this */
/*
* Structure for XFS_IOC_GETBMAP.
* On input, fill in bmv_offset and bmv_length of the first structure
- * to indicate the area of interest in the file, and bmv_entry with the
- * number of array elements given. The first structure is updated on
- * return to give the offset and length for the next call.
+ * to indicate the area of interest in the file, and bmv_entries with
+ * the number of array elements given back. The first structure is
+ * updated on return to give the offset and length for the next call.
*/
#ifndef HAVE_GETBMAP
struct getbmap {
#include "xfs_trans.h"
#include "xfs_sb.h"
#include "xfs_ag.h"
-#include "xfs_dir.h"
#include "xfs_dir2.h"
#include "xfs_dmapi.h"
#include "xfs_mount.h"
#include "xfs_bmap_btree.h"
#include "xfs_alloc_btree.h"
#include "xfs_ialloc_btree.h"
-#include "xfs_dir_sf.h"
#include "xfs_dir2_sf.h"
#include "xfs_attr_sf.h"
#include "xfs_dinode.h"
}
void
-xfs_fs_log_dummy(xfs_mount_t *mp)
+xfs_fs_log_dummy(
+ xfs_mount_t *mp)
{
- xfs_trans_t *tp;
- xfs_inode_t *ip;
-
+ xfs_trans_t *tp;
+ xfs_inode_t *ip;
tp = _xfs_trans_alloc(mp, XFS_TRANS_DUMMY1);
- atomic_inc(&mp->m_active_trans);
if (xfs_trans_reserve(tp, 0, XFS_ICHANGE_LOG_RES(mp), 0, 0, 0)) {
xfs_trans_cancel(tp, 0);
return;
{
switch (inflags) {
case XFS_FSOP_GOING_FLAGS_DEFAULT: {
- struct vfs *vfsp = XFS_MTOVFS(mp);
+ struct bhv_vfs *vfsp = XFS_MTOVFS(mp);
struct super_block *sb = freeze_bdev(vfsp->vfs_super->s_bdev);
if (sb && !IS_ERR(sb)) {
- xfs_force_shutdown(mp, XFS_FORCE_UMOUNT);
+ xfs_force_shutdown(mp, SHUTDOWN_FORCE_UMOUNT);
thaw_bdev(sb->s_bdev, sb);
}
break;
}
case XFS_FSOP_GOING_FLAGS_LOGFLUSH:
- xfs_force_shutdown(mp, XFS_FORCE_UMOUNT);
+ xfs_force_shutdown(mp, SHUTDOWN_FORCE_UMOUNT);
break;
case XFS_FSOP_GOING_FLAGS_NOLOGFLUSH:
- xfs_force_shutdown(mp, XFS_FORCE_UMOUNT|XFS_LOG_IO_ERROR);
+ xfs_force_shutdown(mp,
+ SHUTDOWN_FORCE_UMOUNT | SHUTDOWN_LOG_IO_ERROR);
break;
default:
return XFS_ERROR(EINVAL);
#include "xfs_trans.h"
#include "xfs_sb.h"
#include "xfs_ag.h"
-#include "xfs_dir.h"
#include "xfs_dir2.h"
#include "xfs_dmapi.h"
#include "xfs_mount.h"
#include "xfs_bmap_btree.h"
#include "xfs_alloc_btree.h"
#include "xfs_ialloc_btree.h"
-#include "xfs_dir_sf.h"
#include "xfs_dir2_sf.h"
#include "xfs_attr_sf.h"
#include "xfs_dinode.h"
if (agno >= mp->m_sb.sb_agcount || agbno >= mp->m_sb.sb_agblocks ||
ino != XFS_AGINO_TO_INO(mp, agno, agino)) {
#ifdef DEBUG
+ /* no diagnostics for bulkstat, ino comes from userspace */
+ if (flags & XFS_IMAP_BULKSTAT)
+ return XFS_ERROR(EINVAL);
if (agno >= mp->m_sb.sb_agcount) {
xfs_fs_cmn_err(CE_ALERT, mp,
"xfs_dilocate: agno (%d) >= "
#include "xfs_trans.h"
#include "xfs_sb.h"
#include "xfs_ag.h"
-#include "xfs_dir.h"
#include "xfs_dir2.h"
#include "xfs_dmapi.h"
#include "xfs_mount.h"
#include "xfs_bmap_btree.h"
#include "xfs_alloc_btree.h"
#include "xfs_ialloc_btree.h"
-#include "xfs_dir_sf.h"
#include "xfs_dir2_sf.h"
#include "xfs_attr_sf.h"
#include "xfs_dinode.h"
#include "xfs_trans.h"
#include "xfs_sb.h"
#include "xfs_ag.h"
-#include "xfs_dir.h"
#include "xfs_dir2.h"
#include "xfs_dmapi.h"
#include "xfs_mount.h"
#include "xfs_bmap_btree.h"
#include "xfs_alloc_btree.h"
#include "xfs_ialloc_btree.h"
-#include "xfs_dir_sf.h"
#include "xfs_dir2_sf.h"
#include "xfs_attr_sf.h"
#include "xfs_dinode.h"
*/
STATIC int
xfs_iget_core(
- vnode_t *vp,
+ bhv_vnode_t *vp,
xfs_mount_t *mp,
xfs_trans_t *tp,
xfs_ino_t ino,
xfs_ihash_t *ih;
xfs_inode_t *ip;
xfs_inode_t *iq;
- vnode_t *inode_vp;
+ bhv_vnode_t *inode_vp;
ulong version;
int error;
/* REFERENCED */
* If we have a real type for an on-disk inode, we can set ops(&unlock)
* now. If it's a new inode being created, xfs_ialloc will handle it.
*/
- VFS_INIT_VNODE(XFS_MTOVFS(mp), vp, XFS_ITOBHV(ip), 1);
+ bhv_vfs_init_vnode(XFS_MTOVFS(mp), vp, XFS_ITOBHV(ip), 1);
return 0;
}
xfs_daddr_t bno)
{
struct inode *inode;
- vnode_t *vp = NULL;
+ bhv_vnode_t *vp = NULL;
int error;
XFS_STATS_INC(xs_ig_attempts);
void
xfs_inode_lock_init(
xfs_inode_t *ip,
- vnode_t *vp)
+ bhv_vnode_t *vp)
{
mrlock_init(&ip->i_lock, MRLOCK_ALLOW_EQUAL_PRI|MRLOCK_BARRIER,
"xfsino", (long)vp->v_number);
xfs_iput(xfs_inode_t *ip,
uint lock_flags)
{
- vnode_t *vp = XFS_ITOV(ip);
+ bhv_vnode_t *vp = XFS_ITOV(ip);
vn_trace_entry(vp, "xfs_iput", (inst_t *)__return_address);
-
xfs_iunlock(ip, lock_flags);
-
VN_RELE(vp);
}
xfs_iput_new(xfs_inode_t *ip,
uint lock_flags)
{
- vnode_t *vp = XFS_ITOV(ip);
+ bhv_vnode_t *vp = XFS_ITOV(ip);
struct inode *inode = vn_to_inode(vp);
vn_trace_entry(vp, "xfs_iput_new", (inst_t *)__return_address);
void
xfs_ireclaim(xfs_inode_t *ip)
{
- vnode_t *vp;
+ bhv_vnode_t *vp;
/*
* Remove from old hash list and mount list.
void
xfs_ifunlock(xfs_inode_t *ip)
{
- ASSERT(valusema(&(ip->i_flock)) <= 0);
+ ASSERT(issemalocked(&(ip->i_flock)));
vsema(&(ip->i_flock));
}
/*
- * Copyright (c) 2000-2003,2005 Silicon Graphics, Inc.
+ * Copyright (c) 2000-2006 Silicon Graphics, Inc.
* All Rights Reserved.
*
* This program is free software; you can redistribute it and/or
#include "xfs_trans_priv.h"
#include "xfs_sb.h"
#include "xfs_ag.h"
-#include "xfs_dir.h"
#include "xfs_dir2.h"
#include "xfs_dmapi.h"
#include "xfs_mount.h"
#include "xfs_bmap_btree.h"
#include "xfs_alloc_btree.h"
#include "xfs_ialloc_btree.h"
-#include "xfs_dir_sf.h"
#include "xfs_dir2_sf.h"
#include "xfs_attr_sf.h"
#include "xfs_dinode.h"
xfs_daddr_t bno,
uint imap_flags)
{
+ xfs_imap_t imap;
xfs_buf_t *bp;
int error;
- xfs_imap_t imap;
-#ifdef __KERNEL__
int i;
int ni;
-#endif
if (ip->i_blkno == (xfs_daddr_t)0) {
/*
*/
error = xfs_trans_read_buf(mp, tp, mp->m_ddev_targp, imap.im_blkno,
(int)imap.im_len, XFS_BUF_LOCK, &bp);
-
if (error) {
#ifdef DEBUG
xfs_fs_cmn_err(CE_ALERT, mp, "xfs_itobp: "
#endif /* DEBUG */
return error;
}
-#ifdef __KERNEL__
+
/*
* Validate the magic number and version of every inode in the buffer
* (if DEBUG kernel) or the first inode in the buffer, otherwise.
+ * No validation is done here in userspace (xfs_repair).
*/
-#ifdef DEBUG
+#if !defined(__KERNEL__)
+ ni = 0;
+#elif defined(DEBUG)
ni = (imap_flags & XFS_IMAP_BULKSTAT) ? 0 :
(BBTOB(imap.im_len) >> mp->m_sb.sb_inodelog);
-#else
+#else /* usual case */
ni = (imap_flags & XFS_IMAP_BULKSTAT) ? 0 : 1;
#endif
+
for (i = 0; i < ni; i++) {
int di_ok;
xfs_dinode_t *dip;
if (unlikely(XFS_TEST_ERROR(!di_ok, mp, XFS_ERRTAG_ITOBP_INOTOBP,
XFS_RANDOM_ITOBP_INOTOBP))) {
#ifdef DEBUG
- prdev("bad inode magic/vsn daddr %lld #%d (magic=%x)",
- mp->m_ddev_targp,
+ if (!(imap_flags & XFS_IMAP_BULKSTAT))
+ cmn_err(CE_ALERT,
+ "Device %s - bad inode magic/vsn "
+ "daddr %lld #%d (magic=%x)",
+ XFS_BUFTARG_NAME(mp->m_ddev_targp),
(unsigned long long)imap.im_blkno, i,
INT_GET(dip->di_core.di_magic, ARCH_CONVERT));
#endif
return XFS_ERROR(EFSCORRUPTED);
}
}
-#endif /* __KERNEL__ */
xfs_inobp_check(mp, bp);
STATIC uint
_xfs_dic2xflags(
- xfs_dinode_core_t *dic,
__uint16_t di_flags)
{
uint flags = 0;
flags |= XFS_XFLAG_EXTSIZE;
if (di_flags & XFS_DIFLAG_EXTSZINHERIT)
flags |= XFS_XFLAG_EXTSZINHERIT;
+ if (di_flags & XFS_DIFLAG_NODEFRAG)
+ flags |= XFS_XFLAG_NODEFRAG;
}
return flags;
{
xfs_dinode_core_t *dic = &ip->i_d;
- return _xfs_dic2xflags(dic, dic->di_flags) |
- (XFS_CFORK_Q(dic) ? XFS_XFLAG_HASATTR : 0);
+ return _xfs_dic2xflags(dic->di_flags) |
+ (XFS_CFORK_Q(dic) ? XFS_XFLAG_HASATTR : 0);
}
uint
xfs_dic2xflags(
xfs_dinode_core_t *dic)
{
- return _xfs_dic2xflags(dic, INT_GET(dic->di_flags, ARCH_CONVERT)) |
- (XFS_CFORK_Q_DISK(dic) ? XFS_XFLAG_HASATTR : 0);
+ return _xfs_dic2xflags(INT_GET(dic->di_flags, ARCH_CONVERT)) |
+ (XFS_CFORK_Q_DISK(dic) ? XFS_XFLAG_HASATTR : 0);
}
/*
{
xfs_ino_t ino;
xfs_inode_t *ip;
- vnode_t *vp;
+ bhv_vnode_t *vp;
uint flags;
int error;
di_flags |= XFS_DIFLAG_NOSYMLINKS;
if (pip->i_d.di_flags & XFS_DIFLAG_PROJINHERIT)
di_flags |= XFS_DIFLAG_PROJINHERIT;
+ if ((pip->i_d.di_flags & XFS_DIFLAG_NODEFRAG) &&
+ xfs_inherit_nodefrag)
+ di_flags |= XFS_DIFLAG_NODEFRAG;
ip->i_d.di_flags |= di_flags;
}
/* FALLTHROUGH */
*/
xfs_trans_log_inode(tp, ip, flags);
- /* now that we have an i_mode we can set Linux inode ops (& unlock) */
- VFS_INIT_VNODE(XFS_MTOVFS(tp->t_mountp), vp, XFS_ITOBHV(ip), 1);
+ /* now that we have an i_mode we can setup inode ops and unlock */
+ bhv_vfs_init_vnode(XFS_MTOVFS(tp->t_mountp), vp, XFS_ITOBHV(ip), 1);
*ipp = ip;
return 0;
(xfs_ufsize_t)XFS_MAXIOFFSET(mp)) -
map_first),
XFS_BMAPI_ENTIRE, NULL, 0, imaps, &nimaps,
- NULL))
+ NULL, NULL))
return;
ASSERT(nimaps == 1);
ASSERT(imaps[0].br_startblock == HOLESTARTBLOCK);
xfs_fsize_t last_byte;
xfs_off_t toss_start;
xfs_mount_t *mp;
- vnode_t *vp;
+ bhv_vnode_t *vp;
ASSERT(ismrlocked(&ip->i_iolock, MR_UPDATE) != 0);
ASSERT((new_size == 0) || (new_size <= ip->i_d.di_size));
vn_iowait(vp); /* wait for the completion of any pending DIOs */
/*
- * Call VOP_TOSS_PAGES() or VOP_FLUSHINVAL_PAGES() to get rid of pages and buffers
+ * Call toss_pages or flushinval_pages to get rid of pages
* overlapping the region being removed. We have to use
- * the less efficient VOP_FLUSHINVAL_PAGES() in the case that the
+ * the less efficient flushinval_pages in the case that the
* caller may not be able to finish the truncate without
* dropping the inode's I/O lock. Make sure
* to catch any pages brought in by buffers overlapping
* so that we don't toss things on the same block as
* new_size but before it.
*
- * Before calling VOP_TOSS_PAGES() or VOP_FLUSHINVAL_PAGES(), make sure to
+ * Before calling toss_page or flushinval_pages, make sure to
* call remapf() over the same region if the file is mapped.
* This frees up mapped file references to the pages in the
- * given range and for the VOP_FLUSHINVAL_PAGES() case it ensures
+ * given range and for the flushinval_pages case it ensures
* that we get the latest mapped changes flushed out.
*/
toss_start = XFS_B_TO_FSB(mp, (xfs_ufsize_t)new_size);
last_byte);
if (last_byte > toss_start) {
if (flags & XFS_ITRUNC_DEFINITE) {
- VOP_TOSS_PAGES(vp, toss_start, -1, FI_REMAPF_LOCKED);
+ bhv_vop_toss_pages(vp, toss_start, -1, FI_REMAPF_LOCKED);
} else {
- VOP_FLUSHINVAL_PAGES(vp, toss_start, -1, FI_REMAPF_LOCKED);
+ bhv_vop_flushinval_pages(vp, toss_start, -1, FI_REMAPF_LOCKED);
}
}
* runs.
*/
XFS_BMAP_INIT(&free_list, &first_block);
- error = xfs_bunmapi(ntp, ip, first_unmap_block,
- unmap_len,
+ error = XFS_BUNMAPI(mp, ntp, &ip->i_iocore,
+ first_unmap_block, unmap_len,
XFS_BMAPI_AFLAG(fork) |
(sync ? 0 : XFS_BMAPI_ASYNC),
XFS_ITRUNC_MAX_EXTENTS,
- &first_block, &free_list, &done);
+ &first_block, &free_list,
+ NULL, &done);
if (error) {
/*
* If the bunmapi call encounters an error,
* the inode to become unpinned.
*/
if (!(ip->i_flags & (XFS_IRECLAIM|XFS_IRECLAIMABLE))) {
- vnode_t *vp = XFS_ITOV_NULL(ip);
+ bhv_vnode_t *vp = XFS_ITOV_NULL(ip);
/* make sync come back and flush this inode */
if (vp) {
struct inode *inode = vn_to_inode(vp);
- if (!(inode->i_state & I_NEW))
+ if (!(inode->i_state &
+ (I_NEW|I_FREEING|I_CLEAR)))
mark_inode_dirty_sync(inode);
}
}
ASSERT(ifp->if_bytes <= XFS_IFORK_SIZE(ip, whichfork));
memcpy(cp, ifp->if_u1.if_data, ifp->if_bytes);
}
- if (whichfork == XFS_DATA_FORK) {
- if (unlikely(XFS_DIR_SHORTFORM_VALIDATE_ONDISK(mp, dip))) {
- XFS_ERROR_REPORT("xfs_iflush_fork",
- XFS_ERRLEVEL_LOW, mp);
- return XFS_ERROR(EFSCORRUPTED);
- }
- }
break;
case XFS_DINODE_FMT_EXTENTS:
XFS_STATS_INC(xs_iflush_count);
ASSERT(ismrlocked(&ip->i_lock, MR_UPDATE|MR_ACCESS));
- ASSERT(valusema(&ip->i_flock) <= 0);
+ ASSERT(issemalocked(&(ip->i_flock)));
ASSERT(ip->i_d.di_format != XFS_DINODE_FMT_BTREE ||
ip->i_d.di_nextents > ip->i_df.if_ext_max);
corrupt_out:
xfs_buf_relse(bp);
- xfs_force_shutdown(mp, XFS_CORRUPT_INCORE);
+ xfs_force_shutdown(mp, SHUTDOWN_CORRUPT_INCORE);
xfs_iflush_abort(ip);
/*
* Unlocks the flush lock
xfs_buf_relse(bp);
}
- xfs_force_shutdown(mp, XFS_CORRUPT_INCORE);
+ xfs_force_shutdown(mp, SHUTDOWN_CORRUPT_INCORE);
if(!bufwasdelwri) {
/*
SPLDECL(s);
ASSERT(ismrlocked(&ip->i_lock, MR_UPDATE|MR_ACCESS));
- ASSERT(valusema(&ip->i_flock) <= 0);
+ ASSERT(issemalocked(&(ip->i_flock)));
ASSERT(ip->i_d.di_format != XFS_DINODE_FMT_BTREE ||
ip->i_d.di_nextents > ip->i_df.if_ext_max);
xfs_mount_t *mp)
{
xfs_inode_t *ip;
- vnode_t *vp;
+ bhv_vnode_t *vp;
again:
XFS_MOUNT_ILOCK(mp);
*/
memcpy(ifp->if_u2.if_inline_ext, ifp->if_u1.if_extents,
nextents * sizeof(xfs_bmbt_rec_t));
- kmem_free(ifp->if_u1.if_extents, KM_SLEEP);
+ kmem_free(ifp->if_u1.if_extents, ifp->if_real_bytes);
ifp->if_u1.if_extents = ifp->if_u2.if_inline_ext;
ifp->if_real_bytes = 0;
}
#ifdef __KERNEL__
struct bhv_desc;
+struct bhv_vnode;
struct cred;
struct ktrace;
-struct vnode;
struct xfs_buf;
struct xfs_bmap_free;
struct xfs_bmbt_irec;
void xfs_chash_free(struct xfs_mount *);
xfs_inode_t *xfs_inode_incore(struct xfs_mount *, xfs_ino_t,
struct xfs_trans *);
-void xfs_inode_lock_init(xfs_inode_t *, struct vnode *);
+void xfs_inode_lock_init(xfs_inode_t *, struct bhv_vnode *);
int xfs_iget(struct xfs_mount *, struct xfs_trans *, xfs_ino_t,
uint, uint, xfs_inode_t **, xfs_daddr_t);
void xfs_iput(xfs_inode_t *, uint);
xfs_fsize_t xfs_file_last_byte(xfs_inode_t *);
void xfs_lock_inodes(xfs_inode_t **, int, int, uint);
-xfs_inode_t *xfs_vtoi(struct vnode *vp);
+xfs_inode_t *xfs_vtoi(struct bhv_vnode *vp);
void xfs_synchronize_atime(xfs_inode_t *);
extern struct kmem_zone *xfs_ifork_zone;
extern struct kmem_zone *xfs_inode_zone;
extern struct kmem_zone *xfs_ili_zone;
-extern struct vnodeops xfs_vnodeops;
#endif /* __KERNEL__ */
#include "xfs_buf_item.h"
#include "xfs_sb.h"
#include "xfs_ag.h"
-#include "xfs_dir.h"
#include "xfs_dir2.h"
#include "xfs_dmapi.h"
#include "xfs_mount.h"
#include "xfs_bmap_btree.h"
#include "xfs_alloc_btree.h"
#include "xfs_ialloc_btree.h"
-#include "xfs_dir_sf.h"
#include "xfs_dir2_sf.h"
#include "xfs_attr_sf.h"
#include "xfs_dinode.h"
* inode flush completed and the inode was taken off the AIL.
* So, just get out.
*/
- if ((valusema(&(ip->i_flock)) > 0) ||
+ if (!issemalocked(&(ip->i_flock)) ||
((iip->ili_item.li_flags & XFS_LI_IN_AIL) == 0)) {
iip->ili_pushbuf_flag = 0;
xfs_iunlock(ip, XFS_ILOCK_SHARED);
* If not, we can flush it async.
*/
dopush = ((iip->ili_item.li_flags & XFS_LI_IN_AIL) &&
- (valusema(&(ip->i_flock)) <= 0));
+ issemalocked(&(ip->i_flock)));
iip->ili_pushbuf_flag = 0;
xfs_iunlock(ip, XFS_ILOCK_SHARED);
xfs_buftrace("INODE ITEM PUSH", bp);
ip = iip->ili_inode;
ASSERT(ismrlocked(&(ip->i_lock), MR_ACCESS));
- ASSERT(valusema(&(ip->i_flock)) <= 0);
+ ASSERT(issemalocked(&(ip->i_flock)));
/*
* Since we were able to lock the inode's flush lock and
* we found it on the AIL, the inode must be dirty. This
{
xfs_iflush_abort(iip->ili_inode);
}
+
+/*
+ * convert an xfs_inode_log_format struct from either 32 or 64 bit versions
+ * (which can have different field alignments) to the native version
+ */
+int
+xfs_inode_item_format_convert(
+ xfs_log_iovec_t *buf,
+ xfs_inode_log_format_t *in_f)
+{
+ if (buf->i_len == sizeof(xfs_inode_log_format_32_t)) {
+ xfs_inode_log_format_32_t *in_f32;
+
+ in_f32 = (xfs_inode_log_format_32_t *)buf->i_addr;
+ in_f->ilf_type = in_f32->ilf_type;
+ in_f->ilf_size = in_f32->ilf_size;
+ in_f->ilf_fields = in_f32->ilf_fields;
+ in_f->ilf_asize = in_f32->ilf_asize;
+ in_f->ilf_dsize = in_f32->ilf_dsize;
+ in_f->ilf_ino = in_f32->ilf_ino;
+ /* copy biggest field of ilf_u */
+ memcpy(in_f->ilf_u.ilfu_uuid.__u_bits,
+ in_f32->ilf_u.ilfu_uuid.__u_bits,
+ sizeof(uuid_t));
+ in_f->ilf_blkno = in_f32->ilf_blkno;
+ in_f->ilf_len = in_f32->ilf_len;
+ in_f->ilf_boffset = in_f32->ilf_boffset;
+ return 0;
+ } else if (buf->i_len == sizeof(xfs_inode_log_format_64_t)){
+ xfs_inode_log_format_64_t *in_f64;
+
+ in_f64 = (xfs_inode_log_format_64_t *)buf->i_addr;
+ in_f->ilf_type = in_f64->ilf_type;
+ in_f->ilf_size = in_f64->ilf_size;
+ in_f->ilf_fields = in_f64->ilf_fields;
+ in_f->ilf_asize = in_f64->ilf_asize;
+ in_f->ilf_dsize = in_f64->ilf_dsize;
+ in_f->ilf_ino = in_f64->ilf_ino;
+ /* copy biggest field of ilf_u */
+ memcpy(in_f->ilf_u.ilfu_uuid.__u_bits,
+ in_f64->ilf_u.ilfu_uuid.__u_bits,
+ sizeof(uuid_t));
+ in_f->ilf_blkno = in_f64->ilf_blkno;
+ in_f->ilf_len = in_f64->ilf_len;
+ in_f->ilf_boffset = in_f64->ilf_boffset;
+ return 0;
+ }
+ return EFSCORRUPTED;
+}
* log. The size of the inline data/extents/b-tree root to be logged
* (if any) is indicated in the ilf_dsize field. Changes to this structure
* must be added on to the end.
- *
- * Convention for naming inode log item versions : The current version
- * is always named XFS_LI_INODE. When an inode log item gets superseded,
- * add the latest version of IRIX that will generate logs with that item
- * to the version name.
- *
- * -Version 1 of this structure (XFS_LI_5_3_INODE) included up to the first
- * union (ilf_u) field. This was released with IRIX 5.3-XFS.
- * -Version 2 of this structure (XFS_LI_6_1_INODE) is currently the entire
- * structure. This was released with IRIX 6.0.1-XFS and IRIX 6.1.
- * -Version 3 of this structure (XFS_LI_INODE) is the same as version 2
- * so a new structure definition wasn't necessary. However, we had
- * to add a new type because the inode cluster size changed from 4K
- * to 8K and the version number had to be rev'ved to keep older kernels
- * from trying to recover logs with the 8K buffers in them. The logging
- * code can handle recovery on different-sized clusters now so hopefully
- * this'll be the last time we need to change the inode log item just
- * for a change in the inode cluster size. This new version was
- * released with IRIX 6.2.
*/
typedef struct xfs_inode_log_format {
unsigned short ilf_type; /* inode log item type */
int ilf_boffset; /* off of inode in buffer */
} xfs_inode_log_format_t;
-/* Initial version shipped with IRIX 5.3-XFS */
-typedef struct xfs_inode_log_format_v1 {
- unsigned short ilf_type; /* inode log item type */
- unsigned short ilf_size; /* size of this item */
- uint ilf_fields; /* flags for fields logged */
- uint ilf_dsize; /* size of data/ext/root */
- xfs_ino_t ilf_ino; /* inode number */
+typedef struct xfs_inode_log_format_32 {
+ unsigned short ilf_type; /* 16: inode log item type */
+ unsigned short ilf_size; /* 16: size of this item */
+ uint ilf_fields; /* 32: flags for fields logged */
+ ushort ilf_asize; /* 32: size of attr d/ext/root */
+ ushort ilf_dsize; /* 32: size of data/ext/root */
+ xfs_ino_t ilf_ino; /* 64: inode number */
union {
- xfs_dev_t ilfu_rdev; /* rdev value for dev inode*/
- uuid_t ilfu_uuid; /* mount point value */
+ xfs_dev_t ilfu_rdev; /* 32: rdev value for dev inode*/
+ uuid_t ilfu_uuid; /* 128: mount point value */
+ } ilf_u;
+ __int64_t ilf_blkno; /* 64: blkno of inode buffer */
+ int ilf_len; /* 32: len of inode buffer */
+ int ilf_boffset; /* 32: off of inode in buffer */
+} __attribute__((packed)) xfs_inode_log_format_32_t;
+
+typedef struct xfs_inode_log_format_64 {
+ unsigned short ilf_type; /* 16: inode log item type */
+ unsigned short ilf_size; /* 16: size of this item */
+ uint ilf_fields; /* 32: flags for fields logged */
+ ushort ilf_asize; /* 32: size of attr d/ext/root */
+ ushort ilf_dsize; /* 32: size of data/ext/root */
+ __uint32_t ilf_pad; /* 32: pad for 64 bit boundary */
+ xfs_ino_t ilf_ino; /* 64: inode number */
+ union {
+ xfs_dev_t ilfu_rdev; /* 32: rdev value for dev inode*/
+ uuid_t ilfu_uuid; /* 128: mount point value */
} ilf_u;
-} xfs_inode_log_format_t_v1;
+ __int64_t ilf_blkno; /* 64: blkno of inode buffer */
+ int ilf_len; /* 32: len of inode buffer */
+ int ilf_boffset; /* 32: off of inode in buffer */
+} xfs_inode_log_format_64_t;
/*
* Flags for xfs_trans_log_inode flags field.
extern void xfs_iflush_done(struct xfs_buf *, xfs_inode_log_item_t *);
extern void xfs_istale_done(struct xfs_buf *, xfs_inode_log_item_t *);
extern void xfs_iflush_abort(struct xfs_inode *);
+extern int xfs_inode_item_format_convert(xfs_log_iovec_t *,
+ xfs_inode_log_format_t *);
#endif /* __KERNEL__ */
#include "xfs_trans.h"
#include "xfs_sb.h"
#include "xfs_ag.h"
-#include "xfs_dir.h"
#include "xfs_dir2.h"
+#include "xfs_dfrag.h"
#include "xfs_dmapi.h"
#include "xfs_mount.h"
#include "xfs_bmap_btree.h"
#include "xfs_alloc_btree.h"
#include "xfs_ialloc_btree.h"
-#include "xfs_dir_sf.h"
#include "xfs_dir2_sf.h"
#include "xfs_attr_sf.h"
#include "xfs_dinode.h"
STATIC int
xfs_ioinit(
- struct vfs *vfsp,
+ struct bhv_vfs *vfsp,
struct xfs_mount_args *mntargs,
int flags)
{
xfs_ioops_t xfs_iocore_xfs = {
.xfs_ioinit = (xfs_ioinit_t) xfs_ioinit,
.xfs_bmapi_func = (xfs_bmapi_t) xfs_bmapi,
+ .xfs_bunmapi_func = (xfs_bunmapi_t) xfs_bunmapi,
.xfs_bmap_eof_func = (xfs_bmap_eof_t) xfs_bmap_eof,
.xfs_iomap_write_direct =
(xfs_iomap_write_direct_t) xfs_iomap_write_direct,
.xfs_unlock = (xfs_unlk_t) xfs_iunlock,
.xfs_size_func = (xfs_size_t) xfs_size_fn,
.xfs_iodone = (xfs_iodone_t) fs_noerr,
+ .xfs_swap_extents_func = (xfs_swap_extents_t) xfs_swap_extents,
};
void
/*
- * Copyright (c) 2000-2005 Silicon Graphics, Inc.
+ * Copyright (c) 2000-2006 Silicon Graphics, Inc.
* All Rights Reserved.
*
* This program is free software; you can redistribute it and/or
#include "xfs_trans.h"
#include "xfs_sb.h"
#include "xfs_ag.h"
-#include "xfs_dir.h"
#include "xfs_dir2.h"
#include "xfs_alloc.h"
#include "xfs_dmapi.h"
#include "xfs_bmap_btree.h"
#include "xfs_alloc_btree.h"
#include "xfs_ialloc_btree.h"
-#include "xfs_dir_sf.h"
#include "xfs_dir2_sf.h"
#include "xfs_attr_sf.h"
#include "xfs_dinode.h"
error = XFS_BMAPI(mp, NULL, io, offset_fsb,
(xfs_filblks_t)(end_fsb - offset_fsb),
bmapi_flags, NULL, 0, &imap,
- &nimaps, NULL);
+ &nimaps, NULL, NULL);
if (error)
goto out;
*/
XFS_BMAP_INIT(&free_list, &firstfsb);
nimaps = 1;
- error = xfs_bmapi(tp, ip, offset_fsb, count_fsb,
- bmapi_flag, &firstfsb, 0, &imap, &nimaps, &free_list);
+ error = XFS_BMAPI(mp, tp, io, offset_fsb, count_fsb, bmapi_flag,
+ &firstfsb, 0, &imap, &nimaps, &free_list, NULL);
if (error)
goto error0;
while (count_fsb > 0) {
imaps = nimaps;
firstblock = NULLFSBLOCK;
- error = XFS_BMAPI(mp, NULL, io, start_fsb, count_fsb,
- 0, &firstblock, 0, imap, &imaps, NULL);
+ error = XFS_BMAPI(mp, NULL, io, start_fsb, count_fsb, 0,
+ &firstblock, 0, imap, &imaps, NULL, NULL);
if (error)
return error;
for (n = 0; n < imaps; n++) {
nimaps = XFS_WRITE_IMAPS;
firstblock = NULLFSBLOCK;
- error = xfs_bmapi(NULL, ip, offset_fsb,
+ error = XFS_BMAPI(mp, NULL, io, offset_fsb,
(xfs_filblks_t)(last_fsb - offset_fsb),
XFS_BMAPI_DELAY | XFS_BMAPI_WRITE |
XFS_BMAPI_ENTIRE, &firstblock, 1, imap,
- &nimaps, NULL);
+ &nimaps, NULL, NULL);
if (error && (error != ENOSPC))
return XFS_ERROR(error);
}
/* Go get the actual blocks */
- error = xfs_bmapi(tp, ip, map_start_fsb, count_fsb,
+ error = XFS_BMAPI(mp, tp, io, map_start_fsb, count_fsb,
XFS_BMAPI_WRITE, &first_block, 1,
- imap, &nimaps, &free_list);
+ imap, &nimaps, &free_list, NULL);
if (error)
goto trans_cancel;
*/
XFS_BMAP_INIT(&free_list, &firstfsb);
nimaps = 1;
- error = xfs_bmapi(tp, ip, offset_fsb, count_fsb,
+ error = XFS_BMAPI(mp, tp, io, offset_fsb, count_fsb,
XFS_BMAPI_WRITE|XFS_BMAPI_CONVERT, &firstfsb,
- 1, &imap, &nimaps, &free_list);
+ 1, &imap, &nimaps, &free_list, NULL);
if (error)
goto error_on_bmapi_transaction;
#include "xfs_trans.h"
#include "xfs_sb.h"
#include "xfs_ag.h"
-#include "xfs_dir.h"
#include "xfs_dir2.h"
#include "xfs_dmapi.h"
#include "xfs_mount.h"
#include "xfs_bmap_btree.h"
#include "xfs_alloc_btree.h"
#include "xfs_ialloc_btree.h"
-#include "xfs_dir_sf.h"
#include "xfs_dir2_sf.h"
#include "xfs_attr_sf.h"
#include "xfs_dinode.h"
#include "xfs_error.h"
#include "xfs_btree.h"
-#ifndef HAVE_USERACC
-#define useracc(ubuffer, size, flags, foo) (0)
-#define unuseracc(ubuffer, size, flags)
-#endif
-
STATIC int
xfs_bulkstat_one_iget(
xfs_mount_t *mp, /* mount point for filesystem */
{
xfs_dinode_core_t *dic; /* dinode core info pointer */
xfs_inode_t *ip; /* incore inode pointer */
- vnode_t *vp;
+ bhv_vnode_t *vp;
int error;
error = xfs_iget(mp, NULL, ino, 0, XFS_ILOCK_SHARED, &ip, bno);
(XFS_INODE_CLUSTER_SIZE(mp) >> mp->m_sb.sb_inodelog);
nimask = ~(nicluster - 1);
nbcluster = nicluster >> mp->m_sb.sb_inopblog;
- /*
- * Lock down the user's buffer. If a buffer was not sent, as in the case
- * disk quota code calls here, we skip this.
- */
- if (ubuffer &&
- (error = useracc(ubuffer, ubcount * statstruct_size,
- (B_READ|B_PHYS), NULL))) {
- return error;
- }
/*
* Allocate a page-sized buffer for inode btree records.
* We could try allocating something smaller, but for normal
* Done, we're either out of filesystem or space to put the data.
*/
kmem_free(irbuf, NBPC);
- if (ubuffer)
- unuseracc(ubuffer, ubcount * statstruct_size, (B_READ|B_PHYS));
*ubcountp = ubelem;
if (agno >= mp->m_sb.sb_agcount) {
/*
*/
#define BULKSTAT_FG_IGET 0x1 /* Go through the buffer cache */
#define BULKSTAT_FG_QUICK 0x2 /* No iget, walk the dinode cluster */
-#define BULKSTAT_FG_VFSLOCKED 0x4 /* Already have vfs lock */
/*
* Return stat information in bulk (by-inode) for the filesystem.
#include "xfs_trans.h"
#include "xfs_sb.h"
#include "xfs_ag.h"
-#include "xfs_dir.h"
#include "xfs_dir2.h"
#include "xfs_dmapi.h"
#include "xfs_mount.h"
#include "xfs_ialloc_btree.h"
#include "xfs_log_recover.h"
#include "xfs_trans_priv.h"
-#include "xfs_dir_sf.h"
#include "xfs_dir2_sf.h"
#include "xfs_attr_sf.h"
#include "xfs_dinode.h"
xlog_in_core_t *iclog = (xlog_in_core_t *)iclog_hndl;
if (xlog_state_release_iclog(log, iclog)) {
- xfs_force_shutdown(mp, XFS_LOG_IO_ERROR);
+ xfs_force_shutdown(mp, SHUTDOWN_LOG_IO_ERROR);
return EIO;
}
* just worked.
*/
if (!(mp->m_flags & XFS_MOUNT_NORECOVERY)) {
- int error;
- vfs_t *vfsp = XFS_MTOVFS(mp);
- int readonly = (vfsp->vfs_flag & VFS_RDONLY);
+ bhv_vfs_t *vfsp = XFS_MTOVFS(mp);
+ int error, readonly = (vfsp->vfs_flag & VFS_RDONLY);
if (readonly)
vfsp->vfs_flag &= ~VFS_RDONLY;
return XFS_ERROR(EIO);
if ((error = xlog_write(mp, reg, nentries, tic, start_lsn, NULL, 0))) {
- xfs_force_shutdown(mp, XFS_LOG_IO_ERROR);
+ xfs_force_shutdown(mp, SHUTDOWN_LOG_IO_ERROR);
}
return error;
} /* xfs_log_write */
SPLDECL(s);
int needed = 0, gen;
xlog_t *log = mp->m_log;
- vfs_t *vfsp = XFS_MTOVFS(mp);
+ bhv_vfs_t *vfsp = XFS_MTOVFS(mp);
- if (fs_frozen(vfsp) || XFS_FORCED_SHUTDOWN(mp) ||
+ if (vfs_test_for_freeze(vfsp) || XFS_FORCED_SHUTDOWN(mp) ||
(vfsp->vfs_flag & VFS_RDONLY))
return 0;
XFS_ERRTAG_IODONE_IOERR, XFS_RANDOM_IODONE_IOERR)) {
xfs_ioerror_alert("xlog_iodone", l->l_mp, bp, XFS_BUF_ADDR(bp));
XFS_BUF_STALE(bp);
- xfs_force_shutdown(l->l_mp, XFS_LOG_IO_ERROR);
+ xfs_force_shutdown(l->l_mp, SHUTDOWN_LOG_IO_ERROR);
/*
* This flag will be propagated to the trans-committed
* callback routines to let them know that the log-commit
ASSERT_ALWAYS(iclog);
if ((error = xlog_write(mp, reg, 1, ticket, commitlsnp,
iclog, XLOG_COMMIT_TRANS))) {
- xfs_force_shutdown(mp, XFS_LOG_IO_ERROR);
+ xfs_force_shutdown(mp, SHUTDOWN_LOG_IO_ERROR);
}
return error;
} /* xlog_commit_record */
xfs_cmn_err(XFS_PTAG_LOGRES, CE_ALERT, mp,
"xfs_log_write: reservation ran out. Need to up reservation");
/* If we did not panic, shutdown the filesystem */
- xfs_force_shutdown(mp, XFS_CORRUPT_INCORE);
+ xfs_force_shutdown(mp, SHUTDOWN_CORRUPT_INCORE);
#endif
} else
ticket->t_curr_res -= len;
/*
- * Copyright (c) 2000-2003,2005 Silicon Graphics, Inc.
+ * Copyright (c) 2000-2006 Silicon Graphics, Inc.
* All Rights Reserved.
*
* This program is free software; you can redistribute it and/or
#include "xfs_trans.h"
#include "xfs_sb.h"
#include "xfs_ag.h"
-#include "xfs_dir.h"
#include "xfs_dir2.h"
#include "xfs_dmapi.h"
#include "xfs_mount.h"
#include "xfs_bmap_btree.h"
#include "xfs_alloc_btree.h"
#include "xfs_ialloc_btree.h"
-#include "xfs_dir_sf.h"
#include "xfs_dir2_sf.h"
#include "xfs_attr_sf.h"
#include "xfs_dinode.h"
{
int b;
- printk("%s: SB : uuid = ", __FUNCTION__);
+ cmn_err(CE_DEBUG, "%s: SB : uuid = ", __FUNCTION__);
for (b = 0; b < 16; b++)
- printk("%02x",((unsigned char *)&mp->m_sb.sb_uuid)[b]);
- printk(", fmt = %d\n", XLOG_FMT);
- printk(" log : uuid = ");
+ cmn_err(CE_DEBUG, "%02x", ((uchar_t *)&mp->m_sb.sb_uuid)[b]);
+ cmn_err(CE_DEBUG, ", fmt = %d\n", XLOG_FMT);
+ cmn_err(CE_DEBUG, " log : uuid = ");
for (b = 0; b < 16; b++)
- printk("%02x",((unsigned char *)&head->h_fs_uuid)[b]);
- printk(", fmt = %d\n", INT_GET(head->h_fmt, ARCH_CONVERT));
+ cmn_err(CE_DEBUG, "%02x",((uchar_t *)&head->h_fs_uuid)[b]);
+ cmn_err(CE_DEBUG, ", fmt = %d\n", INT_GET(head->h_fmt, ARCH_CONVERT));
}
#else
#define xlog_header_check_dump(mp, head)
mp = XFS_BUF_FSPRIVATE(bp, xfs_mount_t *);
xfs_ioerror_alert("xlog_recover_iodone",
mp, bp, XFS_BUF_ADDR(bp));
- xfs_force_shutdown(mp, XFS_METADATA_IO_ERROR);
+ xfs_force_shutdown(mp, SHUTDOWN_META_IO_ERROR);
}
XFS_BUF_SET_FSPRIVATE(bp, NULL);
XFS_BUF_CLR_IODONE_FUNC(bp);
buffer_nextp = (xfs_agino_t *)xfs_buf_offset(bp,
next_unlinked_offset);
- INT_SET(*buffer_nextp, ARCH_CONVERT, *logged_nextp);
+ *buffer_nextp = *logged_nextp;
}
return 0;
int attr_index;
uint fields;
xfs_dinode_core_t *dicp;
+ int need_free = 0;
if (pass == XLOG_RECOVER_PASS1) {
return 0;
}
- in_f = (xfs_inode_log_format_t *)item->ri_buf[0].i_addr;
+ if (item->ri_buf[0].i_len == sizeof(xfs_inode_log_format_t)) {
+ in_f = (xfs_inode_log_format_t *)item->ri_buf[0].i_addr;
+ } else {
+ in_f = (xfs_inode_log_format_t *)kmem_alloc(
+ sizeof(xfs_inode_log_format_t), KM_SLEEP);
+ need_free = 1;
+ error = xfs_inode_item_format_convert(&item->ri_buf[0], in_f);
+ if (error)
+ goto error;
+ }
ino = in_f->ilf_ino;
mp = log->l_mp;
if (ITEM_TYPE(item) == XFS_LI_INODE) {
* Inode buffers can be freed, look out for it,
* and do not replay the inode.
*/
- if (xlog_check_buffer_cancelled(log, imap.im_blkno, imap.im_len, 0))
- return 0;
+ if (xlog_check_buffer_cancelled(log, imap.im_blkno, imap.im_len, 0)) {
+ error = 0;
+ goto error;
+ }
bp = xfs_buf_read_flags(mp->m_ddev_targp, imap.im_blkno, imap.im_len,
XFS_BUF_LOCK);
bp, imap.im_blkno);
error = XFS_BUF_GETERROR(bp);
xfs_buf_relse(bp);
- return error;
+ goto error;
}
error = 0;
ASSERT(in_f->ilf_fields & XFS_ILOG_CORE);
dip, bp, ino);
XFS_ERROR_REPORT("xlog_recover_do_inode_trans(1)",
XFS_ERRLEVEL_LOW, mp);
- return XFS_ERROR(EFSCORRUPTED);
+ error = EFSCORRUPTED;
+ goto error;
}
dicp = (xfs_dinode_core_t*)(item->ri_buf[1].i_addr);
if (unlikely(dicp->di_magic != XFS_DINODE_MAGIC)) {
item, ino);
XFS_ERROR_REPORT("xlog_recover_do_inode_trans(2)",
XFS_ERRLEVEL_LOW, mp);
- return XFS_ERROR(EFSCORRUPTED);
+ error = EFSCORRUPTED;
+ goto error;
}
/* Skip replay when the on disk inode is newer than the log one */
/* do nothing */
} else {
xfs_buf_relse(bp);
- return 0;
+ error = 0;
+ goto error;
}
}
/* Take the opportunity to reset the flush iteration count */
xfs_fs_cmn_err(CE_ALERT, mp,
"xfs_inode_recover: Bad regular inode log record, rec ptr 0x%p, ino ptr = 0x%p, ino bp = 0x%p, ino %Ld",
item, dip, bp, ino);
- return XFS_ERROR(EFSCORRUPTED);
+ error = EFSCORRUPTED;
+ goto error;
}
} else if (unlikely((dicp->di_mode & S_IFMT) == S_IFDIR)) {
if ((dicp->di_format != XFS_DINODE_FMT_EXTENTS) &&
xfs_fs_cmn_err(CE_ALERT, mp,
"xfs_inode_recover: Bad dir inode log record, rec ptr 0x%p, ino ptr = 0x%p, ino bp = 0x%p, ino %Ld",
item, dip, bp, ino);
- return XFS_ERROR(EFSCORRUPTED);
+ error = EFSCORRUPTED;
+ goto error;
}
}
if (unlikely(dicp->di_nextents + dicp->di_anextents > dicp->di_nblocks)){
item, dip, bp, ino,
dicp->di_nextents + dicp->di_anextents,
dicp->di_nblocks);
- return XFS_ERROR(EFSCORRUPTED);
+ error = EFSCORRUPTED;
+ goto error;
}
if (unlikely(dicp->di_forkoff > mp->m_sb.sb_inodesize)) {
XFS_CORRUPTION_ERROR("xlog_recover_do_inode_trans(6)",
xfs_fs_cmn_err(CE_ALERT, mp,
"xfs_inode_recover: Bad inode log rec ptr 0x%p, dino ptr 0x%p, dino bp 0x%p, ino %Ld, forkoff 0x%x",
item, dip, bp, ino, dicp->di_forkoff);
- return XFS_ERROR(EFSCORRUPTED);
+ error = EFSCORRUPTED;
+ goto error;
}
if (unlikely(item->ri_buf[1].i_len > sizeof(xfs_dinode_core_t))) {
XFS_CORRUPTION_ERROR("xlog_recover_do_inode_trans(7)",
xfs_fs_cmn_err(CE_ALERT, mp,
"xfs_inode_recover: Bad inode log record length %d, rec ptr 0x%p",
item->ri_buf[1].i_len, item);
- return XFS_ERROR(EFSCORRUPTED);
+ error = EFSCORRUPTED;
+ goto error;
}
/* The core is in in-core format */
xlog_warn("XFS: xlog_recover_do_inode_trans: Invalid flag");
ASSERT(0);
xfs_buf_relse(bp);
- return XFS_ERROR(EIO);
+ error = EIO;
+ goto error;
}
}
error = xfs_bwrite(mp, bp);
}
- return (error);
+error:
+ if (need_free)
+ kmem_free(in_f, sizeof(*in_f));
+ return XFS_ERROR(error);
}
/*
* structure into it, and adds the efi to the AIL with the given
* LSN.
*/
-STATIC void
+STATIC int
xlog_recover_do_efi_trans(
xlog_t *log,
xlog_recover_item_t *item,
xfs_lsn_t lsn,
int pass)
{
+ int error;
xfs_mount_t *mp;
xfs_efi_log_item_t *efip;
xfs_efi_log_format_t *efi_formatp;
SPLDECL(s);
if (pass == XLOG_RECOVER_PASS1) {
- return;
+ return 0;
}
efi_formatp = (xfs_efi_log_format_t *)item->ri_buf[0].i_addr;
- ASSERT(item->ri_buf[0].i_len ==
- (sizeof(xfs_efi_log_format_t) +
- ((efi_formatp->efi_nextents - 1) * sizeof(xfs_extent_t))));
mp = log->l_mp;
efip = xfs_efi_init(mp, efi_formatp->efi_nextents);
- memcpy((char *)&(efip->efi_format), (char *)efi_formatp,
- sizeof(xfs_efi_log_format_t) +
- ((efi_formatp->efi_nextents - 1) * sizeof(xfs_extent_t)));
+ if ((error = xfs_efi_copy_format(&(item->ri_buf[0]),
+ &(efip->efi_format)))) {
+ xfs_efi_item_free(efip);
+ return error;
+ }
efip->efi_next_extent = efi_formatp->efi_nextents;
efip->efi_flags |= XFS_EFI_COMMITTED;
* xfs_trans_update_ail() drops the AIL lock.
*/
xfs_trans_update_ail(mp, (xfs_log_item_t *)efip, lsn, s);
+ return 0;
}
}
efd_formatp = (xfs_efd_log_format_t *)item->ri_buf[0].i_addr;
- ASSERT(item->ri_buf[0].i_len ==
- (sizeof(xfs_efd_log_format_t) +
- ((efd_formatp->efd_nextents - 1) * sizeof(xfs_extent_t))));
+ ASSERT((item->ri_buf[0].i_len == (sizeof(xfs_efd_log_format_32_t) +
+ ((efd_formatp->efd_nextents - 1) * sizeof(xfs_extent_32_t)))) ||
+ (item->ri_buf[0].i_len == (sizeof(xfs_efd_log_format_64_t) +
+ ((efd_formatp->efd_nextents - 1) * sizeof(xfs_extent_64_t)))));
efi_id = efd_formatp->efd_efi_id;
/*
if ((error = xlog_recover_do_buffer_trans(log, item,
pass)))
break;
- } else if ((ITEM_TYPE(item) == XFS_LI_INODE) ||
- (ITEM_TYPE(item) == XFS_LI_6_1_INODE) ||
- (ITEM_TYPE(item) == XFS_LI_5_3_INODE)) {
+ } else if ((ITEM_TYPE(item) == XFS_LI_INODE)) {
if ((error = xlog_recover_do_inode_trans(log, item,
pass)))
break;
} else if (ITEM_TYPE(item) == XFS_LI_EFI) {
- xlog_recover_do_efi_trans(log, item, trans->r_lsn,
- pass);
+ if ((error = xlog_recover_do_efi_trans(log, item, trans->r_lsn,
+ pass)))
+ break;
} else if (ITEM_TYPE(item) == XFS_LI_EFD) {
xlog_recover_do_efd_trans(log, item, pass);
} else if (ITEM_TYPE(item) == XFS_LI_DQUOT) {
if (rhead->h_chksum ||
((log->l_flags & XLOG_CHKSUM_MISMATCH) == 0)) {
cmn_err(CE_DEBUG,
- "XFS: LogR chksum mismatch: was (0x%x) is (0x%x)",
+ "XFS: LogR chksum mismatch: was (0x%x) is (0x%x)\n",
INT_GET(rhead->h_chksum, ARCH_CONVERT), chksum);
cmn_err(CE_DEBUG,
"XFS: Disregard message if filesystem was created with non-DEBUG kernel");
if (XFS_SB_VERSION_HASLOGV2(&log->l_mp->m_sb)) {
cmn_err(CE_DEBUG,
- "XFS: LogR this is a LogV2 filesystem");
+ "XFS: LogR this is a LogV2 filesystem\n");
}
log->l_flags |= XLOG_CHKSUM_MISMATCH;
}
error = xlog_do_recovery_pass(log, head_blk, tail_blk,
XLOG_RECOVER_PASS2);
#ifdef DEBUG
- {
+ if (!error) {
int i;
for (i = 0; i < XLOG_BC_TABLE_SIZE; i++)
log->l_flags &= ~XLOG_RECOVERY_NEEDED;
} else {
cmn_err(CE_DEBUG,
- "!Ending clean XFS mount for filesystem: %s",
+ "!Ending clean XFS mount for filesystem: %s\n",
log->l_mp->m_fsname);
}
return 0;
#include "xfs_trans.h"
#include "xfs_sb.h"
#include "xfs_ag.h"
-#include "xfs_dir.h"
#include "xfs_dir2.h"
#include "xfs_dmapi.h"
#include "xfs_mount.h"
#include "xfs_bmap_btree.h"
#include "xfs_alloc_btree.h"
#include "xfs_ialloc_btree.h"
-#include "xfs_dir_sf.h"
#include "xfs_dir2_sf.h"
#include "xfs_attr_sf.h"
#include "xfs_dinode.h"
kmem_free(mp->m_logname, strlen(mp->m_logname) + 1);
if (remove_bhv) {
- struct vfs *vfsp = XFS_MTOVFS(mp);
+ struct bhv_vfs *vfsp = XFS_MTOVFS(mp);
bhv_remove_all_vfsops(vfsp, 0);
VFS_REMOVEBHV(vfsp, &mp->m_bhv);
xfs_agnumber_t
xfs_initialize_perag(
- struct vfs *vfs,
+ bhv_vfs_t *vfs,
xfs_mount_t *mp,
xfs_agnumber_t agcount)
{
*/
int
xfs_mountfs(
- vfs_t *vfsp,
+ bhv_vfs_t *vfsp,
xfs_mount_t *mp,
int mfsi_flags)
{
xfs_buf_t *bp;
xfs_sb_t *sbp = &(mp->m_sb);
xfs_inode_t *rip;
- vnode_t *rvp = NULL;
+ bhv_vnode_t *rvp = NULL;
int readio_log, writeio_log;
xfs_daddr_t d;
__uint64_t ret64;
vfsp->vfs_altfsid = (xfs_fsid_t *)mp->m_fixedfsid;
mp->m_dmevmask = 0; /* not persistent; set after each mount */
- /*
- * Select the right directory manager.
- */
- mp->m_dirops =
- XFS_SB_VERSION_HASDIRV2(&mp->m_sb) ?
- xfsv2_dirops :
- xfsv1_dirops;
-
- /*
- * Initialize directory manager's entries.
- */
- XFS_DIR_MOUNT(mp);
+ xfs_dir_mount(mp);
/*
* Initialize the attribute manager's entries.
if (unlikely((rip->i_d.di_mode & S_IFMT) != S_IFDIR)) {
cmn_err(CE_WARN, "XFS: corrupted root inode");
- prdev("Root inode %llu is not a directory",
- mp->m_ddev_targp, (unsigned long long)rip->i_ino);
+ cmn_err(CE_WARN, "Device %s - root %llu is not a directory",
+ XFS_BUFTARG_NAME(mp->m_ddev_targp),
+ (unsigned long long)rip->i_ino);
xfs_iunlock(rip, XFS_ILOCK_EXCL);
XFS_ERROR_REPORT("xfs_mountfs_int(2)", XFS_ERRLEVEL_LOW,
mp);
int
xfs_unmountfs(xfs_mount_t *mp, struct cred *cr)
{
- struct vfs *vfsp = XFS_MTOVFS(mp);
+ struct bhv_vfs *vfsp = XFS_MTOVFS(mp);
#if defined(DEBUG) || defined(INDUCE_IO_ERROR)
int64_t fsid;
#endif
xfs_trans_log_buf(tp, bp, first, last);
}
+
+/*
+ * In order to avoid ENOSPC-related deadlock caused by
+ * out-of-order locking of AGF buffer (PV 947395), we place
+ * constraints on the relationship among actual allocations for
+ * data blocks, freelist blocks, and potential file data bmap
+ * btree blocks. However, these restrictions may result in no
+ * actual space allocated for a delayed extent, for example, a data
+ * block in a certain AG is allocated but there is no additional
+ * block for the additional bmap btree block due to a split of the
+ * bmap btree of the file. The result of this may lead to an
+ * infinite loop in xfssyncd when the file gets flushed to disk and
+ * all delayed extents need to be actually allocated. To get around
+ * this, we explicitly set aside a few blocks which will not be
+ * reserved in delayed allocation. Considering the minimum number of
+ * needed freelist blocks is 4 fsbs, a potential split of file's bmap
+ * btree requires 1 fsb, so we set the number of set-aside blocks to 8.
+*/
+#define SET_ASIDE_BLOCKS 8
+
/*
* xfs_mod_incore_sb_unlocked() is a utility routine common used to apply
* a delta to a specified field in the in-core superblock. Simply
return 0;
case XFS_SBS_FDBLOCKS:
- lcounter = (long long)mp->m_sb.sb_fdblocks;
+ lcounter = (long long)mp->m_sb.sb_fdblocks - SET_ASIDE_BLOCKS;
res_used = (long long)(mp->m_resblks - mp->m_resblks_avail);
if (delta > 0) { /* Putting blocks back */
}
}
- mp->m_sb.sb_fdblocks = lcounter;
+ mp->m_sb.sb_fdblocks = lcounter + SET_ASIDE_BLOCKS;
return 0;
case XFS_SBS_FREXTENTS:
lcounter = (long long)mp->m_sb.sb_frextents;
#else
struct cred;
struct log;
-struct vfs;
-struct vnode;
+struct bhv_vfs;
+struct bhv_vnode;
struct xfs_mount_args;
struct xfs_ihash;
struct xfs_chash;
struct xfs_iocore;
struct xfs_bmbt_irec;
struct xfs_bmap_free;
+struct xfs_extdelta;
+struct xfs_swapext;
-extern struct vfsops xfs_vfsops;
-extern struct vnodeops xfs_vnodeops;
+extern struct bhv_vfsops xfs_vfsops;
+extern struct bhv_vnodeops xfs_vnodeops;
#define AIL_LOCK_T lock_t
#define AIL_LOCKINIT(x,y) spinlock_init(x,y)
* Prototypes and functions for the Data Migration subsystem.
*/
-typedef int (*xfs_send_data_t)(int, struct vnode *,
- xfs_off_t, size_t, int, vrwlock_t *);
+typedef int (*xfs_send_data_t)(int, struct bhv_vnode *,
+ xfs_off_t, size_t, int, bhv_vrwlock_t *);
typedef int (*xfs_send_mmap_t)(struct vm_area_struct *, uint);
-typedef int (*xfs_send_destroy_t)(struct vnode *, dm_right_t);
-typedef int (*xfs_send_namesp_t)(dm_eventtype_t, struct vfs *,
- struct vnode *,
- dm_right_t, struct vnode *, dm_right_t,
+typedef int (*xfs_send_destroy_t)(struct bhv_vnode *, dm_right_t);
+typedef int (*xfs_send_namesp_t)(dm_eventtype_t, struct bhv_vfs *,
+ struct bhv_vnode *,
+ dm_right_t, struct bhv_vnode *, dm_right_t,
char *, char *, mode_t, int, int);
-typedef void (*xfs_send_unmount_t)(struct vfs *, struct vnode *,
+typedef void (*xfs_send_unmount_t)(struct bhv_vfs *, struct bhv_vnode *,
dm_right_t, mode_t, int, int);
typedef struct xfs_dmops {
* Prototypes and functions for I/O core modularization.
*/
-typedef int (*xfs_ioinit_t)(struct vfs *,
+typedef int (*xfs_ioinit_t)(struct bhv_vfs *,
struct xfs_mount_args *, int);
typedef int (*xfs_bmapi_t)(struct xfs_trans *, void *,
xfs_fileoff_t, xfs_filblks_t, int,
xfs_fsblock_t *, xfs_extlen_t,
struct xfs_bmbt_irec *, int *,
- struct xfs_bmap_free *);
+ struct xfs_bmap_free *, struct xfs_extdelta *);
+typedef int (*xfs_bunmapi_t)(struct xfs_trans *,
+ void *, xfs_fileoff_t,
+ xfs_filblks_t, int, xfs_extnum_t,
+ xfs_fsblock_t *, struct xfs_bmap_free *,
+ struct xfs_extdelta *, int *);
typedef int (*xfs_bmap_eof_t)(void *, xfs_fileoff_t, int, int *);
typedef int (*xfs_iomap_write_direct_t)(
void *, xfs_off_t, size_t, int,
typedef int (*xfs_lock_nowait_t)(void *, uint);
typedef void (*xfs_unlk_t)(void *, unsigned int);
typedef xfs_fsize_t (*xfs_size_t)(void *);
-typedef xfs_fsize_t (*xfs_iodone_t)(struct vfs *);
+typedef xfs_fsize_t (*xfs_iodone_t)(struct bhv_vfs *);
+typedef int (*xfs_swap_extents_t)(void *, void *,
+ struct xfs_swapext*);
typedef struct xfs_ioops {
xfs_ioinit_t xfs_ioinit;
xfs_bmapi_t xfs_bmapi_func;
+ xfs_bunmapi_t xfs_bunmapi_func;
xfs_bmap_eof_t xfs_bmap_eof_func;
xfs_iomap_write_direct_t xfs_iomap_write_direct;
xfs_iomap_write_delay_t xfs_iomap_write_delay;
xfs_unlk_t xfs_unlock;
xfs_size_t xfs_size_func;
xfs_iodone_t xfs_iodone;
+ xfs_swap_extents_t xfs_swap_extents_func;
} xfs_ioops_t;
#define XFS_IOINIT(vfsp, args, flags) \
(*(mp)->m_io_ops.xfs_ioinit)(vfsp, args, flags)
-#define XFS_BMAPI(mp, trans,io,bno,len,f,first,tot,mval,nmap,flist) \
+#define XFS_BMAPI(mp, trans,io,bno,len,f,first,tot,mval,nmap,flist,delta) \
(*(mp)->m_io_ops.xfs_bmapi_func) \
- (trans,(io)->io_obj,bno,len,f,first,tot,mval,nmap,flist)
+ (trans,(io)->io_obj,bno,len,f,first,tot,mval,nmap,flist,delta)
+#define XFS_BUNMAPI(mp, trans,io,bno,len,f,nexts,first,flist,delta,done) \
+ (*(mp)->m_io_ops.xfs_bunmapi_func) \
+ (trans,(io)->io_obj,bno,len,f,nexts,first,flist,delta,done)
#define XFS_BMAP_EOF(mp, io, endoff, whichfork, eof) \
(*(mp)->m_io_ops.xfs_bmap_eof_func) \
((io)->io_obj, endoff, whichfork, eof)
(*(mp)->m_io_ops.xfs_size_func)((io)->io_obj)
#define XFS_IODONE(vfsp) \
(*(mp)->m_io_ops.xfs_iodone)(vfsp)
+#define XFS_SWAP_EXTENTS(mp, io, tio, sxp) \
+ (*(mp)->m_io_ops.xfs_swap_extents_func) \
+ ((io)->io_obj, (tio)->io_obj, sxp)
#ifdef HAVE_PERCPU_SB
__uint8_t m_inode_quiesce;/* call quiesce on new inodes.
field governed by m_ilock */
__uint8_t m_sectbb_log; /* sectlog - BBSHIFT */
- __uint8_t m_dirversion; /* 1 or 2 */
- xfs_dirops_t m_dirops; /* table of dir funcs */
int m_dirblksize; /* directory block sz--bytes */
int m_dirblkfsbs; /* directory block sz--fsbs */
xfs_dablk_t m_dirdatablk; /* blockno of dir data v2 */
#define XFS_FORCED_SHUTDOWN(mp) ((mp)->m_flags & XFS_MOUNT_FS_SHUTDOWN)
#define xfs_force_shutdown(m,f) \
- VFS_FORCE_SHUTDOWN((XFS_MTOVFS(m)), f, __FILE__, __LINE__)
-
-/*
- * Flags sent to xfs_force_shutdown.
- */
-#define XFS_METADATA_IO_ERROR 0x1
-#define XFS_LOG_IO_ERROR 0x2
-#define XFS_FORCE_UMOUNT 0x4
-#define XFS_CORRUPT_INCORE 0x8 /* Corrupt in-memory data structures */
-#define XFS_SHUTDOWN_REMOTE_REQ 0x10 /* Shutdown came from remote cell */
+ bhv_vfs_force_shutdown((XFS_MTOVFS(m)), f, __FILE__, __LINE__)
/*
* Flags for xfs_mountfs
* Macros for getting from mount to vfs and back.
*/
#define XFS_MTOVFS(mp) xfs_mtovfs(mp)
-static inline struct vfs *xfs_mtovfs(xfs_mount_t *mp)
+static inline struct bhv_vfs *xfs_mtovfs(xfs_mount_t *mp)
{
return bhvtovfs(&mp->m_bhv);
}
}
#define XFS_VFSTOM(vfs) xfs_vfstom(vfs)
-static inline xfs_mount_t *xfs_vfstom(vfs_t *vfs)
+static inline xfs_mount_t *xfs_vfstom(bhv_vfs_t *vfs)
{
return XFS_BHVTOM(bhv_lookup(VFS_BHVHEAD(vfs), &xfs_vfsops));
}
extern xfs_mount_t *xfs_mount_init(void);
extern void xfs_mod_sb(xfs_trans_t *, __int64_t);
extern void xfs_mount_free(xfs_mount_t *mp, int remove_bhv);
-extern int xfs_mountfs(struct vfs *, xfs_mount_t *mp, int);
+extern int xfs_mountfs(struct bhv_vfs *, xfs_mount_t *mp, int);
extern void xfs_mountfs_check_barriers(xfs_mount_t *mp);
extern int xfs_unmountfs(xfs_mount_t *, struct cred *);
extern void xfs_do_force_shutdown(bhv_desc_t *, int, char *, int);
extern int xfs_syncsub(xfs_mount_t *, int, int, int *);
extern int xfs_sync_inodes(xfs_mount_t *, int, int, int *);
-extern xfs_agnumber_t xfs_initialize_perag(struct vfs *, xfs_mount_t *,
+extern xfs_agnumber_t xfs_initialize_perag(struct bhv_vfs *, xfs_mount_t *,
xfs_agnumber_t);
extern void xfs_xlatesb(void *, struct xfs_sb *, int, __int64_t);
#include "xfs_trans.h"
#include "xfs_sb.h"
#include "xfs_ag.h"
-#include "xfs_dir.h"
#include "xfs_dir2.h"
#include "xfs_dmapi.h"
#include "xfs_mount.h"
extern int xfs_qm_dqcheck(xfs_disk_dquot_t *, xfs_dqid_t, uint, uint, char *);
extern int xfs_mount_reset_sbqflags(struct xfs_mount *);
-extern struct bhv_vfsops xfs_qmops;
+extern struct bhv_module_vfsops xfs_qmops;
#endif /* __KERNEL__ */
#include "xfs_inum.h"
#include "xfs_trans.h"
#include "xfs_sb.h"
-#include "xfs_dir.h"
#include "xfs_dir2.h"
#include "xfs_dmapi.h"
#include "xfs_mount.h"
#include "xfs_da_btree.h"
#include "xfs_bmap_btree.h"
-#include "xfs_dir_sf.h"
#include "xfs_dir2_sf.h"
#include "xfs_attr_sf.h"
#include "xfs_dinode.h"
#include "xfs_refcache.h"
#include "xfs_utils.h"
#include "xfs_trans_space.h"
-#include "xfs_dir_leaf.h"
/*
xfs_lock_for_rename(
xfs_inode_t *dp1, /* old (source) directory inode */
xfs_inode_t *dp2, /* new (target) directory inode */
- vname_t *vname1,/* old entry name */
- vname_t *vname2,/* new entry name */
+ bhv_vname_t *vname1,/* old entry name */
+ bhv_vname_t *vname2,/* new entry name */
xfs_inode_t **ipp1, /* inode of old entry */
xfs_inode_t **ipp2, /* inode of new entry, if it
already exists, NULL otherwise. */
int
xfs_rename(
bhv_desc_t *src_dir_bdp,
- vname_t *src_vname,
- vnode_t *target_dir_vp,
- vname_t *target_vname,
+ bhv_vname_t *src_vname,
+ bhv_vnode_t *target_dir_vp,
+ bhv_vname_t *target_vname,
cred_t *credp)
{
xfs_trans_t *tp;
int committed;
xfs_inode_t *inodes[4];
int target_ip_dropped = 0; /* dropped target_ip link? */
- vnode_t *src_dir_vp;
+ bhv_vnode_t *src_dir_vp;
int spaceres;
int target_link_zero = 0;
int num_inodes;
* fit before actually inserting it.
*/
if (spaceres == 0 &&
- (error = XFS_DIR_CANENTER(mp, tp, target_dp, target_name,
- target_namelen))) {
+ (error = xfs_dir_canenter(tp, target_dp, target_name,
+ target_namelen)))
goto error_return;
- }
/*
* If target does not exist and the rename crosses
* directories, adjust the target directory link count
* to account for the ".." reference from the new entry.
*/
- error = XFS_DIR_CREATENAME(mp, tp, target_dp, target_name,
+ error = xfs_dir_createname(tp, target_dp, target_name,
target_namelen, src_ip->i_ino,
&first_block, &free_list, spaceres);
- if (error == ENOSPC) {
+ if (error == ENOSPC)
goto error_return;
- }
- if (error) {
+ if (error)
goto abort_return;
- }
xfs_ichgtime(target_dp, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG);
if (new_parent && src_is_directory) {
error = xfs_bumplink(tp, target_dp);
- if (error) {
+ if (error)
goto abort_return;
- }
}
} else { /* target_ip != NULL */
-
/*
* If target exists and it's a directory, check that both
* target and source are directories and that target can be
/*
* Make sure target dir is empty.
*/
- if (!(XFS_DIR_ISEMPTY(target_ip->i_mount, target_ip)) ||
+ if (!(xfs_dir_isempty(target_ip)) ||
(target_ip->i_d.di_nlink > 2)) {
error = XFS_ERROR(EEXIST);
goto error_return;
* In case there is already an entry with the same
* name at the destination directory, remove it first.
*/
- error = XFS_DIR_REPLACE(mp, tp, target_dp, target_name,
- target_namelen, src_ip->i_ino, &first_block,
- &free_list, spaceres);
- if (error) {
+ error = xfs_dir_replace(tp, target_dp, target_name,
+ target_namelen, src_ip->i_ino,
+ &first_block, &free_list, spaceres);
+ if (error)
goto abort_return;
- }
xfs_ichgtime(target_dp, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG);
/*
* dir no longer points to it.
*/
error = xfs_droplink(tp, target_ip);
- if (error) {
+ if (error)
goto abort_return;
- }
target_ip_dropped = 1;
if (src_is_directory) {
* Drop the link from the old "." entry.
*/
error = xfs_droplink(tp, target_ip);
- if (error) {
+ if (error)
goto abort_return;
- }
}
/* Do this test while we still hold the locks */
* Remove the source.
*/
if (new_parent && src_is_directory) {
-
/*
* Rewrite the ".." entry to point to the new
* directory.
*/
- error = XFS_DIR_REPLACE(mp, tp, src_ip, "..", 2,
- target_dp->i_ino, &first_block,
- &free_list, spaceres);
+ error = xfs_dir_replace(tp, src_ip, "..", 2, target_dp->i_ino,
+ &first_block, &free_list, spaceres);
ASSERT(error != EEXIST);
- if (error) {
+ if (error)
goto abort_return;
- }
xfs_ichgtime(src_ip, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG);
} else {
* entry that's moved no longer points to it.
*/
error = xfs_droplink(tp, src_dp);
- if (error) {
+ if (error)
goto abort_return;
- }
}
- error = XFS_DIR_REMOVENAME(mp, tp, src_dp, src_name, src_namelen,
+ error = xfs_dir_removename(tp, src_dp, src_name, src_namelen,
src_ip->i_ino, &first_block, &free_list, spaceres);
- if (error) {
+ if (error)
goto abort_return;
- }
xfs_ichgtime(src_dp, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG);
/*
* Let interposed file systems know about removed links.
*/
if (target_ip_dropped) {
- VOP_LINK_REMOVED(XFS_ITOV(target_ip), target_dir_vp,
+ bhv_vop_link_removed(XFS_ITOV(target_ip), target_dir_vp,
target_link_zero);
IRELE(target_ip);
}
#include "xfs_trans.h"
#include "xfs_sb.h"
#include "xfs_ag.h"
-#include "xfs_dir.h"
#include "xfs_dir2.h"
#include "xfs_dmapi.h"
#include "xfs_mount.h"
#include "xfs_bmap_btree.h"
#include "xfs_alloc_btree.h"
#include "xfs_ialloc_btree.h"
-#include "xfs_dir_sf.h"
#include "xfs_dir2_sf.h"
#include "xfs_attr_sf.h"
#include "xfs_dinode.h"
cancelflags |= XFS_TRANS_ABORT;
error = xfs_bmapi(tp, ip, oblocks, nblocks - oblocks,
XFS_BMAPI_WRITE | XFS_BMAPI_METADATA, &firstblock,
- resblks, &map, &nmap, &flist);
+ resblks, &map, &nmap, &flist, NULL);
if (!error && nmap < 1)
error = XFS_ERROR(ENOSPC);
if (error)
{
xfs_extlen_t i; /* block number in the extent */
- printk("%Ld: ", (long long)start);
+ cmn_err(CE_DEBUG, "%Ld: ", (long long)start);
for (i = 0; i < len; i++)
- printk("%d", xfs_rtcheck_bit(mp, tp, start + i, 1));
- printk("\n");
+ cmn_err(CE_DEBUG, "%d", xfs_rtcheck_bit(mp, tp, start + i, 1));
+ cmn_err(CE_DEBUG, "\n");
}
/*
(void)xfs_rtget_summary(mp, tp, l, i, &sumbp, &sb, &c);
if (c) {
if (!p) {
- printk("%Ld-%Ld:", 1LL << l,
+ cmn_err(CE_DEBUG, "%Ld-%Ld:", 1LL << l,
XFS_RTMIN((1LL << l) +
((1LL << l) - 1LL),
mp->m_sb.sb_rextents));
p = 1;
}
- printk(" %Ld:%d", (long long)i, c);
+ cmn_err(CE_DEBUG, " %Ld:%d", (long long)i, c);
}
}
if (p)
- printk("\n");
+ cmn_err(CE_DEBUG, "\n");
}
if (sumbp)
xfs_trans_brelse(tp, sumbp);
/*
- * Copyright (c) 2000-2003,2005 Silicon Graphics, Inc.
+ * Copyright (c) 2000-2006 Silicon Graphics, Inc.
* All Rights Reserved.
*
* This program is free software; you can redistribute it and/or
#include "xfs_trans.h"
#include "xfs_sb.h"
#include "xfs_ag.h"
-#include "xfs_dir.h"
#include "xfs_dir2.h"
#include "xfs_dmapi.h"
#include "xfs_mount.h"
#include "xfs_bmap_btree.h"
#include "xfs_alloc_btree.h"
#include "xfs_ialloc_btree.h"
-#include "xfs_dir_sf.h"
#include "xfs_dir2_sf.h"
#include "xfs_attr_sf.h"
#include "xfs_dinode.h"
return 0;
}
+/*
+ * Handle logging requirements of various synchronous types of write.
+ */
+int
+xfs_write_sync_logforce(
+ xfs_mount_t *mp,
+ xfs_inode_t *ip)
+{
+ int error = 0;
+
+ /*
+ * If we're treating this as O_DSYNC and we have not updated the
+ * size, force the log.
+ */
+ if (!(mp->m_flags & XFS_MOUNT_OSYNCISOSYNC) &&
+ !(ip->i_update_size)) {
+ xfs_inode_log_item_t *iip = ip->i_itemp;
+
+ /*
+ * If an allocation transaction occurred
+ * without extending the size, then we have to force
+ * the log up the proper point to ensure that the
+ * allocation is permanent. We can't count on
+ * the fact that buffered writes lock out direct I/O
+ * writes - the direct I/O write could have extended
+ * the size nontransactionally, then finished before
+ * we started. xfs_write_file will think that the file
+ * didn't grow but the update isn't safe unless the
+ * size change is logged.
+ *
+ * Force the log if we've committed a transaction
+ * against the inode or if someone else has and
+ * the commit record hasn't gone to disk (e.g.
+ * the inode is pinned). This guarantees that
+ * all changes affecting the inode are permanent
+ * when we return.
+ */
+ if (iip && iip->ili_last_lsn) {
+ xfs_log_force(mp, iip->ili_last_lsn,
+ XFS_LOG_FORCE | XFS_LOG_SYNC);
+ } else if (xfs_ipincount(ip) > 0) {
+ xfs_log_force(mp, (xfs_lsn_t)0,
+ XFS_LOG_FORCE | XFS_LOG_SYNC);
+ }
+
+ } else {
+ xfs_trans_t *tp;
+
+ /*
+ * O_SYNC or O_DSYNC _with_ a size update are handled
+ * the same way.
+ *
+ * If the write was synchronous then we need to make
+ * sure that the inode modification time is permanent.
+ * We'll have updated the timestamp above, so here
+ * we use a synchronous transaction to log the inode.
+ * It's not fast, but it's necessary.
+ *
+ * If this a dsync write and the size got changed
+ * non-transactionally, then we need to ensure that
+ * the size change gets logged in a synchronous
+ * transaction.
+ */
+ tp = xfs_trans_alloc(mp, XFS_TRANS_WRITE_SYNC);
+ if ((error = xfs_trans_reserve(tp, 0,
+ XFS_SWRITE_LOG_RES(mp),
+ 0, 0, 0))) {
+ /* Transaction reserve failed */
+ xfs_trans_cancel(tp, 0);
+ } else {
+ /* Transaction reserve successful */
+ xfs_ilock(ip, XFS_ILOCK_EXCL);
+ xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL);
+ xfs_trans_ihold(tp, ip);
+ xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
+ xfs_trans_set_sync(tp);
+ error = xfs_trans_commit(tp, 0, NULL);
+ xfs_iunlock(ip, XFS_ILOCK_EXCL);
+ }
+ }
+
+ return error;
+}
+
/*
* Force a shutdown of the filesystem instantly while keeping
* the filesystem consistent. We don't do an unmount here; just shutdown
xfs_mount_t *mp;
mp = XFS_BHVTOM(bdp);
- logerror = flags & XFS_LOG_IO_ERROR;
+ logerror = flags & SHUTDOWN_LOG_IO_ERROR;
- if (!(flags & XFS_FORCE_UMOUNT)) {
- cmn_err(CE_NOTE,
- "xfs_force_shutdown(%s,0x%x) called from line %d of file %s. Return address = 0x%p",
- mp->m_fsname,flags,lnnum,fname,__return_address);
+ if (!(flags & SHUTDOWN_FORCE_UMOUNT)) {
+ cmn_err(CE_NOTE, "xfs_force_shutdown(%s,0x%x) called from "
+ "line %d of file %s. Return address = 0x%p",
+ mp->m_fsname, flags, lnnum, fname, __return_address);
}
/*
* No need to duplicate efforts.
/*
* This flags XFS_MOUNT_FS_SHUTDOWN, makes sure that we don't
* queue up anybody new on the log reservations, and wakes up
- * everybody who's sleeping on log reservations and tells
- * them the bad news.
+ * everybody who's sleeping on log reservations to tell them
+ * the bad news.
*/
if (xfs_log_force_umount(mp, logerror))
return;
- if (flags & XFS_CORRUPT_INCORE) {
+ if (flags & SHUTDOWN_CORRUPT_INCORE) {
xfs_cmn_err(XFS_PTAG_SHUTDOWN_CORRUPT, CE_ALERT, mp,
"Corruption of in-memory data detected. Shutting down filesystem: %s",
mp->m_fsname);
if (XFS_ERRLEVEL_HIGH <= xfs_error_level) {
xfs_stack_trace();
}
- } else if (!(flags & XFS_FORCE_UMOUNT)) {
+ } else if (!(flags & SHUTDOWN_FORCE_UMOUNT)) {
if (logerror) {
xfs_cmn_err(XFS_PTAG_SHUTDOWN_LOGERROR, CE_ALERT, mp,
- "Log I/O Error Detected. Shutting down filesystem: %s",
+ "Log I/O Error Detected. Shutting down filesystem: %s",
+ mp->m_fsname);
+ } else if (flags & SHUTDOWN_DEVICE_REQ) {
+ xfs_cmn_err(XFS_PTAG_SHUTDOWN_IOERROR, CE_ALERT, mp,
+ "All device paths lost. Shutting down filesystem: %s",
mp->m_fsname);
- } else if (!(flags & XFS_SHUTDOWN_REMOTE_REQ)) {
+ } else if (!(flags & SHUTDOWN_REMOTE_REQ)) {
xfs_cmn_err(XFS_PTAG_SHUTDOWN_IOERROR, CE_ALERT, mp,
- "I/O Error Detected. Shutting down filesystem: %s",
+ "I/O Error Detected. Shutting down filesystem: %s",
mp->m_fsname);
}
}
- if (!(flags & XFS_FORCE_UMOUNT)) {
- cmn_err(CE_ALERT,
- "Please umount the filesystem, and rectify the problem(s)");
+ if (!(flags & SHUTDOWN_FORCE_UMOUNT)) {
+ cmn_err(CE_ALERT, "Please umount the filesystem, "
+ "and rectify the problem(s)");
}
}
* from bwrite and we could be tracing a buffer that has
* been reused.
*/
- xfs_force_shutdown(mp, XFS_METADATA_IO_ERROR);
+ xfs_force_shutdown(mp, SHUTDOWN_META_IO_ERROR);
}
return (error);
}
/*
- * Copyright (c) 2000-2003,2005 Silicon Graphics, Inc.
+ * Copyright (c) 2000-2006 Silicon Graphics, Inc.
* All Rights Reserved.
*
* This program is free software; you can redistribute it and/or
* Prototypes for functions in xfs_rw.c.
*/
extern int xfs_write_clear_setuid(struct xfs_inode *ip);
+extern int xfs_write_sync_logforce(struct xfs_mount *mp, struct xfs_inode *ip);
extern int xfs_bwrite(struct xfs_mount *mp, struct xfs_buf *bp);
extern int xfs_bioerror(struct xfs_buf *bp);
extern int xfs_bioerror_relse(struct xfs_buf *bp);
/*
* Prototypes for functions in xfs_vnodeops.c.
*/
-extern int xfs_rwlock(bhv_desc_t *bdp, vrwlock_t write_lock);
-extern void xfs_rwunlock(bhv_desc_t *bdp, vrwlock_t write_lock);
-extern int xfs_setattr(bhv_desc_t *bdp, vattr_t *vap, int flags, cred_t *credp);
+extern int xfs_rwlock(bhv_desc_t *bdp, bhv_vrwlock_t write_lock);
+extern void xfs_rwunlock(bhv_desc_t *bdp, bhv_vrwlock_t write_lock);
+extern int xfs_setattr(bhv_desc_t *, bhv_vattr_t *vap, int flags,
+ cred_t *credp);
extern int xfs_change_file_space(bhv_desc_t *bdp, int cmd, xfs_flock64_t *bf,
xfs_off_t offset, cred_t *credp, int flags);
extern int xfs_set_dmattrs(bhv_desc_t *bdp, u_int evmask, u_int16_t state,
#include "xfs_trans.h"
#include "xfs_sb.h"
#include "xfs_ag.h"
-#include "xfs_dir.h"
#include "xfs_dir2.h"
#include "xfs_dmapi.h"
#include "xfs_mount.h"
#include "xfs_bmap_btree.h"
#include "xfs_alloc_btree.h"
#include "xfs_ialloc_btree.h"
-#include "xfs_dir_sf.h"
#include "xfs_dir2_sf.h"
#include "xfs_attr_sf.h"
#include "xfs_dinode.h"
xfs_mount_t *mp,
uint type)
{
- fs_check_frozen(XFS_MTOVFS(mp), SB_FREEZE_TRANS);
- atomic_inc(&mp->m_active_trans);
-
- return (_xfs_trans_alloc(mp, type));
-
+ vfs_wait_for_freeze(XFS_MTOVFS(mp), SB_FREEZE_TRANS);
+ return _xfs_trans_alloc(mp, type);
}
xfs_trans_t *
{
xfs_trans_t *tp;
- ASSERT(xfs_trans_zone != NULL);
- tp = kmem_zone_zalloc(xfs_trans_zone, KM_SLEEP);
+ atomic_inc(&mp->m_active_trans);
- /*
- * Initialize the transaction structure.
- */
+ tp = kmem_zone_zalloc(xfs_trans_zone, KM_SLEEP);
tp->t_magic = XFS_TRANS_MAGIC;
tp->t_type = type;
tp->t_mountp = mp;
tp->t_busy_free = XFS_LBC_NUM_SLOTS;
XFS_LIC_INIT(&(tp->t_items));
XFS_LBC_INIT(&(tp->t_busy));
-
- return (tp);
+ return tp;
}
/*
tp->t_blk_res = tp->t_blk_res_used;
ntp->t_rtx_res = tp->t_rtx_res - tp->t_rtx_res_used;
tp->t_rtx_res = tp->t_rtx_res_used;
- PFLAGS_DUP(&tp->t_pflags, &ntp->t_pflags);
+ ntp->t_pflags = tp->t_pflags;
XFS_TRANS_DUP_DQINFO(tp->t_mountp, tp, ntp);
uint logcount)
{
int log_flags;
- int error;
- int rsvd;
-
- error = 0;
- rsvd = (tp->t_flags & XFS_TRANS_RESERVE) != 0;
+ int error = 0;
+ int rsvd = (tp->t_flags & XFS_TRANS_RESERVE) != 0;
/* Mark this thread as being in a transaction */
- PFLAGS_SET_FSTRANS(&tp->t_pflags);
+ current_set_flags_nested(&tp->t_pflags, PF_FSTRANS);
/*
* Attempt to reserve the needed disk blocks by decrementing
error = xfs_mod_incore_sb(tp->t_mountp, XFS_SBS_FDBLOCKS,
-blocks, rsvd);
if (error != 0) {
- PFLAGS_RESTORE_FSTRANS(&tp->t_pflags);
+ current_restore_flags_nested(&tp->t_pflags, PF_FSTRANS);
return (XFS_ERROR(ENOSPC));
}
tp->t_blk_res += blocks;
tp->t_blk_res = 0;
}
- PFLAGS_RESTORE_FSTRANS(&tp->t_pflags);
+ current_restore_flags_nested(&tp->t_pflags, PF_FSTRANS);
- return (error);
+ return error;
}
if (commit_lsn == -1 && !shutdown)
shutdown = XFS_ERROR(EIO);
}
- PFLAGS_RESTORE_FSTRANS(&tp->t_pflags);
+ current_restore_flags_nested(&tp->t_pflags, PF_FSTRANS);
xfs_trans_free_items(tp, shutdown? XFS_TRANS_ABORT : 0);
xfs_trans_free_busy(tp);
xfs_trans_free(tp);
*/
nvec = xfs_trans_count_vecs(tp);
if (nvec == 0) {
- xfs_force_shutdown(mp, XFS_LOG_IO_ERROR);
+ xfs_force_shutdown(mp, SHUTDOWN_LOG_IO_ERROR);
goto shut_us_down;
} else if (nvec <= XFS_TRANS_LOGVEC_COUNT) {
log_vector = log_vector_fast;
* had pinned, clean up, free trans structure, and return error.
*/
if (error || commit_lsn == -1) {
- PFLAGS_RESTORE_FSTRANS(&tp->t_pflags);
+ current_restore_flags_nested(&tp->t_pflags, PF_FSTRANS);
xfs_trans_uncommit(tp, flags|XFS_TRANS_ABORT);
return XFS_ERROR(EIO);
}
/*
* Mark this thread as no longer being in a transaction
*/
- PFLAGS_RESTORE_FSTRANS(&tp->t_pflags);
+ current_restore_flags_nested(&tp->t_pflags, PF_FSTRANS);
/*
* Once all the items of the transaction have been copied
*/
if ((tp->t_flags & XFS_TRANS_DIRTY) && !XFS_FORCED_SHUTDOWN(mp)) {
XFS_ERROR_REPORT("xfs_trans_cancel", XFS_ERRLEVEL_LOW, mp);
- xfs_force_shutdown(mp, XFS_CORRUPT_INCORE);
+ xfs_force_shutdown(mp, SHUTDOWN_CORRUPT_INCORE);
}
#ifdef DEBUG
if (!(flags & XFS_TRANS_ABORT)) {
}
/* mark this thread as no longer being in a transaction */
- PFLAGS_RESTORE_FSTRANS(&tp->t_pflags);
+ current_restore_flags_nested(&tp->t_pflags, PF_FSTRANS);
xfs_trans_free_items(tp, flags);
xfs_trans_free_busy(tp);
((mp)->m_sb.sb_inodesize + \
(mp)->m_sb.sb_sectsize * 2 + \
(mp)->m_dirblksize + \
- (XFS_DIR_IS_V1(mp) ? 0 : \
- XFS_FSB_TO_B(mp, (XFS_DAENTER_BMAP1B(mp, XFS_DATA_FORK) + 1))) + \
+ XFS_FSB_TO_B(mp, (XFS_DAENTER_BMAP1B(mp, XFS_DATA_FORK) + 1)) + \
XFS_ALLOCFREE_LOG_RES(mp, 1) + \
- (128 * (4 + \
- (XFS_DIR_IS_V1(mp) ? 0 : \
- XFS_DAENTER_BMAP1B(mp, XFS_DATA_FORK) + 1) + \
+ (128 * (4 + (XFS_DAENTER_BMAP1B(mp, XFS_DATA_FORK) + 1) + \
XFS_ALLOCFREE_LOG_COUNT(mp, 1))))
#define XFS_ADDAFORK_LOG_RES(mp) ((mp)->m_reservations.tr_addafork)
#include "xfs_inum.h"
#include "xfs_trans.h"
#include "xfs_sb.h"
-#include "xfs_dir.h"
#include "xfs_dmapi.h"
#include "xfs_mount.h"
#include "xfs_trans_priv.h"
AIL_UNLOCK(mp, s);
else {
xfs_cmn_err(XFS_PTAG_AILDELETE, CE_ALERT, mp,
- "xfs_trans_delete_ail: attempting to delete a log item that is not in the AIL");
+ "%s: attempting to delete a log item that is not in the AIL",
+ __FUNCTION__);
AIL_UNLOCK(mp, s);
- xfs_force_shutdown(mp, XFS_CORRUPT_INCORE);
+ xfs_force_shutdown(mp, SHUTDOWN_CORRUPT_INCORE);
}
}
}
#include "xfs_trans.h"
#include "xfs_sb.h"
#include "xfs_ag.h"
-#include "xfs_dir.h"
#include "xfs_dir2.h"
#include "xfs_dmapi.h"
#include "xfs_mount.h"
#include "xfs_bmap_btree.h"
#include "xfs_alloc_btree.h"
#include "xfs_ialloc_btree.h"
-#include "xfs_dir_sf.h"
#include "xfs_dir2_sf.h"
#include "xfs_attr_sf.h"
#include "xfs_dinode.h"
if (xfs_error_target == target) {
if (((xfs_req_num++) % xfs_error_mod) == 0) {
xfs_buf_relse(bp);
- printk("Returning error!\n");
+ cmn_err(CE_DEBUG, "Returning error!\n");
return XFS_ERROR(EIO);
}
}
*/
if (tp->t_flags & XFS_TRANS_DIRTY)
xfs_force_shutdown(tp->t_mountp,
- XFS_METADATA_IO_ERROR);
+ SHUTDOWN_META_IO_ERROR);
return error;
}
}
xfs_ioerror_alert("xfs_trans_read_buf", mp,
bp, blkno);
if (tp->t_flags & XFS_TRANS_DIRTY)
- xfs_force_shutdown(tp->t_mountp, XFS_METADATA_IO_ERROR);
+ xfs_force_shutdown(tp->t_mountp, SHUTDOWN_META_IO_ERROR);
xfs_buf_relse(bp);
return error;
}
if (xfs_error_target == target) {
if (((xfs_req_num++) % xfs_error_mod) == 0) {
xfs_force_shutdown(tp->t_mountp,
- XFS_METADATA_IO_ERROR);
+ SHUTDOWN_META_IO_ERROR);
xfs_buf_relse(bp);
- printk("Returning error in trans!\n");
+ cmn_err(CE_DEBUG, "Returning trans error!\n");
return XFS_ERROR(EIO);
}
}
#include "xfs_inum.h"
#include "xfs_trans.h"
#include "xfs_sb.h"
-#include "xfs_dir.h"
#include "xfs_dmapi.h"
#include "xfs_mount.h"
#include "xfs_trans_priv.h"
#include "xfs_trans.h"
#include "xfs_sb.h"
#include "xfs_ag.h"
-#include "xfs_dir.h"
#include "xfs_dir2.h"
#include "xfs_dmapi.h"
#include "xfs_mount.h"
#include "xfs_bmap_btree.h"
#include "xfs_alloc_btree.h"
#include "xfs_ialloc_btree.h"
-#include "xfs_dir_sf.h"
#include "xfs_dir2_sf.h"
#include "xfs_attr_sf.h"
#include "xfs_dinode.h"
break;
} else {
/* out-of-order vacancy */
- printk("OOO vacancy lbcp 0x%p\n", lbcp);
+ cmn_err(CE_DEBUG, "OOO vacancy lbcp 0x%p\n", lbcp);
ASSERT(0);
}
}
XFS_EXTENTADD_SPACE_RES(mp,w))
#define XFS_DAENTER_1B(mp,w) ((w) == XFS_DATA_FORK ? (mp)->m_dirblkfsbs : 1)
#define XFS_DAENTER_DBS(mp,w) \
- (XFS_DA_NODE_MAXDEPTH + \
- ((XFS_DIR_IS_V2(mp) && (w) == XFS_DATA_FORK) ? 2 : 0))
+ (XFS_DA_NODE_MAXDEPTH + (((w) == XFS_DATA_FORK) ? 2 : 0))
#define XFS_DAENTER_BLOCKS(mp,w) \
(XFS_DAENTER_1B(mp,w) * XFS_DAENTER_DBS(mp,w))
#define XFS_DAENTER_BMAP1B(mp,w) \
#define XFS_DAENTER_SPACE_RES(mp,w) \
(XFS_DAENTER_BLOCKS(mp,w) + XFS_DAENTER_BMAPS(mp,w))
#define XFS_DAREMOVE_SPACE_RES(mp,w) XFS_DAENTER_BMAPS(mp,w)
-#define XFS_DIRENTER_MAX_SPLIT(mp,nl) \
- (((mp)->m_sb.sb_blocksize == 512 && \
- XFS_DIR_IS_V1(mp) && \
- (nl) >= XFS_DIR_LEAF_CAN_DOUBLE_SPLIT_LEN) ? 2 : 1)
+#define XFS_DIRENTER_MAX_SPLIT(mp,nl) 1
#define XFS_DIRENTER_SPACE_RES(mp,nl) \
(XFS_DAENTER_SPACE_RES(mp, XFS_DATA_FORK) * \
XFS_DIRENTER_MAX_SPLIT(mp,nl))
* Space reservation values for various transactions.
*/
#define XFS_ADDAFORK_SPACE_RES(mp) \
- ((mp)->m_dirblkfsbs + \
- (XFS_DIR_IS_V1(mp) ? 0 : XFS_DAENTER_BMAP1B(mp, XFS_DATA_FORK)))
+ ((mp)->m_dirblkfsbs + XFS_DAENTER_BMAP1B(mp, XFS_DATA_FORK))
#define XFS_ATTRRM_SPACE_RES(mp) \
XFS_DAREMOVE_SPACE_RES(mp, XFS_ATTR_FORK)
/* This macro is not used - see inline code in xfs_attr_set */
#include "xfs_trans.h"
#include "xfs_sb.h"
#include "xfs_ag.h"
-#include "xfs_dir.h"
#include "xfs_dir2.h"
#include "xfs_dmapi.h"
#include "xfs_mount.h"
#include "xfs_bmap_btree.h"
-#include "xfs_dir_sf.h"
#include "xfs_dir2_sf.h"
#include "xfs_attr_sf.h"
#include "xfs_dinode.h"
*/
int
xfs_get_dir_entry(
- vname_t *dentry,
+ bhv_vname_t *dentry,
xfs_inode_t **ipp)
{
- vnode_t *vp;
+ bhv_vnode_t *vp;
vp = VNAME_TO_VNODE(dentry);
xfs_dir_lookup_int(
bhv_desc_t *dir_bdp,
uint lock_mode,
- vname_t *dentry,
+ bhv_vname_t *dentry,
xfs_ino_t *inum,
xfs_inode_t **ipp)
{
- vnode_t *dir_vp;
+ bhv_vnode_t *dir_vp;
xfs_inode_t *dp;
int error;
dp = XFS_BHVTOI(dir_bdp);
- error = XFS_DIR_LOOKUP(dp->i_mount, NULL, dp,
- VNAME(dentry), VNAMELEN(dentry), inum);
+ error = xfs_dir_lookup(NULL, dp, VNAME(dentry), VNAMELEN(dentry), inum);
if (!error) {
/*
* Unlock the directory. We do this because we can't
#define ITRACE(ip) vn_trace_ref(XFS_ITOV(ip), __FILE__, __LINE__, \
(inst_t *)__return_address)
-extern int xfs_rename (bhv_desc_t *, vname_t *, vnode_t *, vname_t *, cred_t *);
-extern int xfs_get_dir_entry (vname_t *, xfs_inode_t **);
-extern int xfs_dir_lookup_int (bhv_desc_t *, uint, vname_t *, xfs_ino_t *,
+extern int xfs_rename (bhv_desc_t *, bhv_vname_t *, bhv_vnode_t *,
+ bhv_vname_t *, cred_t *);
+extern int xfs_get_dir_entry (bhv_vname_t *, xfs_inode_t **);
+extern int xfs_dir_lookup_int (bhv_desc_t *, uint, bhv_vname_t *, xfs_ino_t *,
xfs_inode_t **);
extern int xfs_truncate_file (xfs_mount_t *, xfs_inode_t *);
extern int xfs_dir_ialloc (xfs_trans_t **, xfs_inode_t *, mode_t, xfs_nlink_t,
#include "xfs_trans.h"
#include "xfs_sb.h"
#include "xfs_ag.h"
-#include "xfs_dir.h"
#include "xfs_dir2.h"
#include "xfs_dmapi.h"
#include "xfs_mount.h"
#include "xfs_bmap_btree.h"
#include "xfs_ialloc_btree.h"
#include "xfs_alloc_btree.h"
-#include "xfs_dir_sf.h"
#include "xfs_dir2_sf.h"
#include "xfs_attr_sf.h"
#include "xfs_dinode.h"
#ifdef XFS_BMBT_TRACE
xfs_bmbt_trace_buf = ktrace_alloc(XFS_BMBT_TRACE_SIZE, KM_SLEEP);
#endif
-#ifdef XFS_DIR_TRACE
- xfs_dir_trace_buf = ktrace_alloc(XFS_DIR_TRACE_SIZE, KM_SLEEP);
-#endif
#ifdef XFS_ATTR_TRACE
xfs_attr_trace_buf = ktrace_alloc(XFS_ATTR_TRACE_SIZE, KM_SLEEP);
#endif
#ifdef XFS_ATTR_TRACE
ktrace_free(xfs_attr_trace_buf);
#endif
-#ifdef XFS_DIR_TRACE
- ktrace_free(xfs_dir_trace_buf);
-#endif
#ifdef XFS_BMBT_TRACE
ktrace_free(xfs_bmbt_trace_buf);
#endif
*/
STATIC int
xfs_start_flags(
- struct vfs *vfs,
+ struct bhv_vfs *vfs,
struct xfs_mount_args *ap,
struct xfs_mount *mp)
{
*/
STATIC int
xfs_finish_flags(
- struct vfs *vfs,
+ struct bhv_vfs *vfs,
struct xfs_mount_args *ap,
struct xfs_mount *mp)
{
struct xfs_mount_args *args,
cred_t *credp)
{
- struct vfs *vfsp = bhvtovfs(bhvp);
+ struct bhv_vfs *vfsp = bhvtovfs(bhvp);
struct bhv_desc *p;
struct xfs_mount *mp = XFS_BHVTOM(bhvp);
struct block_device *ddev, *logdev, *rtdev;
int flags,
cred_t *credp)
{
- struct vfs *vfsp = bhvtovfs(bdp);
+ bhv_vfs_t *vfsp = bhvtovfs(bdp);
xfs_mount_t *mp = XFS_BHVTOM(bdp);
xfs_inode_t *rip;
- vnode_t *rvp;
+ bhv_vnode_t *rvp;
int unmount_event_wanted = 0;
int unmount_event_flags = 0;
int xfs_unmountfs_needed = 0;
int *flags,
struct xfs_mount_args *args)
{
- struct vfs *vfsp = bhvtovfs(bdp);
+ bhv_vfs_t *vfsp = bhvtovfs(bdp);
xfs_mount_t *mp = XFS_BHVTOM(bdp);
- int error;
if (!(*flags & MS_RDONLY)) { /* rw/ro -> rw */
if (vfsp->vfs_flag & VFS_RDONLY)
mp->m_flags &= ~XFS_MOUNT_BARRIER;
}
} else if (!(vfsp->vfs_flag & VFS_RDONLY)) { /* rw -> ro */
- VFS_SYNC(vfsp, SYNC_FSDATA|SYNC_BDFLUSH|SYNC_ATTR, NULL, error);
+ bhv_vfs_sync(vfsp, SYNC_FSDATA|SYNC_BDFLUSH|SYNC_ATTR, NULL);
xfs_quiesce_fs(mp);
xfs_log_unmount_write(mp);
xfs_unmountfs_writesb(mp);
xfs_inode_t *rip = mp->m_rootip;
xfs_inode_t *rbmip;
xfs_inode_t *rsumip = NULL;
- vnode_t *rvp = XFS_ITOV(rip);
+ bhv_vnode_t *rvp = XFS_ITOV(rip);
int error;
xfs_ilock(rip, XFS_ILOCK_EXCL);
STATIC int
xfs_root(
bhv_desc_t *bdp,
- vnode_t **vpp)
+ bhv_vnode_t **vpp)
{
- vnode_t *vp;
+ bhv_vnode_t *vp;
vp = XFS_ITOV((XFS_BHVTOM(bdp))->m_rootip);
VN_HOLD(vp);
STATIC int
xfs_statvfs(
bhv_desc_t *bdp,
- xfs_statfs_t *statp,
- vnode_t *vp)
+ bhv_statvfs_t *statp,
+ bhv_vnode_t *vp)
{
__uint64_t fakeinos;
xfs_extlen_t lsize;
/*
* xfs sync routine for internal use
*
- * This routine supports all of the flags defined for the generic VFS_SYNC
+ * This routine supports all of the flags defined for the generic vfs_sync
* interface as explained above under xfs_sync. In the interests of not
* changing interfaces within the 6.5 family, additional internally-
* required functions are specified within a separate xflags parameter,
xfs_inode_t *ip = NULL;
xfs_inode_t *ip_next;
xfs_buf_t *bp;
- vnode_t *vp = NULL;
+ bhv_vnode_t *vp = NULL;
int error;
int last_error;
uint64_t fflag;
xfs_iunlock(ip, XFS_ILOCK_SHARED);
if (XFS_FORCED_SHUTDOWN(mp)) {
- VOP_TOSS_PAGES(vp, 0, -1, FI_REMAPF);
+ bhv_vop_toss_pages(vp, 0, -1, FI_REMAPF);
} else {
- VOP_FLUSHINVAL_PAGES(vp, 0, -1, FI_REMAPF);
+ bhv_vop_flushinval_pages(vp, 0, -1, FI_REMAPF);
}
xfs_ilock(ip, XFS_ILOCK_SHARED);
* across calls to the buffer cache.
*/
xfs_iunlock(ip, XFS_ILOCK_SHARED);
- VOP_FLUSH_PAGES(vp, (xfs_off_t)0, -1,
- fflag, FI_NONE, error);
+ error = bhv_vop_flush_pages(vp, (xfs_off_t)0,
+ -1, fflag, FI_NONE);
xfs_ilock(ip, XFS_ILOCK_SHARED);
}
* marker and free it.
*/
XFS_MOUNT_ILOCK(mp);
-
IPOINTER_REMOVE(ip, mp);
-
XFS_MOUNT_IUNLOCK(mp);
ASSERT(!(lock_flags &
/*
* xfs sync routine for internal use
*
- * This routine supports all of the flags defined for the generic VFS_SYNC
+ * This routine supports all of the flags defined for the generic vfs_sync
* interface as explained above under xfs_sync. In the interests of not
* changing interfaces within the 6.5 family, additional internally-
* required functions are specified within a separate xflags parameter,
STATIC int
xfs_vget(
bhv_desc_t *bdp,
- vnode_t **vpp,
+ bhv_vnode_t **vpp,
fid_t *fidp)
{
xfs_mount_t *mp = XFS_BHVTOM(bdp);
#define MNTOPT_NOATTR2 "noattr2" /* do not use attr2 attribute format */
STATIC unsigned long
-suffix_strtoul(const char *cp, char **endp, unsigned int base)
+suffix_strtoul(char *s, char **endp, unsigned int base)
{
int last, shift_left_factor = 0;
- char *value = (char *)cp;
+ char *value = s;
last = strlen(value) - 1;
if (value[last] == 'K' || value[last] == 'k') {
value[last] = '\0';
}
- return simple_strtoul(cp, endp, base) << shift_left_factor;
+ return simple_strtoul((const char *)s, endp, base) << shift_left_factor;
}
STATIC int
struct xfs_mount_args *args,
int update)
{
- struct vfs *vfsp = bhvtovfs(bhv);
+ bhv_vfs_t *vfsp = bhvtovfs(bhv);
char *this_char, *value, *eov;
int dsunit, dswidth, vol_dsunit, vol_dswidth;
int iosize;
if (!strcmp(this_char, MNTOPT_LOGBUFS)) {
if (!value || !*value) {
- printk("XFS: %s option requires an argument\n",
+ cmn_err(CE_WARN,
+ "XFS: %s option requires an argument",
this_char);
return EINVAL;
}
args->logbufs = simple_strtoul(value, &eov, 10);
} else if (!strcmp(this_char, MNTOPT_LOGBSIZE)) {
if (!value || !*value) {
- printk("XFS: %s option requires an argument\n",
+ cmn_err(CE_WARN,
+ "XFS: %s option requires an argument",
this_char);
return EINVAL;
}
args->logbufsize = suffix_strtoul(value, &eov, 10);
} else if (!strcmp(this_char, MNTOPT_LOGDEV)) {
if (!value || !*value) {
- printk("XFS: %s option requires an argument\n",
+ cmn_err(CE_WARN,
+ "XFS: %s option requires an argument",
this_char);
return EINVAL;
}
strncpy(args->logname, value, MAXNAMELEN);
} else if (!strcmp(this_char, MNTOPT_MTPT)) {
if (!value || !*value) {
- printk("XFS: %s option requires an argument\n",
+ cmn_err(CE_WARN,
+ "XFS: %s option requires an argument",
this_char);
return EINVAL;
}
strncpy(args->mtpt, value, MAXNAMELEN);
} else if (!strcmp(this_char, MNTOPT_RTDEV)) {
if (!value || !*value) {
- printk("XFS: %s option requires an argument\n",
+ cmn_err(CE_WARN,
+ "XFS: %s option requires an argument",
this_char);
return EINVAL;
}
strncpy(args->rtname, value, MAXNAMELEN);
} else if (!strcmp(this_char, MNTOPT_BIOSIZE)) {
if (!value || !*value) {
- printk("XFS: %s option requires an argument\n",
+ cmn_err(CE_WARN,
+ "XFS: %s option requires an argument",
this_char);
return EINVAL;
}
args->iosizelog = (uint8_t) iosize;
} else if (!strcmp(this_char, MNTOPT_ALLOCSIZE)) {
if (!value || !*value) {
- printk("XFS: %s option requires an argument\n",
+ cmn_err(CE_WARN,
+ "XFS: %s option requires an argument",
this_char);
return EINVAL;
}
args->iosizelog = ffs(iosize) - 1;
} else if (!strcmp(this_char, MNTOPT_IHASHSIZE)) {
if (!value || !*value) {
- printk("XFS: %s option requires an argument\n",
+ cmn_err(CE_WARN,
+ "XFS: %s option requires an argument",
this_char);
return EINVAL;
}
} else if (!strcmp(this_char, MNTOPT_INO64)) {
args->flags |= XFSMNT_INO64;
#if !XFS_BIG_INUMS
- printk("XFS: %s option not allowed on this system\n",
+ cmn_err(CE_WARN,
+ "XFS: %s option not allowed on this system",
this_char);
return EINVAL;
#endif
args->flags |= XFSMNT_SWALLOC;
} else if (!strcmp(this_char, MNTOPT_SUNIT)) {
if (!value || !*value) {
- printk("XFS: %s option requires an argument\n",
+ cmn_err(CE_WARN,
+ "XFS: %s option requires an argument",
this_char);
return EINVAL;
}
dsunit = simple_strtoul(value, &eov, 10);
} else if (!strcmp(this_char, MNTOPT_SWIDTH)) {
if (!value || !*value) {
- printk("XFS: %s option requires an argument\n",
+ cmn_err(CE_WARN,
+ "XFS: %s option requires an argument",
this_char);
return EINVAL;
}
} else if (!strcmp(this_char, MNTOPT_64BITINODE)) {
args->flags &= ~XFSMNT_32BITINODES;
#if !XFS_BIG_INUMS
- printk("XFS: %s option not allowed on this system\n",
+ cmn_err(CE_WARN,
+ "XFS: %s option not allowed on this system",
this_char);
return EINVAL;
#endif
args->flags &= ~XFSMNT_ATTR2;
} else if (!strcmp(this_char, "osyncisdsync")) {
/* no-op, this is now the default */
-printk("XFS: osyncisdsync is now the default, option is deprecated.\n");
+ cmn_err(CE_WARN,
+ "XFS: osyncisdsync is now the default, option is deprecated.");
} else if (!strcmp(this_char, "irixsgid")) {
-printk("XFS: irixsgid is now a sysctl(2) variable, option is deprecated.\n");
+ cmn_err(CE_WARN,
+ "XFS: irixsgid is now a sysctl(2) variable, option is deprecated.");
} else {
- printk("XFS: unknown mount option [%s].\n", this_char);
+ cmn_err(CE_WARN,
+ "XFS: unknown mount option [%s].", this_char);
return EINVAL;
}
}
if (args->flags & XFSMNT_NORECOVERY) {
if ((vfsp->vfs_flag & VFS_RDONLY) == 0) {
- printk("XFS: no-recovery mounts must be read-only.\n");
+ cmn_err(CE_WARN,
+ "XFS: no-recovery mounts must be read-only.");
return EINVAL;
}
}
if ((args->flags & XFSMNT_NOALIGN) && (dsunit || dswidth)) {
- printk(
- "XFS: sunit and swidth options incompatible with the noalign option\n");
+ cmn_err(CE_WARN,
+ "XFS: sunit and swidth options incompatible with the noalign option");
return EINVAL;
}
if ((dsunit && !dswidth) || (!dsunit && dswidth)) {
- printk("XFS: sunit and swidth must be specified together\n");
+ cmn_err(CE_WARN,
+ "XFS: sunit and swidth must be specified together");
return EINVAL;
}
if (dsunit && (dswidth % dsunit != 0)) {
- printk(
- "XFS: stripe width (%d) must be a multiple of the stripe unit (%d)\n",
+ cmn_err(CE_WARN,
+ "XFS: stripe width (%d) must be a multiple of the stripe unit (%d)",
dswidth, dsunit);
return EINVAL;
}
};
struct proc_xfs_info *xfs_infop;
struct xfs_mount *mp = XFS_BHVTOM(bhv);
- struct vfs *vfsp = XFS_MTOVFS(mp);
+ struct bhv_vfs *vfsp = XFS_MTOVFS(mp);
for (xfs_infop = xfs_info; xfs_infop->flag; xfs_infop++) {
if (mp->m_flags & xfs_infop->flag)
}
-vfsops_t xfs_vfsops = {
+bhv_vfsops_t xfs_vfsops = {
BHV_IDENTITY_INIT(VFS_BHV_XFS,VFS_POSITION_XFS),
.vfs_parseargs = xfs_parseargs,
.vfs_showargs = xfs_showargs,
/*
- * Copyright (c) 2000-2005 Silicon Graphics, Inc.
+ * Copyright (c) 2000-2006 Silicon Graphics, Inc.
* All Rights Reserved.
*
* This program is free software; you can redistribute it and/or
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#include <linux/capability.h>
-
#include "xfs.h"
#include "xfs_fs.h"
#include "xfs_types.h"
#include "xfs_trans.h"
#include "xfs_sb.h"
#include "xfs_ag.h"
-#include "xfs_dir.h"
#include "xfs_dir2.h"
#include "xfs_dmapi.h"
#include "xfs_mount.h"
#include "xfs_bmap_btree.h"
#include "xfs_alloc_btree.h"
#include "xfs_ialloc_btree.h"
-#include "xfs_dir_sf.h"
#include "xfs_dir2_sf.h"
#include "xfs_attr_sf.h"
#include "xfs_dinode.h"
#include "xfs_inode.h"
#include "xfs_inode_item.h"
-#include "xfs_dir_leaf.h"
#include "xfs_itable.h"
#include "xfs_btree.h"
#include "xfs_ialloc.h"
#include "xfs_log_priv.h"
#include "xfs_mac.h"
-
-/*
- * The maximum pathlen is 1024 bytes. Since the minimum file system
- * blocksize is 512 bytes, we can get a max of 2 extents back from
- * bmapi.
- */
-#define SYMLINK_MAPS 2
-
-/*
- * For xfs, we check that the file isn't too big to be opened by this kernel.
- * No other open action is required for regular files. Devices are handled
- * through the specfs file system, pipes through fifofs. Device and
- * fifo vnodes are "wrapped" by specfs and fifofs vnodes, respectively,
- * when a new vnode is first looked up or created.
- */
STATIC int
xfs_open(
bhv_desc_t *bdp,
cred_t *credp)
{
int mode;
- vnode_t *vp;
- xfs_inode_t *ip;
-
- vp = BHV_TO_VNODE(bdp);
- ip = XFS_BHVTOI(bdp);
+ bhv_vnode_t *vp = BHV_TO_VNODE(bdp);
+ xfs_inode_t *ip = XFS_BHVTOI(bdp);
if (XFS_FORCED_SHUTDOWN(ip->i_mount))
return XFS_ERROR(EIO);
return 0;
}
+STATIC int
+xfs_close(
+ bhv_desc_t *bdp,
+ int flags,
+ lastclose_t lastclose,
+ cred_t *credp)
+{
+ bhv_vnode_t *vp = BHV_TO_VNODE(bdp);
+ xfs_inode_t *ip = XFS_BHVTOI(bdp);
+
+ if (XFS_FORCED_SHUTDOWN(ip->i_mount))
+ return XFS_ERROR(EIO);
+
+ if (lastclose != L_TRUE || !VN_ISREG(vp))
+ return 0;
+
+ /*
+ * If we previously truncated this file and removed old data in
+ * the process, we want to initiate "early" writeout on the last
+ * close. This is an attempt to combat the notorious NULL files
+ * problem which is particularly noticable from a truncate down,
+ * buffered (re-)write (delalloc), followed by a crash. What we
+ * are effectively doing here is significantly reducing the time
+ * window where we'd otherwise be exposed to that problem.
+ */
+ if (VUNTRUNCATE(vp) && VN_DIRTY(vp) && ip->i_delayed_blks > 0)
+ return bhv_vop_flush_pages(vp, 0, -1, XFS_B_ASYNC, FI_NONE);
+ return 0;
+}
/*
* xfs_getattr
STATIC int
xfs_getattr(
bhv_desc_t *bdp,
- vattr_t *vap,
+ bhv_vattr_t *vap,
int flags,
cred_t *credp)
{
xfs_inode_t *ip;
xfs_mount_t *mp;
- vnode_t *vp;
+ bhv_vnode_t *vp;
vp = BHV_TO_VNODE(bdp);
vn_trace_entry(vp, __FUNCTION__, (inst_t *)__return_address);
int
xfs_setattr(
bhv_desc_t *bdp,
- vattr_t *vap,
+ bhv_vattr_t *vap,
int flags,
cred_t *credp)
{
uid_t uid=0, iuid=0;
gid_t gid=0, igid=0;
int timeflags = 0;
- vnode_t *vp;
+ bhv_vnode_t *vp;
xfs_prid_t projid=0, iprojid=0;
int mandlock_before, mandlock_after;
struct xfs_dquot *udqp, *gdqp, *olddquot1, *olddquot2;
*/
tp = NULL;
lock_flags = XFS_ILOCK_EXCL;
- ASSERT(flags & ATTR_NOLOCK ? flags & ATTR_DMI : 1);
if (flags & ATTR_NOLOCK)
need_iolock = 0;
if (!(mask & XFS_AT_SIZE)) {
((ip->i_d.di_nlink != 0 ||
!(mp->m_flags & XFS_MOUNT_WSYNC))
? 1 : 0));
- if (code) {
+ if (code)
goto abort_return;
- }
+ /*
+ * Truncated "down", so we're removing references
+ * to old data here - if we now delay flushing for
+ * a long time, we expose ourselves unduly to the
+ * notorious NULL files problem. So, we mark this
+ * vnode and flush it when the file is closed, and
+ * do not wait the usual (long) time for writeout.
+ */
+ VTRUNCATE(vp);
}
/*
* Have to do this even if the file's size doesn't change.
di_flags |= XFS_DIFLAG_NODUMP;
if (vap->va_xflags & XFS_XFLAG_PROJINHERIT)
di_flags |= XFS_DIFLAG_PROJINHERIT;
+ if (vap->va_xflags & XFS_XFLAG_NODEFRAG)
+ di_flags |= XFS_DIFLAG_NODEFRAG;
if ((ip->i_d.di_mode & S_IFMT) == S_IFDIR) {
if (vap->va_xflags & XFS_XFLAG_RTINHERIT)
di_flags |= XFS_DIFLAG_RTINHERIT;
*/
mandlock_after = MANDLOCK(vp, ip->i_d.di_mode);
if (mandlock_before != mandlock_after) {
- VOP_VNODE_CHANGE(vp, VCHANGE_FLAGS_ENF_LOCKING,
+ bhv_vop_vnode_change(vp, VCHANGE_FLAGS_ENF_LOCKING,
mandlock_after);
}
}
+/*
+ * The maximum pathlen is 1024 bytes. Since the minimum file system
+ * blocksize is 512 bytes, we can get a max of 2 extents back from
+ * bmapi.
+ */
+#define SYMLINK_MAPS 2
+
/*
* xfs_readlink
*
int count;
xfs_off_t offset;
int pathlen;
- vnode_t *vp;
+ bhv_vnode_t *vp;
int error = 0;
xfs_mount_t *mp;
int nmaps;
nmaps = SYMLINK_MAPS;
error = xfs_bmapi(NULL, ip, 0, XFS_B_TO_FSB(mp, pathlen),
- 0, NULL, 0, mval, &nmaps, NULL);
+ 0, NULL, 0, mval, &nmaps, NULL, NULL);
if (error) {
goto error_return;
nimaps = 1;
xfs_ilock(ip, XFS_ILOCK_SHARED);
- error = xfs_bmapi(NULL, ip, end_fsb, map_len, 0,
- NULL, 0, &imap, &nimaps, NULL);
+ error = XFS_BMAPI(mp, NULL, &ip->i_iocore, end_fsb, map_len, 0,
+ NULL, 0, &imap, &nimaps, NULL, NULL);
xfs_iunlock(ip, XFS_ILOCK_SHARED);
if (!error && (nimaps != 0) &&
nmaps = ARRAY_SIZE(mval);
if ((error = xfs_bmapi(tp, ip, 0, XFS_B_TO_FSB(mp, size),
XFS_BMAPI_METADATA, &first_block, 0, mval, &nmaps,
- &free_list)))
+ &free_list, NULL)))
goto error0;
/*
* Invalidate the block(s).
* Unmap the dead block(s) to the free_list.
*/
if ((error = xfs_bunmapi(tp, ip, 0, size, XFS_BMAPI_METADATA, nmaps,
- &first_block, &free_list, &done)))
+ &first_block, &free_list, NULL, &done)))
goto error1;
ASSERT(done);
/*
return 0;
}
-/*
- *
- */
STATIC int
xfs_inactive_attrs(
xfs_inode_t *ip,
bhv_desc_t *bdp)
{
xfs_inode_t *ip;
- vnode_t *vp;
+ bhv_vnode_t *vp;
xfs_mount_t *mp;
int error;
vp = BHV_TO_VNODE(bdp);
ip = XFS_BHVTOI(bdp);
+ mp = ip->i_mount;
- if (!VN_ISREG(vp) || (ip->i_d.di_mode == 0)) {
+ if (!VN_ISREG(vp) || (ip->i_d.di_mode == 0))
return 0;
- }
/* If this is a read-only mount, don't do this (would generate I/O) */
if (vp->v_vfsp->vfs_flag & VFS_RDONLY)
return 0;
#endif
- mp = ip->i_mount;
-
if (ip->i_d.di_nlink != 0) {
if ((((ip->i_d.di_mode & S_IFMT) == S_IFREG) &&
((ip->i_d.di_size > 0) || (VN_CACHED(vp) > 0 ||
cred_t *credp)
{
xfs_inode_t *ip;
- vnode_t *vp;
- xfs_bmap_free_t free_list;
+ bhv_vnode_t *vp;
+ xfs_bmap_free_t free_list;
xfs_fsblock_t first_block;
int committed;
xfs_trans_t *tp;
cmn_err(CE_NOTE,
"xfs_inactive: xfs_ifree() returned an error = %d on %s",
error, mp->m_fsname);
- xfs_force_shutdown(mp, XFS_METADATA_IO_ERROR);
+ xfs_force_shutdown(mp, SHUTDOWN_META_IO_ERROR);
}
xfs_trans_cancel(tp, XFS_TRANS_RELEASE_LOG_RES|XFS_TRANS_ABORT);
} else {
STATIC int
xfs_lookup(
bhv_desc_t *dir_bdp,
- vname_t *dentry,
- vnode_t **vpp,
+ bhv_vname_t *dentry,
+ bhv_vnode_t **vpp,
int flags,
- vnode_t *rdir,
+ bhv_vnode_t *rdir,
cred_t *credp)
{
xfs_inode_t *dp, *ip;
xfs_ino_t e_inum;
int error;
uint lock_mode;
- vnode_t *dir_vp;
+ bhv_vnode_t *dir_vp;
dir_vp = BHV_TO_VNODE(dir_bdp);
vn_trace_entry(dir_vp, __FUNCTION__, (inst_t *)__return_address);
STATIC int
xfs_create(
bhv_desc_t *dir_bdp,
- vname_t *dentry,
- vattr_t *vap,
- vnode_t **vpp,
+ bhv_vname_t *dentry,
+ bhv_vattr_t *vap,
+ bhv_vnode_t **vpp,
cred_t *credp)
{
char *name = VNAME(dentry);
- vnode_t *dir_vp;
+ bhv_vnode_t *dir_vp;
xfs_inode_t *dp, *ip;
- vnode_t *vp=NULL;
+ bhv_vnode_t *vp = NULL;
xfs_trans_t *tp;
xfs_mount_t *mp;
xfs_dev_t rdev;
if (error)
goto error_return;
- if (resblks == 0 &&
- (error = XFS_DIR_CANENTER(mp, tp, dp, name, namelen)))
+ if (resblks == 0 && (error = xfs_dir_canenter(tp, dp, name, namelen)))
goto error_return;
rdev = (vap->va_mask & XFS_AT_RDEV) ? vap->va_rdev : 0;
error = xfs_dir_ialloc(&tp, dp, vap->va_mode, 1,
xfs_trans_ijoin(tp, dp, XFS_ILOCK_EXCL);
dp_joined_to_trans = B_TRUE;
- error = XFS_DIR_CREATENAME(mp, tp, dp, name, namelen, ip->i_ino,
- &first_block, &free_list,
- resblks ? resblks - XFS_IALLOC_SPACE_RES(mp) : 0);
+ error = xfs_dir_createname(tp, dp, name, namelen, ip->i_ino,
+ &first_block, &free_list, resblks ?
+ resblks - XFS_IALLOC_SPACE_RES(mp) : 0);
if (error) {
ASSERT(error != ENOSPC);
goto abort_return;
* Propagate the fact that the vnode changed after the
* xfs_inode locks have been released.
*/
- VOP_VNODE_CHANGE(vp, VCHANGE_FLAGS_TRUNCATED, 3);
+ bhv_vop_vnode_change(vp, VCHANGE_FLAGS_TRUNCATED, 3);
*vpp = vp;
STATIC int
xfs_lock_dir_and_entry(
xfs_inode_t *dp,
- vname_t *dentry,
+ bhv_vname_t *dentry,
xfs_inode_t *ip) /* inode of entry 'name' */
{
int attempts;
STATIC int
xfs_remove(
bhv_desc_t *dir_bdp,
- vname_t *dentry,
+ bhv_vname_t *dentry,
cred_t *credp)
{
- vnode_t *dir_vp;
+ bhv_vnode_t *dir_vp;
char *name = VNAME(dentry);
xfs_inode_t *dp, *ip;
xfs_trans_t *tp = NULL;
* Entry must exist since we did a lookup in xfs_lock_dir_and_entry.
*/
XFS_BMAP_INIT(&free_list, &first_block);
- error = XFS_DIR_REMOVENAME(mp, tp, dp, name, namelen, ip->i_ino,
- &first_block, &free_list, 0);
+ error = xfs_dir_removename(tp, dp, name, namelen, ip->i_ino,
+ &first_block, &free_list, 0);
if (error) {
ASSERT(error != ENOENT);
REMOVE_DEBUG_TRACE(__LINE__);
/*
* Let interposed file systems know about removed links.
*/
- VOP_LINK_REMOVED(XFS_ITOV(ip), dir_vp, link_zero);
+ bhv_vop_link_removed(XFS_ITOV(ip), dir_vp, link_zero);
IRELE(ip);
STATIC int
xfs_link(
bhv_desc_t *target_dir_bdp,
- vnode_t *src_vp,
- vname_t *dentry,
+ bhv_vnode_t *src_vp,
+ bhv_vname_t *dentry,
cred_t *credp)
{
xfs_inode_t *tdp, *sip;
xfs_fsblock_t first_block;
int cancel_flags;
int committed;
- vnode_t *target_dir_vp;
+ bhv_vnode_t *target_dir_vp;
int resblks;
char *target_name = VNAME(dentry);
int target_namelen;
}
if (resblks == 0 &&
- (error = XFS_DIR_CANENTER(mp, tp, tdp, target_name,
- target_namelen)))
+ (error = xfs_dir_canenter(tp, tdp, target_name, target_namelen)))
goto error_return;
XFS_BMAP_INIT(&free_list, &first_block);
- error = XFS_DIR_CREATENAME(mp, tp, tdp, target_name, target_namelen,
+ error = xfs_dir_createname(tp, tdp, target_name, target_namelen,
sip->i_ino, &first_block, &free_list,
resblks);
if (error)
STATIC int
xfs_mkdir(
bhv_desc_t *dir_bdp,
- vname_t *dentry,
- vattr_t *vap,
- vnode_t **vpp,
+ bhv_vname_t *dentry,
+ bhv_vattr_t *vap,
+ bhv_vnode_t **vpp,
cred_t *credp)
{
char *dir_name = VNAME(dentry);
xfs_inode_t *dp;
xfs_inode_t *cdp; /* inode of created dir */
- vnode_t *cvp; /* vnode of created dir */
+ bhv_vnode_t *cvp; /* vnode of created dir */
xfs_trans_t *tp;
xfs_mount_t *mp;
int cancel_flags;
int committed;
xfs_bmap_free_t free_list;
xfs_fsblock_t first_block;
- vnode_t *dir_vp;
+ bhv_vnode_t *dir_vp;
boolean_t dp_joined_to_trans;
boolean_t created = B_FALSE;
int dm_event_sent = 0;
goto error_return;
if (resblks == 0 &&
- (error = XFS_DIR_CANENTER(mp, tp, dp, dir_name, dir_namelen)))
+ (error = xfs_dir_canenter(tp, dp, dir_name, dir_namelen)))
goto error_return;
/*
* create the directory inode.
XFS_BMAP_INIT(&free_list, &first_block);
- error = XFS_DIR_CREATENAME(mp, tp, dp, dir_name, dir_namelen,
- cdp->i_ino, &first_block, &free_list,
- resblks ? resblks - XFS_IALLOC_SPACE_RES(mp) : 0);
+ error = xfs_dir_createname(tp, dp, dir_name, dir_namelen, cdp->i_ino,
+ &first_block, &free_list, resblks ?
+ resblks - XFS_IALLOC_SPACE_RES(mp) : 0);
if (error) {
ASSERT(error != ENOSPC);
goto error1;
*/
dp->i_gen++;
- error = XFS_DIR_INIT(mp, tp, cdp, dp);
- if (error) {
+ error = xfs_dir_init(tp, cdp, dp);
+ if (error)
goto error2;
- }
cdp->i_gen = 1;
error = xfs_bumplink(tp, dp);
- if (error) {
+ if (error)
goto error2;
- }
cvp = XFS_ITOV(cdp);
STATIC int
xfs_rmdir(
bhv_desc_t *dir_bdp,
- vname_t *dentry,
+ bhv_vname_t *dentry,
cred_t *credp)
{
char *name = VNAME(dentry);
xfs_fsblock_t first_block;
int cancel_flags;
int committed;
- vnode_t *dir_vp;
+ bhv_vnode_t *dir_vp;
int dm_di_mode = 0;
int last_cdp_link;
int namelen;
error = XFS_ERROR(ENOTEMPTY);
goto error_return;
}
- if (!XFS_DIR_ISEMPTY(mp, cdp)) {
+ if (!xfs_dir_isempty(cdp)) {
error = XFS_ERROR(ENOTEMPTY);
goto error_return;
}
- error = XFS_DIR_REMOVENAME(mp, tp, dp, name, namelen, cdp->i_ino,
- &first_block, &free_list, resblks);
- if (error) {
+ error = xfs_dir_removename(tp, dp, name, namelen, cdp->i_ino,
+ &first_block, &free_list, resblks);
+ if (error)
goto error1;
- }
xfs_ichgtime(dp, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG);
/*
* Let interposed file systems know about removed links.
*/
- VOP_LINK_REMOVED(XFS_ITOV(cdp), dir_vp, last_cdp_link);
+ bhv_vop_link_removed(XFS_ITOV(cdp), dir_vp, last_cdp_link);
IRELE(cdp);
/*
- * xfs_readdir
- *
* Read dp's entries starting at uiop->uio_offset and translate them into
* bufsize bytes worth of struct dirents starting at bufbase.
*/
(inst_t *)__return_address);
dp = XFS_BHVTOI(dir_bdp);
- if (XFS_FORCED_SHUTDOWN(dp->i_mount)) {
+ if (XFS_FORCED_SHUTDOWN(dp->i_mount))
return XFS_ERROR(EIO);
- }
lock_mode = xfs_ilock_map_shared(dp);
- error = XFS_DIR_GETDENTS(dp->i_mount, tp, dp, uiop, eofp);
+ error = xfs_dir_getdents(tp, dp, uiop, eofp);
xfs_iunlock_map_shared(dp, lock_mode);
return error;
}
-/*
- * xfs_symlink
- *
- */
STATIC int
xfs_symlink(
bhv_desc_t *dir_bdp,
- vname_t *dentry,
- vattr_t *vap,
+ bhv_vname_t *dentry,
+ bhv_vattr_t *vap,
char *target_path,
- vnode_t **vpp,
+ bhv_vnode_t **vpp,
cred_t *credp)
{
xfs_trans_t *tp;
xfs_bmap_free_t free_list;
xfs_fsblock_t first_block;
boolean_t dp_joined_to_trans;
- vnode_t *dir_vp;
+ bhv_vnode_t *dir_vp;
uint cancel_flags;
int committed;
xfs_fileoff_t first_fsb;
int len, total;
char *path;
- for(total = 0, path = target_path; total < pathlen;) {
+ for (total = 0, path = target_path; total < pathlen;) {
/*
* Skip any slashes.
*/
* Check for ability to enter directory entry, if no space reserved.
*/
if (resblks == 0 &&
- (error = XFS_DIR_CANENTER(mp, tp, dp, link_name, link_namelen)))
+ (error = xfs_dir_canenter(tp, dp, link_name, link_namelen)))
goto error_return;
/*
* Initialize the bmap freelist prior to calling either
error = xfs_bmapi(tp, ip, first_fsb, fs_blocks,
XFS_BMAPI_WRITE | XFS_BMAPI_METADATA,
&first_block, resblks, mval, &nmaps,
- &free_list);
+ &free_list, NULL);
if (error) {
goto error1;
}
/*
* Create the directory entry for the symlink.
*/
- error = XFS_DIR_CREATENAME(mp, tp, dp, link_name, link_namelen,
- ip->i_ino, &first_block, &free_list, resblks);
- if (error) {
+ error = xfs_dir_createname(tp, dp, link_name, link_namelen, ip->i_ino,
+ &first_block, &free_list, resblks);
+ if (error)
goto error1;
- }
xfs_ichgtime(dp, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG);
xfs_trans_log_inode(tp, dp, XFS_ILOG_CORE);
}
if (!error) {
- vnode_t *vp;
+ bhv_vnode_t *vp;
ASSERT(ip);
vp = XFS_ITOV(ip);
int
xfs_rwlock(
bhv_desc_t *bdp,
- vrwlock_t locktype)
+ bhv_vrwlock_t locktype)
{
xfs_inode_t *ip;
- vnode_t *vp;
+ bhv_vnode_t *vp;
vp = BHV_TO_VNODE(bdp);
if (VN_ISDIR(vp))
void
xfs_rwunlock(
bhv_desc_t *bdp,
- vrwlock_t locktype)
+ bhv_vrwlock_t locktype)
{
xfs_inode_t *ip;
- vnode_t *vp;
+ bhv_vnode_t *vp;
vp = BHV_TO_VNODE(bdp);
if (VN_ISDIR(vp))
return error;
}
-
int
xfs_set_dmattrs (
bhv_desc_t *bdp,
return error;
}
-
-/*
- * xfs_reclaim
- */
STATIC int
xfs_reclaim(
bhv_desc_t *bdp)
{
xfs_inode_t *ip;
- vnode_t *vp;
+ bhv_vnode_t *vp;
vp = BHV_TO_VNODE(bdp);
ip = XFS_BHVTOI(bdp);
int sync_mode)
{
xfs_ihash_t *ih = ip->i_hash;
- vnode_t *vp = XFS_ITOV_NULL(ip);
+ bhv_vnode_t *vp = XFS_ITOV_NULL(ip);
int error;
if (vp && VN_BAD(vp))
* Issue the xfs_bmapi() call to allocate the blocks
*/
XFS_BMAP_INIT(&free_list, &firstfsb);
- error = xfs_bmapi(tp, ip, startoffset_fsb,
+ error = XFS_BMAPI(mp, tp, &ip->i_iocore, startoffset_fsb,
allocatesize_fsb, bmapi_flag,
&firstfsb, 0, imapp, &nimaps,
- &free_list);
+ &free_list, NULL);
if (error) {
goto error0;
}
for (offset = startoff; offset <= endoff; offset = lastoffset + 1) {
offset_fsb = XFS_B_TO_FSBT(mp, offset);
nimap = 1;
- error = xfs_bmapi(NULL, ip, offset_fsb, 1, 0, NULL, 0, &imap,
- &nimap, NULL);
+ error = XFS_BMAPI(mp, NULL, &ip->i_iocore, offset_fsb, 1, 0,
+ NULL, 0, &imap, &nimap, NULL, NULL);
if (error || nimap < 1)
break;
ASSERT(imap.br_blockcount >= 1);
xfs_off_t len,
int attr_flags)
{
- vnode_t *vp;
+ bhv_vnode_t *vp;
int committed;
int done;
xfs_off_t end_dmi_offset;
return error;
}
- ASSERT(attr_flags & ATTR_NOLOCK ? attr_flags & ATTR_DMI : 1);
if (attr_flags & ATTR_NOLOCK)
need_iolock = 0;
if (need_iolock) {
if (VN_CACHED(vp) != 0) {
xfs_inval_cached_trace(&ip->i_iocore, ioffset, -1,
ctooff(offtoct(ioffset)), -1);
- VOP_FLUSHINVAL_PAGES(vp, ctooff(offtoct(ioffset)),
+ bhv_vop_flushinval_pages(vp, ctooff(offtoct(ioffset)),
-1, FI_REMAPF_LOCKED);
}
*/
if (rt && !XFS_SB_VERSION_HASEXTFLGBIT(&mp->m_sb)) {
nimap = 1;
- error = xfs_bmapi(NULL, ip, startoffset_fsb, 1, 0, NULL, 0,
- &imap, &nimap, NULL);
+ error = XFS_BMAPI(mp, NULL, &ip->i_iocore, startoffset_fsb,
+ 1, 0, NULL, 0, &imap, &nimap, NULL, NULL);
if (error)
goto out_unlock_iolock;
ASSERT(nimap == 0 || nimap == 1);
startoffset_fsb += mp->m_sb.sb_rextsize - mod;
}
nimap = 1;
- error = xfs_bmapi(NULL, ip, endoffset_fsb - 1, 1, 0, NULL, 0,
- &imap, &nimap, NULL);
+ error = XFS_BMAPI(mp, NULL, &ip->i_iocore, endoffset_fsb - 1,
+ 1, 0, NULL, 0, &imap, &nimap, NULL, NULL);
if (error)
goto out_unlock_iolock;
ASSERT(nimap == 0 || nimap == 1);
* issue the bunmapi() call to free the blocks
*/
XFS_BMAP_INIT(&free_list, &firstfsb);
- error = xfs_bunmapi(tp, ip, startoffset_fsb,
+ error = XFS_BUNMAPI(mp, tp, &ip->i_iocore, startoffset_fsb,
endoffset_fsb - startoffset_fsb,
- 0, 2, &firstfsb, &free_list, &done);
+ 0, 2, &firstfsb, &free_list, NULL, &done);
if (error) {
goto error0;
}
xfs_off_t startoffset;
xfs_off_t llen;
xfs_trans_t *tp;
- vattr_t va;
- vnode_t *vp;
+ bhv_vattr_t va;
+ bhv_vnode_t *vp;
vp = BHV_TO_VNODE(bdp);
vn_trace_entry(vp, __FUNCTION__, (inst_t *)__return_address);
return error;
}
-vnodeops_t xfs_vnodeops = {
+bhv_vnodeops_t xfs_vnodeops = {
BHV_IDENTITY_INIT(VN_BHV_XFS,VNODE_POSITION_XFS),
.vop_open = xfs_open,
+ .vop_close = xfs_close,
.vop_read = xfs_read,
#ifdef HAVE_SENDFILE
.vop_sendfile = xfs_sendfile,
#define vga_readb(a) readb((u8 __iomem *)(a))
#define vga_writeb(v,a) writeb(v, (u8 __iomem *)(a))
-#define VGA_MAP_MEM(x) ((unsigned long) ioremap(x, 0))
+#define VGA_MAP_MEM(x,s) ((unsigned long) ioremap(x, s))
#endif
#include <asm/hardware.h>
#include <asm/io.h>
-#define VGA_MAP_MEM(x) (PCIMEM_BASE + (x))
+#define VGA_MAP_MEM(x,s) (PCIMEM_BASE + (x))
#define vga_readb(x) (*((volatile unsigned char *)x))
#define vga_writeb(x,y) (*((volatile unsigned char *)y) = (x))
* access the videoram directly without any black magic.
*/
-#define VGA_MAP_MEM(x) (unsigned long)phys_to_virt(x)
+#define VGA_MAP_MEM(x,s) (unsigned long)phys_to_virt(x)
#define vga_readb(x) (*(x))
#define vga_writeb(x,y) (*(y) = (x))
extern unsigned long vga_console_iobase;
extern unsigned long vga_console_membase;
-#define VGA_MAP_MEM(x) ((unsigned long) ioremap_nocache(vga_console_membase + (x), 0))
+#define VGA_MAP_MEM(x,s) ((unsigned long) ioremap_nocache(vga_console_membase + (x), s))
#define vga_readb(x) (*(x))
#define vga_writeb(x,y) (*(y) = (x))
* access the videoram directly without any black magic.
*/
-#define VGA_MAP_MEM(x) (unsigned long)phys_to_virt(x)
+#define VGA_MAP_MEM(x,s) (unsigned long)phys_to_virt(x)
#define vga_readb(x) (*(x))
#define vga_writeb(x,y) (*(y) = (x))
* access the videoram directly without any black magic.
*/
-#define VGA_MAP_MEM(x) (0xb0000000L + (unsigned long)(x))
+#define VGA_MAP_MEM(x,s) (0xb0000000L + (unsigned long)(x))
#define vga_readb(x) (*(x))
#define vga_writeb(x,y) (*(y) = (x))
extern unsigned long vgacon_remap_base;
#ifdef __powerpc64__
-#define VGA_MAP_MEM(x) ((unsigned long) ioremap((x), 0))
+#define VGA_MAP_MEM(x,s) ((unsigned long) ioremap((x), s))
#else
-#define VGA_MAP_MEM(x) (x + vgacon_remap_base)
+#define VGA_MAP_MEM(x,s) (x + vgacon_remap_base)
#endif
#define vga_readb(x) (*(x))
return *addr;
}
-#define VGA_MAP_MEM(x) (x)
+#define VGA_MAP_MEM(x,s) (x)
#endif
* access the videoram directly without any black magic.
*/
-#define VGA_MAP_MEM(x) (unsigned long)phys_to_virt(x)
+#define VGA_MAP_MEM(x,s) (unsigned long)phys_to_virt(x)
#define vga_readb(x) (*(x))
#define vga_writeb(x,y) (*(y) = (x))
#ifndef _XTENSA_VGA_H
#define _XTENSA_VGA_H
-#define VGA_MAP_MEM(x) (unsigned long)phys_to_virt(x)
+#define VGA_MAP_MEM(x,s) (unsigned long)phys_to_virt(x)
#define vga_readb(x) (*(x))
#define vga_writeb(x,y) (*(y) = (x))
extern struct dentry * d_splice_alias(struct inode *, struct dentry *);
extern void shrink_dcache_sb(struct super_block *);
extern void shrink_dcache_parent(struct dentry *);
-extern void shrink_dcache_anon(struct hlist_head *);
+extern void shrink_dcache_anon(struct super_block *);
extern int d_invalidate(struct dentry *);
/* only used at mount-time */
extern void bus_rescan_devices(struct bus_type * bus);
-extern struct bus_type * get_bus(struct bus_type * bus);
-extern void put_bus(struct bus_type * bus);
-
-extern struct bus_type * find_bus(char * name);
-
/* iterator helpers for buses */
int bus_for_each_dev(struct bus_type * bus, struct device * start, void * data,
struct subsystem subsys;
struct list_head children;
+ struct list_head devices;
struct list_head interfaces;
struct semaphore sem; /* locks both the children and interfaces lists */
extern int class_register(struct class *);
extern void class_unregister(struct class *);
-extern struct class * class_get(struct class *);
-extern void class_put(struct class *);
-
struct class_attribute {
struct attribute attr;
struct kobject kobj;
char bus_id[BUS_ID_SIZE]; /* position on parent bus */
struct device_attribute uevent_attr;
+ struct device_attribute *devt_attr;
struct semaphore sem; /* semaphore to synchronize calls to
* its driver.
struct dma_coherent_mem *dma_mem; /* internal for coherent mem
override */
+ /* class_device migration path */
+ struct list_head node;
+ struct class *class; /* optional*/
+ dev_t devt; /* dev_t, creates the sysfs "dev" */
+
void (*release)(struct device * dev);
};
extern void driver_attach(struct device_driver * drv);
extern void device_reprobe(struct device *dev);
+/*
+ * Easy functions for dynamically creating devices on the fly
+ */
+extern struct device *device_create(struct class *cls, struct device *parent,
+ dev_t devt, char *fmt, ...)
+ __attribute__((format(printf,4,5)));
+extern void device_destroy(struct class *cls, dev_t devt);
/*
* Platform "fixup" functions - allow the platform to have their say
extern void firmware_unregister(struct subsystem *);
/* debugging and troubleshooting/diagnostic helpers. */
+extern const char *dev_driver_string(struct device *dev);
#define dev_printk(level, dev, format, arg...) \
- printk(level "%s %s: " format , (dev)->driver ? (dev)->driver->name : "" , (dev)->bus_id , ## arg)
+ printk(level "%s %s: " format , dev_driver_string(dev) , (dev)->bus_id , ## arg)
#ifdef DEBUG
#define dev_dbg(dev, format, arg...) \
extern int lease_modify(struct file_lock **, int);
extern int lock_may_read(struct inode *, loff_t start, unsigned long count);
extern int lock_may_write(struct inode *, loff_t start, unsigned long count);
-extern void steal_locks(fl_owner_t from);
struct fasync_struct {
int magic;
--- /dev/null
+/*
+ * ISA bus.
+ */
+
+#ifndef __LINUX_ISA_H
+#define __LINUX_ISA_H
+
+#include <linux/device.h>
+#include <linux/kernel.h>
+
+struct isa_driver {
+ int (*match)(struct device *, unsigned int);
+ int (*probe)(struct device *, unsigned int);
+ int (*remove)(struct device *, unsigned int);
+ void (*shutdown)(struct device *, unsigned int);
+ int (*suspend)(struct device *, unsigned int, pm_message_t);
+ int (*resume)(struct device *, unsigned int);
+
+ struct device_driver driver;
+ struct device *devices;
+};
+
+#define to_isa_driver(x) container_of((x), struct isa_driver, driver)
+
+int isa_register_driver(struct isa_driver *, unsigned int);
+void isa_unregister_driver(struct isa_driver *);
+
+#endif /* __LINUX_ISA_H */
/* match a key against a description */
int (*match)(const struct key *key, const void *desc);
+ /* clear some of the data from a key on revokation (optional)
+ * - the key's semaphore will be write-locked by the caller
+ */
+ void (*revoke)(struct key *key);
+
/* clear the data from a key (optional) */
void (*destroy)(struct key *key);
extern struct key *key_alloc(struct key_type *type,
const char *desc,
- uid_t uid, gid_t gid, key_perm_t perm,
- int not_in_quota);
+ uid_t uid, gid_t gid,
+ struct task_struct *ctx,
+ key_perm_t perm, int not_in_quota);
extern int key_payload_reserve(struct key *key, size_t datalen);
extern int key_instantiate_and_link(struct key *key,
const void *data,
struct key *key);
extern struct key *keyring_alloc(const char *description, uid_t uid, gid_t gid,
- int not_in_quota, struct key *dest);
+ struct task_struct *ctx,
+ int not_in_quota,
+ struct key *dest);
extern int keyring_clear(struct key *keyring);
* the userspace interface
*/
extern struct key root_user_keyring, root_session_keyring;
-extern int alloc_uid_keyring(struct user_struct *user);
+extern int alloc_uid_keyring(struct user_struct *user,
+ struct task_struct *ctx);
extern void switch_uid_keyring(struct user_struct *new_user);
extern int copy_keys(unsigned long clone_flags, struct task_struct *tsk);
extern int copy_thread_group_keys(struct task_struct *tsk);
#define make_key_ref(k) ({ NULL; })
#define key_ref_to_ptr(k) ({ NULL; })
#define is_key_possessed(k) 0
-#define alloc_uid_keyring(u) 0
+#define alloc_uid_keyring(u,c) 0
#define switch_uid_keyring(u) do { } while(0)
#define __install_session_keyring(t, k) ({ NULL; })
#define copy_keys(f,t) 0
#define key_fsgid_changed(t) do { } while(0)
#define key_init() do { } while(0)
+/* Initial keyrings */
+extern struct key root_user_keyring;
+extern struct key root_session_keyring;
+
#endif /* CONFIG_KEYS */
#endif /* __KERNEL__ */
#endif /* _LINUX_KEY_H */
/* The global /sys/kernel/ subsystem for people to chain off of */
extern struct subsystem kernel_subsys;
+/* The global /sys/hypervisor/ subsystem */
+extern struct subsystem hypervisor_subsys;
/**
* Helpers for setting the kset of registered objects.
/* key management security hooks */
#ifdef CONFIG_KEYS
- int (*key_alloc)(struct key *key);
+ int (*key_alloc)(struct key *key, struct task_struct *tsk);
void (*key_free)(struct key *key);
int (*key_permission)(key_ref_t key_ref,
struct task_struct *context,
#ifdef CONFIG_KEYS
#ifdef CONFIG_SECURITY
-static inline int security_key_alloc(struct key *key)
+static inline int security_key_alloc(struct key *key,
+ struct task_struct *tsk)
{
- return security_ops->key_alloc(key);
+ return security_ops->key_alloc(key, tsk);
}
static inline void security_key_free(struct key *key)
#else
-static inline int security_key_alloc(struct key *key)
+static inline int security_key_alloc(struct key *key,
+ struct task_struct *tsk)
{
return 0;
}
struct kset kset;
};
+struct sysdev_class_attribute {
+ struct attribute attr;
+ ssize_t (*show)(struct sysdev_class *, char *);
+ ssize_t (*store)(struct sysdev_class *, const char *, size_t);
+};
+
+#define SYSDEV_CLASS_ATTR(_name,_mode,_show,_store) \
+struct sysdev_class_attribute attr_##_name = { \
+ .attr = {.name = __stringify(_name), .mode = _mode }, \
+ .show = _show, \
+ .store = _store, \
+};
+
extern int sysdev_class_register(struct sysdev_class *);
extern void sysdev_class_unregister(struct sysdev_class *);
-
+extern int sysdev_class_create_file(struct sysdev_class *,
+ struct sysdev_class_attribute *);
+extern void sysdev_class_remove_file(struct sysdev_class *,
+ struct sysdev_class_attribute *);
/**
* Auxillary system device drivers.
*/
extern int tty_unregister_ldisc(int disc);
extern int tty_register_driver(struct tty_driver *driver);
extern int tty_unregister_driver(struct tty_driver *driver);
-extern void tty_register_device(struct tty_driver *driver, unsigned index, struct device *dev);
+extern struct class_device *tty_register_device(struct tty_driver *driver,
+ unsigned index,
+ struct device *dev);
extern void tty_unregister_device(struct tty_driver *driver, unsigned index);
extern int tty_read_raw_data(struct tty_struct *tty, unsigned char *bufp,
int buflen);
* Devices may also have class-specific or vendor-specific descriptors.
*/
+struct ep_device;
+
/**
* struct usb_host_endpoint - host-side endpoint descriptor and queue
* @desc: descriptor for this endpoint, wMaxPacketSize in native byteorder
struct usb_endpoint_descriptor desc;
struct list_head urb_list;
void *hcpriv;
- struct kobject *kobj; /* For sysfs info */
+ struct ep_device *ep_dev; /* For sysfs info */
unsigned char *extra; /* Extra descriptors */
int extralen;
* @condition: binding state of the interface: not bound, binding
* (in probe()), bound to a driver, or unbinding (in disconnect())
* @dev: driver model's view of this device
- * @class_dev: driver model's class view of this device.
+ * @usb_dev: if an interface is bound to the USB major, this will point
+ * to the sysfs representation for that device.
*
* USB device drivers attach to interfaces on a physical device. Each
* interface encapsulates a single high level function, such as feeding
* bound to */
enum usb_interface_condition condition; /* state of binding */
struct device dev; /* interface specific device info */
- struct class_device *class_dev;
+ struct device *usb_dev; /* pointer to the usb class's device, if any */
};
#define to_usb_interface(d) container_of(d, struct usb_interface, dev)
#define interface_to_usbdev(intf) \
char *serial; /* iSerialNumber string, if present */
struct list_head filelist;
- struct class_device *class_dev;
+ struct device *usbfs_dev;
struct dentry *usbfs_dentry; /* usbfs dentry entry for the device */
/*
/* USB port reset for device reinitialization */
extern int usb_reset_device(struct usb_device *dev);
+extern int usb_reset_composite_device(struct usb_device *dev,
+ struct usb_interface *iface);
extern struct usb_device *usb_find_device(u16 vendor_id, u16 product_id);
* do (or don't) show up otherwise in the filesystem.
* @suspend: Called when the device is going to be suspended by the system.
* @resume: Called when the device is being resumed by the system.
+ * @pre_reset: Called by usb_reset_composite_device() when the device
+ * is about to be reset.
+ * @post_reset: Called by usb_reset_composite_device() after the device
+ * has been reset.
* @id_table: USB drivers use ID table to support hotplugging.
* Export this with MODULE_DEVICE_TABLE(usb,...). This must be set
* or your driver's probe function will never get called.
int (*suspend) (struct usb_interface *intf, pm_message_t message);
int (*resume) (struct usb_interface *intf);
+ void (*pre_reset) (struct usb_interface *intf);
+ void (*post_reset) (struct usb_interface *intf);
+
const struct usb_device_id *id_table;
struct usb_dynids dynids;
extern int usb_control_msg(struct usb_device *dev, unsigned int pipe,
__u8 request, __u8 requesttype, __u16 value, __u16 index,
void *data, __u16 size, int timeout);
+extern int usb_interrupt_msg(struct usb_device *usb_dev, unsigned int pipe,
+ void *data, int len, int *actual_length, int timeout);
extern int usb_bulk_msg(struct usb_device *usb_dev, unsigned int pipe,
void *data, int len, int *actual_length,
int timeout);
u8 power;
/* sl811 relies on an external source of VBUS current */
- void (*port_power)(struct device *dev, int is_on);
+ void (*port_power)(struct device *dev, int is_on);
/* pulse sl811 nRST (probably with a GPIO) */
- void (*reset)(struct device *dev);
+ void (*reset)(struct device *dev);
// some boards need something like these:
- // int (*check_overcurrent)(struct device *dev);
- // void (*clock_enable)(struct device *dev, int is_on);
+ // int (*check_overcurrent)(struct device *dev);
+ // void (*clock_enable)(struct device *dev, int is_on);
};
*/
#ifndef MAX_WBITS
# define MAX_WBITS 15 /* 32K LZ77 window */
+#endif
+
+/* default windowBits for decompression. MAX_WBITS is for compression only */
+#ifndef DEF_WBITS
+# define DEF_WBITS MAX_WBITS
+#endif
+
+/* default memLevel */
+#if MAX_MEM_LEVEL >= 8
+# define DEF_MEM_LEVEL 8
+#else
+# define DEF_MEM_LEVEL MAX_MEM_LEVEL
#endif
/* Type declarations */
/* zlib.h -- interface of the 'zlib' general purpose compression library
- version 1.1.3, July 9th, 1998
- Copyright (C) 1995-1998 Jean-loup Gailly and Mark Adler
+ Copyright (C) 1995-2005 Jean-loup Gailly and Mark Adler
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
The data format used by the zlib library is described by RFCs (Request for
- Comments) 1950 to 1952 in the files ftp://ds.internic.net/rfc/rfc1950.txt
+ Comments) 1950 to 1952 in the files http://www.ietf.org/rfc/rfc1950.txt
(zlib format), rfc1951.txt (deflate format) and rfc1952.txt (gzip format).
*/
#include <linux/zconf.h>
-#define ZLIB_VERSION "1.1.3"
+/* zlib deflate based on ZLIB_VERSION "1.1.3" */
+/* zlib inflate based on ZLIB_VERSION "1.2.3" */
+
+/*
+ This is a modified version of zlib for use inside the Linux kernel.
+ The main changes are to perform all memory allocation in advance.
+
+ Inflation Changes:
+ * Z_PACKET_FLUSH is added and used by ppp_deflate. Before returning
+ this checks there is no more input data available and the next data
+ is a STORED block. It also resets the mode to be read for the next
+ data, all as per PPP requirements.
+ * Addition of zlib_inflateIncomp which copies incompressible data into
+ the history window and adjusts the accoutning without calling
+ zlib_inflate itself to inflate the data.
+*/
/*
The 'zlib' compression library provides in-memory compression and
application must provide more input and/or consume the output
(providing more output space) before each call.
+ The compressed data format used by default by the in-memory functions is
+ the zlib format, which is a zlib wrapper documented in RFC 1950, wrapped
+ around a deflate stream, which is itself documented in RFC 1951.
+
The library also supports reading and writing files in gzip (.gz) format
with an interface similar to that of stdio.
+ The zlib format was designed to be compact and fast for use in memory
+ and on communications channels. The gzip format was designed for single-
+ file compression on file systems, has a larger header than zlib to maintain
+ directory information, and uses a different, slower check method than zlib.
+
The library does not install any signal handler. The decoder checks
the consistency of the compressed data, so the library should never
crash even in case of corrupted input.
#define Z_SYNC_FLUSH 3
#define Z_FULL_FLUSH 4
#define Z_FINISH 5
-/* Allowed flush values; see deflate() below for details */
+#define Z_BLOCK 6 /* Only for inflate at present */
+/* Allowed flush values; see deflate() and inflate() below for details */
#define Z_OK 0
#define Z_STREAM_END 1
/* basic functions */
-extern const char * zlib_zlibVersion (void);
-/* The application can compare zlibVersion and ZLIB_VERSION for consistency.
- If the first character differs, the library code actually used is
- not compatible with the zlib.h header file used by the application.
- This check is automatically made by deflateInit and inflateInit.
- */
-
extern int zlib_deflate_workspacesize (void);
/*
Returns the number of bytes that needs to be allocated for a per-
extern int zlib_inflate (z_streamp strm, int flush);
/*
inflate decompresses as much data as possible, and stops when the input
- buffer becomes empty or the output buffer becomes full. It may some
- introduce some output latency (reading input without producing any output)
- except when forced to flush.
+ buffer becomes empty or the output buffer becomes full. It may introduce
+ some output latency (reading input without producing any output) except when
+ forced to flush.
The detailed semantics are as follows. inflate performs one or both of the
following actions:
must be called again after making room in the output buffer because there
might be more output pending.
- If the parameter flush is set to Z_SYNC_FLUSH, inflate flushes as much
- output as possible to the output buffer. The flushing behavior of inflate is
- not specified for values of the flush parameter other than Z_SYNC_FLUSH
- and Z_FINISH, but the current implementation actually flushes as much output
- as possible anyway.
+ The flush parameter of inflate() can be Z_NO_FLUSH, Z_SYNC_FLUSH,
+ Z_FINISH, or Z_BLOCK. Z_SYNC_FLUSH requests that inflate() flush as much
+ output as possible to the output buffer. Z_BLOCK requests that inflate() stop
+ if and when it gets to the next deflate block boundary. When decoding the
+ zlib or gzip format, this will cause inflate() to return immediately after
+ the header and before the first block. When doing a raw inflate, inflate()
+ will go ahead and process the first block, and will return when it gets to
+ the end of that block, or when it runs out of data.
+
+ The Z_BLOCK option assists in appending to or combining deflate streams.
+ Also to assist in this, on return inflate() will set strm->data_type to the
+ number of unused bits in the last byte taken from strm->next_in, plus 64
+ if inflate() is currently decoding the last block in the deflate stream,
+ plus 128 if inflate() returned immediately after decoding an end-of-block
+ code or decoding the complete header up to just before the first byte of the
+ deflate stream. The end-of-block will not be indicated until all of the
+ uncompressed data from that block has been written to strm->next_out. The
+ number of unused bits may in general be greater than seven, except when
+ bit 7 of data_type is set, in which case the number of unused bits will be
+ less than eight.
inflate() should normally be called until it returns Z_STREAM_END or an
error. However if all decompression is to be performed in a single step
uncompressed data. (The size of the uncompressed data may have been saved
by the compressor for this purpose.) The next operation on this stream must
be inflateEnd to deallocate the decompression state. The use of Z_FINISH
- is never required, but can be used to inform inflate that a faster routine
+ is never required, but can be used to inform inflate that a faster approach
may be used for the single inflate() call.
- If a preset dictionary is needed at this point (see inflateSetDictionary
- below), inflate sets strm-adler to the adler32 checksum of the
- dictionary chosen by the compressor and returns Z_NEED_DICT; otherwise
- it sets strm->adler to the adler32 checksum of all output produced
- so far (that is, total_out bytes) and returns Z_OK, Z_STREAM_END or
- an error code as described below. At the end of the stream, inflate()
- checks that its computed adler32 checksum is equal to that saved by the
- compressor and returns Z_STREAM_END only if the checksum is correct.
+ In this implementation, inflate() always flushes as much output as
+ possible to the output buffer, and always uses the faster approach on the
+ first call. So the only effect of the flush parameter in this implementation
+ is on the return value of inflate(), as noted below, or when it returns early
+ because Z_BLOCK is used.
+
+ If a preset dictionary is needed after this call (see inflateSetDictionary
+ below), inflate sets strm->adler to the adler32 checksum of the dictionary
+ chosen by the compressor and returns Z_NEED_DICT; otherwise it sets
+ strm->adler to the adler32 checksum of all output produced so far (that is,
+ total_out bytes) and returns Z_OK, Z_STREAM_END or an error code as described
+ below. At the end of the stream, inflate() checks that its computed adler32
+ checksum is equal to that saved by the compressor and returns Z_STREAM_END
+ only if the checksum is correct.
+
+ inflate() will decompress and check either zlib-wrapped or gzip-wrapped
+ deflate data. The header type is detected automatically. Any information
+ contained in the gzip header is not retained, so applications that need that
+ information should instead use raw inflate, see inflateInit2() below, or
+ inflateBack() and perform their own processing of the gzip header and
+ trailer.
inflate() returns Z_OK if some progress has been made (more input processed
or more output produced), Z_STREAM_END if the end of the compressed data has
been reached and all uncompressed output has been produced, Z_NEED_DICT if a
preset dictionary is needed at this point, Z_DATA_ERROR if the input data was
- corrupted (input stream not conforming to the zlib format or incorrect
- adler32 checksum), Z_STREAM_ERROR if the stream structure was inconsistent
- (for example if next_in or next_out was NULL), Z_MEM_ERROR if there was not
- enough memory, Z_BUF_ERROR if no progress is possible or if there was not
- enough room in the output buffer when Z_FINISH is used. In the Z_DATA_ERROR
- case, the application may then call inflateSync to look for a good
- compression block.
+ corrupted (input stream not conforming to the zlib format or incorrect check
+ value), Z_STREAM_ERROR if the stream structure was inconsistent (for example
+ if next_in or next_out was NULL), Z_MEM_ERROR if there was not enough memory,
+ Z_BUF_ERROR if no progress is possible or if there was not enough room in the
+ output buffer when Z_FINISH is used. Note that Z_BUF_ERROR is not fatal, and
+ inflate() can be called again with more input and more output space to
+ continue decompressing. If Z_DATA_ERROR is returned, the application may then
+ call inflateSync() to look for a good compression block if a partial recovery
+ of the data is desired.
*/
The windowBits parameter is the base two logarithm of the maximum window
size (the size of the history buffer). It should be in the range 8..15 for
this version of the library. The default value is 15 if inflateInit is used
- instead. If a compressed stream with a larger window size is given as
- input, inflate() will return with the error code Z_DATA_ERROR instead of
- trying to allocate a larger window.
-
- inflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough
- memory, Z_STREAM_ERROR if a parameter is invalid (such as a negative
- memLevel). msg is set to null if there is no error message. inflateInit2
- does not perform any decompression apart from reading the zlib header if
- present: this will be done by inflate(). (So next_in and avail_in may be
- modified, but next_out and avail_out are unchanged.)
+ instead. windowBits must be greater than or equal to the windowBits value
+ provided to deflateInit2() while compressing, or it must be equal to 15 if
+ deflateInit2() was not used. If a compressed stream with a larger window
+ size is given as input, inflate() will return with the error code
+ Z_DATA_ERROR instead of trying to allocate a larger window.
+
+ windowBits can also be -8..-15 for raw inflate. In this case, -windowBits
+ determines the window size. inflate() will then process raw deflate data,
+ not looking for a zlib or gzip header, not generating a check value, and not
+ looking for any check values for comparison at the end of the stream. This
+ is for use with other formats that use the deflate compressed data format
+ such as zip. Those formats provide their own check values. If a custom
+ format is developed using the raw deflate format for compressed data, it is
+ recommended that a check value such as an adler32 or a crc32 be applied to
+ the uncompressed data as is done in the zlib, gzip, and zip formats. For
+ most applications, the zlib format should be used as is. Note that comments
+ above on the use in deflateInit2() applies to the magnitude of windowBits.
+
+ windowBits can also be greater than 15 for optional gzip decoding. Add
+ 32 to windowBits to enable zlib and gzip decoding with automatic header
+ detection, or add 16 to decode only the gzip format (the zlib format will
+ return a Z_DATA_ERROR). If a gzip stream is being decoded, strm->adler is
+ a crc32 instead of an adler32.
+
+ inflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough
+ memory, Z_STREAM_ERROR if a parameter is invalid (such as a null strm). msg
+ is set to null if there is no error message. inflateInit2 does not perform
+ any decompression apart from reading the zlib header if present: this will
+ be done by inflate(). (So next_in and avail_in may be modified, but next_out
+ and avail_out are unchanged.)
*/
extern int zlib_inflateSetDictionary (z_streamp strm,
uInt dictLength);
/*
Initializes the decompression dictionary from the given uncompressed byte
- sequence. This function must be called immediately after a call of inflate
- if this call returned Z_NEED_DICT. The dictionary chosen by the compressor
- can be determined from the Adler32 value returned by this call of
- inflate. The compressor and decompressor must use exactly the same
- dictionary (see deflateSetDictionary).
+ sequence. This function must be called immediately after a call of inflate,
+ if that call returned Z_NEED_DICT. The dictionary chosen by the compressor
+ can be determined from the adler32 value returned by that call of inflate.
+ The compressor and decompressor must use exactly the same dictionary (see
+ deflateSetDictionary). For raw inflate, this function can be called
+ immediately after inflateInit2() or inflateReset() and before any call of
+ inflate() to set the dictionary. The application must insure that the
+ dictionary that was used for compression is provided.
inflateSetDictionary returns Z_OK if success, Z_STREAM_ERROR if a
parameter is invalid (such as NULL dictionary) or the stream state is
inconsistent, Z_DATA_ERROR if the given dictionary doesn't match the
- expected one (incorrect Adler32 value). inflateSetDictionary does not
+ expected one (incorrect adler32 value). inflateSetDictionary does not
perform any decompression: this will be done by subsequent calls of
inflate().
*/
containing the data at next_in (except that the data is not output).
*/
- /* various hacks, don't look :) */
-
-/* deflateInit and inflateInit are macros to allow checking the zlib version
- * and the compiler's view of z_stream:
- */
-extern int zlib_deflateInit_ (z_streamp strm, int level,
- const char *version, int stream_size);
-extern int zlib_inflateInit_ (z_streamp strm,
- const char *version, int stream_size);
-extern int zlib_deflateInit2_ (z_streamp strm, int level, int method,
- int windowBits, int memLevel,
- int strategy, const char *version,
- int stream_size);
-extern int zlib_inflateInit2_ (z_streamp strm, int windowBits,
- const char *version, int stream_size);
#define zlib_deflateInit(strm, level) \
- zlib_deflateInit_((strm), (level), ZLIB_VERSION, sizeof(z_stream))
+ zlib_deflateInit2((strm), (level), Z_DEFLATED, MAX_WBITS, \
+ DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY)
#define zlib_inflateInit(strm) \
- zlib_inflateInit_((strm), ZLIB_VERSION, sizeof(z_stream))
-#define zlib_deflateInit2(strm, level, method, windowBits, memLevel, strategy) \
- zlib_deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\
- (strategy), ZLIB_VERSION, sizeof(z_stream))
-#define zlib_inflateInit2(strm, windowBits) \
- zlib_inflateInit2_((strm), (windowBits), ZLIB_VERSION, sizeof(z_stream))
+ zlib_inflateInit2((strm), DEF_WBITS)
+extern int zlib_deflateInit2(z_streamp strm, int level, int method,
+ int windowBits, int memLevel,
+ int strategy);
+extern int zlib_inflateInit2(z_streamp strm, int windowBits);
#if !defined(_Z_UTIL_H) && !defined(NO_DUMMY_DECL)
struct internal_state {int dummy;}; /* hack for buggy compilers */
#endif
-extern const char * zlib_zError (int err);
-#if 0
-extern int zlib_inflateSyncPoint (z_streamp z);
-#endif
-extern const uLong * zlib_get_crc_table (void);
-
#endif /* _ZLIB_H */
/* common constants */
-#ifndef DEF_WBITS
-# define DEF_WBITS MAX_WBITS
-#endif
-/* default windowBits for decompression. MAX_WBITS is for compression only */
-
-#if MAX_MEM_LEVEL >= 8
-# define DEF_MEM_LEVEL 8
-#else
-# define DEF_MEM_LEVEL MAX_MEM_LEVEL
-#endif
-/* default memLevel */
-
#define STORED_BLOCK 0
#define STATIC_TREES 1
#define DYN_TREES 2
#define AC97_HAS_NO_MIC (1<<15) /* no MIC volume */
#define AC97_HAS_NO_TONE (1<<16) /* no Tone volume */
#define AC97_HAS_NO_STD_PCM (1<<17) /* no standard AC97 PCM volume and mute */
+#define AC97_HAS_NO_AUX (1<<18) /* no standard AC97 AUX volume and mute */
/* rates indexes */
#define AC97_RATES_FRONT_DAC 0
#define SNDRV_SEQ_PORT_TYPE_DIRECT_SAMPLE (1<<11) /* Sampling device (support sample download) */
#define SNDRV_SEQ_PORT_TYPE_SAMPLE (1<<12) /* Sampling device (sample can be downloaded at any time) */
/*...*/
+#define SNDRV_SEQ_PORT_TYPE_HARDWARE (1<<16) /* driver for a hardware device */
+#define SNDRV_SEQ_PORT_TYPE_SOFTWARE (1<<17) /* implemented in software */
+#define SNDRV_SEQ_PORT_TYPE_SYNTHESIZER (1<<18) /* generates sound */
+#define SNDRV_SEQ_PORT_TYPE_PORT (1<<19) /* connects to other device(s) */
#define SNDRV_SEQ_PORT_TYPE_APPLICATION (1<<20) /* application (sequencer/editor) */
/* misc. conditioning flags */
* *
*****************************************************************************/
-#define SNDRV_PCM_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 7)
+#define SNDRV_PCM_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 8)
typedef unsigned long snd_pcm_uframes_t;
typedef signed long snd_pcm_sframes_t;
/* init.c */
-extern unsigned int snd_cards_lock;
extern struct snd_card *snd_cards[SNDRV_CARDS];
-extern rwlock_t snd_card_rwlock;
+int snd_card_locked(int card);
#if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE)
#define SND_MIXER_OSS_NOTIFY_REGISTER 0
#define SND_MIXER_OSS_NOTIFY_DISCONNECT 1
#define A_IOCFG_GPOUT0 0x0044 /* analog/digital */
#define A_IOCFG_DISABLE_ANALOG 0x0040 /* = 'enable' for Audigy2 (chiprev=4) */
#define A_IOCFG_ENABLE_DIGITAL 0x0004
+#define A_IOCFG_ENABLE_DIGITAL_AUDIGY4 0x0080
#define A_IOCFG_UNKNOWN_20 0x0020
#define A_IOCFG_DISABLE_AC97_FRONT 0x0080 /* turn off ac97 front -> front (10k2.1) */
#define A_IOCFG_GPOUT1 0x0002 /* IR? drive's internal bypass (?) */
unsigned char emu1212m; /* EMU 1212m card */
unsigned char spi_dac; /* SPI interface for DAC */
unsigned char i2c_adc; /* I2C interface for ADC */
+ unsigned char adc_1361t; /* Use Philips 1361T ADC */
const char *driver;
const char *name;
const char *id; /* for backward compatibility - can be NULL if not needed */
/* buffer for information */
struct snd_info_buffer {
char *buffer; /* pointer to begin of buffer */
- char *curr; /* current position in buffer */
- unsigned long size; /* current size */
- unsigned long len; /* total length of buffer */
+ unsigned int curr; /* current position in buffer */
+ unsigned int size; /* current size */
+ unsigned int len; /* total length of buffer */
int stop; /* stop flag */
int error; /* error code */
};
struct snd_info_entry;
struct snd_info_entry_text {
- unsigned long read_size;
- unsigned long write_size;
void (*read) (struct snd_info_entry *entry, struct snd_info_buffer *buffer);
void (*write) (struct snd_info_entry *entry, struct snd_info_buffer *buffer);
};
static inline void snd_info_set_text_ops(struct snd_info_entry *entry,
void *private_data,
- long read_size,
void (*read)(struct snd_info_entry *, struct snd_info_buffer *))
{
entry->private_data = private_data;
- entry->c.text.read_size = read_size;
entry->c.text.read = read;
}
struct snd_info_entry **entryp) { return -EINVAL; }
static inline void snd_info_set_text_ops(struct snd_info_entry *entry __attribute__((unused)),
void *private_data,
- long read_size,
void (*read)(struct snd_info_entry *, struct snd_info_buffer *)) {}
static inline int snd_info_check_reserved_words(const char *str) { return 1; }
#define MPU401_HW_PC98II 18 /* Roland PC98II */
#define MPU401_HW_AUREAL 19 /* Aureal Vortex */
+#define MPU401_INFO_INPUT (1 << 0) /* input stream */
+#define MPU401_INFO_OUTPUT (1 << 1) /* output stream */
+#define MPU401_INFO_INTEGRATED (1 << 2) /* integrated h/w port */
+#define MPU401_INFO_MMIO (1 << 3) /* MMIO access */
+#define MPU401_INFO_TX_IRQ (1 << 4) /* independent TX irq */
+
#define MPU401_MODE_BIT_INPUT 0
#define MPU401_MODE_BIT_OUTPUT 1
#define MPU401_MODE_BIT_INPUT_TRIGGER 2
struct snd_rawmidi *rmidi;
unsigned short hardware; /* MPU401_HW_XXXX */
+ unsigned int info_flags; /* MPU401_INFO_XXX */
unsigned long port; /* base port of MPU-401 chip */
unsigned long cport; /* port + 1 (usually) */
struct resource *res; /* port resource */
*/
-irqreturn_t snd_mpu401_uart_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+irqreturn_t snd_mpu401_uart_interrupt(int irq, void *dev_id,
+ struct pt_regs *regs);
+irqreturn_t snd_mpu401_uart_interrupt_tx(int irq, void *dev_id,
+ struct pt_regs *regs);
int snd_mpu401_uart_new(struct snd_card *card,
int device,
unsigned short hardware,
unsigned long port,
- int integrated,
+ unsigned int info_flags,
int irq,
int irq_flags,
struct snd_rawmidi ** rrawmidi);
/* -- mmap -- */
volatile struct snd_pcm_mmap_status *status;
volatile struct snd_pcm_mmap_control *control;
- atomic_t mmap_count;
/* -- locking / scheduling -- */
wait_queue_head_t sleep;
struct snd_pcm_group *group; /* pointer to current group */
/* -- assigned files -- */
void *file;
- struct file *ffile;
+ int ref_count;
+ atomic_t mmap_count;
+ unsigned int f_flags;
void (*pcm_release)(struct snd_pcm_substream *);
#if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE)
/* -- OSS things -- */
unsigned int hw_opened: 1;
};
-#define SUBSTREAM_BUSY(substream) ((substream)->file != NULL)
+#define SUBSTREAM_BUSY(substream) ((substream)->ref_count > 0)
struct snd_pcm_str {
void _snd_pcm_hw_params_any(struct snd_pcm_hw_params *params);
void _snd_pcm_hw_param_setempty(struct snd_pcm_hw_params *params, snd_pcm_hw_param_t var);
-int snd_pcm_hw_param_near(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params,
- snd_pcm_hw_param_t var,
- unsigned int val, int *dir);
-int snd_pcm_hw_param_set(struct snd_pcm_substream *pcm,
- struct snd_pcm_hw_params *params,
- snd_pcm_hw_param_t var,
- unsigned int val, int dir);
int snd_pcm_hw_params_choose(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params);
int snd_pcm_hw_refine(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params);
static inline void snd_pcm_mmap_data_open(struct vm_area_struct *area)
{
struct snd_pcm_substream *substream = (struct snd_pcm_substream *)area->vm_private_data;
- atomic_inc(&substream->runtime->mmap_count);
+ atomic_inc(&substream->mmap_count);
}
static inline void snd_pcm_mmap_data_close(struct vm_area_struct *area)
{
struct snd_pcm_substream *substream = (struct snd_pcm_substream *)area->vm_private_data;
- atomic_dec(&substream->runtime->mmap_count);
+ atomic_dec(&substream->mmap_count);
}
/* mmap for io-memory area */
*
*/
-extern int snd_pcm_hw_param_mask(struct snd_pcm_substream *pcm, struct snd_pcm_hw_params *params,
- snd_pcm_hw_param_t var, const struct snd_mask *val);
-extern unsigned int snd_pcm_hw_param_value_min(const struct snd_pcm_hw_params *params,
- snd_pcm_hw_param_t var, int *dir);
-extern unsigned int snd_pcm_hw_param_value_max(const struct snd_pcm_hw_params *params,
- snd_pcm_hw_param_t var, int *dir);
-extern int _snd_pcm_hw_param_min(struct snd_pcm_hw_params *params,
- snd_pcm_hw_param_t var, unsigned int val, int dir);
-extern int _snd_pcm_hw_param_setinteger(struct snd_pcm_hw_params *params,
- snd_pcm_hw_param_t var);
-extern int _snd_pcm_hw_param_set(struct snd_pcm_hw_params *params,
- snd_pcm_hw_param_t var, unsigned int val, int dir);
-
-/* To share the same code we have alsa-lib */
-#define INLINE static inline
-#define assert(a) (void)(a)
+int snd_pcm_hw_param_first(struct snd_pcm_substream *pcm,
+ struct snd_pcm_hw_params *params,
+ snd_pcm_hw_param_t var, int *dir);
+int snd_pcm_hw_param_last(struct snd_pcm_substream *pcm,
+ struct snd_pcm_hw_params *params,
+ snd_pcm_hw_param_t var, int *dir);
+int snd_pcm_hw_param_value(const struct snd_pcm_hw_params *params,
+ snd_pcm_hw_param_t var, int *dir);
#define SNDRV_MASK_BITS 64 /* we use so far 64bits only */
#define SNDRV_MASK_SIZE (SNDRV_MASK_BITS / 32)
#define MASK_OFS(i) ((i) >> 5)
#define MASK_BIT(i) (1U << ((i) & 31))
-INLINE unsigned int ld2(u_int32_t v)
+static inline unsigned int ld2(u_int32_t v)
{
unsigned r = 0;
return r;
}
-INLINE size_t snd_mask_sizeof(void)
+static inline size_t snd_mask_sizeof(void)
{
return sizeof(struct snd_mask);
}
-INLINE void snd_mask_none(struct snd_mask *mask)
+static inline void snd_mask_none(struct snd_mask *mask)
{
memset(mask, 0, sizeof(*mask));
}
-INLINE void snd_mask_any(struct snd_mask *mask)
+static inline void snd_mask_any(struct snd_mask *mask)
{
memset(mask, 0xff, SNDRV_MASK_SIZE * sizeof(u_int32_t));
}
-INLINE int snd_mask_empty(const struct snd_mask *mask)
+static inline int snd_mask_empty(const struct snd_mask *mask)
{
int i;
for (i = 0; i < SNDRV_MASK_SIZE; i++)
return 1;
}
-INLINE unsigned int snd_mask_min(const struct snd_mask *mask)
+static inline unsigned int snd_mask_min(const struct snd_mask *mask)
{
int i;
- assert(!snd_mask_empty(mask));
for (i = 0; i < SNDRV_MASK_SIZE; i++) {
if (mask->bits[i])
return ffs(mask->bits[i]) - 1 + (i << 5);
return 0;
}
-INLINE unsigned int snd_mask_max(const struct snd_mask *mask)
+static inline unsigned int snd_mask_max(const struct snd_mask *mask)
{
int i;
- assert(!snd_mask_empty(mask));
for (i = SNDRV_MASK_SIZE - 1; i >= 0; i--) {
if (mask->bits[i])
return ld2(mask->bits[i]) + (i << 5);
return 0;
}
-INLINE void snd_mask_set(struct snd_mask *mask, unsigned int val)
+static inline void snd_mask_set(struct snd_mask *mask, unsigned int val)
{
- assert(val <= SNDRV_MASK_BITS);
mask->bits[MASK_OFS(val)] |= MASK_BIT(val);
}
-INLINE void snd_mask_reset(struct snd_mask *mask, unsigned int val)
+static inline void snd_mask_reset(struct snd_mask *mask, unsigned int val)
{
- assert(val <= SNDRV_MASK_BITS);
mask->bits[MASK_OFS(val)] &= ~MASK_BIT(val);
}
-INLINE void snd_mask_set_range(struct snd_mask *mask, unsigned int from, unsigned int to)
+static inline void snd_mask_set_range(struct snd_mask *mask,
+ unsigned int from, unsigned int to)
{
unsigned int i;
- assert(to <= SNDRV_MASK_BITS && from <= to);
for (i = from; i <= to; i++)
mask->bits[MASK_OFS(i)] |= MASK_BIT(i);
}
-INLINE void snd_mask_reset_range(struct snd_mask *mask, unsigned int from, unsigned int to)
+static inline void snd_mask_reset_range(struct snd_mask *mask,
+ unsigned int from, unsigned int to)
{
unsigned int i;
- assert(to <= SNDRV_MASK_BITS && from <= to);
for (i = from; i <= to; i++)
mask->bits[MASK_OFS(i)] &= ~MASK_BIT(i);
}
-INLINE void snd_mask_leave(struct snd_mask *mask, unsigned int val)
+static inline void snd_mask_leave(struct snd_mask *mask, unsigned int val)
{
unsigned int v;
- assert(val <= SNDRV_MASK_BITS);
v = mask->bits[MASK_OFS(val)] & MASK_BIT(val);
snd_mask_none(mask);
mask->bits[MASK_OFS(val)] = v;
}
-INLINE void snd_mask_intersect(struct snd_mask *mask, const struct snd_mask *v)
+static inline void snd_mask_intersect(struct snd_mask *mask,
+ const struct snd_mask *v)
{
int i;
for (i = 0; i < SNDRV_MASK_SIZE; i++)
mask->bits[i] &= v->bits[i];
}
-INLINE int snd_mask_eq(const struct snd_mask *mask, const struct snd_mask *v)
+static inline int snd_mask_eq(const struct snd_mask *mask,
+ const struct snd_mask *v)
{
return ! memcmp(mask, v, SNDRV_MASK_SIZE * sizeof(u_int32_t));
}
-INLINE void snd_mask_copy(struct snd_mask *mask, const struct snd_mask *v)
+static inline void snd_mask_copy(struct snd_mask *mask,
+ const struct snd_mask *v)
{
*mask = *v;
}
-INLINE int snd_mask_test(const struct snd_mask *mask, unsigned int val)
+static inline int snd_mask_test(const struct snd_mask *mask, unsigned int val)
{
- assert(val <= SNDRV_MASK_BITS);
return mask->bits[MASK_OFS(val)] & MASK_BIT(val);
}
-INLINE int snd_mask_single(const struct snd_mask *mask)
+static inline int snd_mask_single(const struct snd_mask *mask)
{
int i, c = 0;
- assert(!snd_mask_empty(mask));
for (i = 0; i < SNDRV_MASK_SIZE; i++) {
if (! mask->bits[i])
continue;
return 1;
}
-INLINE int snd_mask_refine(struct snd_mask *mask, const struct snd_mask *v)
+static inline int snd_mask_refine(struct snd_mask *mask,
+ const struct snd_mask *v)
{
struct snd_mask old;
- assert(!snd_mask_empty(mask));
snd_mask_copy(&old, mask);
snd_mask_intersect(mask, v);
if (snd_mask_empty(mask))
return !snd_mask_eq(mask, &old);
}
-INLINE int snd_mask_refine_first(struct snd_mask *mask)
+static inline int snd_mask_refine_first(struct snd_mask *mask)
{
- assert(!snd_mask_empty(mask));
if (snd_mask_single(mask))
return 0;
snd_mask_leave(mask, snd_mask_min(mask));
return 1;
}
-INLINE int snd_mask_refine_last(struct snd_mask *mask)
+static inline int snd_mask_refine_last(struct snd_mask *mask)
{
- assert(!snd_mask_empty(mask));
if (snd_mask_single(mask))
return 0;
snd_mask_leave(mask, snd_mask_max(mask));
return 1;
}
-INLINE int snd_mask_refine_min(struct snd_mask *mask, unsigned int val)
+static inline int snd_mask_refine_min(struct snd_mask *mask, unsigned int val)
{
- assert(!snd_mask_empty(mask));
if (snd_mask_min(mask) >= val)
return 0;
snd_mask_reset_range(mask, 0, val - 1);
return 1;
}
-INLINE int snd_mask_refine_max(struct snd_mask *mask, unsigned int val)
+static inline int snd_mask_refine_max(struct snd_mask *mask, unsigned int val)
{
- assert(!snd_mask_empty(mask));
if (snd_mask_max(mask) <= val)
return 0;
snd_mask_reset_range(mask, val + 1, SNDRV_MASK_BITS);
return 1;
}
-INLINE int snd_mask_refine_set(struct snd_mask *mask, unsigned int val)
+static inline int snd_mask_refine_set(struct snd_mask *mask, unsigned int val)
{
int changed;
- assert(!snd_mask_empty(mask));
changed = !snd_mask_single(mask);
snd_mask_leave(mask, val);
if (snd_mask_empty(mask))
return changed;
}
-INLINE int snd_mask_value(const struct snd_mask *mask)
+static inline int snd_mask_value(const struct snd_mask *mask)
{
- assert(!snd_mask_empty(mask));
return snd_mask_min(mask);
}
-INLINE void snd_interval_any(struct snd_interval *i)
+static inline void snd_interval_any(struct snd_interval *i)
{
i->min = 0;
i->openmin = 0;
i->empty = 0;
}
-INLINE void snd_interval_none(struct snd_interval *i)
+static inline void snd_interval_none(struct snd_interval *i)
{
i->empty = 1;
}
-INLINE int snd_interval_checkempty(const struct snd_interval *i)
+static inline int snd_interval_checkempty(const struct snd_interval *i)
{
return (i->min > i->max ||
(i->min == i->max && (i->openmin || i->openmax)));
}
-INLINE int snd_interval_empty(const struct snd_interval *i)
+static inline int snd_interval_empty(const struct snd_interval *i)
{
return i->empty;
}
-INLINE int snd_interval_single(const struct snd_interval *i)
+static inline int snd_interval_single(const struct snd_interval *i)
{
- assert(!snd_interval_empty(i));
return (i->min == i->max ||
(i->min + 1 == i->max && i->openmax));
}
-INLINE int snd_interval_value(const struct snd_interval *i)
+static inline int snd_interval_value(const struct snd_interval *i)
{
- assert(snd_interval_single(i));
return i->min;
}
-INLINE int snd_interval_min(const struct snd_interval *i)
+static inline int snd_interval_min(const struct snd_interval *i)
{
- assert(!snd_interval_empty(i));
return i->min;
}
-INLINE int snd_interval_max(const struct snd_interval *i)
+static inline int snd_interval_max(const struct snd_interval *i)
{
unsigned int v;
- assert(!snd_interval_empty(i));
v = i->max;
if (i->openmax)
v--;
return v;
}
-INLINE int snd_interval_test(const struct snd_interval *i, unsigned int val)
+static inline int snd_interval_test(const struct snd_interval *i, unsigned int val)
{
return !((i->min > val || (i->min == val && i->openmin) ||
i->max < val || (i->max == val && i->openmax)));
}
-INLINE void snd_interval_copy(struct snd_interval *d, const struct snd_interval *s)
+static inline void snd_interval_copy(struct snd_interval *d, const struct snd_interval *s)
{
*d = *s;
}
-INLINE int snd_interval_setinteger(struct snd_interval *i)
+static inline int snd_interval_setinteger(struct snd_interval *i)
{
if (i->integer)
return 0;
return 1;
}
-INLINE int snd_interval_eq(const struct snd_interval *i1, const struct snd_interval *i2)
+static inline int snd_interval_eq(const struct snd_interval *i1, const struct snd_interval *i2)
{
if (i1->empty)
return i2->empty;
return 0;
}
-#undef INLINE
-#undef assert
-
#endif /* __SOUND_PCM_PARAMS_H */
struct snd_rawmidi;
struct snd_rawmidi_substream;
+struct snd_seq_port_info;
struct snd_rawmidi_ops {
int (*open) (struct snd_rawmidi_substream * substream);
struct snd_rawmidi_global_ops {
int (*dev_register) (struct snd_rawmidi * rmidi);
int (*dev_unregister) (struct snd_rawmidi * rmidi);
+ void (*get_port_info)(struct snd_rawmidi *rmidi, int number,
+ struct snd_seq_port_info *info);
};
struct snd_rawmidi_runtime {
/* include/version.h. Generated by configure. */
-#define CONFIG_SND_VERSION "1.0.11rc4"
-#define CONFIG_SND_DATE " (Wed Mar 22 10:27:24 2006 UTC)"
+#define CONFIG_SND_VERSION "1.0.12rc1"
+#define CONFIG_SND_DATE " (Thu Jun 22 13:55:50 2006 UTC)"
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/pm.h>
-
+#include <linux/console.h>
#include "power.h"
* fields when reaping, so a sample either gets all the additions of a
* given child after it's reaped, or none so this sample is before reaping.
*
- * tasklist_lock locking optimisation:
- * If we are current and single threaded, we do not need to take the tasklist
- * lock or the siglock. No one else can take our signal_struct away,
- * no one else can reap the children to update signal->c* counters, and
- * no one else can race with the signal-> fields.
- * If we do not take the tasklist_lock, the signal-> fields could be read
- * out of order while another thread was just exiting. So we place a
- * read memory barrier when we avoid the lock. On the writer side,
- * write memory barrier is implied in __exit_signal as __exit_signal releases
- * the siglock spinlock after updating the signal-> fields.
- *
- * We don't really need the siglock when we access the non c* fields
- * of the signal_struct (for RUSAGE_SELF) even in multithreaded
- * case, since we take the tasklist lock for read and the non c* signal->
- * fields are updated only in __exit_signal, which is called with
- * tasklist_lock taken for write, hence these two threads cannot execute
- * concurrently.
+ * Locking:
+ * We need to take the siglock for CHILDEREN, SELF and BOTH
+ * for the cases current multithreaded, non-current single threaded
+ * non-current multithreaded. Thread traversal is now safe with
+ * the siglock held.
+ * Strictly speaking, we donot need to take the siglock if we are current and
+ * single threaded, as no one else can take our signal_struct away, no one
+ * else can reap the children to update signal->c* counters, and no one else
+ * can race with the signal-> fields. If we do not take any lock, the
+ * signal-> fields could be read out of order while another thread was just
+ * exiting. So we should place a read memory barrier when we avoid the lock.
+ * On the writer side, write memory barrier is implied in __exit_signal
+ * as __exit_signal releases the siglock spinlock after updating the signal->
+ * fields. But we don't do this yet to keep things simple.
*
*/
struct task_struct *t;
unsigned long flags;
cputime_t utime, stime;
- int need_lock = 0;
memset((char *) r, 0, sizeof *r);
utime = stime = cputime_zero;
- if (p != current || !thread_group_empty(p))
- need_lock = 1;
-
- if (need_lock) {
- read_lock(&tasklist_lock);
- if (unlikely(!p->signal)) {
- read_unlock(&tasklist_lock);
- return;
- }
- } else
- /* See locking comments above */
- smp_rmb();
+ rcu_read_lock();
+ if (!lock_task_sighand(p, &flags)) {
+ rcu_read_unlock();
+ return;
+ }
switch (who) {
case RUSAGE_BOTH:
case RUSAGE_CHILDREN:
- spin_lock_irqsave(&p->sighand->siglock, flags);
utime = p->signal->cutime;
stime = p->signal->cstime;
r->ru_nvcsw = p->signal->cnvcsw;
r->ru_nivcsw = p->signal->cnivcsw;
r->ru_minflt = p->signal->cmin_flt;
r->ru_majflt = p->signal->cmaj_flt;
- spin_unlock_irqrestore(&p->sighand->siglock, flags);
if (who == RUSAGE_CHILDREN)
break;
BUG();
}
- if (need_lock)
- read_unlock(&tasklist_lock);
+ unlock_task_sighand(p, &flags);
+ rcu_read_unlock();
+
cputime_to_timeval(utime, &r->ru_utime);
cputime_to_timeval(stime, &r->ru_stime);
}
new->mq_bytes = 0;
new->locked_shm = 0;
- if (alloc_uid_keyring(new) < 0) {
+ if (alloc_uid_keyring(new, current) < 0) {
kmem_cache_free(uid_cachep, new);
return NULL;
}
/* be noisy on error issues */
if (error == -EEXIST)
- pr_debug("kobject_add failed for %s with -EEXIST, "
+ printk("kobject_add failed for %s with -EEXIST, "
"don't try to register things with the "
"same name in the same directory.\n",
kobject_name(kobj));
else
- pr_debug("kobject_add failed for %s (%d)\n",
+ printk("kobject_add failed for %s (%d)\n",
kobject_name(kobj), error);
- /* dump_stack(); */
+ dump_stack();
}
return error;
memset((char *)s->head, 0, (unsigned)(s->hash_size-1)*sizeof(*s->head));
/* ========================================================================= */
-int zlib_deflateInit_(
- z_streamp strm,
- int level,
- const char *version,
- int stream_size
-)
-{
- return zlib_deflateInit2_(strm, level, Z_DEFLATED, MAX_WBITS,
- DEF_MEM_LEVEL,
- Z_DEFAULT_STRATEGY, version, stream_size);
- /* To do: ignore strm->next_in if we use it as window */
-}
-
-/* ========================================================================= */
-int zlib_deflateInit2_(
+int zlib_deflateInit2(
z_streamp strm,
int level,
int method,
int windowBits,
int memLevel,
- int strategy,
- const char *version,
- int stream_size
+ int strategy
)
{
deflate_state *s;
int noheader = 0;
- static char* my_version = ZLIB_VERSION;
deflate_workspace *mem;
ush *overlay;
* output size for (length,distance) codes is <= 24 bits.
*/
- if (version == NULL || version[0] != my_version[0] ||
- stream_size != sizeof(z_stream)) {
- return Z_VERSION_ERROR;
- }
if (strm == NULL) return Z_STREAM_ERROR;
strm->msg = NULL;
EXPORT_SYMBOL(zlib_deflate_workspacesize);
EXPORT_SYMBOL(zlib_deflate);
-EXPORT_SYMBOL(zlib_deflateInit_);
-EXPORT_SYMBOL(zlib_deflateInit2_);
+EXPORT_SYMBOL(zlib_deflateInit2);
EXPORT_SYMBOL(zlib_deflateEnd);
EXPORT_SYMBOL(zlib_deflateReset);
MODULE_LICENSE("GPL");
obj-$(CONFIG_ZLIB_INFLATE) += zlib_inflate.o
-zlib_inflate-objs := infblock.o infcodes.o inffast.o inflate.o \
- inflate_sync.o inftrees.o infutil.o inflate_syms.o
+zlib_inflate-objs := inffast.o inflate.o \
+ inftrees.o inflate_syms.o
+++ /dev/null
-/* infblock.c -- interpret and process block types to last block
- * Copyright (C) 1995-1998 Mark Adler
- * For conditions of distribution and use, see copyright notice in zlib.h
- */
-
-#include <linux/zutil.h>
-#include "infblock.h"
-#include "inftrees.h"
-#include "infcodes.h"
-#include "infutil.h"
-
-struct inflate_codes_state;
-
-/* simplify the use of the inflate_huft type with some defines */
-#define exop word.what.Exop
-#define bits word.what.Bits
-
-/* Table for deflate from PKZIP's appnote.txt. */
-static const uInt border[] = { /* Order of the bit length code lengths */
- 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15};
-
-/*
- Notes beyond the 1.93a appnote.txt:
-
- 1. Distance pointers never point before the beginning of the output
- stream.
- 2. Distance pointers can point back across blocks, up to 32k away.
- 3. There is an implied maximum of 7 bits for the bit length table and
- 15 bits for the actual data.
- 4. If only one code exists, then it is encoded using one bit. (Zero
- would be more efficient, but perhaps a little confusing.) If two
- codes exist, they are coded using one bit each (0 and 1).
- 5. There is no way of sending zero distance codes--a dummy must be
- sent if there are none. (History: a pre 2.0 version of PKZIP would
- store blocks with no distance codes, but this was discovered to be
- too harsh a criterion.) Valid only for 1.93a. 2.04c does allow
- zero distance codes, which is sent as one code of zero bits in
- length.
- 6. There are up to 286 literal/length codes. Code 256 represents the
- end-of-block. Note however that the static length tree defines
- 288 codes just to fill out the Huffman codes. Codes 286 and 287
- cannot be used though, since there is no length base or extra bits
- defined for them. Similarily, there are up to 30 distance codes.
- However, static trees define 32 codes (all 5 bits) to fill out the
- Huffman codes, but the last two had better not show up in the data.
- 7. Unzip can check dynamic Huffman blocks for complete code sets.
- The exception is that a single code would not be complete (see #4).
- 8. The five bits following the block type is really the number of
- literal codes sent minus 257.
- 9. Length codes 8,16,16 are interpreted as 13 length codes of 8 bits
- (1+6+6). Therefore, to output three times the length, you output
- three codes (1+1+1), whereas to output four times the same length,
- you only need two codes (1+3). Hmm.
- 10. In the tree reconstruction algorithm, Code = Code + Increment
- only if BitLength(i) is not zero. (Pretty obvious.)
- 11. Correction: 4 Bits: # of Bit Length codes - 4 (4 - 19)
- 12. Note: length code 284 can represent 227-258, but length code 285
- really is 258. The last length deserves its own, short code
- since it gets used a lot in very redundant files. The length
- 258 is special since 258 - 3 (the min match length) is 255.
- 13. The literal/length and distance code bit lengths are read as a
- single stream of lengths. It is possible (and advantageous) for
- a repeat code (16, 17, or 18) to go across the boundary between
- the two sets of lengths.
- */
-
-
-void zlib_inflate_blocks_reset(
- inflate_blocks_statef *s,
- z_streamp z,
- uLong *c
-)
-{
- if (c != NULL)
- *c = s->check;
- if (s->mode == CODES)
- zlib_inflate_codes_free(s->sub.decode.codes, z);
- s->mode = TYPE;
- s->bitk = 0;
- s->bitb = 0;
- s->read = s->write = s->window;
- if (s->checkfn != NULL)
- z->adler = s->check = (*s->checkfn)(0L, NULL, 0);
-}
-
-inflate_blocks_statef *zlib_inflate_blocks_new(
- z_streamp z,
- check_func c,
- uInt w
-)
-{
- inflate_blocks_statef *s;
-
- s = &WS(z)->working_blocks_state;
- s->hufts = WS(z)->working_hufts;
- s->window = WS(z)->working_window;
- s->end = s->window + w;
- s->checkfn = c;
- s->mode = TYPE;
- zlib_inflate_blocks_reset(s, z, NULL);
- return s;
-}
-
-
-int zlib_inflate_blocks(
- inflate_blocks_statef *s,
- z_streamp z,
- int r
-)
-{
- uInt t; /* temporary storage */
- uLong b; /* bit buffer */
- uInt k; /* bits in bit buffer */
- Byte *p; /* input data pointer */
- uInt n; /* bytes available there */
- Byte *q; /* output window write pointer */
- uInt m; /* bytes to end of window or read pointer */
-
- /* copy input/output information to locals (UPDATE macro restores) */
- LOAD
-
- /* process input based on current state */
- while (1) switch (s->mode)
- {
- case TYPE:
- NEEDBITS(3)
- t = (uInt)b & 7;
- s->last = t & 1;
- switch (t >> 1)
- {
- case 0: /* stored */
- DUMPBITS(3)
- t = k & 7; /* go to byte boundary */
- DUMPBITS(t)
- s->mode = LENS; /* get length of stored block */
- break;
- case 1: /* fixed */
- {
- uInt bl, bd;
- inflate_huft *tl, *td;
-
- zlib_inflate_trees_fixed(&bl, &bd, &tl, &td, s->hufts, z);
- s->sub.decode.codes = zlib_inflate_codes_new(bl, bd, tl, td, z);
- if (s->sub.decode.codes == NULL)
- {
- r = Z_MEM_ERROR;
- LEAVE
- }
- }
- DUMPBITS(3)
- s->mode = CODES;
- break;
- case 2: /* dynamic */
- DUMPBITS(3)
- s->mode = TABLE;
- break;
- case 3: /* illegal */
- DUMPBITS(3)
- s->mode = B_BAD;
- z->msg = (char*)"invalid block type";
- r = Z_DATA_ERROR;
- LEAVE
- }
- break;
- case LENS:
- NEEDBITS(32)
- if ((((~b) >> 16) & 0xffff) != (b & 0xffff))
- {
- s->mode = B_BAD;
- z->msg = (char*)"invalid stored block lengths";
- r = Z_DATA_ERROR;
- LEAVE
- }
- s->sub.left = (uInt)b & 0xffff;
- b = k = 0; /* dump bits */
- s->mode = s->sub.left ? STORED : (s->last ? DRY : TYPE);
- break;
- case STORED:
- if (n == 0)
- LEAVE
- NEEDOUT
- t = s->sub.left;
- if (t > n) t = n;
- if (t > m) t = m;
- memcpy(q, p, t);
- p += t; n -= t;
- q += t; m -= t;
- if ((s->sub.left -= t) != 0)
- break;
- s->mode = s->last ? DRY : TYPE;
- break;
- case TABLE:
- NEEDBITS(14)
- s->sub.trees.table = t = (uInt)b & 0x3fff;
-#ifndef PKZIP_BUG_WORKAROUND
- if ((t & 0x1f) > 29 || ((t >> 5) & 0x1f) > 29)
- {
- s->mode = B_BAD;
- z->msg = (char*)"too many length or distance symbols";
- r = Z_DATA_ERROR;
- LEAVE
- }
-#endif
- {
- s->sub.trees.blens = WS(z)->working_blens;
- }
- DUMPBITS(14)
- s->sub.trees.index = 0;
- s->mode = BTREE;
- case BTREE:
- while (s->sub.trees.index < 4 + (s->sub.trees.table >> 10))
- {
- NEEDBITS(3)
- s->sub.trees.blens[border[s->sub.trees.index++]] = (uInt)b & 7;
- DUMPBITS(3)
- }
- while (s->sub.trees.index < 19)
- s->sub.trees.blens[border[s->sub.trees.index++]] = 0;
- s->sub.trees.bb = 7;
- t = zlib_inflate_trees_bits(s->sub.trees.blens, &s->sub.trees.bb,
- &s->sub.trees.tb, s->hufts, z);
- if (t != Z_OK)
- {
- r = t;
- if (r == Z_DATA_ERROR)
- s->mode = B_BAD;
- LEAVE
- }
- s->sub.trees.index = 0;
- s->mode = DTREE;
- case DTREE:
- while (t = s->sub.trees.table,
- s->sub.trees.index < 258 + (t & 0x1f) + ((t >> 5) & 0x1f))
- {
- inflate_huft *h;
- uInt i, j, c;
-
- t = s->sub.trees.bb;
- NEEDBITS(t)
- h = s->sub.trees.tb + ((uInt)b & zlib_inflate_mask[t]);
- t = h->bits;
- c = h->base;
- if (c < 16)
- {
- DUMPBITS(t)
- s->sub.trees.blens[s->sub.trees.index++] = c;
- }
- else /* c == 16..18 */
- {
- i = c == 18 ? 7 : c - 14;
- j = c == 18 ? 11 : 3;
- NEEDBITS(t + i)
- DUMPBITS(t)
- j += (uInt)b & zlib_inflate_mask[i];
- DUMPBITS(i)
- i = s->sub.trees.index;
- t = s->sub.trees.table;
- if (i + j > 258 + (t & 0x1f) + ((t >> 5) & 0x1f) ||
- (c == 16 && i < 1))
- {
- s->mode = B_BAD;
- z->msg = (char*)"invalid bit length repeat";
- r = Z_DATA_ERROR;
- LEAVE
- }
- c = c == 16 ? s->sub.trees.blens[i - 1] : 0;
- do {
- s->sub.trees.blens[i++] = c;
- } while (--j);
- s->sub.trees.index = i;
- }
- }
- s->sub.trees.tb = NULL;
- {
- uInt bl, bd;
- inflate_huft *tl, *td;
- inflate_codes_statef *c;
-
- bl = 9; /* must be <= 9 for lookahead assumptions */
- bd = 6; /* must be <= 9 for lookahead assumptions */
- t = s->sub.trees.table;
- t = zlib_inflate_trees_dynamic(257 + (t & 0x1f), 1 + ((t >> 5) & 0x1f),
- s->sub.trees.blens, &bl, &bd, &tl, &td,
- s->hufts, z);
- if (t != Z_OK)
- {
- if (t == (uInt)Z_DATA_ERROR)
- s->mode = B_BAD;
- r = t;
- LEAVE
- }
- if ((c = zlib_inflate_codes_new(bl, bd, tl, td, z)) == NULL)
- {
- r = Z_MEM_ERROR;
- LEAVE
- }
- s->sub.decode.codes = c;
- }
- s->mode = CODES;
- case CODES:
- UPDATE
- if ((r = zlib_inflate_codes(s, z, r)) != Z_STREAM_END)
- return zlib_inflate_flush(s, z, r);
- r = Z_OK;
- zlib_inflate_codes_free(s->sub.decode.codes, z);
- LOAD
- if (!s->last)
- {
- s->mode = TYPE;
- break;
- }
- s->mode = DRY;
- case DRY:
- FLUSH
- if (s->read != s->write)
- LEAVE
- s->mode = B_DONE;
- case B_DONE:
- r = Z_STREAM_END;
- LEAVE
- case B_BAD:
- r = Z_DATA_ERROR;
- LEAVE
- default:
- r = Z_STREAM_ERROR;
- LEAVE
- }
-}
-
-
-int zlib_inflate_blocks_free(
- inflate_blocks_statef *s,
- z_streamp z
-)
-{
- zlib_inflate_blocks_reset(s, z, NULL);
- return Z_OK;
-}
-
-
-#if 0
-void zlib_inflate_set_dictionary(
- inflate_blocks_statef *s,
- const Byte *d,
- uInt n
-)
-{
- memcpy(s->window, d, n);
- s->read = s->write = s->window + n;
-}
-#endif /* 0 */
-
-
-/* Returns true if inflate is currently at the end of a block generated
- * by Z_SYNC_FLUSH or Z_FULL_FLUSH.
- * IN assertion: s != NULL
- */
-#if 0
-int zlib_inflate_blocks_sync_point(
- inflate_blocks_statef *s
-)
-{
- return s->mode == LENS;
-}
-#endif /* 0 */
+++ /dev/null
-/* infblock.h -- header to use infblock.c
- * Copyright (C) 1995-1998 Mark Adler
- * For conditions of distribution and use, see copyright notice in zlib.h
- */
-
-/* WARNING: this file should *not* be used by applications. It is
- part of the implementation of the compression library and is
- subject to change. Applications should only use zlib.h.
- */
-
-#ifndef _INFBLOCK_H
-#define _INFBLOCK_H
-
-struct inflate_blocks_state;
-typedef struct inflate_blocks_state inflate_blocks_statef;
-
-extern inflate_blocks_statef * zlib_inflate_blocks_new (
- z_streamp z,
- check_func c, /* check function */
- uInt w); /* window size */
-
-extern int zlib_inflate_blocks (
- inflate_blocks_statef *,
- z_streamp ,
- int); /* initial return code */
-
-extern void zlib_inflate_blocks_reset (
- inflate_blocks_statef *,
- z_streamp ,
- uLong *); /* check value on output */
-
-extern int zlib_inflate_blocks_free (
- inflate_blocks_statef *,
- z_streamp);
-
-#if 0
-extern void zlib_inflate_set_dictionary (
- inflate_blocks_statef *s,
- const Byte *d, /* dictionary */
- uInt n); /* dictionary length */
-#endif /* 0 */
-
-#if 0
-extern int zlib_inflate_blocks_sync_point (
- inflate_blocks_statef *s);
-#endif /* 0 */
-
-#endif /* _INFBLOCK_H */
+++ /dev/null
-/* infcodes.c -- process literals and length/distance pairs
- * Copyright (C) 1995-1998 Mark Adler
- * For conditions of distribution and use, see copyright notice in zlib.h
- */
-
-#include <linux/zutil.h>
-#include "inftrees.h"
-#include "infblock.h"
-#include "infcodes.h"
-#include "infutil.h"
-#include "inffast.h"
-
-/* simplify the use of the inflate_huft type with some defines */
-#define exop word.what.Exop
-#define bits word.what.Bits
-
-inflate_codes_statef *zlib_inflate_codes_new(
- uInt bl,
- uInt bd,
- inflate_huft *tl,
- inflate_huft *td, /* need separate declaration for Borland C++ */
- z_streamp z
-)
-{
- inflate_codes_statef *c;
-
- c = &WS(z)->working_state;
- {
- c->mode = START;
- c->lbits = (Byte)bl;
- c->dbits = (Byte)bd;
- c->ltree = tl;
- c->dtree = td;
- }
- return c;
-}
-
-
-int zlib_inflate_codes(
- inflate_blocks_statef *s,
- z_streamp z,
- int r
-)
-{
- uInt j; /* temporary storage */
- inflate_huft *t; /* temporary pointer */
- uInt e; /* extra bits or operation */
- uLong b; /* bit buffer */
- uInt k; /* bits in bit buffer */
- Byte *p; /* input data pointer */
- uInt n; /* bytes available there */
- Byte *q; /* output window write pointer */
- uInt m; /* bytes to end of window or read pointer */
- Byte *f; /* pointer to copy strings from */
- inflate_codes_statef *c = s->sub.decode.codes; /* codes state */
-
- /* copy input/output information to locals (UPDATE macro restores) */
- LOAD
-
- /* process input and output based on current state */
- while (1) switch (c->mode)
- { /* waiting for "i:"=input, "o:"=output, "x:"=nothing */
- case START: /* x: set up for LEN */
-#ifndef SLOW
- if (m >= 258 && n >= 10)
- {
- UPDATE
- r = zlib_inflate_fast(c->lbits, c->dbits, c->ltree, c->dtree, s, z);
- LOAD
- if (r != Z_OK)
- {
- c->mode = r == Z_STREAM_END ? WASH : BADCODE;
- break;
- }
- }
-#endif /* !SLOW */
- c->sub.code.need = c->lbits;
- c->sub.code.tree = c->ltree;
- c->mode = LEN;
- case LEN: /* i: get length/literal/eob next */
- j = c->sub.code.need;
- NEEDBITS(j)
- t = c->sub.code.tree + ((uInt)b & zlib_inflate_mask[j]);
- DUMPBITS(t->bits)
- e = (uInt)(t->exop);
- if (e == 0) /* literal */
- {
- c->sub.lit = t->base;
- c->mode = LIT;
- break;
- }
- if (e & 16) /* length */
- {
- c->sub.copy.get = e & 15;
- c->len = t->base;
- c->mode = LENEXT;
- break;
- }
- if ((e & 64) == 0) /* next table */
- {
- c->sub.code.need = e;
- c->sub.code.tree = t + t->base;
- break;
- }
- if (e & 32) /* end of block */
- {
- c->mode = WASH;
- break;
- }
- c->mode = BADCODE; /* invalid code */
- z->msg = (char*)"invalid literal/length code";
- r = Z_DATA_ERROR;
- LEAVE
- case LENEXT: /* i: getting length extra (have base) */
- j = c->sub.copy.get;
- NEEDBITS(j)
- c->len += (uInt)b & zlib_inflate_mask[j];
- DUMPBITS(j)
- c->sub.code.need = c->dbits;
- c->sub.code.tree = c->dtree;
- c->mode = DIST;
- case DIST: /* i: get distance next */
- j = c->sub.code.need;
- NEEDBITS(j)
- t = c->sub.code.tree + ((uInt)b & zlib_inflate_mask[j]);
- DUMPBITS(t->bits)
- e = (uInt)(t->exop);
- if (e & 16) /* distance */
- {
- c->sub.copy.get = e & 15;
- c->sub.copy.dist = t->base;
- c->mode = DISTEXT;
- break;
- }
- if ((e & 64) == 0) /* next table */
- {
- c->sub.code.need = e;
- c->sub.code.tree = t + t->base;
- break;
- }
- c->mode = BADCODE; /* invalid code */
- z->msg = (char*)"invalid distance code";
- r = Z_DATA_ERROR;
- LEAVE
- case DISTEXT: /* i: getting distance extra */
- j = c->sub.copy.get;
- NEEDBITS(j)
- c->sub.copy.dist += (uInt)b & zlib_inflate_mask[j];
- DUMPBITS(j)
- c->mode = COPY;
- case COPY: /* o: copying bytes in window, waiting for space */
- f = q - c->sub.copy.dist;
- while (f < s->window) /* modulo window size-"while" instead */
- f += s->end - s->window; /* of "if" handles invalid distances */
- while (c->len)
- {
- NEEDOUT
- OUTBYTE(*f++)
- if (f == s->end)
- f = s->window;
- c->len--;
- }
- c->mode = START;
- break;
- case LIT: /* o: got literal, waiting for output space */
- NEEDOUT
- OUTBYTE(c->sub.lit)
- c->mode = START;
- break;
- case WASH: /* o: got eob, possibly more output */
- if (k > 7) /* return unused byte, if any */
- {
- k -= 8;
- n++;
- p--; /* can always return one */
- }
- FLUSH
- if (s->read != s->write)
- LEAVE
- c->mode = END;
- case END:
- r = Z_STREAM_END;
- LEAVE
- case BADCODE: /* x: got error */
- r = Z_DATA_ERROR;
- LEAVE
- default:
- r = Z_STREAM_ERROR;
- LEAVE
- }
-#ifdef NEED_DUMMY_RETURN
- return Z_STREAM_ERROR; /* Some dumb compilers complain without this */
-#endif
-}
-
-
-void zlib_inflate_codes_free(
- inflate_codes_statef *c,
- z_streamp z
-)
-{
-}
+++ /dev/null
-/* infcodes.h -- header to use infcodes.c
- * Copyright (C) 1995-1998 Mark Adler
- * For conditions of distribution and use, see copyright notice in zlib.h
- */
-
-/* WARNING: this file should *not* be used by applications. It is
- part of the implementation of the compression library and is
- subject to change. Applications should only use zlib.h.
- */
-
-#ifndef _INFCODES_H
-#define _INFCODES_H
-
-#include "infblock.h"
-
-struct inflate_codes_state;
-typedef struct inflate_codes_state inflate_codes_statef;
-
-extern inflate_codes_statef *zlib_inflate_codes_new (
- uInt, uInt,
- inflate_huft *, inflate_huft *,
- z_streamp );
-
-extern int zlib_inflate_codes (
- inflate_blocks_statef *,
- z_streamp ,
- int);
-
-extern void zlib_inflate_codes_free (
- inflate_codes_statef *,
- z_streamp );
-
-#endif /* _INFCODES_H */
-/* inffast.c -- process literals and length/distance pairs fast
- * Copyright (C) 1995-1998 Mark Adler
- * For conditions of distribution and use, see copyright notice in zlib.h
+/* inffast.c -- fast decoding
+ * Copyright (C) 1995-2004 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
*/
#include <linux/zutil.h>
#include "inftrees.h"
-#include "infblock.h"
-#include "infcodes.h"
-#include "infutil.h"
+#include "inflate.h"
#include "inffast.h"
-struct inflate_codes_state;
-
-/* simplify the use of the inflate_huft type with some defines */
-#define exop word.what.Exop
-#define bits word.what.Bits
-
-/* macros for bit input with no checking and for returning unused bytes */
-#define GRABBITS(j) {while(k<(j)){b|=((uLong)NEXTBYTE)<<k;k+=8;}}
-#define UNGRAB {c=z->avail_in-n;c=(k>>3)<c?k>>3:c;n+=c;p-=c;k-=c<<3;}
-
-/* Called with number of bytes left to write in window at least 258
- (the maximum string length) and number of input bytes available
- at least ten. The ten bytes are six bytes for the longest length/
- distance pair plus four bytes for overloading the bit buffer. */
-
-int zlib_inflate_fast(
- uInt bl,
- uInt bd,
- inflate_huft *tl,
- inflate_huft *td, /* need separate declaration for Borland C++ */
- inflate_blocks_statef *s,
- z_streamp z
-)
+#ifndef ASMINF
+
+/* Allow machine dependent optimization for post-increment or pre-increment.
+ Based on testing to date,
+ Pre-increment preferred for:
+ - PowerPC G3 (Adler)
+ - MIPS R5000 (Randers-Pehrson)
+ Post-increment preferred for:
+ - none
+ No measurable difference:
+ - Pentium III (Anderson)
+ - M68060 (Nikl)
+ */
+#ifdef POSTINC
+# define OFF 0
+# define PUP(a) *(a)++
+#else
+# define OFF 1
+# define PUP(a) *++(a)
+#endif
+
+/*
+ Decode literal, length, and distance codes and write out the resulting
+ literal and match bytes until either not enough input or output is
+ available, an end-of-block is encountered, or a data error is encountered.
+ When large enough input and output buffers are supplied to inflate(), for
+ example, a 16K input buffer and a 64K output buffer, more than 95% of the
+ inflate execution time is spent in this routine.
+
+ Entry assumptions:
+
+ state->mode == LEN
+ strm->avail_in >= 6
+ strm->avail_out >= 258
+ start >= strm->avail_out
+ state->bits < 8
+
+ On return, state->mode is one of:
+
+ LEN -- ran out of enough output space or enough available input
+ TYPE -- reached end of block code, inflate() to interpret next block
+ BAD -- error in block data
+
+ Notes:
+
+ - The maximum input bits used by a length/distance pair is 15 bits for the
+ length code, 5 bits for the length extra, 15 bits for the distance code,
+ and 13 bits for the distance extra. This totals 48 bits, or six bytes.
+ Therefore if strm->avail_in >= 6, then there is enough input to avoid
+ checking for available input while decoding.
+
+ - The maximum bytes that a single length/distance pair can output is 258
+ bytes, which is the maximum length that can be coded. inflate_fast()
+ requires strm->avail_out >= 258 for each loop to avoid checking for
+ output space.
+ */
+void inflate_fast(strm, start)
+z_streamp strm;
+unsigned start; /* inflate()'s starting value for strm->avail_out */
{
- inflate_huft *t; /* temporary pointer */
- uInt e; /* extra bits or operation */
- uLong b; /* bit buffer */
- uInt k; /* bits in bit buffer */
- Byte *p; /* input data pointer */
- uInt n; /* bytes available there */
- Byte *q; /* output window write pointer */
- uInt m; /* bytes to end of window or read pointer */
- uInt ml; /* mask for literal/length tree */
- uInt md; /* mask for distance tree */
- uInt c; /* bytes to copy */
- uInt d; /* distance back to copy from */
- Byte *r; /* copy source pointer */
-
- /* load input, output, bit values */
- LOAD
-
- /* initialize masks */
- ml = zlib_inflate_mask[bl];
- md = zlib_inflate_mask[bd];
-
- /* do until not enough input or output space for fast loop */
- do { /* assume called with m >= 258 && n >= 10 */
- /* get literal/length code */
- GRABBITS(20) /* max bits for literal/length code */
- if ((e = (t = tl + ((uInt)b & ml))->exop) == 0)
- {
- DUMPBITS(t->bits)
- *q++ = (Byte)t->base;
- m--;
- continue;
- }
+ struct inflate_state *state;
+ unsigned char *in; /* local strm->next_in */
+ unsigned char *last; /* while in < last, enough input available */
+ unsigned char *out; /* local strm->next_out */
+ unsigned char *beg; /* inflate()'s initial strm->next_out */
+ unsigned char *end; /* while out < end, enough space available */
+#ifdef INFLATE_STRICT
+ unsigned dmax; /* maximum distance from zlib header */
+#endif
+ unsigned wsize; /* window size or zero if not using window */
+ unsigned whave; /* valid bytes in the window */
+ unsigned write; /* window write index */
+ unsigned char *window; /* allocated sliding window, if wsize != 0 */
+ unsigned long hold; /* local strm->hold */
+ unsigned bits; /* local strm->bits */
+ code const *lcode; /* local strm->lencode */
+ code const *dcode; /* local strm->distcode */
+ unsigned lmask; /* mask for first level of length codes */
+ unsigned dmask; /* mask for first level of distance codes */
+ code this; /* retrieved table entry */
+ unsigned op; /* code bits, operation, extra bits, or */
+ /* window position, window bytes to copy */
+ unsigned len; /* match length, unused bytes */
+ unsigned dist; /* match distance */
+ unsigned char *from; /* where to copy match from */
+
+ /* copy state to local variables */
+ state = (struct inflate_state *)strm->state;
+ in = strm->next_in - OFF;
+ last = in + (strm->avail_in - 5);
+ out = strm->next_out - OFF;
+ beg = out - (start - strm->avail_out);
+ end = out + (strm->avail_out - 257);
+#ifdef INFLATE_STRICT
+ dmax = state->dmax;
+#endif
+ wsize = state->wsize;
+ whave = state->whave;
+ write = state->write;
+ window = state->window;
+ hold = state->hold;
+ bits = state->bits;
+ lcode = state->lencode;
+ dcode = state->distcode;
+ lmask = (1U << state->lenbits) - 1;
+ dmask = (1U << state->distbits) - 1;
+
+ /* decode literals and length/distances until end-of-block or not enough
+ input data or output space */
do {
- DUMPBITS(t->bits)
- if (e & 16)
- {
- /* get extra bits for length */
- e &= 15;
- c = t->base + ((uInt)b & zlib_inflate_mask[e]);
- DUMPBITS(e)
-
- /* decode distance base of block to copy */
- GRABBITS(15); /* max bits for distance code */
- e = (t = td + ((uInt)b & md))->exop;
- do {
- DUMPBITS(t->bits)
- if (e & 16)
- {
- /* get extra bits to add to distance base */
- e &= 15;
- GRABBITS(e) /* get extra bits (up to 13) */
- d = t->base + ((uInt)b & zlib_inflate_mask[e]);
- DUMPBITS(e)
-
- /* do the copy */
- m -= c;
- r = q - d;
- if (r < s->window) /* wrap if needed */
- {
- do {
- r += s->end - s->window; /* force pointer in window */
- } while (r < s->window); /* covers invalid distances */
- e = s->end - r;
- if (c > e)
- {
- c -= e; /* wrapped copy */
- do {
- *q++ = *r++;
- } while (--e);
- r = s->window;
- do {
- *q++ = *r++;
- } while (--c);
- }
- else /* normal copy */
- {
- *q++ = *r++; c--;
- *q++ = *r++; c--;
- do {
- *q++ = *r++;
- } while (--c);
- }
+ if (bits < 15) {
+ hold += (unsigned long)(PUP(in)) << bits;
+ bits += 8;
+ hold += (unsigned long)(PUP(in)) << bits;
+ bits += 8;
+ }
+ this = lcode[hold & lmask];
+ dolen:
+ op = (unsigned)(this.bits);
+ hold >>= op;
+ bits -= op;
+ op = (unsigned)(this.op);
+ if (op == 0) { /* literal */
+ PUP(out) = (unsigned char)(this.val);
+ }
+ else if (op & 16) { /* length base */
+ len = (unsigned)(this.val);
+ op &= 15; /* number of extra bits */
+ if (op) {
+ if (bits < op) {
+ hold += (unsigned long)(PUP(in)) << bits;
+ bits += 8;
+ }
+ len += (unsigned)hold & ((1U << op) - 1);
+ hold >>= op;
+ bits -= op;
+ }
+ if (bits < 15) {
+ hold += (unsigned long)(PUP(in)) << bits;
+ bits += 8;
+ hold += (unsigned long)(PUP(in)) << bits;
+ bits += 8;
+ }
+ this = dcode[hold & dmask];
+ dodist:
+ op = (unsigned)(this.bits);
+ hold >>= op;
+ bits -= op;
+ op = (unsigned)(this.op);
+ if (op & 16) { /* distance base */
+ dist = (unsigned)(this.val);
+ op &= 15; /* number of extra bits */
+ if (bits < op) {
+ hold += (unsigned long)(PUP(in)) << bits;
+ bits += 8;
+ if (bits < op) {
+ hold += (unsigned long)(PUP(in)) << bits;
+ bits += 8;
+ }
+ }
+ dist += (unsigned)hold & ((1U << op) - 1);
+#ifdef INFLATE_STRICT
+ if (dist > dmax) {
+ strm->msg = (char *)"invalid distance too far back";
+ state->mode = BAD;
+ break;
+ }
+#endif
+ hold >>= op;
+ bits -= op;
+ op = (unsigned)(out - beg); /* max distance in output */
+ if (dist > op) { /* see if copy from window */
+ op = dist - op; /* distance back in window */
+ if (op > whave) {
+ strm->msg = (char *)"invalid distance too far back";
+ state->mode = BAD;
+ break;
+ }
+ from = window - OFF;
+ if (write == 0) { /* very common case */
+ from += wsize - op;
+ if (op < len) { /* some from window */
+ len -= op;
+ do {
+ PUP(out) = PUP(from);
+ } while (--op);
+ from = out - dist; /* rest from output */
+ }
+ }
+ else if (write < op) { /* wrap around window */
+ from += wsize + write - op;
+ op -= write;
+ if (op < len) { /* some from end of window */
+ len -= op;
+ do {
+ PUP(out) = PUP(from);
+ } while (--op);
+ from = window - OFF;
+ if (write < len) { /* some from start of window */
+ op = write;
+ len -= op;
+ do {
+ PUP(out) = PUP(from);
+ } while (--op);
+ from = out - dist; /* rest from output */
+ }
+ }
+ }
+ else { /* contiguous in window */
+ from += write - op;
+ if (op < len) { /* some from window */
+ len -= op;
+ do {
+ PUP(out) = PUP(from);
+ } while (--op);
+ from = out - dist; /* rest from output */
+ }
+ }
+ while (len > 2) {
+ PUP(out) = PUP(from);
+ PUP(out) = PUP(from);
+ PUP(out) = PUP(from);
+ len -= 3;
+ }
+ if (len) {
+ PUP(out) = PUP(from);
+ if (len > 1)
+ PUP(out) = PUP(from);
+ }
+ }
+ else {
+ from = out - dist; /* copy direct from output */
+ do { /* minimum length is three */
+ PUP(out) = PUP(from);
+ PUP(out) = PUP(from);
+ PUP(out) = PUP(from);
+ len -= 3;
+ } while (len > 2);
+ if (len) {
+ PUP(out) = PUP(from);
+ if (len > 1)
+ PUP(out) = PUP(from);
+ }
+ }
+ }
+ else if ((op & 64) == 0) { /* 2nd level distance code */
+ this = dcode[this.val + (hold & ((1U << op) - 1))];
+ goto dodist;
}
- else /* normal copy */
- {
- *q++ = *r++; c--;
- *q++ = *r++; c--;
- do {
- *q++ = *r++;
- } while (--c);
+ else {
+ strm->msg = (char *)"invalid distance code";
+ state->mode = BAD;
+ break;
}
+ }
+ else if ((op & 64) == 0) { /* 2nd level length code */
+ this = lcode[this.val + (hold & ((1U << op) - 1))];
+ goto dolen;
+ }
+ else if (op & 32) { /* end-of-block */
+ state->mode = TYPE;
break;
- }
- else if ((e & 64) == 0)
- {
- t += t->base;
- e = (t += ((uInt)b & zlib_inflate_mask[e]))->exop;
- }
- else
- {
- z->msg = (char*)"invalid distance code";
- UNGRAB
- UPDATE
- return Z_DATA_ERROR;
- }
- } while (1);
- break;
- }
- if ((e & 64) == 0)
- {
- t += t->base;
- if ((e = (t += ((uInt)b & zlib_inflate_mask[e]))->exop) == 0)
- {
- DUMPBITS(t->bits)
- *q++ = (Byte)t->base;
- m--;
- break;
}
- }
- else if (e & 32)
- {
- UNGRAB
- UPDATE
- return Z_STREAM_END;
- }
- else
- {
- z->msg = (char*)"invalid literal/length code";
- UNGRAB
- UPDATE
- return Z_DATA_ERROR;
- }
- } while (1);
- } while (m >= 258 && n >= 10);
-
- /* not enough input or output--restore pointers and return */
- UNGRAB
- UPDATE
- return Z_OK;
+ else {
+ strm->msg = (char *)"invalid literal/length code";
+ state->mode = BAD;
+ break;
+ }
+ } while (in < last && out < end);
+
+ /* return unused bytes (on entry, bits < 8, so in won't go too far back) */
+ len = bits >> 3;
+ in -= len;
+ bits -= len << 3;
+ hold &= (1U << bits) - 1;
+
+ /* update state and return */
+ strm->next_in = in + OFF;
+ strm->next_out = out + OFF;
+ strm->avail_in = (unsigned)(in < last ? 5 + (last - in) : 5 - (in - last));
+ strm->avail_out = (unsigned)(out < end ?
+ 257 + (end - out) : 257 - (out - end));
+ state->hold = hold;
+ state->bits = bits;
+ return;
}
+
+/*
+ inflate_fast() speedups that turned out slower (on a PowerPC G3 750CXe):
+ - Using bit fields for code structure
+ - Different op definition to avoid & for extra bits (do & for table bits)
+ - Three separate decoding do-loops for direct, window, and write == 0
+ - Special case for distance > 1 copies to do overlapped load and store copy
+ - Explicit branch predictions (based on measured branch probabilities)
+ - Deferring match copy and interspersed it with decoding subsequent codes
+ - Swapping literal/length else
+ - Swapping window/direct else
+ - Larger unrolled copy loops (three is about right)
+ - Moving len -= 3 statement into middle of loop
+ */
+
+#endif /* !ASMINF */
/* inffast.h -- header to use inffast.c
- * Copyright (C) 1995-1998 Mark Adler
- * For conditions of distribution and use, see copyright notice in zlib.h
+ * Copyright (C) 1995-2003 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
*/
/* WARNING: this file should *not* be used by applications. It is
subject to change. Applications should only use zlib.h.
*/
-extern int zlib_inflate_fast (
- uInt,
- uInt,
- inflate_huft *,
- inflate_huft *,
- inflate_blocks_statef *,
- z_streamp );
+void inflate_fast (z_streamp strm, unsigned start);
--- /dev/null
+ /* inffixed.h -- table for decoding fixed codes
+ * Generated automatically by makefixed().
+ */
+
+ /* WARNING: this file should *not* be used by applications. It
+ is part of the implementation of the compression library and
+ is subject to change. Applications should only use zlib.h.
+ */
+
+ static const code lenfix[512] = {
+ {96,7,0},{0,8,80},{0,8,16},{20,8,115},{18,7,31},{0,8,112},{0,8,48},
+ {0,9,192},{16,7,10},{0,8,96},{0,8,32},{0,9,160},{0,8,0},{0,8,128},
+ {0,8,64},{0,9,224},{16,7,6},{0,8,88},{0,8,24},{0,9,144},{19,7,59},
+ {0,8,120},{0,8,56},{0,9,208},{17,7,17},{0,8,104},{0,8,40},{0,9,176},
+ {0,8,8},{0,8,136},{0,8,72},{0,9,240},{16,7,4},{0,8,84},{0,8,20},
+ {21,8,227},{19,7,43},{0,8,116},{0,8,52},{0,9,200},{17,7,13},{0,8,100},
+ {0,8,36},{0,9,168},{0,8,4},{0,8,132},{0,8,68},{0,9,232},{16,7,8},
+ {0,8,92},{0,8,28},{0,9,152},{20,7,83},{0,8,124},{0,8,60},{0,9,216},
+ {18,7,23},{0,8,108},{0,8,44},{0,9,184},{0,8,12},{0,8,140},{0,8,76},
+ {0,9,248},{16,7,3},{0,8,82},{0,8,18},{21,8,163},{19,7,35},{0,8,114},
+ {0,8,50},{0,9,196},{17,7,11},{0,8,98},{0,8,34},{0,9,164},{0,8,2},
+ {0,8,130},{0,8,66},{0,9,228},{16,7,7},{0,8,90},{0,8,26},{0,9,148},
+ {20,7,67},{0,8,122},{0,8,58},{0,9,212},{18,7,19},{0,8,106},{0,8,42},
+ {0,9,180},{0,8,10},{0,8,138},{0,8,74},{0,9,244},{16,7,5},{0,8,86},
+ {0,8,22},{64,8,0},{19,7,51},{0,8,118},{0,8,54},{0,9,204},{17,7,15},
+ {0,8,102},{0,8,38},{0,9,172},{0,8,6},{0,8,134},{0,8,70},{0,9,236},
+ {16,7,9},{0,8,94},{0,8,30},{0,9,156},{20,7,99},{0,8,126},{0,8,62},
+ {0,9,220},{18,7,27},{0,8,110},{0,8,46},{0,9,188},{0,8,14},{0,8,142},
+ {0,8,78},{0,9,252},{96,7,0},{0,8,81},{0,8,17},{21,8,131},{18,7,31},
+ {0,8,113},{0,8,49},{0,9,194},{16,7,10},{0,8,97},{0,8,33},{0,9,162},
+ {0,8,1},{0,8,129},{0,8,65},{0,9,226},{16,7,6},{0,8,89},{0,8,25},
+ {0,9,146},{19,7,59},{0,8,121},{0,8,57},{0,9,210},{17,7,17},{0,8,105},
+ {0,8,41},{0,9,178},{0,8,9},{0,8,137},{0,8,73},{0,9,242},{16,7,4},
+ {0,8,85},{0,8,21},{16,8,258},{19,7,43},{0,8,117},{0,8,53},{0,9,202},
+ {17,7,13},{0,8,101},{0,8,37},{0,9,170},{0,8,5},{0,8,133},{0,8,69},
+ {0,9,234},{16,7,8},{0,8,93},{0,8,29},{0,9,154},{20,7,83},{0,8,125},
+ {0,8,61},{0,9,218},{18,7,23},{0,8,109},{0,8,45},{0,9,186},{0,8,13},
+ {0,8,141},{0,8,77},{0,9,250},{16,7,3},{0,8,83},{0,8,19},{21,8,195},
+ {19,7,35},{0,8,115},{0,8,51},{0,9,198},{17,7,11},{0,8,99},{0,8,35},
+ {0,9,166},{0,8,3},{0,8,131},{0,8,67},{0,9,230},{16,7,7},{0,8,91},
+ {0,8,27},{0,9,150},{20,7,67},{0,8,123},{0,8,59},{0,9,214},{18,7,19},
+ {0,8,107},{0,8,43},{0,9,182},{0,8,11},{0,8,139},{0,8,75},{0,9,246},
+ {16,7,5},{0,8,87},{0,8,23},{64,8,0},{19,7,51},{0,8,119},{0,8,55},
+ {0,9,206},{17,7,15},{0,8,103},{0,8,39},{0,9,174},{0,8,7},{0,8,135},
+ {0,8,71},{0,9,238},{16,7,9},{0,8,95},{0,8,31},{0,9,158},{20,7,99},
+ {0,8,127},{0,8,63},{0,9,222},{18,7,27},{0,8,111},{0,8,47},{0,9,190},
+ {0,8,15},{0,8,143},{0,8,79},{0,9,254},{96,7,0},{0,8,80},{0,8,16},
+ {20,8,115},{18,7,31},{0,8,112},{0,8,48},{0,9,193},{16,7,10},{0,8,96},
+ {0,8,32},{0,9,161},{0,8,0},{0,8,128},{0,8,64},{0,9,225},{16,7,6},
+ {0,8,88},{0,8,24},{0,9,145},{19,7,59},{0,8,120},{0,8,56},{0,9,209},
+ {17,7,17},{0,8,104},{0,8,40},{0,9,177},{0,8,8},{0,8,136},{0,8,72},
+ {0,9,241},{16,7,4},{0,8,84},{0,8,20},{21,8,227},{19,7,43},{0,8,116},
+ {0,8,52},{0,9,201},{17,7,13},{0,8,100},{0,8,36},{0,9,169},{0,8,4},
+ {0,8,132},{0,8,68},{0,9,233},{16,7,8},{0,8,92},{0,8,28},{0,9,153},
+ {20,7,83},{0,8,124},{0,8,60},{0,9,217},{18,7,23},{0,8,108},{0,8,44},
+ {0,9,185},{0,8,12},{0,8,140},{0,8,76},{0,9,249},{16,7,3},{0,8,82},
+ {0,8,18},{21,8,163},{19,7,35},{0,8,114},{0,8,50},{0,9,197},{17,7,11},
+ {0,8,98},{0,8,34},{0,9,165},{0,8,2},{0,8,130},{0,8,66},{0,9,229},
+ {16,7,7},{0,8,90},{0,8,26},{0,9,149},{20,7,67},{0,8,122},{0,8,58},
+ {0,9,213},{18,7,19},{0,8,106},{0,8,42},{0,9,181},{0,8,10},{0,8,138},
+ {0,8,74},{0,9,245},{16,7,5},{0,8,86},{0,8,22},{64,8,0},{19,7,51},
+ {0,8,118},{0,8,54},{0,9,205},{17,7,15},{0,8,102},{0,8,38},{0,9,173},
+ {0,8,6},{0,8,134},{0,8,70},{0,9,237},{16,7,9},{0,8,94},{0,8,30},
+ {0,9,157},{20,7,99},{0,8,126},{0,8,62},{0,9,221},{18,7,27},{0,8,110},
+ {0,8,46},{0,9,189},{0,8,14},{0,8,142},{0,8,78},{0,9,253},{96,7,0},
+ {0,8,81},{0,8,17},{21,8,131},{18,7,31},{0,8,113},{0,8,49},{0,9,195},
+ {16,7,10},{0,8,97},{0,8,33},{0,9,163},{0,8,1},{0,8,129},{0,8,65},
+ {0,9,227},{16,7,6},{0,8,89},{0,8,25},{0,9,147},{19,7,59},{0,8,121},
+ {0,8,57},{0,9,211},{17,7,17},{0,8,105},{0,8,41},{0,9,179},{0,8,9},
+ {0,8,137},{0,8,73},{0,9,243},{16,7,4},{0,8,85},{0,8,21},{16,8,258},
+ {19,7,43},{0,8,117},{0,8,53},{0,9,203},{17,7,13},{0,8,101},{0,8,37},
+ {0,9,171},{0,8,5},{0,8,133},{0,8,69},{0,9,235},{16,7,8},{0,8,93},
+ {0,8,29},{0,9,155},{20,7,83},{0,8,125},{0,8,61},{0,9,219},{18,7,23},
+ {0,8,109},{0,8,45},{0,9,187},{0,8,13},{0,8,141},{0,8,77},{0,9,251},
+ {16,7,3},{0,8,83},{0,8,19},{21,8,195},{19,7,35},{0,8,115},{0,8,51},
+ {0,9,199},{17,7,11},{0,8,99},{0,8,35},{0,9,167},{0,8,3},{0,8,131},
+ {0,8,67},{0,9,231},{16,7,7},{0,8,91},{0,8,27},{0,9,151},{20,7,67},
+ {0,8,123},{0,8,59},{0,9,215},{18,7,19},{0,8,107},{0,8,43},{0,9,183},
+ {0,8,11},{0,8,139},{0,8,75},{0,9,247},{16,7,5},{0,8,87},{0,8,23},
+ {64,8,0},{19,7,51},{0,8,119},{0,8,55},{0,9,207},{17,7,15},{0,8,103},
+ {0,8,39},{0,9,175},{0,8,7},{0,8,135},{0,8,71},{0,9,239},{16,7,9},
+ {0,8,95},{0,8,31},{0,9,159},{20,7,99},{0,8,127},{0,8,63},{0,9,223},
+ {18,7,27},{0,8,111},{0,8,47},{0,9,191},{0,8,15},{0,8,143},{0,8,79},
+ {0,9,255}
+ };
+
+ static const code distfix[32] = {
+ {16,5,1},{23,5,257},{19,5,17},{27,5,4097},{17,5,5},{25,5,1025},
+ {21,5,65},{29,5,16385},{16,5,3},{24,5,513},{20,5,33},{28,5,8193},
+ {18,5,9},{26,5,2049},{22,5,129},{64,5,0},{16,5,2},{23,5,385},
+ {19,5,25},{27,5,6145},{17,5,7},{25,5,1537},{21,5,97},{29,5,24577},
+ {16,5,4},{24,5,769},{20,5,49},{28,5,12289},{18,5,13},{26,5,3073},
+ {22,5,193},{64,5,0}
+ };
-/* inflate.c -- zlib interface to inflate modules
- * Copyright (C) 1995-1998 Mark Adler
- * For conditions of distribution and use, see copyright notice in zlib.h
+/* inflate.c -- zlib decompression
+ * Copyright (C) 1995-2005 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ *
+ * Based on zlib 1.2.3 but modified for the Linux Kernel by
+ * Richard Purdie <richard@openedhand.com>
+ *
+ * Changes mainly for static instead of dynamic memory allocation
+ *
*/
#include <linux/zutil.h>
-#include "infblock.h"
+#include "inftrees.h"
+#include "inflate.h"
+#include "inffast.h"
#include "infutil.h"
int zlib_inflate_workspacesize(void)
{
- return sizeof(struct inflate_workspace);
+ return sizeof(struct inflate_workspace);
}
+int zlib_inflateReset(z_streamp strm)
+{
+ struct inflate_state *state;
+
+ if (strm == NULL || strm->state == NULL) return Z_STREAM_ERROR;
+ state = (struct inflate_state *)strm->state;
+ strm->total_in = strm->total_out = state->total = 0;
+ strm->msg = NULL;
+ strm->adler = 1; /* to support ill-conceived Java test suite */
+ state->mode = HEAD;
+ state->last = 0;
+ state->havedict = 0;
+ state->dmax = 32768U;
+ state->hold = 0;
+ state->bits = 0;
+ state->lencode = state->distcode = state->next = state->codes;
-int zlib_inflateReset(
- z_streamp z
-)
+ /* Initialise Window */
+ state->wsize = 1U << state->wbits;
+ state->write = 0;
+ state->whave = 0;
+
+ return Z_OK;
+}
+
+#if 0
+int zlib_inflatePrime(z_streamp strm, int bits, int value)
{
- if (z == NULL || z->state == NULL || z->workspace == NULL)
- return Z_STREAM_ERROR;
- z->total_in = z->total_out = 0;
- z->msg = NULL;
- z->state->mode = z->state->nowrap ? BLOCKS : METHOD;
- zlib_inflate_blocks_reset(z->state->blocks, z, NULL);
- return Z_OK;
+ struct inflate_state *state;
+
+ if (strm == NULL || strm->state == NULL) return Z_STREAM_ERROR;
+ state = (struct inflate_state *)strm->state;
+ if (bits > 16 || state->bits + bits > 32) return Z_STREAM_ERROR;
+ value &= (1L << bits) - 1;
+ state->hold += value << state->bits;
+ state->bits += bits;
+ return Z_OK;
}
+#endif
+
+int zlib_inflateInit2(z_streamp strm, int windowBits)
+{
+ struct inflate_state *state;
+
+ if (strm == NULL) return Z_STREAM_ERROR;
+ strm->msg = NULL; /* in case we return an error */
+
+ state = &WS(strm)->inflate_state;
+ strm->state = (struct internal_state *)state;
+
+ if (windowBits < 0) {
+ state->wrap = 0;
+ windowBits = -windowBits;
+ }
+ else {
+ state->wrap = (windowBits >> 4) + 1;
+ }
+ if (windowBits < 8 || windowBits > 15) {
+ return Z_STREAM_ERROR;
+ }
+ state->wbits = (unsigned)windowBits;
+ state->window = &WS(strm)->working_window[0];
+ return zlib_inflateReset(strm);
+}
-int zlib_inflateEnd(
- z_streamp z
-)
+/*
+ Return state with length and distance decoding tables and index sizes set to
+ fixed code decoding. This returns fixed tables from inffixed.h.
+ */
+static void zlib_fixedtables(struct inflate_state *state)
{
- if (z == NULL || z->state == NULL || z->workspace == NULL)
- return Z_STREAM_ERROR;
- if (z->state->blocks != NULL)
- zlib_inflate_blocks_free(z->state->blocks, z);
- z->state = NULL;
- return Z_OK;
+# include "inffixed.h"
+ state->lencode = lenfix;
+ state->lenbits = 9;
+ state->distcode = distfix;
+ state->distbits = 5;
}
-int zlib_inflateInit2_(
- z_streamp z,
- int w,
- const char *version,
- int stream_size
-)
+/*
+ Update the window with the last wsize (normally 32K) bytes written before
+ returning. This is only called when a window is already in use, or when
+ output has been written during this inflate call, but the end of the deflate
+ stream has not been reached yet. It is also called to window dictionary data
+ when a dictionary is loaded.
+
+ Providing output buffers larger than 32K to inflate() should provide a speed
+ advantage, since only the last 32K of output is copied to the sliding window
+ upon return from inflate(), and since all distances after the first 32K of
+ output will fall in the output data, making match copies simpler and faster.
+ The advantage may be dependent on the size of the processor's data caches.
+ */
+static void zlib_updatewindow(z_streamp strm, unsigned out)
{
- if (version == NULL || version[0] != ZLIB_VERSION[0] ||
- stream_size != sizeof(z_stream) || z->workspace == NULL)
- return Z_VERSION_ERROR;
-
- /* initialize state */
- z->msg = NULL;
- z->state = &WS(z)->internal_state;
- z->state->blocks = NULL;
-
- /* handle undocumented nowrap option (no zlib header or check) */
- z->state->nowrap = 0;
- if (w < 0)
- {
- w = - w;
- z->state->nowrap = 1;
- }
-
- /* set window size */
- if (w < 8 || w > 15)
- {
- zlib_inflateEnd(z);
- return Z_STREAM_ERROR;
- }
- z->state->wbits = (uInt)w;
-
- /* create inflate_blocks state */
- if ((z->state->blocks =
- zlib_inflate_blocks_new(z, z->state->nowrap ? NULL : zlib_adler32, (uInt)1 << w))
- == NULL)
- {
- zlib_inflateEnd(z);
- return Z_MEM_ERROR;
- }
-
- /* reset state */
- zlib_inflateReset(z);
- return Z_OK;
+ struct inflate_state *state;
+ unsigned copy, dist;
+
+ state = (struct inflate_state *)strm->state;
+
+ /* copy state->wsize or less output bytes into the circular window */
+ copy = out - strm->avail_out;
+ if (copy >= state->wsize) {
+ memcpy(state->window, strm->next_out - state->wsize, state->wsize);
+ state->write = 0;
+ state->whave = state->wsize;
+ }
+ else {
+ dist = state->wsize - state->write;
+ if (dist > copy) dist = copy;
+ memcpy(state->window + state->write, strm->next_out - copy, dist);
+ copy -= dist;
+ if (copy) {
+ memcpy(state->window, strm->next_out - copy, copy);
+ state->write = copy;
+ state->whave = state->wsize;
+ }
+ else {
+ state->write += dist;
+ if (state->write == state->wsize) state->write = 0;
+ if (state->whave < state->wsize) state->whave += dist;
+ }
+ }
}
* At the end of a Deflate-compressed PPP packet, we expect to have seen
* a `stored' block type value but not the (zero) length bytes.
*/
-static int zlib_inflate_packet_flush(inflate_blocks_statef *s)
+/*
+ Returns true if inflate is currently at the end of a block generated by
+ Z_SYNC_FLUSH or Z_FULL_FLUSH. This function is used by one PPP
+ implementation to provide an additional safety check. PPP uses
+ Z_SYNC_FLUSH but removes the length bytes of the resulting empty stored
+ block. When decompressing, PPP checks that at the end of input packet,
+ inflate is waiting for these length bytes.
+ */
+static int zlib_inflateSyncPacket(z_streamp strm)
{
- if (s->mode != LENS)
- return Z_DATA_ERROR;
- s->mode = TYPE;
+ struct inflate_state *state;
+
+ if (strm == NULL || strm->state == NULL) return Z_STREAM_ERROR;
+ state = (struct inflate_state *)strm->state;
+
+ if (state->mode == STORED && state->bits == 0) {
+ state->mode = TYPE;
+ return Z_OK;
+ }
+ return Z_DATA_ERROR;
+}
+
+/* Macros for inflate(): */
+
+/* check function to use adler32() for zlib or crc32() for gzip */
+#define UPDATE(check, buf, len) zlib_adler32(check, buf, len)
+
+/* Load registers with state in inflate() for speed */
+#define LOAD() \
+ do { \
+ put = strm->next_out; \
+ left = strm->avail_out; \
+ next = strm->next_in; \
+ have = strm->avail_in; \
+ hold = state->hold; \
+ bits = state->bits; \
+ } while (0)
+
+/* Restore state from registers in inflate() */
+#define RESTORE() \
+ do { \
+ strm->next_out = put; \
+ strm->avail_out = left; \
+ strm->next_in = next; \
+ strm->avail_in = have; \
+ state->hold = hold; \
+ state->bits = bits; \
+ } while (0)
+
+/* Clear the input bit accumulator */
+#define INITBITS() \
+ do { \
+ hold = 0; \
+ bits = 0; \
+ } while (0)
+
+/* Get a byte of input into the bit accumulator, or return from inflate()
+ if there is no input available. */
+#define PULLBYTE() \
+ do { \
+ if (have == 0) goto inf_leave; \
+ have--; \
+ hold += (unsigned long)(*next++) << bits; \
+ bits += 8; \
+ } while (0)
+
+/* Assure that there are at least n bits in the bit accumulator. If there is
+ not enough available input to do that, then return from inflate(). */
+#define NEEDBITS(n) \
+ do { \
+ while (bits < (unsigned)(n)) \
+ PULLBYTE(); \
+ } while (0)
+
+/* Return the low n bits of the bit accumulator (n < 16) */
+#define BITS(n) \
+ ((unsigned)hold & ((1U << (n)) - 1))
+
+/* Remove n bits from the bit accumulator */
+#define DROPBITS(n) \
+ do { \
+ hold >>= (n); \
+ bits -= (unsigned)(n); \
+ } while (0)
+
+/* Remove zero to seven bits as needed to go to a byte boundary */
+#define BYTEBITS() \
+ do { \
+ hold >>= bits & 7; \
+ bits -= bits & 7; \
+ } while (0)
+
+/* Reverse the bytes in a 32-bit value */
+#define REVERSE(q) \
+ ((((q) >> 24) & 0xff) + (((q) >> 8) & 0xff00) + \
+ (((q) & 0xff00) << 8) + (((q) & 0xff) << 24))
+
+/*
+ inflate() uses a state machine to process as much input data and generate as
+ much output data as possible before returning. The state machine is
+ structured roughly as follows:
+
+ for (;;) switch (state) {
+ ...
+ case STATEn:
+ if (not enough input data or output space to make progress)
+ return;
+ ... make progress ...
+ state = STATEm;
+ break;
+ ...
+ }
+
+ so when inflate() is called again, the same case is attempted again, and
+ if the appropriate resources are provided, the machine proceeds to the
+ next state. The NEEDBITS() macro is usually the way the state evaluates
+ whether it can proceed or should return. NEEDBITS() does the return if
+ the requested bits are not available. The typical use of the BITS macros
+ is:
+
+ NEEDBITS(n);
+ ... do something with BITS(n) ...
+ DROPBITS(n);
+
+ where NEEDBITS(n) either returns from inflate() if there isn't enough
+ input left to load n bits into the accumulator, or it continues. BITS(n)
+ gives the low n bits in the accumulator. When done, DROPBITS(n) drops
+ the low n bits off the accumulator. INITBITS() clears the accumulator
+ and sets the number of available bits to zero. BYTEBITS() discards just
+ enough bits to put the accumulator on a byte boundary. After BYTEBITS()
+ and a NEEDBITS(8), then BITS(8) would return the next byte in the stream.
+
+ NEEDBITS(n) uses PULLBYTE() to get an available byte of input, or to return
+ if there is no input available. The decoding of variable length codes uses
+ PULLBYTE() directly in order to pull just enough bytes to decode the next
+ code, and no more.
+
+ Some states loop until they get enough input, making sure that enough
+ state information is maintained to continue the loop where it left off
+ if NEEDBITS() returns in the loop. For example, want, need, and keep
+ would all have to actually be part of the saved state in case NEEDBITS()
+ returns:
+
+ case STATEw:
+ while (want < need) {
+ NEEDBITS(n);
+ keep[want++] = BITS(n);
+ DROPBITS(n);
+ }
+ state = STATEx;
+ case STATEx:
+
+ As shown above, if the next state is also the next case, then the break
+ is omitted.
+
+ A state may also return if there is not enough output space available to
+ complete that state. Those states are copying stored data, writing a
+ literal byte, and copying a matching string.
+
+ When returning, a "goto inf_leave" is used to update the total counters,
+ update the check value, and determine whether any progress has been made
+ during that inflate() call in order to return the proper return code.
+ Progress is defined as a change in either strm->avail_in or strm->avail_out.
+ When there is a window, goto inf_leave will update the window with the last
+ output written. If a goto inf_leave occurs in the middle of decompression
+ and there is no window currently, goto inf_leave will create one and copy
+ output to the window for the next call of inflate().
+
+ In this implementation, the flush parameter of inflate() only affects the
+ return code (per zlib.h). inflate() always writes as much as possible to
+ strm->next_out, given the space available and the provided input--the effect
+ documented in zlib.h of Z_SYNC_FLUSH. Furthermore, inflate() always defers
+ the allocation of and copying into a sliding window until necessary, which
+ provides the effect documented in zlib.h for Z_FINISH when the entire input
+ stream available. So the only thing the flush parameter actually does is:
+ when flush is set to Z_FINISH, inflate() cannot return Z_OK. Instead it
+ will return Z_BUF_ERROR if it has not reached the end of the stream.
+ */
+
+int zlib_inflate(z_streamp strm, int flush)
+{
+ struct inflate_state *state;
+ unsigned char *next; /* next input */
+ unsigned char *put; /* next output */
+ unsigned have, left; /* available input and output */
+ unsigned long hold; /* bit buffer */
+ unsigned bits; /* bits in bit buffer */
+ unsigned in, out; /* save starting available input and output */
+ unsigned copy; /* number of stored or match bytes to copy */
+ unsigned char *from; /* where to copy match bytes from */
+ code this; /* current decoding table entry */
+ code last; /* parent table entry */
+ unsigned len; /* length to copy for repeats, bits to drop */
+ int ret; /* return code */
+ static const unsigned short order[19] = /* permutation of code lengths */
+ {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15};
+
+ if (strm == NULL || strm->state == NULL || strm->next_out == NULL ||
+ (strm->next_in == NULL && strm->avail_in != 0))
+ return Z_STREAM_ERROR;
+
+ state = (struct inflate_state *)strm->state;
+
+ if (state->mode == TYPE) state->mode = TYPEDO; /* skip check */
+ LOAD();
+ in = have;
+ out = left;
+ ret = Z_OK;
+ for (;;)
+ switch (state->mode) {
+ case HEAD:
+ if (state->wrap == 0) {
+ state->mode = TYPEDO;
+ break;
+ }
+ NEEDBITS(16);
+ if (
+ ((BITS(8) << 8) + (hold >> 8)) % 31) {
+ strm->msg = (char *)"incorrect header check";
+ state->mode = BAD;
+ break;
+ }
+ if (BITS(4) != Z_DEFLATED) {
+ strm->msg = (char *)"unknown compression method";
+ state->mode = BAD;
+ break;
+ }
+ DROPBITS(4);
+ len = BITS(4) + 8;
+ if (len > state->wbits) {
+ strm->msg = (char *)"invalid window size";
+ state->mode = BAD;
+ break;
+ }
+ state->dmax = 1U << len;
+ strm->adler = state->check = zlib_adler32(0L, NULL, 0);
+ state->mode = hold & 0x200 ? DICTID : TYPE;
+ INITBITS();
+ break;
+ case DICTID:
+ NEEDBITS(32);
+ strm->adler = state->check = REVERSE(hold);
+ INITBITS();
+ state->mode = DICT;
+ case DICT:
+ if (state->havedict == 0) {
+ RESTORE();
+ return Z_NEED_DICT;
+ }
+ strm->adler = state->check = zlib_adler32(0L, NULL, 0);
+ state->mode = TYPE;
+ case TYPE:
+ if (flush == Z_BLOCK) goto inf_leave;
+ case TYPEDO:
+ if (state->last) {
+ BYTEBITS();
+ state->mode = CHECK;
+ break;
+ }
+ NEEDBITS(3);
+ state->last = BITS(1);
+ DROPBITS(1);
+ switch (BITS(2)) {
+ case 0: /* stored block */
+ state->mode = STORED;
+ break;
+ case 1: /* fixed block */
+ zlib_fixedtables(state);
+ state->mode = LEN; /* decode codes */
+ break;
+ case 2: /* dynamic block */
+ state->mode = TABLE;
+ break;
+ case 3:
+ strm->msg = (char *)"invalid block type";
+ state->mode = BAD;
+ }
+ DROPBITS(2);
+ break;
+ case STORED:
+ BYTEBITS(); /* go to byte boundary */
+ NEEDBITS(32);
+ if ((hold & 0xffff) != ((hold >> 16) ^ 0xffff)) {
+ strm->msg = (char *)"invalid stored block lengths";
+ state->mode = BAD;
+ break;
+ }
+ state->length = (unsigned)hold & 0xffff;
+ INITBITS();
+ state->mode = COPY;
+ case COPY:
+ copy = state->length;
+ if (copy) {
+ if (copy > have) copy = have;
+ if (copy > left) copy = left;
+ if (copy == 0) goto inf_leave;
+ memcpy(put, next, copy);
+ have -= copy;
+ next += copy;
+ left -= copy;
+ put += copy;
+ state->length -= copy;
+ break;
+ }
+ state->mode = TYPE;
+ break;
+ case TABLE:
+ NEEDBITS(14);
+ state->nlen = BITS(5) + 257;
+ DROPBITS(5);
+ state->ndist = BITS(5) + 1;
+ DROPBITS(5);
+ state->ncode = BITS(4) + 4;
+ DROPBITS(4);
+#ifndef PKZIP_BUG_WORKAROUND
+ if (state->nlen > 286 || state->ndist > 30) {
+ strm->msg = (char *)"too many length or distance symbols";
+ state->mode = BAD;
+ break;
+ }
+#endif
+ state->have = 0;
+ state->mode = LENLENS;
+ case LENLENS:
+ while (state->have < state->ncode) {
+ NEEDBITS(3);
+ state->lens[order[state->have++]] = (unsigned short)BITS(3);
+ DROPBITS(3);
+ }
+ while (state->have < 19)
+ state->lens[order[state->have++]] = 0;
+ state->next = state->codes;
+ state->lencode = (code const *)(state->next);
+ state->lenbits = 7;
+ ret = zlib_inflate_table(CODES, state->lens, 19, &(state->next),
+ &(state->lenbits), state->work);
+ if (ret) {
+ strm->msg = (char *)"invalid code lengths set";
+ state->mode = BAD;
+ break;
+ }
+ state->have = 0;
+ state->mode = CODELENS;
+ case CODELENS:
+ while (state->have < state->nlen + state->ndist) {
+ for (;;) {
+ this = state->lencode[BITS(state->lenbits)];
+ if ((unsigned)(this.bits) <= bits) break;
+ PULLBYTE();
+ }
+ if (this.val < 16) {
+ NEEDBITS(this.bits);
+ DROPBITS(this.bits);
+ state->lens[state->have++] = this.val;
+ }
+ else {
+ if (this.val == 16) {
+ NEEDBITS(this.bits + 2);
+ DROPBITS(this.bits);
+ if (state->have == 0) {
+ strm->msg = (char *)"invalid bit length repeat";
+ state->mode = BAD;
+ break;
+ }
+ len = state->lens[state->have - 1];
+ copy = 3 + BITS(2);
+ DROPBITS(2);
+ }
+ else if (this.val == 17) {
+ NEEDBITS(this.bits + 3);
+ DROPBITS(this.bits);
+ len = 0;
+ copy = 3 + BITS(3);
+ DROPBITS(3);
+ }
+ else {
+ NEEDBITS(this.bits + 7);
+ DROPBITS(this.bits);
+ len = 0;
+ copy = 11 + BITS(7);
+ DROPBITS(7);
+ }
+ if (state->have + copy > state->nlen + state->ndist) {
+ strm->msg = (char *)"invalid bit length repeat";
+ state->mode = BAD;
+ break;
+ }
+ while (copy--)
+ state->lens[state->have++] = (unsigned short)len;
+ }
+ }
+
+ /* handle error breaks in while */
+ if (state->mode == BAD) break;
+
+ /* build code tables */
+ state->next = state->codes;
+ state->lencode = (code const *)(state->next);
+ state->lenbits = 9;
+ ret = zlib_inflate_table(LENS, state->lens, state->nlen, &(state->next),
+ &(state->lenbits), state->work);
+ if (ret) {
+ strm->msg = (char *)"invalid literal/lengths set";
+ state->mode = BAD;
+ break;
+ }
+ state->distcode = (code const *)(state->next);
+ state->distbits = 6;
+ ret = zlib_inflate_table(DISTS, state->lens + state->nlen, state->ndist,
+ &(state->next), &(state->distbits), state->work);
+ if (ret) {
+ strm->msg = (char *)"invalid distances set";
+ state->mode = BAD;
+ break;
+ }
+ state->mode = LEN;
+ case LEN:
+ if (have >= 6 && left >= 258) {
+ RESTORE();
+ inflate_fast(strm, out);
+ LOAD();
+ break;
+ }
+ for (;;) {
+ this = state->lencode[BITS(state->lenbits)];
+ if ((unsigned)(this.bits) <= bits) break;
+ PULLBYTE();
+ }
+ if (this.op && (this.op & 0xf0) == 0) {
+ last = this;
+ for (;;) {
+ this = state->lencode[last.val +
+ (BITS(last.bits + last.op) >> last.bits)];
+ if ((unsigned)(last.bits + this.bits) <= bits) break;
+ PULLBYTE();
+ }
+ DROPBITS(last.bits);
+ }
+ DROPBITS(this.bits);
+ state->length = (unsigned)this.val;
+ if ((int)(this.op) == 0) {
+ state->mode = LIT;
+ break;
+ }
+ if (this.op & 32) {
+ state->mode = TYPE;
+ break;
+ }
+ if (this.op & 64) {
+ strm->msg = (char *)"invalid literal/length code";
+ state->mode = BAD;
+ break;
+ }
+ state->extra = (unsigned)(this.op) & 15;
+ state->mode = LENEXT;
+ case LENEXT:
+ if (state->extra) {
+ NEEDBITS(state->extra);
+ state->length += BITS(state->extra);
+ DROPBITS(state->extra);
+ }
+ state->mode = DIST;
+ case DIST:
+ for (;;) {
+ this = state->distcode[BITS(state->distbits)];
+ if ((unsigned)(this.bits) <= bits) break;
+ PULLBYTE();
+ }
+ if ((this.op & 0xf0) == 0) {
+ last = this;
+ for (;;) {
+ this = state->distcode[last.val +
+ (BITS(last.bits + last.op) >> last.bits)];
+ if ((unsigned)(last.bits + this.bits) <= bits) break;
+ PULLBYTE();
+ }
+ DROPBITS(last.bits);
+ }
+ DROPBITS(this.bits);
+ if (this.op & 64) {
+ strm->msg = (char *)"invalid distance code";
+ state->mode = BAD;
+ break;
+ }
+ state->offset = (unsigned)this.val;
+ state->extra = (unsigned)(this.op) & 15;
+ state->mode = DISTEXT;
+ case DISTEXT:
+ if (state->extra) {
+ NEEDBITS(state->extra);
+ state->offset += BITS(state->extra);
+ DROPBITS(state->extra);
+ }
+#ifdef INFLATE_STRICT
+ if (state->offset > state->dmax) {
+ strm->msg = (char *)"invalid distance too far back";
+ state->mode = BAD;
+ break;
+ }
+#endif
+ if (state->offset > state->whave + out - left) {
+ strm->msg = (char *)"invalid distance too far back";
+ state->mode = BAD;
+ break;
+ }
+ state->mode = MATCH;
+ case MATCH:
+ if (left == 0) goto inf_leave;
+ copy = out - left;
+ if (state->offset > copy) { /* copy from window */
+ copy = state->offset - copy;
+ if (copy > state->write) {
+ copy -= state->write;
+ from = state->window + (state->wsize - copy);
+ }
+ else
+ from = state->window + (state->write - copy);
+ if (copy > state->length) copy = state->length;
+ }
+ else { /* copy from output */
+ from = put - state->offset;
+ copy = state->length;
+ }
+ if (copy > left) copy = left;
+ left -= copy;
+ state->length -= copy;
+ do {
+ *put++ = *from++;
+ } while (--copy);
+ if (state->length == 0) state->mode = LEN;
+ break;
+ case LIT:
+ if (left == 0) goto inf_leave;
+ *put++ = (unsigned char)(state->length);
+ left--;
+ state->mode = LEN;
+ break;
+ case CHECK:
+ if (state->wrap) {
+ NEEDBITS(32);
+ out -= left;
+ strm->total_out += out;
+ state->total += out;
+ if (out)
+ strm->adler = state->check =
+ UPDATE(state->check, put - out, out);
+ out = left;
+ if ((
+ REVERSE(hold)) != state->check) {
+ strm->msg = (char *)"incorrect data check";
+ state->mode = BAD;
+ break;
+ }
+ INITBITS();
+ }
+ state->mode = DONE;
+ case DONE:
+ ret = Z_STREAM_END;
+ goto inf_leave;
+ case BAD:
+ ret = Z_DATA_ERROR;
+ goto inf_leave;
+ case MEM:
+ return Z_MEM_ERROR;
+ case SYNC:
+ default:
+ return Z_STREAM_ERROR;
+ }
+
+ /*
+ Return from inflate(), updating the total counts and the check value.
+ If there was no progress during the inflate() call, return a buffer
+ error. Call zlib_updatewindow() to create and/or update the window state.
+ */
+ inf_leave:
+ RESTORE();
+ if (state->wsize || (state->mode < CHECK && out != strm->avail_out))
+ zlib_updatewindow(strm, out);
+
+ in -= strm->avail_in;
+ out -= strm->avail_out;
+ strm->total_in += in;
+ strm->total_out += out;
+ state->total += out;
+ if (state->wrap && out)
+ strm->adler = state->check =
+ UPDATE(state->check, strm->next_out - out, out);
+
+ strm->data_type = state->bits + (state->last ? 64 : 0) +
+ (state->mode == TYPE ? 128 : 0);
+ if (((in == 0 && out == 0) || flush == Z_FINISH) && ret == Z_OK)
+ ret = Z_BUF_ERROR;
+
+ if (flush == Z_PACKET_FLUSH && ret == Z_OK &&
+ (strm->avail_out != 0 || strm->avail_in == 0))
+ return zlib_inflateSyncPacket(strm);
+ return ret;
+}
+
+int zlib_inflateEnd(z_streamp strm)
+{
+ if (strm == NULL || strm->state == NULL)
+ return Z_STREAM_ERROR;
return Z_OK;
}
+#if 0
+int zlib_inflateSetDictionary(z_streamp strm, const Byte *dictionary,
+ uInt dictLength)
+{
+ struct inflate_state *state;
+ unsigned long id;
+
+ /* check state */
+ if (strm == NULL || strm->state == NULL) return Z_STREAM_ERROR;
+ state = (struct inflate_state *)strm->state;
+ if (state->wrap != 0 && state->mode != DICT)
+ return Z_STREAM_ERROR;
+
+ /* check for correct dictionary id */
+ if (state->mode == DICT) {
+ id = zlib_adler32(0L, NULL, 0);
+ id = zlib_adler32(id, dictionary, dictLength);
+ if (id != state->check)
+ return Z_DATA_ERROR;
+ }
+
+ /* copy dictionary to window */
+ zlib_updatewindow(strm, strm->avail_out);
-int zlib_inflateInit_(
- z_streamp z,
- const char *version,
- int stream_size
-)
+ if (dictLength > state->wsize) {
+ memcpy(state->window, dictionary + dictLength - state->wsize,
+ state->wsize);
+ state->whave = state->wsize;
+ }
+ else {
+ memcpy(state->window + state->wsize - dictLength, dictionary,
+ dictLength);
+ state->whave = dictLength;
+ }
+ state->havedict = 1;
+ return Z_OK;
+}
+#endif
+
+#if 0
+/*
+ Search buf[0..len-1] for the pattern: 0, 0, 0xff, 0xff. Return when found
+ or when out of input. When called, *have is the number of pattern bytes
+ found in order so far, in 0..3. On return *have is updated to the new
+ state. If on return *have equals four, then the pattern was found and the
+ return value is how many bytes were read including the last byte of the
+ pattern. If *have is less than four, then the pattern has not been found
+ yet and the return value is len. In the latter case, zlib_syncsearch() can be
+ called again with more data and the *have state. *have is initialized to
+ zero for the first call.
+ */
+static unsigned zlib_syncsearch(unsigned *have, unsigned char *buf,
+ unsigned len)
{
- return zlib_inflateInit2_(z, DEF_WBITS, version, stream_size);
+ unsigned got;
+ unsigned next;
+
+ got = *have;
+ next = 0;
+ while (next < len && got < 4) {
+ if ((int)(buf[next]) == (got < 2 ? 0 : 0xff))
+ got++;
+ else if (buf[next])
+ got = 0;
+ else
+ got = 4 - got;
+ next++;
+ }
+ *have = got;
+ return next;
}
+#endif
-#undef NEEDBYTE
-#undef NEXTBYTE
-#define NEEDBYTE {if(z->avail_in==0)goto empty;r=trv;}
-#define NEXTBYTE (z->avail_in--,z->total_in++,*z->next_in++)
+#if 0
+int zlib_inflateSync(z_streamp strm)
+{
+ unsigned len; /* number of bytes to look at or looked at */
+ unsigned long in, out; /* temporary to save total_in and total_out */
+ unsigned char buf[4]; /* to restore bit buffer to byte string */
+ struct inflate_state *state;
+
+ /* check parameters */
+ if (strm == NULL || strm->state == NULL) return Z_STREAM_ERROR;
+ state = (struct inflate_state *)strm->state;
+ if (strm->avail_in == 0 && state->bits < 8) return Z_BUF_ERROR;
+
+ /* if first time, start search in bit buffer */
+ if (state->mode != SYNC) {
+ state->mode = SYNC;
+ state->hold <<= state->bits & 7;
+ state->bits -= state->bits & 7;
+ len = 0;
+ while (state->bits >= 8) {
+ buf[len++] = (unsigned char)(state->hold);
+ state->hold >>= 8;
+ state->bits -= 8;
+ }
+ state->have = 0;
+ zlib_syncsearch(&(state->have), buf, len);
+ }
+
+ /* search available input */
+ len = zlib_syncsearch(&(state->have), strm->next_in, strm->avail_in);
+ strm->avail_in -= len;
+ strm->next_in += len;
+ strm->total_in += len;
+
+ /* return no joy or set up to restart inflate() on a new block */
+ if (state->have != 4) return Z_DATA_ERROR;
+ in = strm->total_in; out = strm->total_out;
+ zlib_inflateReset(strm);
+ strm->total_in = in; strm->total_out = out;
+ state->mode = TYPE;
+ return Z_OK;
+}
+#endif
-int zlib_inflate(
- z_streamp z,
- int f
-)
+/*
+ * This subroutine adds the data at next_in/avail_in to the output history
+ * without performing any output. The output buffer must be "caught up";
+ * i.e. no pending output but this should always be the case. The state must
+ * be waiting on the start of a block (i.e. mode == TYPE or HEAD). On exit,
+ * the output will also be caught up, and the checksum will have been updated
+ * if need be.
+ */
+int zlib_inflateIncomp(z_stream *z)
{
- int r, trv;
- uInt b;
-
- if (z == NULL || z->state == NULL || z->next_in == NULL)
- return Z_STREAM_ERROR;
- trv = f == Z_FINISH ? Z_BUF_ERROR : Z_OK;
- r = Z_BUF_ERROR;
- while (1) switch (z->state->mode)
- {
- case METHOD:
- NEEDBYTE
- if (((z->state->sub.method = NEXTBYTE) & 0xf) != Z_DEFLATED)
- {
- z->state->mode = I_BAD;
- z->msg = (char*)"unknown compression method";
- z->state->sub.marker = 5; /* can't try inflateSync */
- break;
- }
- if ((z->state->sub.method >> 4) + 8 > z->state->wbits)
- {
- z->state->mode = I_BAD;
- z->msg = (char*)"invalid window size";
- z->state->sub.marker = 5; /* can't try inflateSync */
- break;
- }
- z->state->mode = FLAG;
- case FLAG:
- NEEDBYTE
- b = NEXTBYTE;
- if (((z->state->sub.method << 8) + b) % 31)
- {
- z->state->mode = I_BAD;
- z->msg = (char*)"incorrect header check";
- z->state->sub.marker = 5; /* can't try inflateSync */
- break;
- }
- if (!(b & PRESET_DICT))
- {
- z->state->mode = BLOCKS;
- break;
- }
- z->state->mode = DICT4;
- case DICT4:
- NEEDBYTE
- z->state->sub.check.need = (uLong)NEXTBYTE << 24;
- z->state->mode = DICT3;
- case DICT3:
- NEEDBYTE
- z->state->sub.check.need += (uLong)NEXTBYTE << 16;
- z->state->mode = DICT2;
- case DICT2:
- NEEDBYTE
- z->state->sub.check.need += (uLong)NEXTBYTE << 8;
- z->state->mode = DICT1;
- case DICT1:
- NEEDBYTE
- z->state->sub.check.need += (uLong)NEXTBYTE;
- z->adler = z->state->sub.check.need;
- z->state->mode = DICT0;
- return Z_NEED_DICT;
- case DICT0:
- z->state->mode = I_BAD;
- z->msg = (char*)"need dictionary";
- z->state->sub.marker = 0; /* can try inflateSync */
- return Z_STREAM_ERROR;
- case BLOCKS:
- r = zlib_inflate_blocks(z->state->blocks, z, r);
- if (f == Z_PACKET_FLUSH && z->avail_in == 0 && z->avail_out != 0)
- r = zlib_inflate_packet_flush(z->state->blocks);
- if (r == Z_DATA_ERROR)
- {
- z->state->mode = I_BAD;
- z->state->sub.marker = 0; /* can try inflateSync */
- break;
- }
- if (r == Z_OK)
- r = trv;
- if (r != Z_STREAM_END)
- return r;
- r = trv;
- zlib_inflate_blocks_reset(z->state->blocks, z, &z->state->sub.check.was);
- if (z->state->nowrap)
- {
- z->state->mode = I_DONE;
- break;
- }
- z->state->mode = CHECK4;
- case CHECK4:
- NEEDBYTE
- z->state->sub.check.need = (uLong)NEXTBYTE << 24;
- z->state->mode = CHECK3;
- case CHECK3:
- NEEDBYTE
- z->state->sub.check.need += (uLong)NEXTBYTE << 16;
- z->state->mode = CHECK2;
- case CHECK2:
- NEEDBYTE
- z->state->sub.check.need += (uLong)NEXTBYTE << 8;
- z->state->mode = CHECK1;
- case CHECK1:
- NEEDBYTE
- z->state->sub.check.need += (uLong)NEXTBYTE;
-
- if (z->state->sub.check.was != z->state->sub.check.need)
- {
- z->state->mode = I_BAD;
- z->msg = (char*)"incorrect data check";
- z->state->sub.marker = 5; /* can't try inflateSync */
- break;
- }
- z->state->mode = I_DONE;
- case I_DONE:
- return Z_STREAM_END;
- case I_BAD:
- return Z_DATA_ERROR;
- default:
- return Z_STREAM_ERROR;
- }
- empty:
- if (f != Z_PACKET_FLUSH)
- return r;
- z->state->mode = I_BAD;
- z->msg = (char *)"need more for packet flush";
- z->state->sub.marker = 0; /* can try inflateSync */
- return Z_DATA_ERROR;
+ struct inflate_state *state = (struct inflate_state *)z->state;
+ Byte *saved_no = z->next_out;
+ uInt saved_ao = z->avail_out;
+
+ if (state->mode != TYPE && state->mode != HEAD)
+ return Z_DATA_ERROR;
+
+ /* Setup some variables to allow misuse of updateWindow */
+ z->avail_out = 0;
+ z->next_out = z->next_in + z->avail_in;
+
+ zlib_updatewindow(z, z->avail_in);
+
+ /* Restore saved variables */
+ z->avail_out = saved_ao;
+ z->next_out = saved_no;
+
+ z->adler = state->check =
+ UPDATE(state->check, z->next_in, z->avail_in);
+
+ z->total_out += z->avail_in;
+ z->total_in += z->avail_in;
+ z->next_in += z->avail_in;
+ state->total += z->avail_in;
+ z->avail_in = 0;
+
+ return Z_OK;
}
--- /dev/null
+/* inflate.h -- internal inflate state definition
+ * Copyright (C) 1995-2004 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+ part of the implementation of the compression library and is
+ subject to change. Applications should only use zlib.h.
+ */
+
+/* Possible inflate modes between inflate() calls */
+typedef enum {
+ HEAD, /* i: waiting for magic header */
+ FLAGS, /* i: waiting for method and flags (gzip) */
+ TIME, /* i: waiting for modification time (gzip) */
+ OS, /* i: waiting for extra flags and operating system (gzip) */
+ EXLEN, /* i: waiting for extra length (gzip) */
+ EXTRA, /* i: waiting for extra bytes (gzip) */
+ NAME, /* i: waiting for end of file name (gzip) */
+ COMMENT, /* i: waiting for end of comment (gzip) */
+ HCRC, /* i: waiting for header crc (gzip) */
+ DICTID, /* i: waiting for dictionary check value */
+ DICT, /* waiting for inflateSetDictionary() call */
+ TYPE, /* i: waiting for type bits, including last-flag bit */
+ TYPEDO, /* i: same, but skip check to exit inflate on new block */
+ STORED, /* i: waiting for stored size (length and complement) */
+ COPY, /* i/o: waiting for input or output to copy stored block */
+ TABLE, /* i: waiting for dynamic block table lengths */
+ LENLENS, /* i: waiting for code length code lengths */
+ CODELENS, /* i: waiting for length/lit and distance code lengths */
+ LEN, /* i: waiting for length/lit code */
+ LENEXT, /* i: waiting for length extra bits */
+ DIST, /* i: waiting for distance code */
+ DISTEXT, /* i: waiting for distance extra bits */
+ MATCH, /* o: waiting for output space to copy string */
+ LIT, /* o: waiting for output space to write literal */
+ CHECK, /* i: waiting for 32-bit check value */
+ LENGTH, /* i: waiting for 32-bit length (gzip) */
+ DONE, /* finished check, done -- remain here until reset */
+ BAD, /* got a data error -- remain here until reset */
+ MEM, /* got an inflate() memory error -- remain here until reset */
+ SYNC /* looking for synchronization bytes to restart inflate() */
+} inflate_mode;
+
+/*
+ State transitions between above modes -
+
+ (most modes can go to the BAD or MEM mode -- not shown for clarity)
+
+ Process header:
+ HEAD -> (gzip) or (zlib)
+ (gzip) -> FLAGS -> TIME -> OS -> EXLEN -> EXTRA -> NAME
+ NAME -> COMMENT -> HCRC -> TYPE
+ (zlib) -> DICTID or TYPE
+ DICTID -> DICT -> TYPE
+ Read deflate blocks:
+ TYPE -> STORED or TABLE or LEN or CHECK
+ STORED -> COPY -> TYPE
+ TABLE -> LENLENS -> CODELENS -> LEN
+ Read deflate codes:
+ LEN -> LENEXT or LIT or TYPE
+ LENEXT -> DIST -> DISTEXT -> MATCH -> LEN
+ LIT -> LEN
+ Process trailer:
+ CHECK -> LENGTH -> DONE
+ */
+
+/* state maintained between inflate() calls. Approximately 7K bytes. */
+struct inflate_state {
+ inflate_mode mode; /* current inflate mode */
+ int last; /* true if processing last block */
+ int wrap; /* bit 0 true for zlib, bit 1 true for gzip */
+ int havedict; /* true if dictionary provided */
+ int flags; /* gzip header method and flags (0 if zlib) */
+ unsigned dmax; /* zlib header max distance (INFLATE_STRICT) */
+ unsigned long check; /* protected copy of check value */
+ unsigned long total; /* protected copy of output count */
+ /* gz_headerp head; */ /* where to save gzip header information */
+ /* sliding window */
+ unsigned wbits; /* log base 2 of requested window size */
+ unsigned wsize; /* window size or zero if not using window */
+ unsigned whave; /* valid bytes in the window */
+ unsigned write; /* window write index */
+ unsigned char *window; /* allocated sliding window, if needed */
+ /* bit accumulator */
+ unsigned long hold; /* input bit accumulator */
+ unsigned bits; /* number of bits in "in" */
+ /* for string and stored block copying */
+ unsigned length; /* literal or length of data to copy */
+ unsigned offset; /* distance back to copy string from */
+ /* for table and code decoding */
+ unsigned extra; /* extra bits needed */
+ /* fixed and dynamic code tables */
+ code const *lencode; /* starting table for length/literal codes */
+ code const *distcode; /* starting table for distance codes */
+ unsigned lenbits; /* index bits for lencode */
+ unsigned distbits; /* index bits for distcode */
+ /* dynamic table building */
+ unsigned ncode; /* number of code length code lengths */
+ unsigned nlen; /* number of length code lengths */
+ unsigned ndist; /* number of distance code lengths */
+ unsigned have; /* number of code lengths in lens[] */
+ code *next; /* next available space in codes[] */
+ unsigned short lens[320]; /* temporary storage for code lengths */
+ unsigned short work[288]; /* work area for code table building */
+ code codes[ENOUGH]; /* space for code tables */
+};
EXPORT_SYMBOL(zlib_inflate_workspacesize);
EXPORT_SYMBOL(zlib_inflate);
-EXPORT_SYMBOL(zlib_inflateInit_);
-EXPORT_SYMBOL(zlib_inflateInit2_);
+EXPORT_SYMBOL(zlib_inflateInit2);
EXPORT_SYMBOL(zlib_inflateEnd);
EXPORT_SYMBOL(zlib_inflateReset);
EXPORT_SYMBOL(zlib_inflateIncomp);
+++ /dev/null
-/* inflate.c -- zlib interface to inflate modules
- * Copyright (C) 1995-1998 Mark Adler
- * For conditions of distribution and use, see copyright notice in zlib.h
- */
-
-#include <linux/zutil.h>
-#include "infblock.h"
-#include "infutil.h"
-
-#if 0
-int zlib_inflateSync(
- z_streamp z
-)
-{
- uInt n; /* number of bytes to look at */
- Byte *p; /* pointer to bytes */
- uInt m; /* number of marker bytes found in a row */
- uLong r, w; /* temporaries to save total_in and total_out */
-
- /* set up */
- if (z == NULL || z->state == NULL)
- return Z_STREAM_ERROR;
- if (z->state->mode != I_BAD)
- {
- z->state->mode = I_BAD;
- z->state->sub.marker = 0;
- }
- if ((n = z->avail_in) == 0)
- return Z_BUF_ERROR;
- p = z->next_in;
- m = z->state->sub.marker;
-
- /* search */
- while (n && m < 4)
- {
- static const Byte mark[4] = {0, 0, 0xff, 0xff};
- if (*p == mark[m])
- m++;
- else if (*p)
- m = 0;
- else
- m = 4 - m;
- p++, n--;
- }
-
- /* restore */
- z->total_in += p - z->next_in;
- z->next_in = p;
- z->avail_in = n;
- z->state->sub.marker = m;
-
- /* return no joy or set up to restart on a new block */
- if (m != 4)
- return Z_DATA_ERROR;
- r = z->total_in; w = z->total_out;
- zlib_inflateReset(z);
- z->total_in = r; z->total_out = w;
- z->state->mode = BLOCKS;
- return Z_OK;
-}
-#endif /* 0 */
-
-
-/* Returns true if inflate is currently at the end of a block generated
- * by Z_SYNC_FLUSH or Z_FULL_FLUSH. This function is used by one PPP
- * implementation to provide an additional safety check. PPP uses Z_SYNC_FLUSH
- * but removes the length bytes of the resulting empty stored block. When
- * decompressing, PPP checks that at the end of input packet, inflate is
- * waiting for these length bytes.
- */
-#if 0
-int zlib_inflateSyncPoint(
- z_streamp z
-)
-{
- if (z == NULL || z->state == NULL || z->state->blocks == NULL)
- return Z_STREAM_ERROR;
- return zlib_inflate_blocks_sync_point(z->state->blocks);
-}
-#endif /* 0 */
-
-/*
- * This subroutine adds the data at next_in/avail_in to the output history
- * without performing any output. The output buffer must be "caught up";
- * i.e. no pending output (hence s->read equals s->write), and the state must
- * be BLOCKS (i.e. we should be willing to see the start of a series of
- * BLOCKS). On exit, the output will also be caught up, and the checksum
- * will have been updated if need be.
- */
-static int zlib_inflate_addhistory(inflate_blocks_statef *s,
- z_stream *z)
-{
- uLong b; /* bit buffer */ /* NOT USED HERE */
- uInt k; /* bits in bit buffer */ /* NOT USED HERE */
- uInt t; /* temporary storage */
- Byte *p; /* input data pointer */
- uInt n; /* bytes available there */
- Byte *q; /* output window write pointer */
- uInt m; /* bytes to end of window or read pointer */
-
- if (s->read != s->write)
- return Z_STREAM_ERROR;
- if (s->mode != TYPE)
- return Z_DATA_ERROR;
-
- /* we're ready to rock */
- LOAD
- /* while there is input ready, copy to output buffer, moving
- * pointers as needed.
- */
- while (n) {
- t = n; /* how many to do */
- /* is there room until end of buffer? */
- if (t > m) t = m;
- /* update check information */
- if (s->checkfn != NULL)
- s->check = (*s->checkfn)(s->check, q, t);
- memcpy(q, p, t);
- q += t;
- p += t;
- n -= t;
- z->total_out += t;
- s->read = q; /* drag read pointer forward */
-/* WWRAP */ /* expand WWRAP macro by hand to handle s->read */
- if (q == s->end) {
- s->read = q = s->window;
- m = WAVAIL;
- }
- }
- UPDATE
- return Z_OK;
-}
-
-
-/*
- * This subroutine adds the data at next_in/avail_in to the output history
- * without performing any output. The output buffer must be "caught up";
- * i.e. no pending output (hence s->read equals s->write), and the state must
- * be BLOCKS (i.e. we should be willing to see the start of a series of
- * BLOCKS). On exit, the output will also be caught up, and the checksum
- * will have been updated if need be.
- */
-
-int zlib_inflateIncomp(
- z_stream *z
-
-)
-{
- if (z->state->mode != BLOCKS)
- return Z_DATA_ERROR;
- return zlib_inflate_addhistory(z->state->blocks, z);
-}
/* inftrees.c -- generate Huffman trees for efficient decoding
- * Copyright (C) 1995-1998 Mark Adler
- * For conditions of distribution and use, see copyright notice in zlib.h
+ * Copyright (C) 1995-2005 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
*/
#include <linux/zutil.h>
#include "inftrees.h"
-#include "infutil.h"
-static const char inflate_copyright[] __attribute_used__ =
- " inflate 1.1.3 Copyright 1995-1998 Mark Adler ";
+#define MAXBITS 15
+
+const char inflate_copyright[] =
+ " inflate 1.2.3 Copyright 1995-2005 Mark Adler ";
/*
If you use the zlib library in a product, an acknowledgment is welcome
in the documentation of your product. If for some reason you cannot
include such an acknowledgment, I would appreciate that you keep this
copyright string in the executable of your product.
*/
-struct internal_state;
-
-/* simplify the use of the inflate_huft type with some defines */
-#define exop word.what.Exop
-#define bits word.what.Bits
-
-
-static int huft_build (
- uInt *, /* code lengths in bits */
- uInt, /* number of codes */
- uInt, /* number of "simple" codes */
- const uInt *, /* list of base values for non-simple codes */
- const uInt *, /* list of extra bits for non-simple codes */
- inflate_huft **, /* result: starting table */
- uInt *, /* maximum lookup bits (returns actual) */
- inflate_huft *, /* space for trees */
- uInt *, /* hufts used in space */
- uInt * ); /* space for values */
-
-/* Tables for deflate from PKZIP's appnote.txt. */
-static const uInt cplens[31] = { /* Copy lengths for literal codes 257..285 */
- 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31,
- 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0};
- /* see note #13 above about 258 */
-static const uInt cplext[31] = { /* Extra bits for literal codes 257..285 */
- 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2,
- 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 112, 112}; /* 112==invalid */
-static const uInt cpdist[30] = { /* Copy offsets for distance codes 0..29 */
- 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193,
- 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145,
- 8193, 12289, 16385, 24577};
-static const uInt cpdext[30] = { /* Extra bits for distance codes */
- 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6,
- 7, 7, 8, 8, 9, 9, 10, 10, 11, 11,
- 12, 12, 13, 13};
/*
- Huffman code decoding is performed using a multi-level table lookup.
- The fastest way to decode is to simply build a lookup table whose
- size is determined by the longest code. However, the time it takes
- to build this table can also be a factor if the data being decoded
- is not very long. The most common codes are necessarily the
- shortest codes, so those codes dominate the decoding time, and hence
- the speed. The idea is you can have a shorter table that decodes the
- shorter, more probable codes, and then point to subsidiary tables for
- the longer codes. The time it costs to decode the longer codes is
- then traded against the time it takes to make longer tables.
-
- This results of this trade are in the variables lbits and dbits
- below. lbits is the number of bits the first level table for literal/
- length codes can decode in one step, and dbits is the same thing for
- the distance codes. Subsequent tables are also less than or equal to
- those sizes. These values may be adjusted either when all of the
- codes are shorter than that, in which case the longest code length in
- bits is used, or when the shortest code is *longer* than the requested
- table size, in which case the length of the shortest code in bits is
- used.
-
- There are two different values for the two tables, since they code a
- different number of possibilities each. The literal/length table
- codes 286 possible values, or in a flat code, a little over eight
- bits. The distance table codes 30 possible values, or a little less
- than five bits, flat. The optimum values for speed end up being
- about one bit more than those, so lbits is 8+1 and dbits is 5+1.
- The optimum values may differ though from machine to machine, and
- possibly even between compilers. Your mileage may vary.
+ Build a set of tables to decode the provided canonical Huffman code.
+ The code lengths are lens[0..codes-1]. The result starts at *table,
+ whose indices are 0..2^bits-1. work is a writable array of at least
+ lens shorts, which is used as a work area. type is the type of code
+ to be generated, CODES, LENS, or DISTS. On return, zero is success,
+ -1 is an invalid code, and +1 means that ENOUGH isn't enough. table
+ on return points to the next available entry's address. bits is the
+ requested root table index bits, and on return it is the actual root
+ table index bits. It will differ if the request is greater than the
+ longest code or if it is less than the shortest code.
*/
-
-
-/* If BMAX needs to be larger than 16, then h and x[] should be uLong. */
-#define BMAX 15 /* maximum bit length of any code */
-
-static int huft_build(
- uInt *b, /* code lengths in bits (all assumed <= BMAX) */
- uInt n, /* number of codes (assumed <= 288) */
- uInt s, /* number of simple-valued codes (0..s-1) */
- const uInt *d, /* list of base values for non-simple codes */
- const uInt *e, /* list of extra bits for non-simple codes */
- inflate_huft **t, /* result: starting table */
- uInt *m, /* maximum lookup bits, returns actual */
- inflate_huft *hp, /* space for trees */
- uInt *hn, /* hufts used in space */
- uInt *v /* working area: values in order of bit length */
-)
-/* Given a list of code lengths and a maximum table size, make a set of
- tables to decode that set of codes. Return Z_OK on success, Z_BUF_ERROR
- if the given code set is incomplete (the tables are still built in this
- case), Z_DATA_ERROR if the input is invalid (an over-subscribed set of
- lengths), or Z_MEM_ERROR if not enough memory. */
+int zlib_inflate_table(type, lens, codes, table, bits, work)
+codetype type;
+unsigned short *lens;
+unsigned codes;
+code **table;
+unsigned *bits;
+unsigned short *work;
{
+ unsigned len; /* a code's length in bits */
+ unsigned sym; /* index of code symbols */
+ unsigned min, max; /* minimum and maximum code lengths */
+ unsigned root; /* number of index bits for root table */
+ unsigned curr; /* number of index bits for current table */
+ unsigned drop; /* code bits to drop for sub-table */
+ int left; /* number of prefix codes available */
+ unsigned used; /* code entries in table used */
+ unsigned huff; /* Huffman code */
+ unsigned incr; /* for incrementing code, index */
+ unsigned fill; /* index for replicating entries */
+ unsigned low; /* low bits for current root entry */
+ unsigned mask; /* mask for low root bits */
+ code this; /* table entry for duplication */
+ code *next; /* next available space in table */
+ const unsigned short *base; /* base value table to use */
+ const unsigned short *extra; /* extra bits table to use */
+ int end; /* use base and extra for symbol > end */
+ unsigned short count[MAXBITS+1]; /* number of codes of each length */
+ unsigned short offs[MAXBITS+1]; /* offsets in table for each length */
+ static const unsigned short lbase[31] = { /* Length codes 257..285 base */
+ 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31,
+ 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0};
+ static const unsigned short lext[31] = { /* Length codes 257..285 extra */
+ 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18,
+ 19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 16, 201, 196};
+ static const unsigned short dbase[32] = { /* Distance codes 0..29 base */
+ 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193,
+ 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145,
+ 8193, 12289, 16385, 24577, 0, 0};
+ static const unsigned short dext[32] = { /* Distance codes 0..29 extra */
+ 16, 16, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22,
+ 23, 23, 24, 24, 25, 25, 26, 26, 27, 27,
+ 28, 28, 29, 29, 64, 64};
+
+ /*
+ Process a set of code lengths to create a canonical Huffman code. The
+ code lengths are lens[0..codes-1]. Each length corresponds to the
+ symbols 0..codes-1. The Huffman code is generated by first sorting the
+ symbols by length from short to long, and retaining the symbol order
+ for codes with equal lengths. Then the code starts with all zero bits
+ for the first code of the shortest length, and the codes are integer
+ increments for the same length, and zeros are appended as the length
+ increases. For the deflate format, these bits are stored backwards
+ from their more natural integer increment ordering, and so when the
+ decoding tables are built in the large loop below, the integer codes
+ are incremented backwards.
+
+ This routine assumes, but does not check, that all of the entries in
+ lens[] are in the range 0..MAXBITS. The caller must assure this.
+ 1..MAXBITS is interpreted as that code length. zero means that that
+ symbol does not occur in this code.
+
+ The codes are sorted by computing a count of codes for each length,
+ creating from that a table of starting indices for each length in the
+ sorted table, and then entering the symbols in order in the sorted
+ table. The sorted table is work[], with that space being provided by
+ the caller.
+
+ The length counts are used for other purposes as well, i.e. finding
+ the minimum and maximum length codes, determining if there are any
+ codes at all, checking for a valid set of lengths, and looking ahead
+ at length counts to determine sub-table sizes when building the
+ decoding tables.
+ */
+
+ /* accumulate lengths for codes (assumes lens[] all in 0..MAXBITS) */
+ for (len = 0; len <= MAXBITS; len++)
+ count[len] = 0;
+ for (sym = 0; sym < codes; sym++)
+ count[lens[sym]]++;
+
+ /* bound code lengths, force root to be within code lengths */
+ root = *bits;
+ for (max = MAXBITS; max >= 1; max--)
+ if (count[max] != 0) break;
+ if (root > max) root = max;
+ if (max == 0) { /* no symbols to code at all */
+ this.op = (unsigned char)64; /* invalid code marker */
+ this.bits = (unsigned char)1;
+ this.val = (unsigned short)0;
+ *(*table)++ = this; /* make a table to force an error */
+ *(*table)++ = this;
+ *bits = 1;
+ return 0; /* no symbols, but wait for decoding to report error */
+ }
+ for (min = 1; min <= MAXBITS; min++)
+ if (count[min] != 0) break;
+ if (root < min) root = min;
+
+ /* check for an over-subscribed or incomplete set of lengths */
+ left = 1;
+ for (len = 1; len <= MAXBITS; len++) {
+ left <<= 1;
+ left -= count[len];
+ if (left < 0) return -1; /* over-subscribed */
+ }
+ if (left > 0 && (type == CODES || max != 1))
+ return -1; /* incomplete set */
+
+ /* generate offsets into symbol table for each length for sorting */
+ offs[1] = 0;
+ for (len = 1; len < MAXBITS; len++)
+ offs[len + 1] = offs[len] + count[len];
+
+ /* sort symbols by length, by symbol order within each length */
+ for (sym = 0; sym < codes; sym++)
+ if (lens[sym] != 0) work[offs[lens[sym]]++] = (unsigned short)sym;
+
+ /*
+ Create and fill in decoding tables. In this loop, the table being
+ filled is at next and has curr index bits. The code being used is huff
+ with length len. That code is converted to an index by dropping drop
+ bits off of the bottom. For codes where len is less than drop + curr,
+ those top drop + curr - len bits are incremented through all values to
+ fill the table with replicated entries.
+
+ root is the number of index bits for the root table. When len exceeds
+ root, sub-tables are created pointed to by the root entry with an index
+ of the low root bits of huff. This is saved in low to check for when a
+ new sub-table should be started. drop is zero when the root table is
+ being filled, and drop is root when sub-tables are being filled.
+
+ When a new sub-table is needed, it is necessary to look ahead in the
+ code lengths to determine what size sub-table is needed. The length
+ counts are used for this, and so count[] is decremented as codes are
+ entered in the tables.
+
+ used keeps track of how many table entries have been allocated from the
+ provided *table space. It is checked when a LENS table is being made
+ against the space in *table, ENOUGH, minus the maximum space needed by
+ the worst case distance code, MAXD. This should never happen, but the
+ sufficiency of ENOUGH has not been proven exhaustively, hence the check.
+ This assumes that when type == LENS, bits == 9.
+
+ sym increments through all symbols, and the loop terminates when
+ all codes of length max, i.e. all codes, have been processed. This
+ routine permits incomplete codes, so another loop after this one fills
+ in the rest of the decoding tables with invalid code markers.
+ */
+
+ /* set up for code type */
+ switch (type) {
+ case CODES:
+ base = extra = work; /* dummy value--not used */
+ end = 19;
+ break;
+ case LENS:
+ base = lbase;
+ base -= 257;
+ extra = lext;
+ extra -= 257;
+ end = 256;
+ break;
+ default: /* DISTS */
+ base = dbase;
+ extra = dext;
+ end = -1;
+ }
- uInt a; /* counter for codes of length k */
- uInt c[BMAX+1]; /* bit length count table */
- uInt f; /* i repeats in table every f entries */
- int g; /* maximum code length */
- int h; /* table level */
- register uInt i; /* counter, current code */
- register uInt j; /* counter */
- register int k; /* number of bits in current code */
- int l; /* bits per table (returned in m) */
- uInt mask; /* (1 << w) - 1, to avoid cc -O bug on HP */
- register uInt *p; /* pointer into c[], b[], or v[] */
- inflate_huft *q; /* points to current table */
- struct inflate_huft_s r; /* table entry for structure assignment */
- inflate_huft *u[BMAX]; /* table stack */
- register int w; /* bits before this table == (l * h) */
- uInt x[BMAX+1]; /* bit offsets, then code stack */
- uInt *xp; /* pointer into x */
- int y; /* number of dummy codes added */
- uInt z; /* number of entries in current table */
-
-
- /* Generate counts for each bit length */
- p = c;
-#define C0 *p++ = 0;
-#define C2 C0 C0 C0 C0
-#define C4 C2 C2 C2 C2
- C4 /* clear c[]--assume BMAX+1 is 16 */
- p = b; i = n;
- do {
- c[*p++]++; /* assume all entries <= BMAX */
- } while (--i);
- if (c[0] == n) /* null input--all zero length codes */
- {
- *t = NULL;
- *m = 0;
- return Z_OK;
- }
-
-
- /* Find minimum and maximum length, bound *m by those */
- l = *m;
- for (j = 1; j <= BMAX; j++)
- if (c[j])
- break;
- k = j; /* minimum code length */
- if ((uInt)l < j)
- l = j;
- for (i = BMAX; i; i--)
- if (c[i])
- break;
- g = i; /* maximum code length */
- if ((uInt)l > i)
- l = i;
- *m = l;
-
-
- /* Adjust last length count to fill out codes, if needed */
- for (y = 1 << j; j < i; j++, y <<= 1)
- if ((y -= c[j]) < 0)
- return Z_DATA_ERROR;
- if ((y -= c[i]) < 0)
- return Z_DATA_ERROR;
- c[i] += y;
-
-
- /* Generate starting offsets into the value table for each length */
- x[1] = j = 0;
- p = c + 1; xp = x + 2;
- while (--i) { /* note that i == g from above */
- *xp++ = (j += *p++);
- }
-
-
- /* Make a table of values in order of bit lengths */
- p = b; i = 0;
- do {
- if ((j = *p++) != 0)
- v[x[j]++] = i;
- } while (++i < n);
- n = x[g]; /* set n to length of v */
-
-
- /* Generate the Huffman codes and for each, make the table entries */
- x[0] = i = 0; /* first Huffman code is zero */
- p = v; /* grab values in bit order */
- h = -1; /* no tables yet--level -1 */
- w = -l; /* bits decoded == (l * h) */
- u[0] = NULL; /* just to keep compilers happy */
- q = NULL; /* ditto */
- z = 0; /* ditto */
-
- /* go through the bit lengths (k already is bits in shortest code) */
- for (; k <= g; k++)
- {
- a = c[k];
- while (a--)
- {
- /* here i is the Huffman code of length k bits for value *p */
- /* make tables up to required level */
- while (k > w + l)
- {
- h++;
- w += l; /* previous table always l bits */
-
- /* compute minimum size table less than or equal to l bits */
- z = g - w;
- z = z > (uInt)l ? l : z; /* table size upper limit */
- if ((f = 1 << (j = k - w)) > a + 1) /* try a k-w bit table */
- { /* too few codes for k-w bit table */
- f -= a + 1; /* deduct codes from patterns left */
- xp = c + k;
- if (j < z)
- while (++j < z) /* try smaller tables up to z bits */
- {
- if ((f <<= 1) <= *++xp)
- break; /* enough codes to use up j bits */
- f -= *xp; /* else deduct codes from patterns */
- }
+ /* initialize state for loop */
+ huff = 0; /* starting code */
+ sym = 0; /* starting code symbol */
+ len = min; /* starting code length */
+ next = *table; /* current table to fill in */
+ curr = root; /* current table index bits */
+ drop = 0; /* current bits to drop from code for index */
+ low = (unsigned)(-1); /* trigger new sub-table when len > root */
+ used = 1U << root; /* use root table entries */
+ mask = used - 1; /* mask for comparing low */
+
+ /* check available table space */
+ if (type == LENS && used >= ENOUGH - MAXD)
+ return 1;
+
+ /* process all codes and make table entries */
+ for (;;) {
+ /* create table entry */
+ this.bits = (unsigned char)(len - drop);
+ if ((int)(work[sym]) < end) {
+ this.op = (unsigned char)0;
+ this.val = work[sym];
}
- z = 1 << j; /* table entries for j-bit table */
-
- /* allocate new table */
- if (*hn + z > MANY) /* (note: doesn't matter for fixed) */
- return Z_DATA_ERROR; /* overflow of MANY */
- u[h] = q = hp + *hn;
- *hn += z;
-
- /* connect to last table, if there is one */
- if (h)
- {
- x[h] = i; /* save pattern for backing up */
- r.bits = (Byte)l; /* bits to dump before this table */
- r.exop = (Byte)j; /* bits in this table */
- j = i >> (w - l);
- r.base = (uInt)(q - u[h-1] - j); /* offset to this table */
- u[h-1][j] = r; /* connect to last table */
+ else if ((int)(work[sym]) > end) {
+ this.op = (unsigned char)(extra[work[sym]]);
+ this.val = base[work[sym]];
+ }
+ else {
+ this.op = (unsigned char)(32 + 64); /* end of block */
+ this.val = 0;
}
- else
- *t = q; /* first table is returned result */
- }
-
- /* set up table entry in r */
- r.bits = (Byte)(k - w);
- if (p >= v + n)
- r.exop = 128 + 64; /* out of values--invalid code */
- else if (*p < s)
- {
- r.exop = (Byte)(*p < 256 ? 0 : 32 + 64); /* 256 is end-of-block */
- r.base = *p++; /* simple code is just the value */
- }
- else
- {
- r.exop = (Byte)(e[*p - s] + 16 + 64);/* non-simple--look up in lists */
- r.base = d[*p++ - s];
- }
-
- /* fill code-like entries with r */
- f = 1 << (k - w);
- for (j = i >> w; j < z; j += f)
- q[j] = r;
-
- /* backwards increment the k-bit code i */
- for (j = 1 << (k - 1); i & j; j >>= 1)
- i ^= j;
- i ^= j;
-
- /* backup over finished tables */
- mask = (1 << w) - 1; /* needed on HP, cc -O bug */
- while ((i & mask) != x[h])
- {
- h--; /* don't need to update q */
- w -= l;
- mask = (1 << w) - 1;
- }
- }
- }
+ /* replicate for those indices with low len bits equal to huff */
+ incr = 1U << (len - drop);
+ fill = 1U << curr;
+ min = fill; /* save offset to next table */
+ do {
+ fill -= incr;
+ next[(huff >> drop) + fill] = this;
+ } while (fill != 0);
+
+ /* backwards increment the len-bit code huff */
+ incr = 1U << (len - 1);
+ while (huff & incr)
+ incr >>= 1;
+ if (incr != 0) {
+ huff &= incr - 1;
+ huff += incr;
+ }
+ else
+ huff = 0;
- /* Return Z_BUF_ERROR if we were given an incomplete table */
- return y != 0 && g != 1 ? Z_BUF_ERROR : Z_OK;
-}
+ /* go to next symbol, update count, len */
+ sym++;
+ if (--(count[len]) == 0) {
+ if (len == max) break;
+ len = lens[work[sym]];
+ }
+ /* create new sub-table if needed */
+ if (len > root && (huff & mask) != low) {
+ /* if first time, transition to sub-tables */
+ if (drop == 0)
+ drop = root;
+
+ /* increment past last table */
+ next += min; /* here min is 1 << curr */
+
+ /* determine length of next table */
+ curr = len - drop;
+ left = (int)(1 << curr);
+ while (curr + drop < max) {
+ left -= count[curr + drop];
+ if (left <= 0) break;
+ curr++;
+ left <<= 1;
+ }
-int zlib_inflate_trees_bits(
- uInt *c, /* 19 code lengths */
- uInt *bb, /* bits tree desired/actual depth */
- inflate_huft **tb, /* bits tree result */
- inflate_huft *hp, /* space for trees */
- z_streamp z /* for messages */
-)
-{
- int r;
- uInt hn = 0; /* hufts used in space */
- uInt *v; /* work area for huft_build */
-
- v = WS(z)->tree_work_area_1;
- r = huft_build(c, 19, 19, NULL, NULL, tb, bb, hp, &hn, v);
- if (r == Z_DATA_ERROR)
- z->msg = (char*)"oversubscribed dynamic bit lengths tree";
- else if (r == Z_BUF_ERROR || *bb == 0)
- {
- z->msg = (char*)"incomplete dynamic bit lengths tree";
- r = Z_DATA_ERROR;
- }
- return r;
-}
+ /* check for enough space */
+ used += 1U << curr;
+ if (type == LENS && used >= ENOUGH - MAXD)
+ return 1;
-int zlib_inflate_trees_dynamic(
- uInt nl, /* number of literal/length codes */
- uInt nd, /* number of distance codes */
- uInt *c, /* that many (total) code lengths */
- uInt *bl, /* literal desired/actual bit depth */
- uInt *bd, /* distance desired/actual bit depth */
- inflate_huft **tl, /* literal/length tree result */
- inflate_huft **td, /* distance tree result */
- inflate_huft *hp, /* space for trees */
- z_streamp z /* for messages */
-)
-{
- int r;
- uInt hn = 0; /* hufts used in space */
- uInt *v; /* work area for huft_build */
-
- /* allocate work area */
- v = WS(z)->tree_work_area_2;
-
- /* build literal/length tree */
- r = huft_build(c, nl, 257, cplens, cplext, tl, bl, hp, &hn, v);
- if (r != Z_OK || *bl == 0)
- {
- if (r == Z_DATA_ERROR)
- z->msg = (char*)"oversubscribed literal/length tree";
- else if (r != Z_MEM_ERROR)
- {
- z->msg = (char*)"incomplete literal/length tree";
- r = Z_DATA_ERROR;
- }
- return r;
- }
-
- /* build distance tree */
- r = huft_build(c + nl, nd, 0, cpdist, cpdext, td, bd, hp, &hn, v);
- if (r != Z_OK || (*bd == 0 && nl > 257))
- {
- if (r == Z_DATA_ERROR)
- z->msg = (char*)"oversubscribed distance tree";
- else if (r == Z_BUF_ERROR) {
-#ifdef PKZIP_BUG_WORKAROUND
- r = Z_OK;
- }
-#else
- z->msg = (char*)"incomplete distance tree";
- r = Z_DATA_ERROR;
- }
- else if (r != Z_MEM_ERROR)
- {
- z->msg = (char*)"empty distance tree with lengths";
- r = Z_DATA_ERROR;
+ /* point entry in root table to sub-table */
+ low = huff & mask;
+ (*table)[low].op = (unsigned char)curr;
+ (*table)[low].bits = (unsigned char)root;
+ (*table)[low].val = (unsigned short)(next - *table);
+ }
}
- return r;
-#endif
- }
- /* done */
- return Z_OK;
-}
+ /*
+ Fill in rest of table for incomplete codes. This loop is similar to the
+ loop above in incrementing huff for table indices. It is assumed that
+ len is equal to curr + drop, so there is no loop needed to increment
+ through high index bits. When the current sub-table is filled, the loop
+ drops back to the root table to fill in any remaining entries there.
+ */
+ this.op = (unsigned char)64; /* invalid code marker */
+ this.bits = (unsigned char)(len - drop);
+ this.val = (unsigned short)0;
+ while (huff != 0) {
+ /* when done with sub-table, drop back to root table */
+ if (drop != 0 && (huff & mask) != low) {
+ drop = 0;
+ len = root;
+ next = *table;
+ this.bits = (unsigned char)len;
+ }
+ /* put invalid code marker in table */
+ next[huff >> drop] = this;
-int zlib_inflate_trees_fixed(
- uInt *bl, /* literal desired/actual bit depth */
- uInt *bd, /* distance desired/actual bit depth */
- inflate_huft **tl, /* literal/length tree result */
- inflate_huft **td, /* distance tree result */
- inflate_huft *hp, /* space for trees */
- z_streamp z /* for memory allocation */
-)
-{
- int i; /* temporary variable */
- unsigned l[288]; /* length list for huft_build */
- uInt *v; /* work area for huft_build */
-
- /* set up literal table */
- for (i = 0; i < 144; i++)
- l[i] = 8;
- for (; i < 256; i++)
- l[i] = 9;
- for (; i < 280; i++)
- l[i] = 7;
- for (; i < 288; i++) /* make a complete, but wrong code set */
- l[i] = 8;
- *bl = 9;
- v = WS(z)->tree_work_area_1;
- if ((i = huft_build(l, 288, 257, cplens, cplext, tl, bl, hp, &i, v)) != 0)
- return i;
-
- /* set up distance table */
- for (i = 0; i < 30; i++) /* make an incomplete code set */
- l[i] = 5;
- *bd = 5;
- if ((i = huft_build(l, 30, 0, cpdist, cpdext, td, bd, hp, &i, v)) > 1)
- return i;
-
- return Z_OK;
+ /* backwards increment the len-bit code huff */
+ incr = 1U << (len - 1);
+ while (huff & incr)
+ incr >>= 1;
+ if (incr != 0) {
+ huff &= incr - 1;
+ huff += incr;
+ }
+ else
+ huff = 0;
+ }
+
+ /* set return parameters */
+ *table += used;
+ *bits = root;
+ return 0;
}
/* inftrees.h -- header to use inftrees.c
- * Copyright (C) 1995-1998 Mark Adler
- * For conditions of distribution and use, see copyright notice in zlib.h
+ * Copyright (C) 1995-2005 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
*/
/* WARNING: this file should *not* be used by applications. It is
subject to change. Applications should only use zlib.h.
*/
-/* Huffman code lookup table entry--this entry is four bytes for machines
- that have 16-bit pointers (e.g. PC's in the small or medium model). */
-
-#ifndef _INFTREES_H
-#define _INFTREES_H
-
-typedef struct inflate_huft_s inflate_huft;
-
-struct inflate_huft_s {
- union {
- struct {
- Byte Exop; /* number of extra bits or operation */
- Byte Bits; /* number of bits in this code or subcode */
- } what;
- uInt pad; /* pad structure to a power of 2 (4 bytes for */
- } word; /* 16-bit, 8 bytes for 32-bit int's) */
- uInt base; /* literal, length base, distance base,
- or table offset */
-};
+/* Structure for decoding tables. Each entry provides either the
+ information needed to do the operation requested by the code that
+ indexed that table entry, or it provides a pointer to another
+ table that indexes more bits of the code. op indicates whether
+ the entry is a pointer to another table, a literal, a length or
+ distance, an end-of-block, or an invalid code. For a table
+ pointer, the low four bits of op is the number of index bits of
+ that table. For a length or distance, the low four bits of op
+ is the number of extra bits to get after the code. bits is
+ the number of bits in this code or part of the code to drop off
+ of the bit buffer. val is the actual byte to output in the case
+ of a literal, the base length or distance, or the offset from
+ the current table to the next table. Each entry is four bytes. */
+typedef struct {
+ unsigned char op; /* operation, extra bits, table bits */
+ unsigned char bits; /* bits in this part of the code */
+ unsigned short val; /* offset in table or code value */
+} code;
+
+/* op values as set by inflate_table():
+ 00000000 - literal
+ 0000tttt - table link, tttt != 0 is the number of table index bits
+ 0001eeee - length or distance, eeee is the number of extra bits
+ 01100000 - end of block
+ 01000000 - invalid code
+ */
/* Maximum size of dynamic tree. The maximum found in a long but non-
- exhaustive search was 1004 huft structures (850 for length/literals
- and 154 for distances, the latter actually the result of an
- exhaustive search). The actual maximum is not known, but the
- value below is more than safe. */
-#define MANY 1440
-
-extern int zlib_inflate_trees_bits (
- uInt *, /* 19 code lengths */
- uInt *, /* bits tree desired/actual depth */
- inflate_huft **, /* bits tree result */
- inflate_huft *, /* space for trees */
- z_streamp); /* for messages */
-
-extern int zlib_inflate_trees_dynamic (
- uInt, /* number of literal/length codes */
- uInt, /* number of distance codes */
- uInt *, /* that many (total) code lengths */
- uInt *, /* literal desired/actual bit depth */
- uInt *, /* distance desired/actual bit depth */
- inflate_huft **, /* literal/length tree result */
- inflate_huft **, /* distance tree result */
- inflate_huft *, /* space for trees */
- z_streamp); /* for messages */
-
-extern int zlib_inflate_trees_fixed (
- uInt *, /* literal desired/actual bit depth */
- uInt *, /* distance desired/actual bit depth */
- inflate_huft **, /* literal/length tree result */
- inflate_huft **, /* distance tree result */
- inflate_huft *, /* space for trees */
- z_streamp); /* for memory allocation */
-
-#endif /* _INFTREES_H */
+ exhaustive search was 1444 code structures (852 for length/literals
+ and 592 for distances, the latter actually the result of an
+ exhaustive search). The true maximum is not known, but the value
+ below is more than safe. */
+#define ENOUGH 2048
+#define MAXD 592
+
+/* Type of code to build for inftable() */
+typedef enum {
+ CODES,
+ LENS,
+ DISTS
+} codetype;
+
+extern int zlib_inflate_table (codetype type, unsigned short *lens,
+ unsigned codes, code **table,
+ unsigned *bits, unsigned short *work);
+++ /dev/null
-/* inflate_util.c -- data and routines common to blocks and codes
- * Copyright (C) 1995-1998 Mark Adler
- * For conditions of distribution and use, see copyright notice in zlib.h
- */
-
-#include <linux/zutil.h>
-#include "infblock.h"
-#include "inftrees.h"
-#include "infcodes.h"
-#include "infutil.h"
-
-struct inflate_codes_state;
-
-/* And'ing with mask[n] masks the lower n bits */
-uInt zlib_inflate_mask[17] = {
- 0x0000,
- 0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff,
- 0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, 0xffff
-};
-
-
-/* copy as much as possible from the sliding window to the output area */
-int zlib_inflate_flush(
- inflate_blocks_statef *s,
- z_streamp z,
- int r
-)
-{
- uInt n;
- Byte *p;
- Byte *q;
-
- /* local copies of source and destination pointers */
- p = z->next_out;
- q = s->read;
-
- /* compute number of bytes to copy as far as end of window */
- n = (uInt)((q <= s->write ? s->write : s->end) - q);
- if (n > z->avail_out) n = z->avail_out;
- if (n && r == Z_BUF_ERROR) r = Z_OK;
-
- /* update counters */
- z->avail_out -= n;
- z->total_out += n;
-
- /* update check information */
- if (s->checkfn != NULL)
- z->adler = s->check = (*s->checkfn)(s->check, q, n);
-
- /* copy as far as end of window */
- memcpy(p, q, n);
- p += n;
- q += n;
-
- /* see if more to copy at beginning of window */
- if (q == s->end)
- {
- /* wrap pointers */
- q = s->window;
- if (s->write == s->end)
- s->write = s->window;
-
- /* compute bytes to copy */
- n = (uInt)(s->write - q);
- if (n > z->avail_out) n = z->avail_out;
- if (n && r == Z_BUF_ERROR) r = Z_OK;
-
- /* update counters */
- z->avail_out -= n;
- z->total_out += n;
-
- /* update check information */
- if (s->checkfn != NULL)
- z->adler = s->check = (*s->checkfn)(s->check, q, n);
-
- /* copy */
- memcpy(p, q, n);
- p += n;
- q += n;
- }
-
- /* update pointers */
- z->next_out = p;
- s->read = q;
-
- /* done */
- return r;
-}
#ifndef _INFUTIL_H
#define _INFUTIL_H
-#include <linux/zconf.h>
-#include "inftrees.h"
-#include "infcodes.h"
-
-typedef enum {
- TYPE, /* get type bits (3, including end bit) */
- LENS, /* get lengths for stored */
- STORED, /* processing stored block */
- TABLE, /* get table lengths */
- BTREE, /* get bit lengths tree for a dynamic block */
- DTREE, /* get length, distance trees for a dynamic block */
- CODES, /* processing fixed or dynamic block */
- DRY, /* output remaining window bytes */
- B_DONE, /* finished last block, done */
- B_BAD} /* got a data error--stuck here */
-inflate_block_mode;
-
-/* inflate blocks semi-private state */
-struct inflate_blocks_state {
-
- /* mode */
- inflate_block_mode mode; /* current inflate_block mode */
-
- /* mode dependent information */
- union {
- uInt left; /* if STORED, bytes left to copy */
- struct {
- uInt table; /* table lengths (14 bits) */
- uInt index; /* index into blens (or border) */
- uInt *blens; /* bit lengths of codes */
- uInt bb; /* bit length tree depth */
- inflate_huft *tb; /* bit length decoding tree */
- } trees; /* if DTREE, decoding info for trees */
- struct {
- inflate_codes_statef
- *codes;
- } decode; /* if CODES, current state */
- } sub; /* submode */
- uInt last; /* true if this block is the last block */
-
- /* mode independent information */
- uInt bitk; /* bits in bit buffer */
- uLong bitb; /* bit buffer */
- inflate_huft *hufts; /* single malloc for tree space */
- Byte *window; /* sliding window */
- Byte *end; /* one byte after sliding window */
- Byte *read; /* window read pointer */
- Byte *write; /* window write pointer */
- check_func checkfn; /* check function */
- uLong check; /* check on output */
-
-};
-
-
-/* defines for inflate input/output */
-/* update pointers and return */
-#define UPDBITS {s->bitb=b;s->bitk=k;}
-#define UPDIN {z->avail_in=n;z->total_in+=p-z->next_in;z->next_in=p;}
-#define UPDOUT {s->write=q;}
-#define UPDATE {UPDBITS UPDIN UPDOUT}
-#define LEAVE {UPDATE return zlib_inflate_flush(s,z,r);}
-/* get bytes and bits */
-#define LOADIN {p=z->next_in;n=z->avail_in;b=s->bitb;k=s->bitk;}
-#define NEEDBYTE {if(n)r=Z_OK;else LEAVE}
-#define NEXTBYTE (n--,*p++)
-#define NEEDBITS(j) {while(k<(j)){NEEDBYTE;b|=((uLong)NEXTBYTE)<<k;k+=8;}}
-#define DUMPBITS(j) {b>>=(j);k-=(j);}
-/* output bytes */
-#define WAVAIL (uInt)(q<s->read?s->read-q-1:s->end-q)
-#define LOADOUT {q=s->write;m=(uInt)WAVAIL;}
-#define WRAP {if(q==s->end&&s->read!=s->window){q=s->window;m=(uInt)WAVAIL;}}
-#define FLUSH {UPDOUT r=zlib_inflate_flush(s,z,r); LOADOUT}
-#define NEEDOUT {if(m==0){WRAP if(m==0){FLUSH WRAP if(m==0) LEAVE}}r=Z_OK;}
-#define OUTBYTE(a) {*q++=(Byte)(a);m--;}
-/* load local pointers */
-#define LOAD {LOADIN LOADOUT}
-
-/* masks for lower bits (size given to avoid silly warnings with Visual C++) */
-extern uInt zlib_inflate_mask[17];
-
-/* copy as much as possible from the sliding window to the output area */
-extern int zlib_inflate_flush (
- inflate_blocks_statef *,
- z_streamp ,
- int);
-
-/* inflate private state */
-typedef enum {
- METHOD, /* waiting for method byte */
- FLAG, /* waiting for flag byte */
- DICT4, /* four dictionary check bytes to go */
- DICT3, /* three dictionary check bytes to go */
- DICT2, /* two dictionary check bytes to go */
- DICT1, /* one dictionary check byte to go */
- DICT0, /* waiting for inflateSetDictionary */
- BLOCKS, /* decompressing blocks */
- CHECK4, /* four check bytes to go */
- CHECK3, /* three check bytes to go */
- CHECK2, /* two check bytes to go */
- CHECK1, /* one check byte to go */
- I_DONE, /* finished check, done */
- I_BAD} /* got an error--stay here */
-inflate_mode;
-
-struct internal_state {
-
- /* mode */
- inflate_mode mode; /* current inflate mode */
-
- /* mode dependent information */
- union {
- uInt method; /* if FLAGS, method byte */
- struct {
- uLong was; /* computed check value */
- uLong need; /* stream check value */
- } check; /* if CHECK, check values to compare */
- uInt marker; /* if BAD, inflateSync's marker bytes count */
- } sub; /* submode */
-
- /* mode independent information */
- int nowrap; /* flag for no wrapper */
- uInt wbits; /* log2(window size) (8..15, defaults to 15) */
- inflate_blocks_statef
- *blocks; /* current inflate_blocks state */
-
-};
-
-/* inflate codes private state */
-typedef enum { /* waiting for "i:"=input, "o:"=output, "x:"=nothing */
- START, /* x: set up for LEN */
- LEN, /* i: get length/literal/eob next */
- LENEXT, /* i: getting length extra (have base) */
- DIST, /* i: get distance next */
- DISTEXT, /* i: getting distance extra */
- COPY, /* o: copying bytes in window, waiting for space */
- LIT, /* o: got literal, waiting for output space */
- WASH, /* o: got eob, possibly still output waiting */
- END, /* x: got eob and all data flushed */
- BADCODE} /* x: got error */
-inflate_codes_mode;
-
-struct inflate_codes_state {
-
- /* mode */
- inflate_codes_mode mode; /* current inflate_codes mode */
-
- /* mode dependent information */
- uInt len;
- union {
- struct {
- inflate_huft *tree; /* pointer into tree */
- uInt need; /* bits needed */
- } code; /* if LEN or DIST, where in tree */
- uInt lit; /* if LIT, literal */
- struct {
- uInt get; /* bits to get for extra */
- uInt dist; /* distance back to copy from */
- } copy; /* if EXT or COPY, where and how much */
- } sub; /* submode */
-
- /* mode independent information */
- Byte lbits; /* ltree bits decoded per branch */
- Byte dbits; /* dtree bits decoder per branch */
- inflate_huft *ltree; /* literal/length/eob tree */
- inflate_huft *dtree; /* distance tree */
-
-};
+#include <linux/zlib.h>
/* memory allocation for inflation */
struct inflate_workspace {
- inflate_codes_statef working_state;
- struct inflate_blocks_state working_blocks_state;
- struct internal_state internal_state;
- unsigned int tree_work_area_1[19];
- unsigned int tree_work_area_2[288];
- unsigned working_blens[258 + 0x1f + 0x1f];
- inflate_huft working_hufts[MANY];
+ struct inflate_state inflate_state;
unsigned char working_window[1 << MAX_WBITS];
};
}
#ifdef CONFIG_KEYS
-static inline int dummy_key_alloc(struct key *key)
+static inline int dummy_key_alloc(struct key *key, struct task_struct *ctx)
{
return 0;
}
* instantiate the key or discard it before returning
*/
struct key *key_alloc(struct key_type *type, const char *desc,
- uid_t uid, gid_t gid, key_perm_t perm,
- int not_in_quota)
+ uid_t uid, gid_t gid, struct task_struct *ctx,
+ key_perm_t perm, int not_in_quota)
{
struct key_user *user = NULL;
struct key *key;
#endif
/* let the security module know about the key */
- ret = security_key_alloc(key);
+ ret = security_key_alloc(key, ctx);
if (ret < 0)
goto security_error;
/* allocate a new key */
key = key_alloc(ktype, description, current->fsuid, current->fsgid,
- perm, not_in_quota);
+ current, perm, not_in_quota);
if (IS_ERR(key)) {
key_ref = ERR_PTR(PTR_ERR(key));
goto error_3;
* it */
down_write(&key->sem);
set_bit(KEY_FLAG_REVOKED, &key->flags);
+
+ if (key->type->revoke)
+ key->type->revoke(key);
+
up_write(&key->sem);
} /* end key_revoke() */
* allocate a keyring and link into the destination keyring
*/
struct key *keyring_alloc(const char *description, uid_t uid, gid_t gid,
- int not_in_quota, struct key *dest)
+ struct task_struct *ctx, int not_in_quota,
+ struct key *dest)
{
struct key *keyring;
int ret;
keyring = key_alloc(&key_type_keyring, description,
- uid, gid,
+ uid, gid, ctx,
(KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_ALL,
not_in_quota);
/*
* allocate the keyrings to be associated with a UID
*/
-int alloc_uid_keyring(struct user_struct *user)
+int alloc_uid_keyring(struct user_struct *user,
+ struct task_struct *ctx)
{
struct key *uid_keyring, *session_keyring;
char buf[20];
/* concoct a default session keyring */
sprintf(buf, "_uid_ses.%u", user->uid);
- session_keyring = keyring_alloc(buf, user->uid, (gid_t) -1, 0, NULL);
+ session_keyring = keyring_alloc(buf, user->uid, (gid_t) -1, ctx, 0, NULL);
if (IS_ERR(session_keyring)) {
ret = PTR_ERR(session_keyring);
goto error;
* keyring */
sprintf(buf, "_uid.%u", user->uid);
- uid_keyring = keyring_alloc(buf, user->uid, (gid_t) -1, 0,
+ uid_keyring = keyring_alloc(buf, user->uid, (gid_t) -1, ctx, 0,
session_keyring);
if (IS_ERR(uid_keyring)) {
key_put(session_keyring);
sprintf(buf, "_tid.%u", tsk->pid);
- keyring = keyring_alloc(buf, tsk->uid, tsk->gid, 1, NULL);
+ keyring = keyring_alloc(buf, tsk->uid, tsk->gid, tsk, 1, NULL);
if (IS_ERR(keyring)) {
ret = PTR_ERR(keyring);
goto error;
if (!tsk->signal->process_keyring) {
sprintf(buf, "_pid.%u", tsk->tgid);
- keyring = keyring_alloc(buf, tsk->uid, tsk->gid, 1, NULL);
+ keyring = keyring_alloc(buf, tsk->uid, tsk->gid, tsk, 1, NULL);
if (IS_ERR(keyring)) {
ret = PTR_ERR(keyring);
goto error;
if (!keyring) {
sprintf(buf, "_ses.%u", tsk->tgid);
- keyring = keyring_alloc(buf, tsk->uid, tsk->gid, 1, NULL);
+ keyring = keyring_alloc(buf, tsk->uid, tsk->gid, tsk, 1, NULL);
if (IS_ERR(keyring))
return PTR_ERR(keyring);
}
struct request_key_auth *rka;
key_ref_t key_ref, ret, err;
+ might_sleep();
+
/* we want to return -EAGAIN or -ENOKEY if any of the keyrings were
* searchable, but we failed to find a key or we found a negative key;
* otherwise we want to return a sample error (probably -EACCES) if
*/
if (context->request_key_auth &&
context == current &&
- type != &key_type_request_key_auth &&
- key_validate(context->request_key_auth) == 0
+ type != &key_type_request_key_auth
) {
- rka = context->request_key_auth->payload.data;
+ /* defend against the auth key being revoked */
+ down_read(&context->request_key_auth->sem);
- key_ref = search_process_keyrings(type, description, match,
- rka->context);
+ if (key_validate(context->request_key_auth) == 0) {
+ rka = context->request_key_auth->payload.data;
- if (!IS_ERR(key_ref))
- goto found;
+ key_ref = search_process_keyrings(type, description,
+ match, rka->context);
- switch (PTR_ERR(key_ref)) {
- case -EAGAIN: /* no key */
- if (ret)
+ up_read(&context->request_key_auth->sem);
+
+ if (!IS_ERR(key_ref))
+ goto found;
+
+ switch (PTR_ERR(key_ref)) {
+ case -EAGAIN: /* no key */
+ if (ret)
+ break;
+ case -ENOKEY: /* negative key */
+ ret = key_ref;
break;
- case -ENOKEY: /* negative key */
- ret = key_ref;
- break;
- default:
- err = key_ref;
- break;
+ default:
+ err = key_ref;
+ break;
+ }
+ } else {
+ up_read(&context->request_key_auth->sem);
}
}
keyring = find_keyring_by_name(name, 0);
if (PTR_ERR(keyring) == -ENOKEY) {
/* not found - try and create a new one */
- keyring = keyring_alloc(name, tsk->uid, tsk->gid, 0, NULL);
+ keyring = keyring_alloc(name, tsk->uid, tsk->gid, tsk, 0, NULL);
if (IS_ERR(keyring)) {
ret = PTR_ERR(keyring);
goto error2;
/* allocate a new session keyring */
sprintf(desc, "_req.%u", key->serial);
- keyring = keyring_alloc(desc, current->fsuid, current->fsgid, 1, NULL);
+ keyring = keyring_alloc(desc, current->fsuid, current->fsgid,
+ current, 1, NULL);
if (IS_ERR(keyring)) {
ret = PTR_ERR(keyring);
goto error_alloc;
/* create a key and add it to the queue */
key = key_alloc(type, description,
- current->fsuid, current->fsgid, KEY_POS_ALL, 0);
+ current->fsuid, current->fsgid,
+ current, KEY_POS_ALL, 0);
if (IS_ERR(key))
goto alloc_failed;
static int request_key_auth_instantiate(struct key *, const void *, size_t);
static void request_key_auth_describe(const struct key *, struct seq_file *);
+static void request_key_auth_revoke(struct key *);
static void request_key_auth_destroy(struct key *);
static long request_key_auth_read(const struct key *, char __user *, size_t);
.def_datalen = sizeof(struct request_key_auth),
.instantiate = request_key_auth_instantiate,
.describe = request_key_auth_describe,
+ .revoke = request_key_auth_revoke,
.destroy = request_key_auth_destroy,
.read = request_key_auth_read,
};
} /* end request_key_auth_read() */
+/*****************************************************************************/
+/*
+ * handle revocation of an authorisation token key
+ * - called with the key sem write-locked
+ */
+static void request_key_auth_revoke(struct key *key)
+{
+ struct request_key_auth *rka = key->payload.data;
+
+ kenter("{%d}", key->serial);
+
+ if (rka->context) {
+ put_task_struct(rka->context);
+ rka->context = NULL;
+ }
+
+} /* end request_key_auth_revoke() */
+
/*****************************************************************************/
/*
* destroy an instantiation authorisation token key
kenter("{%d}", key->serial);
+ if (rka->context) {
+ put_task_struct(rka->context);
+ rka->context = NULL;
+ }
+
key_put(rka->target_key);
kfree(rka);
* another process */
if (current->request_key_auth) {
/* it is - use that instantiation context here too */
+ down_read(¤t->request_key_auth->sem);
+
+ /* if the auth key has been revoked, then the key we're
+ * servicing is already instantiated */
+ if (test_bit(KEY_FLAG_REVOKED,
+ ¤t->request_key_auth->flags))
+ goto auth_key_revoked;
+
irka = current->request_key_auth->payload.data;
rka->context = irka->context;
rka->pid = irka->pid;
+ get_task_struct(rka->context);
+
+ up_read(¤t->request_key_auth->sem);
}
else {
/* it isn't - use this process as the context */
rka->context = current;
rka->pid = current->pid;
+ get_task_struct(rka->context);
}
rka->target_key = key_get(target);
sprintf(desc, "%x", target->serial);
authkey = key_alloc(&key_type_request_key_auth, desc,
- current->fsuid, current->fsgid,
+ current->fsuid, current->fsgid, current,
KEY_POS_VIEW | KEY_POS_READ | KEY_POS_SEARCH |
KEY_USR_VIEW, 1);
if (IS_ERR(authkey)) {
if (ret < 0)
goto error_inst;
- kleave(" = {%d})", authkey->serial);
+ kleave(" = {%d}", authkey->serial);
return authkey;
+auth_key_revoked:
+ up_read(¤t->request_key_auth->sem);
+ kfree(rka);
+ kleave("= -EKEYREVOKED");
+ return ERR_PTR(-EKEYREVOKED);
+
error_inst:
key_revoke(authkey);
key_put(authkey);
return size;
}
+#ifdef CONFIG_KEYS
+
+static int selinux_key_alloc(struct key *k, struct task_struct *tsk)
+{
+ struct task_security_struct *tsec = tsk->security;
+ struct key_security_struct *ksec;
+
+ ksec = kzalloc(sizeof(struct key_security_struct), GFP_KERNEL);
+ if (!ksec)
+ return -ENOMEM;
+
+ ksec->obj = k;
+ ksec->sid = tsec->sid;
+ k->security = ksec;
+
+ return 0;
+}
+
+static void selinux_key_free(struct key *k)
+{
+ struct key_security_struct *ksec = k->security;
+
+ k->security = NULL;
+ kfree(ksec);
+}
+
+static int selinux_key_permission(key_ref_t key_ref,
+ struct task_struct *ctx,
+ key_perm_t perm)
+{
+ struct key *key;
+ struct task_security_struct *tsec;
+ struct key_security_struct *ksec;
+
+ key = key_ref_to_ptr(key_ref);
+
+ tsec = ctx->security;
+ ksec = key->security;
+
+ /* if no specific permissions are requested, we skip the
+ permission check. No serious, additional covert channels
+ appear to be created. */
+ if (perm == 0)
+ return 0;
+
+ return avc_has_perm(tsec->sid, ksec->sid,
+ SECCLASS_KEY, perm, NULL);
+}
+
+#endif
+
static struct security_operations selinux_ops = {
.ptrace = selinux_ptrace,
.capget = selinux_capget,
.xfrm_state_delete_security = selinux_xfrm_state_delete,
.xfrm_policy_lookup = selinux_xfrm_policy_lookup,
#endif
+
+#ifdef CONFIG_KEYS
+ .key_alloc = selinux_key_alloc,
+ .key_free = selinux_key_free,
+ .key_permission = selinux_key_permission,
+#endif
};
static __init int selinux_init(void)
} else {
printk(KERN_INFO "SELinux: Starting in permissive mode\n");
}
+
+#ifdef CONFIG_KEYS
+ /* Add security information to initial keyrings */
+ security_key_alloc(&root_user_keyring, current);
+ security_key_alloc(&root_session_keyring, current);
+#endif
+
return 0;
}
S_(SECCLASS_PACKET, PACKET__SEND, "send")
S_(SECCLASS_PACKET, PACKET__RECV, "recv")
S_(SECCLASS_PACKET, PACKET__RELABELTO, "relabelto")
+ S_(SECCLASS_KEY, KEY__VIEW, "view")
+ S_(SECCLASS_KEY, KEY__READ, "read")
+ S_(SECCLASS_KEY, KEY__WRITE, "write")
+ S_(SECCLASS_KEY, KEY__SEARCH, "search")
+ S_(SECCLASS_KEY, KEY__LINK, "link")
+ S_(SECCLASS_KEY, KEY__SETATTR, "setattr")
#define PACKET__SEND 0x00000001UL
#define PACKET__RECV 0x00000002UL
#define PACKET__RELABELTO 0x00000004UL
+
+#define KEY__VIEW 0x00000001UL
+#define KEY__READ 0x00000002UL
+#define KEY__WRITE 0x00000004UL
+#define KEY__SEARCH 0x00000008UL
+#define KEY__LINK 0x00000010UL
+#define KEY__SETATTR 0x00000020UL
+
S_("netlink_kobject_uevent_socket")
S_("appletalk_socket")
S_("packet")
+ S_("key")
#define SECCLASS_NETLINK_KOBJECT_UEVENT_SOCKET 55
#define SECCLASS_APPLETALK_SOCKET 56
#define SECCLASS_PACKET 57
+#define SECCLASS_KEY 58
/*
* Security identifier indices for initial entities
u32 peer_sid; /* SID of peer */
};
+struct key_security_struct {
+ struct key *obj; /* back pointer */
+ u32 sid; /* SID of key */
+};
+
extern unsigned int selinux_checkreqprot;
#endif /* _SELINUX_OBJSEC_H_ */
source "sound/ppc/Kconfig"
+source "sound/aoa/Kconfig"
+
source "sound/arm/Kconfig"
source "sound/mips/Kconfig"
obj-$(CONFIG_SOUND) += soundcore.o
obj-$(CONFIG_SOUND_PRIME) += oss/
obj-$(CONFIG_DMASOUND) += oss/
-obj-$(CONFIG_SND) += core/ i2c/ drivers/ isa/ pci/ ppc/ arm/ synth/ usb/ sparc/ parisc/ pcmcia/ mips/
+obj-$(CONFIG_SND) += core/ i2c/ drivers/ isa/ pci/ ppc/ arm/ synth/ usb/ sparc/ parisc/ pcmcia/ mips/ aoa/
ifeq ($(CONFIG_SND),y)
obj-y += last.o
--- /dev/null
+menu "Apple Onboard Audio driver"
+ depends on SND!=n && PPC
+
+config SND_AOA
+ tristate "Apple Onboard Audio driver"
+ depends on SOUND && SND_PCM
+ ---help---
+ This option enables the new driver for the various
+ Apple Onboard Audio components.
+
+source "sound/aoa/fabrics/Kconfig"
+
+source "sound/aoa/codecs/Kconfig"
+
+source "sound/aoa/soundbus/Kconfig"
+
+endmenu
--- /dev/null
+obj-$(CONFIG_SND_AOA) += core/
+obj-$(CONFIG_SND_AOA) += codecs/
+obj-$(CONFIG_SND_AOA) += fabrics/
+obj-$(CONFIG_SND_AOA_SOUNDBUS) += soundbus/
--- /dev/null
+/*
+ * Apple Onboard Audio GPIO definitions
+ *
+ * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
+ *
+ * GPL v2, can be found in COPYING.
+ */
+
+#ifndef __AOA_GPIO_H
+#define __AOA_GPIO_H
+#include <linux/workqueue.h>
+#include <linux/mutex.h>
+#include <asm/prom.h>
+
+typedef void (*notify_func_t)(void *data);
+
+enum notify_type {
+ AOA_NOTIFY_HEADPHONE,
+ AOA_NOTIFY_LINE_IN,
+ AOA_NOTIFY_LINE_OUT,
+};
+
+struct gpio_runtime;
+struct gpio_methods {
+ /* for initialisation/de-initialisation of the GPIO layer */
+ void (*init)(struct gpio_runtime *rt);
+ void (*exit)(struct gpio_runtime *rt);
+
+ /* turn off headphone, speakers, lineout */
+ void (*all_amps_off)(struct gpio_runtime *rt);
+ /* turn headphone, speakers, lineout back to previous setting */
+ void (*all_amps_restore)(struct gpio_runtime *rt);
+
+ void (*set_headphone)(struct gpio_runtime *rt, int on);
+ void (*set_speakers)(struct gpio_runtime *rt, int on);
+ void (*set_lineout)(struct gpio_runtime *rt, int on);
+
+ int (*get_headphone)(struct gpio_runtime *rt);
+ int (*get_speakers)(struct gpio_runtime *rt);
+ int (*get_lineout)(struct gpio_runtime *rt);
+
+ void (*set_hw_reset)(struct gpio_runtime *rt, int on);
+
+ /* use this to be notified of any events. The notification
+ * function is passed the data, and is called in process
+ * context by the use of schedule_work.
+ * The interface for it is that setting a function to NULL
+ * removes it, and they return 0 if the operation succeeded,
+ * and -EBUSY if the notification is already assigned by
+ * someone else. */
+ int (*set_notify)(struct gpio_runtime *rt,
+ enum notify_type type,
+ notify_func_t notify,
+ void *data);
+ /* returns 0 if not plugged in, 1 if plugged in
+ * or a negative error code */
+ int (*get_detect)(struct gpio_runtime *rt,
+ enum notify_type type);
+};
+
+struct gpio_notification {
+ notify_func_t notify;
+ void *data;
+ void *gpio_private;
+ struct work_struct work;
+ struct mutex mutex;
+};
+
+struct gpio_runtime {
+ /* to be assigned by fabric */
+ struct device_node *node;
+ /* since everyone needs this pointer anyway... */
+ struct gpio_methods *methods;
+ /* to be used by the gpio implementation */
+ int implementation_private;
+ struct gpio_notification headphone_notify;
+ struct gpio_notification line_in_notify;
+ struct gpio_notification line_out_notify;
+};
+
+#endif /* __AOA_GPIO_H */
--- /dev/null
+/*
+ * Apple Onboard Audio definitions
+ *
+ * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
+ *
+ * GPL v2, can be found in COPYING.
+ */
+
+#ifndef __AOA_H
+#define __AOA_H
+#include <asm/prom.h>
+#include <linux/module.h>
+/* So apparently there's a reason for requiring driver.h to be included first! */
+#include <sound/driver.h>
+#include <sound/core.h>
+#include <sound/asound.h>
+#include <sound/control.h>
+#include "aoa-gpio.h"
+#include "soundbus/soundbus.h"
+
+#define MAX_CODEC_NAME_LEN 32
+
+struct aoa_codec {
+ char name[MAX_CODEC_NAME_LEN];
+
+ struct module *owner;
+
+ /* called when the fabric wants to init this codec.
+ * Do alsa card manipulations from here. */
+ int (*init)(struct aoa_codec *codec);
+
+ /* called when the fabric is done with the codec.
+ * The alsa card will be cleaned up so don't bother. */
+ void (*exit)(struct aoa_codec *codec);
+
+ /* May be NULL, but can be used by the fabric.
+ * Refcounting is the codec driver's responsibility */
+ struct device_node *node;
+
+ /* assigned by fabric before init() is called, points
+ * to the soundbus device. Cannot be NULL. */
+ struct soundbus_dev *soundbus_dev;
+
+ /* assigned by the fabric before init() is called, points
+ * to the fabric's gpio runtime record for the relevant
+ * device. */
+ struct gpio_runtime *gpio;
+
+ /* assigned by the fabric before init() is called, contains
+ * a codec specific bitmask of what outputs and inputs are
+ * actually connected */
+ u32 connected;
+
+ /* data the fabric can associate with this structure */
+ void *fabric_data;
+
+ /* private! */
+ struct list_head list;
+ struct aoa_fabric *fabric;
+};
+
+/* return 0 on success */
+extern int
+aoa_codec_register(struct aoa_codec *codec);
+extern void
+aoa_codec_unregister(struct aoa_codec *codec);
+
+#define MAX_LAYOUT_NAME_LEN 32
+
+struct aoa_fabric {
+ char name[MAX_LAYOUT_NAME_LEN];
+
+ struct module *owner;
+
+ /* once codecs register, they are passed here after.
+ * They are of course not initialised, since the
+ * fabric is responsible for initialising some fields
+ * in the codec structure! */
+ int (*found_codec)(struct aoa_codec *codec);
+ /* called for each codec when it is removed,
+ * also in the case that aoa_fabric_unregister
+ * is called and all codecs are removed
+ * from this fabric.
+ * Also called if found_codec returned 0 but
+ * the codec couldn't initialise. */
+ void (*remove_codec)(struct aoa_codec *codec);
+ /* If found_codec returned 0, and the codec
+ * could be initialised, this is called. */
+ void (*attached_codec)(struct aoa_codec *codec);
+};
+
+/* return 0 on success, -EEXIST if another fabric is
+ * registered, -EALREADY if the same fabric is registered.
+ * Passing NULL can be used to test for the presence
+ * of another fabric, if -EALREADY is returned there is
+ * no other fabric present.
+ * In the case that the function returns -EALREADY
+ * and the fabric passed is not NULL, all codecs
+ * that are not assigned yet are passed to the fabric
+ * again for reconsideration. */
+extern int
+aoa_fabric_register(struct aoa_fabric *fabric);
+
+/* it is vital to call this when the fabric exits!
+ * When calling, the remove_codec will be called
+ * for all codecs, unless it is NULL. */
+extern void
+aoa_fabric_unregister(struct aoa_fabric *fabric);
+
+/* if for some reason you want to get rid of a codec
+ * before the fabric is removed, use this.
+ * Note that remove_codec is called for it! */
+extern void
+aoa_fabric_unlink_codec(struct aoa_codec *codec);
+
+/* alsa help methods */
+struct aoa_card {
+ struct snd_card *alsa_card;
+};
+
+extern int aoa_snd_device_new(snd_device_type_t type,
+ void * device_data, struct snd_device_ops * ops);
+extern struct snd_card *aoa_get_card(void);
+extern int aoa_snd_ctl_add(struct snd_kcontrol* control);
+
+/* GPIO stuff */
+extern struct gpio_methods *pmf_gpio_methods;
+extern struct gpio_methods *ftr_gpio_methods;
+/* extern struct gpio_methods *map_gpio_methods; */
+
+#endif /* __AOA_H */
--- /dev/null
+config SND_AOA_ONYX
+ tristate "support Onyx chip"
+ depends on SND_AOA
+ ---help---
+ This option enables support for the Onyx (pcm3052)
+ codec chip found in the latest Apple machines
+ (most of those with digital audio output).
+
+#config SND_AOA_TOPAZ
+# tristate "support Topaz chips"
+# depends on SND_AOA
+# ---help---
+# This option enables support for the Topaz (CS84xx)
+# codec chips found in the latest Apple machines,
+# these chips do the digital input and output on
+# some PowerMacs.
+
+config SND_AOA_TAS
+ tristate "support TAS chips"
+ depends on SND_AOA
+ ---help---
+ This option enables support for the tas chips
+ found in a lot of Apple Machines, especially
+ iBooks and PowerBooks without digital.
+
+config SND_AOA_TOONIE
+ tristate "support Toonie chip"
+ depends on SND_AOA
+ ---help---
+ This option enables support for the toonie codec
+ found in the Mac Mini. If you have a Mac Mini and
+ want to hear sound, select this option.
--- /dev/null
+obj-$(CONFIG_SND_AOA_ONYX) += snd-aoa-codec-onyx.o
+obj-$(CONFIG_SND_AOA_TAS) += snd-aoa-codec-tas.o
+obj-$(CONFIG_SND_AOA_TOONIE) += snd-aoa-codec-toonie.o
--- /dev/null
+/*
+ * Apple Onboard Audio driver for Onyx codec
+ *
+ * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
+ *
+ * GPL v2, can be found in COPYING.
+ *
+ *
+ * This is a driver for the pcm3052 codec chip (codenamed Onyx)
+ * that is present in newer Apple hardware (with digital output).
+ *
+ * The Onyx codec has the following connections (listed by the bit
+ * to be used in aoa_codec.connected):
+ * 0: analog output
+ * 1: digital output
+ * 2: line input
+ * 3: microphone input
+ * Note that even though I know of no machine that has for example
+ * the digital output connected but not the analog, I have handled
+ * all the different cases in the code so that this driver may serve
+ * as a good example of what to do.
+ *
+ * NOTE: This driver assumes that there's at most one chip to be
+ * used with one alsa card, in form of creating all kinds
+ * of mixer elements without regard for their existence.
+ * But snd-aoa assumes that there's at most one card, so
+ * this means you can only have one onyx on a system. This
+ * should probably be fixed by changing the assumption of
+ * having just a single card on a system, and making the
+ * 'card' pointer accessible to anyone who needs it instead
+ * of hiding it in the aoa_snd_* functions...
+ *
+ */
+#include <linux/delay.h>
+#include <linux/module.h>
+MODULE_AUTHOR("Johannes Berg <johannes@sipsolutions.net>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("pcm3052 (onyx) codec driver for snd-aoa");
+
+#include "snd-aoa-codec-onyx.h"
+#include "../aoa.h"
+#include "../soundbus/soundbus.h"
+
+
+#define PFX "snd-aoa-codec-onyx: "
+
+struct onyx {
+ /* cache registers 65 to 80, they are write-only! */
+ u8 cache[16];
+ struct i2c_client i2c;
+ struct aoa_codec codec;
+ u32 initialised:1,
+ spdif_locked:1,
+ analog_locked:1,
+ original_mute:2;
+ int open_count;
+ struct codec_info *codec_info;
+
+ /* mutex serializes concurrent access to the device
+ * and this structure.
+ */
+ struct mutex mutex;
+};
+#define codec_to_onyx(c) container_of(c, struct onyx, codec)
+
+/* both return 0 if all ok, else on error */
+static int onyx_read_register(struct onyx *onyx, u8 reg, u8 *value)
+{
+ s32 v;
+
+ if (reg != ONYX_REG_CONTROL) {
+ *value = onyx->cache[reg-FIRSTREGISTER];
+ return 0;
+ }
+ v = i2c_smbus_read_byte_data(&onyx->i2c, reg);
+ if (v < 0)
+ return -1;
+ *value = (u8)v;
+ onyx->cache[ONYX_REG_CONTROL-FIRSTREGISTER] = *value;
+ return 0;
+}
+
+static int onyx_write_register(struct onyx *onyx, u8 reg, u8 value)
+{
+ int result;
+
+ result = i2c_smbus_write_byte_data(&onyx->i2c, reg, value);
+ if (!result)
+ onyx->cache[reg-FIRSTREGISTER] = value;
+ return result;
+}
+
+/* alsa stuff */
+
+static int onyx_dev_register(struct snd_device *dev)
+{
+ return 0;
+}
+
+static struct snd_device_ops ops = {
+ .dev_register = onyx_dev_register,
+};
+
+/* this is necessary because most alsa mixer programs
+ * can't properly handle the negative range */
+#define VOLUME_RANGE_SHIFT 128
+
+static int onyx_snd_vol_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 2;
+ uinfo->value.integer.min = -128 + VOLUME_RANGE_SHIFT;
+ uinfo->value.integer.max = -1 + VOLUME_RANGE_SHIFT;
+ return 0;
+}
+
+static int onyx_snd_vol_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct onyx *onyx = snd_kcontrol_chip(kcontrol);
+ s8 l, r;
+
+ mutex_lock(&onyx->mutex);
+ onyx_read_register(onyx, ONYX_REG_DAC_ATTEN_LEFT, &l);
+ onyx_read_register(onyx, ONYX_REG_DAC_ATTEN_RIGHT, &r);
+ mutex_unlock(&onyx->mutex);
+
+ ucontrol->value.integer.value[0] = l + VOLUME_RANGE_SHIFT;
+ ucontrol->value.integer.value[1] = r + VOLUME_RANGE_SHIFT;
+
+ return 0;
+}
+
+static int onyx_snd_vol_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct onyx *onyx = snd_kcontrol_chip(kcontrol);
+ s8 l, r;
+
+ mutex_lock(&onyx->mutex);
+ onyx_read_register(onyx, ONYX_REG_DAC_ATTEN_LEFT, &l);
+ onyx_read_register(onyx, ONYX_REG_DAC_ATTEN_RIGHT, &r);
+
+ if (l + VOLUME_RANGE_SHIFT == ucontrol->value.integer.value[0] &&
+ r + VOLUME_RANGE_SHIFT == ucontrol->value.integer.value[1]) {
+ mutex_unlock(&onyx->mutex);
+ return 0;
+ }
+
+ onyx_write_register(onyx, ONYX_REG_DAC_ATTEN_LEFT,
+ ucontrol->value.integer.value[0]
+ - VOLUME_RANGE_SHIFT);
+ onyx_write_register(onyx, ONYX_REG_DAC_ATTEN_RIGHT,
+ ucontrol->value.integer.value[1]
+ - VOLUME_RANGE_SHIFT);
+ mutex_unlock(&onyx->mutex);
+
+ return 1;
+}
+
+static struct snd_kcontrol_new volume_control = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Master Playback Volume",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = onyx_snd_vol_info,
+ .get = onyx_snd_vol_get,
+ .put = onyx_snd_vol_put,
+};
+
+/* like above, this is necessary because a lot
+ * of alsa mixer programs don't handle ranges
+ * that don't start at 0 properly.
+ * even alsamixer is one of them... */
+#define INPUTGAIN_RANGE_SHIFT (-3)
+
+static int onyx_snd_inputgain_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 3 + INPUTGAIN_RANGE_SHIFT;
+ uinfo->value.integer.max = 28 + INPUTGAIN_RANGE_SHIFT;
+ return 0;
+}
+
+static int onyx_snd_inputgain_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct onyx *onyx = snd_kcontrol_chip(kcontrol);
+ u8 ig;
+
+ mutex_lock(&onyx->mutex);
+ onyx_read_register(onyx, ONYX_REG_ADC_CONTROL, &ig);
+ mutex_unlock(&onyx->mutex);
+
+ ucontrol->value.integer.value[0] =
+ (ig & ONYX_ADC_PGA_GAIN_MASK) + INPUTGAIN_RANGE_SHIFT;
+
+ return 0;
+}
+
+static int onyx_snd_inputgain_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct onyx *onyx = snd_kcontrol_chip(kcontrol);
+ u8 v, n;
+
+ mutex_lock(&onyx->mutex);
+ onyx_read_register(onyx, ONYX_REG_ADC_CONTROL, &v);
+ n = v;
+ n &= ~ONYX_ADC_PGA_GAIN_MASK;
+ n |= (ucontrol->value.integer.value[0] - INPUTGAIN_RANGE_SHIFT)
+ & ONYX_ADC_PGA_GAIN_MASK;
+ onyx_write_register(onyx, ONYX_REG_ADC_CONTROL, n);
+ mutex_unlock(&onyx->mutex);
+
+ return n != v;
+}
+
+static struct snd_kcontrol_new inputgain_control = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Master Capture Volume",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = onyx_snd_inputgain_info,
+ .get = onyx_snd_inputgain_get,
+ .put = onyx_snd_inputgain_put,
+};
+
+static int onyx_snd_capture_source_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ static char *texts[] = { "Line-In", "Microphone" };
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = 2;
+ if (uinfo->value.enumerated.item > 1)
+ uinfo->value.enumerated.item = 1;
+ strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+ return 0;
+}
+
+static int onyx_snd_capture_source_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct onyx *onyx = snd_kcontrol_chip(kcontrol);
+ s8 v;
+
+ mutex_lock(&onyx->mutex);
+ onyx_read_register(onyx, ONYX_REG_ADC_CONTROL, &v);
+ mutex_unlock(&onyx->mutex);
+
+ ucontrol->value.enumerated.item[0] = !!(v&ONYX_ADC_INPUT_MIC);
+
+ return 0;
+}
+
+static void onyx_set_capture_source(struct onyx *onyx, int mic)
+{
+ s8 v;
+
+ mutex_lock(&onyx->mutex);
+ onyx_read_register(onyx, ONYX_REG_ADC_CONTROL, &v);
+ v &= ~ONYX_ADC_INPUT_MIC;
+ if (mic)
+ v |= ONYX_ADC_INPUT_MIC;
+ onyx_write_register(onyx, ONYX_REG_ADC_CONTROL, v);
+ mutex_unlock(&onyx->mutex);
+}
+
+static int onyx_snd_capture_source_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ onyx_set_capture_source(snd_kcontrol_chip(kcontrol),
+ ucontrol->value.enumerated.item[0]);
+ return 1;
+}
+
+static struct snd_kcontrol_new capture_source_control = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ /* If we name this 'Input Source', it properly shows up in
+ * alsamixer as a selection, * but it's shown under the
+ * 'Playback' category.
+ * If I name it 'Capture Source', it shows up in strange
+ * ways (two bools of which one can be selected at a
+ * time) but at least it's shown in the 'Capture'
+ * category.
+ * I was told that this was due to backward compatibility,
+ * but I don't understand then why the mangling is *not*
+ * done when I name it "Input Source".....
+ */
+ .name = "Capture Source",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = onyx_snd_capture_source_info,
+ .get = onyx_snd_capture_source_get,
+ .put = onyx_snd_capture_source_put,
+};
+
+static int onyx_snd_mute_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 2;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int onyx_snd_mute_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct onyx *onyx = snd_kcontrol_chip(kcontrol);
+ u8 c;
+
+ mutex_lock(&onyx->mutex);
+ onyx_read_register(onyx, ONYX_REG_DAC_CONTROL, &c);
+ mutex_unlock(&onyx->mutex);
+
+ ucontrol->value.integer.value[0] = !(c & ONYX_MUTE_LEFT);
+ ucontrol->value.integer.value[1] = !(c & ONYX_MUTE_RIGHT);
+
+ return 0;
+}
+
+static int onyx_snd_mute_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct onyx *onyx = snd_kcontrol_chip(kcontrol);
+ u8 v = 0, c = 0;
+ int err = -EBUSY;
+
+ mutex_lock(&onyx->mutex);
+ if (onyx->analog_locked)
+ goto out_unlock;
+
+ onyx_read_register(onyx, ONYX_REG_DAC_CONTROL, &v);
+ c = v;
+ c &= ~(ONYX_MUTE_RIGHT | ONYX_MUTE_LEFT);
+ if (!ucontrol->value.integer.value[0])
+ c |= ONYX_MUTE_LEFT;
+ if (!ucontrol->value.integer.value[1])
+ c |= ONYX_MUTE_RIGHT;
+ err = onyx_write_register(onyx, ONYX_REG_DAC_CONTROL, c);
+
+ out_unlock:
+ mutex_unlock(&onyx->mutex);
+
+ return !err ? (v != c) : err;
+}
+
+static struct snd_kcontrol_new mute_control = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Master Playback Switch",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = onyx_snd_mute_info,
+ .get = onyx_snd_mute_get,
+ .put = onyx_snd_mute_put,
+};
+
+
+static int onyx_snd_single_bit_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+#define FLAG_POLARITY_INVERT 1
+#define FLAG_SPDIFLOCK 2
+
+static int onyx_snd_single_bit_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct onyx *onyx = snd_kcontrol_chip(kcontrol);
+ u8 c;
+ long int pv = kcontrol->private_value;
+ u8 polarity = (pv >> 16) & FLAG_POLARITY_INVERT;
+ u8 address = (pv >> 8) & 0xff;
+ u8 mask = pv & 0xff;
+
+ mutex_lock(&onyx->mutex);
+ onyx_read_register(onyx, address, &c);
+ mutex_unlock(&onyx->mutex);
+
+ ucontrol->value.integer.value[0] = !!(c & mask) ^ polarity;
+
+ return 0;
+}
+
+static int onyx_snd_single_bit_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct onyx *onyx = snd_kcontrol_chip(kcontrol);
+ u8 v = 0, c = 0;
+ int err;
+ long int pv = kcontrol->private_value;
+ u8 polarity = (pv >> 16) & FLAG_POLARITY_INVERT;
+ u8 spdiflock = (pv >> 16) & FLAG_SPDIFLOCK;
+ u8 address = (pv >> 8) & 0xff;
+ u8 mask = pv & 0xff;
+
+ mutex_lock(&onyx->mutex);
+ if (spdiflock && onyx->spdif_locked) {
+ /* even if alsamixer doesn't care.. */
+ err = -EBUSY;
+ goto out_unlock;
+ }
+ onyx_read_register(onyx, address, &v);
+ c = v;
+ c &= ~(mask);
+ if (!!ucontrol->value.integer.value[0] ^ polarity)
+ c |= mask;
+ err = onyx_write_register(onyx, address, c);
+
+ out_unlock:
+ mutex_unlock(&onyx->mutex);
+
+ return !err ? (v != c) : err;
+}
+
+#define SINGLE_BIT(n, type, description, address, mask, flags) \
+static struct snd_kcontrol_new n##_control = { \
+ .iface = SNDRV_CTL_ELEM_IFACE_##type, \
+ .name = description, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \
+ .info = onyx_snd_single_bit_info, \
+ .get = onyx_snd_single_bit_get, \
+ .put = onyx_snd_single_bit_put, \
+ .private_value = (flags << 16) | (address << 8) | mask \
+}
+
+SINGLE_BIT(spdif,
+ MIXER,
+ SNDRV_CTL_NAME_IEC958("", PLAYBACK, SWITCH),
+ ONYX_REG_DIG_INFO4,
+ ONYX_SPDIF_ENABLE,
+ FLAG_SPDIFLOCK);
+SINGLE_BIT(ovr1,
+ MIXER,
+ "Oversampling Rate",
+ ONYX_REG_DAC_CONTROL,
+ ONYX_OVR1,
+ 0);
+SINGLE_BIT(flt0,
+ MIXER,
+ "Fast Digital Filter Rolloff",
+ ONYX_REG_DAC_FILTER,
+ ONYX_ROLLOFF_FAST,
+ FLAG_POLARITY_INVERT);
+SINGLE_BIT(hpf,
+ MIXER,
+ "Highpass Filter",
+ ONYX_REG_ADC_HPF_BYPASS,
+ ONYX_HPF_DISABLE,
+ FLAG_POLARITY_INVERT);
+SINGLE_BIT(dm12,
+ MIXER,
+ "Digital De-Emphasis",
+ ONYX_REG_DAC_DEEMPH,
+ ONYX_DIGDEEMPH_CTRL,
+ 0);
+
+static int onyx_spdif_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+ uinfo->count = 1;
+ return 0;
+}
+
+static int onyx_spdif_mask_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ /* datasheet page 30, all others are 0 */
+ ucontrol->value.iec958.status[0] = 0x3e;
+ ucontrol->value.iec958.status[1] = 0xff;
+
+ ucontrol->value.iec958.status[3] = 0x3f;
+ ucontrol->value.iec958.status[4] = 0x0f;
+
+ return 0;
+}
+
+static struct snd_kcontrol_new onyx_spdif_mask = {
+ .access = SNDRV_CTL_ELEM_ACCESS_READ,
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,CON_MASK),
+ .info = onyx_spdif_info,
+ .get = onyx_spdif_mask_get,
+};
+
+static int onyx_spdif_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct onyx *onyx = snd_kcontrol_chip(kcontrol);
+ u8 v;
+
+ mutex_lock(&onyx->mutex);
+ onyx_read_register(onyx, ONYX_REG_DIG_INFO1, &v);
+ ucontrol->value.iec958.status[0] = v & 0x3e;
+
+ onyx_read_register(onyx, ONYX_REG_DIG_INFO2, &v);
+ ucontrol->value.iec958.status[1] = v;
+
+ onyx_read_register(onyx, ONYX_REG_DIG_INFO3, &v);
+ ucontrol->value.iec958.status[3] = v & 0x3f;
+
+ onyx_read_register(onyx, ONYX_REG_DIG_INFO4, &v);
+ ucontrol->value.iec958.status[4] = v & 0x0f;
+ mutex_unlock(&onyx->mutex);
+
+ return 0;
+}
+
+static int onyx_spdif_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct onyx *onyx = snd_kcontrol_chip(kcontrol);
+ u8 v;
+
+ mutex_lock(&onyx->mutex);
+ onyx_read_register(onyx, ONYX_REG_DIG_INFO1, &v);
+ v = (v & ~0x3e) | (ucontrol->value.iec958.status[0] & 0x3e);
+ onyx_write_register(onyx, ONYX_REG_DIG_INFO1, v);
+
+ v = ucontrol->value.iec958.status[1];
+ onyx_write_register(onyx, ONYX_REG_DIG_INFO2, v);
+
+ onyx_read_register(onyx, ONYX_REG_DIG_INFO3, &v);
+ v = (v & ~0x3f) | (ucontrol->value.iec958.status[3] & 0x3f);
+ onyx_write_register(onyx, ONYX_REG_DIG_INFO3, v);
+
+ onyx_read_register(onyx, ONYX_REG_DIG_INFO4, &v);
+ v = (v & ~0x0f) | (ucontrol->value.iec958.status[4] & 0x0f);
+ onyx_write_register(onyx, ONYX_REG_DIG_INFO4, v);
+ mutex_unlock(&onyx->mutex);
+
+ return 1;
+}
+
+static struct snd_kcontrol_new onyx_spdif_ctrl = {
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
+ .info = onyx_spdif_info,
+ .get = onyx_spdif_get,
+ .put = onyx_spdif_put,
+};
+
+/* our registers */
+
+static u8 register_map[] = {
+ ONYX_REG_DAC_ATTEN_LEFT,
+ ONYX_REG_DAC_ATTEN_RIGHT,
+ ONYX_REG_CONTROL,
+ ONYX_REG_DAC_CONTROL,
+ ONYX_REG_DAC_DEEMPH,
+ ONYX_REG_DAC_FILTER,
+ ONYX_REG_DAC_OUTPHASE,
+ ONYX_REG_ADC_CONTROL,
+ ONYX_REG_ADC_HPF_BYPASS,
+ ONYX_REG_DIG_INFO1,
+ ONYX_REG_DIG_INFO2,
+ ONYX_REG_DIG_INFO3,
+ ONYX_REG_DIG_INFO4
+};
+
+static u8 initial_values[ARRAY_SIZE(register_map)] = {
+ 0x80, 0x80, /* muted */
+ ONYX_MRST | ONYX_SRST, /* but handled specially! */
+ ONYX_MUTE_LEFT | ONYX_MUTE_RIGHT,
+ 0, /* no deemphasis */
+ ONYX_DAC_FILTER_ALWAYS,
+ ONYX_OUTPHASE_INVERTED,
+ (-1 /*dB*/ + 8) & 0xF, /* line in selected, -1 dB gain*/
+ ONYX_ADC_HPF_ALWAYS,
+ (1<<2), /* pcm audio */
+ 2, /* category: pcm coder */
+ 0, /* sampling frequency 44.1 kHz, clock accuracy level II */
+ 1 /* 24 bit depth */
+};
+
+/* reset registers of chip, either to initial or to previous values */
+static int onyx_register_init(struct onyx *onyx)
+{
+ int i;
+ u8 val;
+ u8 regs[sizeof(initial_values)];
+
+ if (!onyx->initialised) {
+ memcpy(regs, initial_values, sizeof(initial_values));
+ if (onyx_read_register(onyx, ONYX_REG_CONTROL, &val))
+ return -1;
+ val &= ~ONYX_SILICONVERSION;
+ val |= initial_values[3];
+ regs[3] = val;
+ } else {
+ for (i=0; i<sizeof(register_map); i++)
+ regs[i] = onyx->cache[register_map[i]-FIRSTREGISTER];
+ }
+
+ for (i=0; i<sizeof(register_map); i++) {
+ if (onyx_write_register(onyx, register_map[i], regs[i]))
+ return -1;
+ }
+ onyx->initialised = 1;
+ return 0;
+}
+
+static struct transfer_info onyx_transfers[] = {
+ /* this is first so we can skip it if no input is present...
+ * No hardware exists with that, but it's here as an example
+ * of what to do :) */
+ {
+ /* analog input */
+ .formats = SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_S16_BE |
+ SNDRV_PCM_FMTBIT_S24_BE,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .transfer_in = 1,
+ .must_be_clock_source = 0,
+ .tag = 0,
+ },
+ {
+ /* if analog and digital are currently off, anything should go,
+ * so this entry describes everything we can do... */
+ .formats = SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_S16_BE |
+ SNDRV_PCM_FMTBIT_S24_BE
+#ifdef SNDRV_PCM_FMTBIT_COMPRESSED_16BE
+ | SNDRV_PCM_FMTBIT_COMPRESSED_16BE
+#endif
+ ,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .tag = 0,
+ },
+ {
+ /* analog output */
+ .formats = SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_S16_BE |
+ SNDRV_PCM_FMTBIT_S24_BE,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .transfer_in = 0,
+ .must_be_clock_source = 0,
+ .tag = 1,
+ },
+ {
+ /* digital pcm output, also possible for analog out */
+ .formats = SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_S16_BE |
+ SNDRV_PCM_FMTBIT_S24_BE,
+ .rates = SNDRV_PCM_RATE_32000 |
+ SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000,
+ .transfer_in = 0,
+ .must_be_clock_source = 0,
+ .tag = 2,
+ },
+#ifdef SNDRV_PCM_FMTBIT_COMPRESSED_16BE
+Once alsa gets supports for this kind of thing we can add it...
+ {
+ /* digital compressed output */
+ .formats = SNDRV_PCM_FMTBIT_COMPRESSED_16BE,
+ .rates = SNDRV_PCM_RATE_32000 |
+ SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000,
+ .tag = 2,
+ },
+#endif
+ {}
+};
+
+static int onyx_usable(struct codec_info_item *cii,
+ struct transfer_info *ti,
+ struct transfer_info *out)
+{
+ u8 v;
+ struct onyx *onyx = cii->codec_data;
+ int spdif_enabled, analog_enabled;
+
+ mutex_lock(&onyx->mutex);
+ onyx_read_register(onyx, ONYX_REG_DIG_INFO4, &v);
+ spdif_enabled = !!(v & ONYX_SPDIF_ENABLE);
+ onyx_read_register(onyx, ONYX_REG_DAC_CONTROL, &v);
+ analog_enabled =
+ (v & (ONYX_MUTE_RIGHT|ONYX_MUTE_LEFT))
+ != (ONYX_MUTE_RIGHT|ONYX_MUTE_LEFT);
+ mutex_unlock(&onyx->mutex);
+
+ switch (ti->tag) {
+ case 0: return 1;
+ case 1: return analog_enabled;
+ case 2: return spdif_enabled;
+ }
+ return 1;
+}
+
+static int onyx_prepare(struct codec_info_item *cii,
+ struct bus_info *bi,
+ struct snd_pcm_substream *substream)
+{
+ u8 v;
+ struct onyx *onyx = cii->codec_data;
+ int err = -EBUSY;
+
+ mutex_lock(&onyx->mutex);
+
+#ifdef SNDRV_PCM_FMTBIT_COMPRESSED_16BE
+ if (substream->runtime->format == SNDRV_PCM_FMTBIT_COMPRESSED_16BE) {
+ /* mute and lock analog output */
+ onyx_read_register(onyx, ONYX_REG_DAC_CONTROL, &v);
+ if (onyx_write_register(onyx
+ ONYX_REG_DAC_CONTROL,
+ v | ONYX_MUTE_RIGHT | ONYX_MUTE_LEFT))
+ goto out_unlock;
+ onyx->analog_locked = 1;
+ err = 0;
+ goto out_unlock;
+ }
+#endif
+ switch (substream->runtime->rate) {
+ case 32000:
+ case 44100:
+ case 48000:
+ /* these rates are ok for all outputs */
+ /* FIXME: program spdif channel control bits here so that
+ * userspace doesn't have to if it only plays pcm! */
+ err = 0;
+ goto out_unlock;
+ default:
+ /* got some rate that the digital output can't do,
+ * so disable and lock it */
+ onyx_read_register(cii->codec_data, ONYX_REG_DIG_INFO4, &v);
+ if (onyx_write_register(onyx,
+ ONYX_REG_DIG_INFO4,
+ v & ~ONYX_SPDIF_ENABLE))
+ goto out_unlock;
+ onyx->spdif_locked = 1;
+ err = 0;
+ goto out_unlock;
+ }
+
+ out_unlock:
+ mutex_unlock(&onyx->mutex);
+
+ return err;
+}
+
+static int onyx_open(struct codec_info_item *cii,
+ struct snd_pcm_substream *substream)
+{
+ struct onyx *onyx = cii->codec_data;
+
+ mutex_lock(&onyx->mutex);
+ onyx->open_count++;
+ mutex_unlock(&onyx->mutex);
+
+ return 0;
+}
+
+static int onyx_close(struct codec_info_item *cii,
+ struct snd_pcm_substream *substream)
+{
+ struct onyx *onyx = cii->codec_data;
+
+ mutex_lock(&onyx->mutex);
+ onyx->open_count--;
+ if (!onyx->open_count)
+ onyx->spdif_locked = onyx->analog_locked = 0;
+ mutex_unlock(&onyx->mutex);
+
+ return 0;
+}
+
+static int onyx_switch_clock(struct codec_info_item *cii,
+ enum clock_switch what)
+{
+ struct onyx *onyx = cii->codec_data;
+
+ mutex_lock(&onyx->mutex);
+ /* this *MUST* be more elaborate later... */
+ switch (what) {
+ case CLOCK_SWITCH_PREPARE_SLAVE:
+ onyx->codec.gpio->methods->all_amps_off(onyx->codec.gpio);
+ break;
+ case CLOCK_SWITCH_SLAVE:
+ onyx->codec.gpio->methods->all_amps_restore(onyx->codec.gpio);
+ break;
+ default: /* silence warning */
+ break;
+ }
+ mutex_unlock(&onyx->mutex);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+
+static int onyx_suspend(struct codec_info_item *cii, pm_message_t state)
+{
+ struct onyx *onyx = cii->codec_data;
+ u8 v;
+ int err = -ENXIO;
+
+ mutex_lock(&onyx->mutex);
+ if (onyx_read_register(onyx, ONYX_REG_CONTROL, &v))
+ goto out_unlock;
+ onyx_write_register(onyx, ONYX_REG_CONTROL, v | ONYX_ADPSV | ONYX_DAPSV);
+ /* Apple does a sleep here but the datasheet says to do it on resume */
+ err = 0;
+ out_unlock:
+ mutex_unlock(&onyx->mutex);
+
+ return err;
+}
+
+static int onyx_resume(struct codec_info_item *cii)
+{
+ struct onyx *onyx = cii->codec_data;
+ u8 v;
+ int err = -ENXIO;
+
+ mutex_lock(&onyx->mutex);
+ /* take codec out of suspend */
+ if (onyx_read_register(onyx, ONYX_REG_CONTROL, &v))
+ goto out_unlock;
+ onyx_write_register(onyx, ONYX_REG_CONTROL, v & ~(ONYX_ADPSV | ONYX_DAPSV));
+ /* FIXME: should divide by sample rate, but 8k is the lowest we go */
+ msleep(2205000/8000);
+ /* reset all values */
+ onyx_register_init(onyx);
+ err = 0;
+ out_unlock:
+ mutex_unlock(&onyx->mutex);
+
+ return err;
+}
+
+#endif /* CONFIG_PM */
+
+static struct codec_info onyx_codec_info = {
+ .transfers = onyx_transfers,
+ .sysclock_factor = 256,
+ .bus_factor = 64,
+ .owner = THIS_MODULE,
+ .usable = onyx_usable,
+ .prepare = onyx_prepare,
+ .open = onyx_open,
+ .close = onyx_close,
+ .switch_clock = onyx_switch_clock,
+#ifdef CONFIG_PM
+ .suspend = onyx_suspend,
+ .resume = onyx_resume,
+#endif
+};
+
+static int onyx_init_codec(struct aoa_codec *codec)
+{
+ struct onyx *onyx = codec_to_onyx(codec);
+ struct snd_kcontrol *ctl;
+ struct codec_info *ci = &onyx_codec_info;
+ u8 v;
+ int err;
+
+ if (!onyx->codec.gpio || !onyx->codec.gpio->methods) {
+ printk(KERN_ERR PFX "gpios not assigned!!\n");
+ return -EINVAL;
+ }
+
+ onyx->codec.gpio->methods->set_hw_reset(onyx->codec.gpio, 0);
+ msleep(1);
+ onyx->codec.gpio->methods->set_hw_reset(onyx->codec.gpio, 1);
+ msleep(1);
+ onyx->codec.gpio->methods->set_hw_reset(onyx->codec.gpio, 0);
+ msleep(1);
+
+ if (onyx_register_init(onyx)) {
+ printk(KERN_ERR PFX "failed to initialise onyx registers\n");
+ return -ENODEV;
+ }
+
+ if (aoa_snd_device_new(SNDRV_DEV_LOWLEVEL, onyx, &ops)) {
+ printk(KERN_ERR PFX "failed to create onyx snd device!\n");
+ return -ENODEV;
+ }
+
+ /* nothing connected? what a joke! */
+ if ((onyx->codec.connected & 0xF) == 0)
+ return -ENOTCONN;
+
+ /* if no inputs are present... */
+ if ((onyx->codec.connected & 0xC) == 0) {
+ if (!onyx->codec_info)
+ onyx->codec_info = kmalloc(sizeof(struct codec_info), GFP_KERNEL);
+ if (!onyx->codec_info)
+ return -ENOMEM;
+ ci = onyx->codec_info;
+ *ci = onyx_codec_info;
+ ci->transfers++;
+ }
+
+ /* if no outputs are present... */
+ if ((onyx->codec.connected & 3) == 0) {
+ if (!onyx->codec_info)
+ onyx->codec_info = kmalloc(sizeof(struct codec_info), GFP_KERNEL);
+ if (!onyx->codec_info)
+ return -ENOMEM;
+ ci = onyx->codec_info;
+ /* this is fine as there have to be inputs
+ * if we end up in this part of the code */
+ *ci = onyx_codec_info;
+ ci->transfers[1].formats = 0;
+ }
+
+ if (onyx->codec.soundbus_dev->attach_codec(onyx->codec.soundbus_dev,
+ aoa_get_card(),
+ ci, onyx)) {
+ printk(KERN_ERR PFX "error creating onyx pcm\n");
+ return -ENODEV;
+ }
+#define ADDCTL(n) \
+ do { \
+ ctl = snd_ctl_new1(&n, onyx); \
+ if (ctl) { \
+ ctl->id.device = \
+ onyx->codec.soundbus_dev->pcm->device; \
+ err = aoa_snd_ctl_add(ctl); \
+ if (err) \
+ goto error; \
+ } \
+ } while (0)
+
+ if (onyx->codec.soundbus_dev->pcm) {
+ /* give the user appropriate controls
+ * depending on what inputs are connected */
+ if ((onyx->codec.connected & 0xC) == 0xC)
+ ADDCTL(capture_source_control);
+ else if (onyx->codec.connected & 4)
+ onyx_set_capture_source(onyx, 0);
+ else
+ onyx_set_capture_source(onyx, 1);
+ if (onyx->codec.connected & 0xC)
+ ADDCTL(inputgain_control);
+
+ /* depending on what output is connected,
+ * give the user appropriate controls */
+ if (onyx->codec.connected & 1) {
+ ADDCTL(volume_control);
+ ADDCTL(mute_control);
+ ADDCTL(ovr1_control);
+ ADDCTL(flt0_control);
+ ADDCTL(hpf_control);
+ ADDCTL(dm12_control);
+ /* spdif control defaults to off */
+ }
+ if (onyx->codec.connected & 2) {
+ ADDCTL(onyx_spdif_mask);
+ ADDCTL(onyx_spdif_ctrl);
+ }
+ if ((onyx->codec.connected & 3) == 3)
+ ADDCTL(spdif_control);
+ /* if only S/PDIF is connected, enable it unconditionally */
+ if ((onyx->codec.connected & 3) == 2) {
+ onyx_read_register(onyx, ONYX_REG_DIG_INFO4, &v);
+ v |= ONYX_SPDIF_ENABLE;
+ onyx_write_register(onyx, ONYX_REG_DIG_INFO4, v);
+ }
+ }
+#undef ADDCTL
+ printk(KERN_INFO PFX "attached to onyx codec via i2c\n");
+
+ return 0;
+ error:
+ onyx->codec.soundbus_dev->detach_codec(onyx->codec.soundbus_dev, onyx);
+ snd_device_free(aoa_get_card(), onyx);
+ return err;
+}
+
+static void onyx_exit_codec(struct aoa_codec *codec)
+{
+ struct onyx *onyx = codec_to_onyx(codec);
+
+ if (!onyx->codec.soundbus_dev) {
+ printk(KERN_ERR PFX "onyx_exit_codec called without soundbus_dev!\n");
+ return;
+ }
+ onyx->codec.soundbus_dev->detach_codec(onyx->codec.soundbus_dev, onyx);
+}
+
+static struct i2c_driver onyx_driver;
+
+static int onyx_create(struct i2c_adapter *adapter,
+ struct device_node *node,
+ int addr)
+{
+ struct onyx *onyx;
+ u8 dummy;
+
+ onyx = kzalloc(sizeof(struct onyx), GFP_KERNEL);
+
+ if (!onyx)
+ return -ENOMEM;
+
+ mutex_init(&onyx->mutex);
+ onyx->i2c.driver = &onyx_driver;
+ onyx->i2c.adapter = adapter;
+ onyx->i2c.addr = addr & 0x7f;
+ strlcpy(onyx->i2c.name, "onyx audio codec", I2C_NAME_SIZE-1);
+
+ if (i2c_attach_client(&onyx->i2c)) {
+ printk(KERN_ERR PFX "failed to attach to i2c\n");
+ goto fail;
+ }
+
+ /* we try to read from register ONYX_REG_CONTROL
+ * to check if the codec is present */
+ if (onyx_read_register(onyx, ONYX_REG_CONTROL, &dummy) != 0) {
+ i2c_detach_client(&onyx->i2c);
+ printk(KERN_ERR PFX "failed to read control register\n");
+ goto fail;
+ }
+
+ strlcpy(onyx->codec.name, "onyx", MAX_CODEC_NAME_LEN-1);
+ onyx->codec.owner = THIS_MODULE;
+ onyx->codec.init = onyx_init_codec;
+ onyx->codec.exit = onyx_exit_codec;
+ onyx->codec.node = of_node_get(node);
+
+ if (aoa_codec_register(&onyx->codec)) {
+ i2c_detach_client(&onyx->i2c);
+ goto fail;
+ }
+ printk(KERN_DEBUG PFX "created and attached onyx instance\n");
+ return 0;
+ fail:
+ kfree(onyx);
+ return -EINVAL;
+}
+
+static int onyx_i2c_attach(struct i2c_adapter *adapter)
+{
+ struct device_node *busnode, *dev = NULL;
+ struct pmac_i2c_bus *bus;
+
+ bus = pmac_i2c_adapter_to_bus(adapter);
+ if (bus == NULL)
+ return -ENODEV;
+ busnode = pmac_i2c_get_bus_node(bus);
+
+ while ((dev = of_get_next_child(busnode, dev)) != NULL) {
+ if (device_is_compatible(dev, "pcm3052")) {
+ u32 *addr;
+ printk(KERN_DEBUG PFX "found pcm3052\n");
+ addr = (u32 *) get_property(dev, "reg", NULL);
+ if (!addr)
+ return -ENODEV;
+ return onyx_create(adapter, dev, (*addr)>>1);
+ }
+ }
+
+ /* if that didn't work, try desperate mode for older
+ * machines that have stuff missing from the device tree */
+
+ if (!device_is_compatible(busnode, "k2-i2c"))
+ return -ENODEV;
+
+ printk(KERN_DEBUG PFX "found k2-i2c, checking if onyx chip is on it\n");
+ /* probe both possible addresses for the onyx chip */
+ if (onyx_create(adapter, NULL, 0x46) == 0)
+ return 0;
+ return onyx_create(adapter, NULL, 0x47);
+}
+
+static int onyx_i2c_detach(struct i2c_client *client)
+{
+ struct onyx *onyx = container_of(client, struct onyx, i2c);
+ int err;
+
+ if ((err = i2c_detach_client(client)))
+ return err;
+ aoa_codec_unregister(&onyx->codec);
+ of_node_put(onyx->codec.node);
+ if (onyx->codec_info)
+ kfree(onyx->codec_info);
+ kfree(onyx);
+ return 0;
+}
+
+static struct i2c_driver onyx_driver = {
+ .driver = {
+ .name = "aoa_codec_onyx",
+ .owner = THIS_MODULE,
+ },
+ .attach_adapter = onyx_i2c_attach,
+ .detach_client = onyx_i2c_detach,
+};
+
+static int __init onyx_init(void)
+{
+ return i2c_add_driver(&onyx_driver);
+}
+
+static void __exit onyx_exit(void)
+{
+ i2c_del_driver(&onyx_driver);
+}
+
+module_init(onyx_init);
+module_exit(onyx_exit);
--- /dev/null
+/*
+ * Apple Onboard Audio driver for Onyx codec (header)
+ *
+ * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
+ *
+ * GPL v2, can be found in COPYING.
+ */
+#ifndef __SND_AOA_CODEC_ONYX_H
+#define __SND_AOA_CODEC_ONYX_H
+#include <stddef.h>
+#include <linux/i2c.h>
+#include <linux/i2c-dev.h>
+#include <asm/pmac_low_i2c.h>
+#include <asm/prom.h>
+
+/* PCM3052 register definitions */
+
+/* the attenuation registers take values from
+ * -1 (0dB) to -127 (-63.0 dB) or others (muted) */
+#define ONYX_REG_DAC_ATTEN_LEFT 65
+#define FIRSTREGISTER ONYX_REG_DAC_ATTEN_LEFT
+#define ONYX_REG_DAC_ATTEN_RIGHT 66
+
+#define ONYX_REG_CONTROL 67
+# define ONYX_MRST (1<<7)
+# define ONYX_SRST (1<<6)
+# define ONYX_ADPSV (1<<5)
+# define ONYX_DAPSV (1<<4)
+# define ONYX_SILICONVERSION (1<<0)
+/* all others reserved */
+
+#define ONYX_REG_DAC_CONTROL 68
+# define ONYX_OVR1 (1<<6)
+# define ONYX_MUTE_RIGHT (1<<1)
+# define ONYX_MUTE_LEFT (1<<0)
+
+#define ONYX_REG_DAC_DEEMPH 69
+# define ONYX_DIGDEEMPH_SHIFT 5
+# define ONYX_DIGDEEMPH_MASK (3<<ONYX_DIGDEEMPH_SHIFT)
+# define ONYX_DIGDEEMPH_CTRL (1<<4)
+
+#define ONYX_REG_DAC_FILTER 70
+# define ONYX_ROLLOFF_FAST (1<<5)
+# define ONYX_DAC_FILTER_ALWAYS (1<<2)
+
+#define ONYX_REG_DAC_OUTPHASE 71
+# define ONYX_OUTPHASE_INVERTED (1<<0)
+
+#define ONYX_REG_ADC_CONTROL 72
+# define ONYX_ADC_INPUT_MIC (1<<5)
+/* 8 + input gain in dB, valid range for input gain is -4 .. 20 dB */
+# define ONYX_ADC_PGA_GAIN_MASK 0x1f
+
+#define ONYX_REG_ADC_HPF_BYPASS 75
+# define ONYX_HPF_DISABLE (1<<3)
+# define ONYX_ADC_HPF_ALWAYS (1<<2)
+
+#define ONYX_REG_DIG_INFO1 77
+# define ONYX_MASK_DIN_TO_BPZ (1<<7)
+/* bits 1-5 control channel bits 1-5 */
+# define ONYX_DIGOUT_DISABLE (1<<0)
+
+#define ONYX_REG_DIG_INFO2 78
+/* controls channel bits 8-15 */
+
+#define ONYX_REG_DIG_INFO3 79
+/* control channel bits 24-29, high 2 bits reserved */
+
+#define ONYX_REG_DIG_INFO4 80
+# define ONYX_VALIDL (1<<7)
+# define ONYX_VALIDR (1<<6)
+# define ONYX_SPDIF_ENABLE (1<<5)
+/* lower 4 bits control bits 32-35 of channel control and word length */
+# define ONYX_WORDLEN_MASK (0xF)
+
+#endif /* __SND_AOA_CODEC_ONYX_H */
--- /dev/null
+/*
+ This is the program used to generate below table.
+
+#include <stdio.h>
+#include <math.h>
+int main() {
+ int dB2;
+ printf("/" "* This file is only included exactly once!\n");
+ printf(" *\n");
+ printf(" * If they'd only tell us that generating this table was\n");
+ printf(" * as easy as calculating\n");
+ printf(" * hwvalue = 1048576.0*exp(0.057564628*dB*2)\n");
+ printf(" * :) *" "/\n");
+ printf("static int tas_gaintable[] = {\n");
+ printf(" 0x000000, /" "* -infinity dB *" "/\n");
+ for (dB2=-140;dB2<=36;dB2++)
+ printf(" 0x%.6x, /" "* %-02.1f dB *" "/\n", (int)(1048576.0*exp(0.057564628*dB2)), dB2/2.0);
+ printf("};\n\n");
+}
+
+*/
+
+/* This file is only included exactly once!
+ *
+ * If they'd only tell us that generating this table was
+ * as easy as calculating
+ * hwvalue = 1048576.0*exp(0.057564628*dB*2)
+ * :) */
+static int tas_gaintable[] = {
+ 0x000000, /* -infinity dB */
+ 0x00014b, /* -70.0 dB */
+ 0x00015f, /* -69.5 dB */
+ 0x000174, /* -69.0 dB */
+ 0x00018a, /* -68.5 dB */
+ 0x0001a1, /* -68.0 dB */
+ 0x0001ba, /* -67.5 dB */
+ 0x0001d4, /* -67.0 dB */
+ 0x0001f0, /* -66.5 dB */
+ 0x00020d, /* -66.0 dB */
+ 0x00022c, /* -65.5 dB */
+ 0x00024d, /* -65.0 dB */
+ 0x000270, /* -64.5 dB */
+ 0x000295, /* -64.0 dB */
+ 0x0002bc, /* -63.5 dB */
+ 0x0002e6, /* -63.0 dB */
+ 0x000312, /* -62.5 dB */
+ 0x000340, /* -62.0 dB */
+ 0x000372, /* -61.5 dB */
+ 0x0003a6, /* -61.0 dB */
+ 0x0003dd, /* -60.5 dB */
+ 0x000418, /* -60.0 dB */
+ 0x000456, /* -59.5 dB */
+ 0x000498, /* -59.0 dB */
+ 0x0004de, /* -58.5 dB */
+ 0x000528, /* -58.0 dB */
+ 0x000576, /* -57.5 dB */
+ 0x0005c9, /* -57.0 dB */
+ 0x000620, /* -56.5 dB */
+ 0x00067d, /* -56.0 dB */
+ 0x0006e0, /* -55.5 dB */
+ 0x000748, /* -55.0 dB */
+ 0x0007b7, /* -54.5 dB */
+ 0x00082c, /* -54.0 dB */
+ 0x0008a8, /* -53.5 dB */
+ 0x00092b, /* -53.0 dB */
+ 0x0009b6, /* -52.5 dB */
+ 0x000a49, /* -52.0 dB */
+ 0x000ae5, /* -51.5 dB */
+ 0x000b8b, /* -51.0 dB */
+ 0x000c3a, /* -50.5 dB */
+ 0x000cf3, /* -50.0 dB */
+ 0x000db8, /* -49.5 dB */
+ 0x000e88, /* -49.0 dB */
+ 0x000f64, /* -48.5 dB */
+ 0x00104e, /* -48.0 dB */
+ 0x001145, /* -47.5 dB */
+ 0x00124b, /* -47.0 dB */
+ 0x001361, /* -46.5 dB */
+ 0x001487, /* -46.0 dB */
+ 0x0015be, /* -45.5 dB */
+ 0x001708, /* -45.0 dB */
+ 0x001865, /* -44.5 dB */
+ 0x0019d8, /* -44.0 dB */
+ 0x001b60, /* -43.5 dB */
+ 0x001cff, /* -43.0 dB */
+ 0x001eb7, /* -42.5 dB */
+ 0x002089, /* -42.0 dB */
+ 0x002276, /* -41.5 dB */
+ 0x002481, /* -41.0 dB */
+ 0x0026ab, /* -40.5 dB */
+ 0x0028f5, /* -40.0 dB */
+ 0x002b63, /* -39.5 dB */
+ 0x002df5, /* -39.0 dB */
+ 0x0030ae, /* -38.5 dB */
+ 0x003390, /* -38.0 dB */
+ 0x00369e, /* -37.5 dB */
+ 0x0039db, /* -37.0 dB */
+ 0x003d49, /* -36.5 dB */
+ 0x0040ea, /* -36.0 dB */
+ 0x0044c3, /* -35.5 dB */
+ 0x0048d6, /* -35.0 dB */
+ 0x004d27, /* -34.5 dB */
+ 0x0051b9, /* -34.0 dB */
+ 0x005691, /* -33.5 dB */
+ 0x005bb2, /* -33.0 dB */
+ 0x006121, /* -32.5 dB */
+ 0x0066e3, /* -32.0 dB */
+ 0x006cfb, /* -31.5 dB */
+ 0x007370, /* -31.0 dB */
+ 0x007a48, /* -30.5 dB */
+ 0x008186, /* -30.0 dB */
+ 0x008933, /* -29.5 dB */
+ 0x009154, /* -29.0 dB */
+ 0x0099f1, /* -28.5 dB */
+ 0x00a310, /* -28.0 dB */
+ 0x00acba, /* -27.5 dB */
+ 0x00b6f6, /* -27.0 dB */
+ 0x00c1cd, /* -26.5 dB */
+ 0x00cd49, /* -26.0 dB */
+ 0x00d973, /* -25.5 dB */
+ 0x00e655, /* -25.0 dB */
+ 0x00f3fb, /* -24.5 dB */
+ 0x010270, /* -24.0 dB */
+ 0x0111c0, /* -23.5 dB */
+ 0x0121f9, /* -23.0 dB */
+ 0x013328, /* -22.5 dB */
+ 0x01455b, /* -22.0 dB */
+ 0x0158a2, /* -21.5 dB */
+ 0x016d0e, /* -21.0 dB */
+ 0x0182af, /* -20.5 dB */
+ 0x019999, /* -20.0 dB */
+ 0x01b1de, /* -19.5 dB */
+ 0x01cb94, /* -19.0 dB */
+ 0x01e6cf, /* -18.5 dB */
+ 0x0203a7, /* -18.0 dB */
+ 0x022235, /* -17.5 dB */
+ 0x024293, /* -17.0 dB */
+ 0x0264db, /* -16.5 dB */
+ 0x02892c, /* -16.0 dB */
+ 0x02afa3, /* -15.5 dB */
+ 0x02d862, /* -15.0 dB */
+ 0x03038a, /* -14.5 dB */
+ 0x033142, /* -14.0 dB */
+ 0x0361af, /* -13.5 dB */
+ 0x0394fa, /* -13.0 dB */
+ 0x03cb50, /* -12.5 dB */
+ 0x0404de, /* -12.0 dB */
+ 0x0441d5, /* -11.5 dB */
+ 0x048268, /* -11.0 dB */
+ 0x04c6d0, /* -10.5 dB */
+ 0x050f44, /* -10.0 dB */
+ 0x055c04, /* -9.5 dB */
+ 0x05ad50, /* -9.0 dB */
+ 0x06036e, /* -8.5 dB */
+ 0x065ea5, /* -8.0 dB */
+ 0x06bf44, /* -7.5 dB */
+ 0x07259d, /* -7.0 dB */
+ 0x079207, /* -6.5 dB */
+ 0x0804dc, /* -6.0 dB */
+ 0x087e80, /* -5.5 dB */
+ 0x08ff59, /* -5.0 dB */
+ 0x0987d5, /* -4.5 dB */
+ 0x0a1866, /* -4.0 dB */
+ 0x0ab189, /* -3.5 dB */
+ 0x0b53be, /* -3.0 dB */
+ 0x0bff91, /* -2.5 dB */
+ 0x0cb591, /* -2.0 dB */
+ 0x0d765a, /* -1.5 dB */
+ 0x0e4290, /* -1.0 dB */
+ 0x0f1adf, /* -0.5 dB */
+ 0x100000, /* 0.0 dB */
+ 0x10f2b4, /* 0.5 dB */
+ 0x11f3c9, /* 1.0 dB */
+ 0x13041a, /* 1.5 dB */
+ 0x14248e, /* 2.0 dB */
+ 0x15561a, /* 2.5 dB */
+ 0x1699c0, /* 3.0 dB */
+ 0x17f094, /* 3.5 dB */
+ 0x195bb8, /* 4.0 dB */
+ 0x1adc61, /* 4.5 dB */
+ 0x1c73d5, /* 5.0 dB */
+ 0x1e236d, /* 5.5 dB */
+ 0x1fec98, /* 6.0 dB */
+ 0x21d0d9, /* 6.5 dB */
+ 0x23d1cd, /* 7.0 dB */
+ 0x25f125, /* 7.5 dB */
+ 0x2830af, /* 8.0 dB */
+ 0x2a9254, /* 8.5 dB */
+ 0x2d1818, /* 9.0 dB */
+ 0x2fc420, /* 9.5 dB */
+ 0x3298b0, /* 10.0 dB */
+ 0x35982f, /* 10.5 dB */
+ 0x38c528, /* 11.0 dB */
+ 0x3c224c, /* 11.5 dB */
+ 0x3fb278, /* 12.0 dB */
+ 0x4378b0, /* 12.5 dB */
+ 0x477829, /* 13.0 dB */
+ 0x4bb446, /* 13.5 dB */
+ 0x5030a1, /* 14.0 dB */
+ 0x54f106, /* 14.5 dB */
+ 0x59f980, /* 15.0 dB */
+ 0x5f4e52, /* 15.5 dB */
+ 0x64f403, /* 16.0 dB */
+ 0x6aef5e, /* 16.5 dB */
+ 0x714575, /* 17.0 dB */
+ 0x77fbaa, /* 17.5 dB */
+ 0x7f17af, /* 18.0 dB */
+};
+
--- /dev/null
+/*
+ * Apple Onboard Audio driver for tas codec
+ *
+ * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
+ *
+ * GPL v2, can be found in COPYING.
+ *
+ * Open questions:
+ * - How to distinguish between 3004 and versions?
+ *
+ * FIXMEs:
+ * - This codec driver doesn't honour the 'connected'
+ * property of the aoa_codec struct, hence if
+ * it is used in machines where not everything is
+ * connected it will display wrong mixer elements.
+ * - Driver assumes that the microphone is always
+ * monaureal and connected to the right channel of
+ * the input. This should also be a codec-dependent
+ * flag, maybe the codec should have 3 different
+ * bits for the three different possibilities how
+ * it can be hooked up...
+ * But as long as I don't see any hardware hooked
+ * up that way...
+ * - As Apple notes in their code, the tas3004 seems
+ * to delay the right channel by one sample. You can
+ * see this when for example recording stereo in
+ * audacity, or recording the tas output via cable
+ * on another machine (use a sinus generator or so).
+ * I tried programming the BiQuads but couldn't
+ * make the delay work, maybe someone can read the
+ * datasheet and fix it. The relevant Apple comment
+ * is in AppleTAS3004Audio.cpp lines 1637 ff. Note
+ * that their comment describing how they program
+ * the filters sucks...
+ *
+ * Other things:
+ * - this should actually register *two* aoa_codec
+ * structs since it has two inputs. Then it must
+ * use the prepare callback to forbid running the
+ * secondary output on a different clock.
+ * Also, whatever bus knows how to do this must
+ * provide two soundbus_dev devices and the fabric
+ * must be able to link them correctly.
+ *
+ * I don't even know if Apple ever uses the second
+ * port on the tas3004 though, I don't think their
+ * i2s controllers can even do it. OTOH, they all
+ * derive the clocks from common clocks, so it
+ * might just be possible. The framework allows the
+ * codec to refine the transfer_info items in the
+ * usable callback, so we can simply remove the
+ * rates the second instance is not using when it
+ * actually is in use.
+ * Maybe we'll need to make the sound busses have
+ * a 'clock group id' value so the codec can
+ * determine if the two outputs can be driven at
+ * the same time. But that is likely overkill, up
+ * to the fabric to not link them up incorrectly,
+ * and up to the hardware designer to not wire
+ * them up in some weird unusable way.
+ */
+#include <stddef.h>
+#include <linux/i2c.h>
+#include <linux/i2c-dev.h>
+#include <asm/pmac_low_i2c.h>
+#include <asm/prom.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+MODULE_AUTHOR("Johannes Berg <johannes@sipsolutions.net>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("tas codec driver for snd-aoa");
+
+#include "snd-aoa-codec-tas.h"
+#include "snd-aoa-codec-tas-gain-table.h"
+#include "../aoa.h"
+#include "../soundbus/soundbus.h"
+
+
+#define PFX "snd-aoa-codec-tas: "
+
+struct tas {
+ struct aoa_codec codec;
+ struct i2c_client i2c;
+ u32 muted_l:1, muted_r:1,
+ controls_created:1;
+ u8 cached_volume_l, cached_volume_r;
+ u8 mixer_l[3], mixer_r[3];
+ u8 acr;
+};
+
+static struct tas *codec_to_tas(struct aoa_codec *codec)
+{
+ return container_of(codec, struct tas, codec);
+}
+
+static inline int tas_write_reg(struct tas *tas, u8 reg, u8 len, u8 *data)
+{
+ if (len == 1)
+ return i2c_smbus_write_byte_data(&tas->i2c, reg, *data);
+ else
+ return i2c_smbus_write_i2c_block_data(&tas->i2c, reg, len, data);
+}
+
+static void tas_set_volume(struct tas *tas)
+{
+ u8 block[6];
+ int tmp;
+ u8 left, right;
+
+ left = tas->cached_volume_l;
+ right = tas->cached_volume_r;
+
+ if (left > 177) left = 177;
+ if (right > 177) right = 177;
+
+ if (tas->muted_l) left = 0;
+ if (tas->muted_r) right = 0;
+
+ /* analysing the volume and mixer tables shows
+ * that they are similar enough when we shift
+ * the mixer table down by 4 bits. The error
+ * is miniscule, in just one item the error
+ * is 1, at a value of 0x07f17b (mixer table
+ * value is 0x07f17a) */
+ tmp = tas_gaintable[left];
+ block[0] = tmp>>20;
+ block[1] = tmp>>12;
+ block[2] = tmp>>4;
+ tmp = tas_gaintable[right];
+ block[3] = tmp>>20;
+ block[4] = tmp>>12;
+ block[5] = tmp>>4;
+ tas_write_reg(tas, TAS_REG_VOL, 6, block);
+}
+
+static void tas_set_mixer(struct tas *tas)
+{
+ u8 block[9];
+ int tmp, i;
+ u8 val;
+
+ for (i=0;i<3;i++) {
+ val = tas->mixer_l[i];
+ if (val > 177) val = 177;
+ tmp = tas_gaintable[val];
+ block[3*i+0] = tmp>>16;
+ block[3*i+1] = tmp>>8;
+ block[3*i+2] = tmp;
+ }
+ tas_write_reg(tas, TAS_REG_LMIX, 9, block);
+
+ for (i=0;i<3;i++) {
+ val = tas->mixer_r[i];
+ if (val > 177) val = 177;
+ tmp = tas_gaintable[val];
+ block[3*i+0] = tmp>>16;
+ block[3*i+1] = tmp>>8;
+ block[3*i+2] = tmp;
+ }
+ tas_write_reg(tas, TAS_REG_RMIX, 9, block);
+}
+
+/* alsa stuff */
+
+static int tas_dev_register(struct snd_device *dev)
+{
+ return 0;
+}
+
+static struct snd_device_ops ops = {
+ .dev_register = tas_dev_register,
+};
+
+static int tas_snd_vol_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 2;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 177;
+ return 0;
+}
+
+static int tas_snd_vol_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tas *tas = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.integer.value[0] = tas->cached_volume_l;
+ ucontrol->value.integer.value[1] = tas->cached_volume_r;
+ return 0;
+}
+
+static int tas_snd_vol_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tas *tas = snd_kcontrol_chip(kcontrol);
+
+ if (tas->cached_volume_l == ucontrol->value.integer.value[0]
+ && tas->cached_volume_r == ucontrol->value.integer.value[1])
+ return 0;
+
+ tas->cached_volume_l = ucontrol->value.integer.value[0];
+ tas->cached_volume_r = ucontrol->value.integer.value[1];
+ tas_set_volume(tas);
+ return 1;
+}
+
+static struct snd_kcontrol_new volume_control = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Master Playback Volume",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = tas_snd_vol_info,
+ .get = tas_snd_vol_get,
+ .put = tas_snd_vol_put,
+};
+
+static int tas_snd_mute_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 2;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int tas_snd_mute_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tas *tas = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.integer.value[0] = !tas->muted_l;
+ ucontrol->value.integer.value[1] = !tas->muted_r;
+ return 0;
+}
+
+static int tas_snd_mute_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tas *tas = snd_kcontrol_chip(kcontrol);
+
+ if (tas->muted_l == !ucontrol->value.integer.value[0]
+ && tas->muted_r == !ucontrol->value.integer.value[1])
+ return 0;
+
+ tas->muted_l = !ucontrol->value.integer.value[0];
+ tas->muted_r = !ucontrol->value.integer.value[1];
+ tas_set_volume(tas);
+ return 1;
+}
+
+static struct snd_kcontrol_new mute_control = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Master Playback Switch",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = tas_snd_mute_info,
+ .get = tas_snd_mute_get,
+ .put = tas_snd_mute_put,
+};
+
+static int tas_snd_mixer_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 2;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 177;
+ return 0;
+}
+
+static int tas_snd_mixer_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tas *tas = snd_kcontrol_chip(kcontrol);
+ int idx = kcontrol->private_value;
+
+ ucontrol->value.integer.value[0] = tas->mixer_l[idx];
+ ucontrol->value.integer.value[1] = tas->mixer_r[idx];
+
+ return 0;
+}
+
+static int tas_snd_mixer_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tas *tas = snd_kcontrol_chip(kcontrol);
+ int idx = kcontrol->private_value;
+
+ if (tas->mixer_l[idx] == ucontrol->value.integer.value[0]
+ && tas->mixer_r[idx] == ucontrol->value.integer.value[1])
+ return 0;
+
+ tas->mixer_l[idx] = ucontrol->value.integer.value[0];
+ tas->mixer_r[idx] = ucontrol->value.integer.value[1];
+
+ tas_set_mixer(tas);
+ return 1;
+}
+
+#define MIXER_CONTROL(n,descr,idx) \
+static struct snd_kcontrol_new n##_control = { \
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = descr " Playback Volume", \
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \
+ .info = tas_snd_mixer_info, \
+ .get = tas_snd_mixer_get, \
+ .put = tas_snd_mixer_put, \
+ .private_value = idx, \
+}
+
+MIXER_CONTROL(pcm1, "PCM1", 0);
+MIXER_CONTROL(monitor, "Monitor", 2);
+
+static int tas_snd_capture_source_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ static char *texts[] = { "Line-In", "Microphone" };
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = 2;
+ if (uinfo->value.enumerated.item > 1)
+ uinfo->value.enumerated.item = 1;
+ strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+ return 0;
+}
+
+static int tas_snd_capture_source_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tas *tas = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.enumerated.item[0] = !!(tas->acr & TAS_ACR_INPUT_B);
+ return 0;
+}
+
+static int tas_snd_capture_source_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tas *tas = snd_kcontrol_chip(kcontrol);
+ int oldacr = tas->acr;
+
+ tas->acr &= ~TAS_ACR_INPUT_B;
+ if (ucontrol->value.enumerated.item[0])
+ tas->acr |= TAS_ACR_INPUT_B;
+ if (oldacr == tas->acr)
+ return 0;
+ tas_write_reg(tas, TAS_REG_ACR, 1, &tas->acr);
+ return 1;
+}
+
+static struct snd_kcontrol_new capture_source_control = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ /* If we name this 'Input Source', it properly shows up in
+ * alsamixer as a selection, * but it's shown under the
+ * 'Playback' category.
+ * If I name it 'Capture Source', it shows up in strange
+ * ways (two bools of which one can be selected at a
+ * time) but at least it's shown in the 'Capture'
+ * category.
+ * I was told that this was due to backward compatibility,
+ * but I don't understand then why the mangling is *not*
+ * done when I name it "Input Source".....
+ */
+ .name = "Capture Source",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = tas_snd_capture_source_info,
+ .get = tas_snd_capture_source_get,
+ .put = tas_snd_capture_source_put,
+};
+
+
+static struct transfer_info tas_transfers[] = {
+ {
+ /* input */
+ .formats = SNDRV_PCM_FMTBIT_S16_BE | SNDRV_PCM_FMTBIT_S16_BE |
+ SNDRV_PCM_FMTBIT_S24_BE | SNDRV_PCM_FMTBIT_S24_BE,
+ .rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000,
+ .transfer_in = 1,
+ },
+ {
+ /* output */
+ .formats = SNDRV_PCM_FMTBIT_S16_BE | SNDRV_PCM_FMTBIT_S16_BE |
+ SNDRV_PCM_FMTBIT_S24_BE | SNDRV_PCM_FMTBIT_S24_BE,
+ .rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000,
+ .transfer_in = 0,
+ },
+ {}
+};
+
+static int tas_usable(struct codec_info_item *cii,
+ struct transfer_info *ti,
+ struct transfer_info *out)
+{
+ return 1;
+}
+
+static int tas_reset_init(struct tas *tas)
+{
+ u8 tmp;
+ tas->codec.gpio->methods->set_hw_reset(tas->codec.gpio, 0);
+ msleep(1);
+ tas->codec.gpio->methods->set_hw_reset(tas->codec.gpio, 1);
+ msleep(1);
+ tas->codec.gpio->methods->set_hw_reset(tas->codec.gpio, 0);
+ msleep(1);
+
+ tas->acr &= ~TAS_ACR_ANALOG_PDOWN;
+ tas->acr |= TAS_ACR_B_MONAUREAL | TAS_ACR_B_MON_SEL_RIGHT;
+ if (tas_write_reg(tas, TAS_REG_ACR, 1, &tas->acr))
+ return -ENODEV;
+
+ tmp = TAS_MCS_SCLK64 | TAS_MCS_SPORT_MODE_I2S | TAS_MCS_SPORT_WL_24BIT;
+ if (tas_write_reg(tas, TAS_REG_MCS, 1, &tmp))
+ return -ENODEV;
+
+ tmp = 0;
+ if (tas_write_reg(tas, TAS_REG_MCS2, 1, &tmp))
+ return -ENODEV;
+
+ return 0;
+}
+
+/* we are controlled via i2c and assume that is always up
+ * If that wasn't the case, we'd have to suspend once
+ * our i2c device is suspended, and then take note of that! */
+static int tas_suspend(struct tas *tas)
+{
+ tas->acr |= TAS_ACR_ANALOG_PDOWN;
+ tas_write_reg(tas, TAS_REG_ACR, 1, &tas->acr);
+ return 0;
+}
+
+static int tas_resume(struct tas *tas)
+{
+ /* reset codec */
+ tas_reset_init(tas);
+ tas_set_volume(tas);
+ tas_set_mixer(tas);
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int _tas_suspend(struct codec_info_item *cii, pm_message_t state)
+{
+ return tas_suspend(cii->codec_data);
+}
+
+static int _tas_resume(struct codec_info_item *cii)
+{
+ return tas_resume(cii->codec_data);
+}
+#endif
+
+static struct codec_info tas_codec_info = {
+ .transfers = tas_transfers,
+ /* in theory, we can drive it at 512 too...
+ * but so far the framework doesn't allow
+ * for that and I don't see much point in it. */
+ .sysclock_factor = 256,
+ /* same here, could be 32 for just one 16 bit format */
+ .bus_factor = 64,
+ .owner = THIS_MODULE,
+ .usable = tas_usable,
+#ifdef CONFIG_PM
+ .suspend = _tas_suspend,
+ .resume = _tas_resume,
+#endif
+};
+
+static int tas_init_codec(struct aoa_codec *codec)
+{
+ struct tas *tas = codec_to_tas(codec);
+ int err;
+
+ if (!tas->codec.gpio || !tas->codec.gpio->methods) {
+ printk(KERN_ERR PFX "gpios not assigned!!\n");
+ return -EINVAL;
+ }
+
+ if (tas_reset_init(tas)) {
+ printk(KERN_ERR PFX "tas failed to initialise\n");
+ return -ENXIO;
+ }
+
+ if (tas->codec.soundbus_dev->attach_codec(tas->codec.soundbus_dev,
+ aoa_get_card(),
+ &tas_codec_info, tas)) {
+ printk(KERN_ERR PFX "error attaching tas to soundbus\n");
+ return -ENODEV;
+ }
+
+ if (aoa_snd_device_new(SNDRV_DEV_LOWLEVEL, tas, &ops)) {
+ printk(KERN_ERR PFX "failed to create tas snd device!\n");
+ return -ENODEV;
+ }
+ err = aoa_snd_ctl_add(snd_ctl_new1(&volume_control, tas));
+ if (err)
+ goto error;
+
+ err = aoa_snd_ctl_add(snd_ctl_new1(&mute_control, tas));
+ if (err)
+ goto error;
+
+ err = aoa_snd_ctl_add(snd_ctl_new1(&pcm1_control, tas));
+ if (err)
+ goto error;
+
+ err = aoa_snd_ctl_add(snd_ctl_new1(&monitor_control, tas));
+ if (err)
+ goto error;
+
+ err = aoa_snd_ctl_add(snd_ctl_new1(&capture_source_control, tas));
+ if (err)
+ goto error;
+
+ return 0;
+ error:
+ tas->codec.soundbus_dev->detach_codec(tas->codec.soundbus_dev, tas);
+ snd_device_free(aoa_get_card(), tas);
+ return err;
+}
+
+static void tas_exit_codec(struct aoa_codec *codec)
+{
+ struct tas *tas = codec_to_tas(codec);
+
+ if (!tas->codec.soundbus_dev)
+ return;
+ tas->codec.soundbus_dev->detach_codec(tas->codec.soundbus_dev, tas);
+}
+
+
+static struct i2c_driver tas_driver;
+
+static int tas_create(struct i2c_adapter *adapter,
+ struct device_node *node,
+ int addr)
+{
+ struct tas *tas;
+
+ tas = kzalloc(sizeof(struct tas), GFP_KERNEL);
+
+ if (!tas)
+ return -ENOMEM;
+
+ tas->i2c.driver = &tas_driver;
+ tas->i2c.adapter = adapter;
+ tas->i2c.addr = addr;
+ strlcpy(tas->i2c.name, "tas audio codec", I2C_NAME_SIZE-1);
+
+ if (i2c_attach_client(&tas->i2c)) {
+ printk(KERN_ERR PFX "failed to attach to i2c\n");
+ goto fail;
+ }
+
+ strlcpy(tas->codec.name, "tas", MAX_CODEC_NAME_LEN-1);
+ tas->codec.owner = THIS_MODULE;
+ tas->codec.init = tas_init_codec;
+ tas->codec.exit = tas_exit_codec;
+ tas->codec.node = of_node_get(node);
+
+ if (aoa_codec_register(&tas->codec)) {
+ goto detach;
+ }
+ printk(KERN_DEBUG "snd-aoa-codec-tas: created and attached tas instance\n");
+ return 0;
+ detach:
+ i2c_detach_client(&tas->i2c);
+ fail:
+ kfree(tas);
+ return -EINVAL;
+}
+
+static int tas_i2c_attach(struct i2c_adapter *adapter)
+{
+ struct device_node *busnode, *dev = NULL;
+ struct pmac_i2c_bus *bus;
+
+ bus = pmac_i2c_adapter_to_bus(adapter);
+ if (bus == NULL)
+ return -ENODEV;
+ busnode = pmac_i2c_get_bus_node(bus);
+
+ while ((dev = of_get_next_child(busnode, dev)) != NULL) {
+ if (device_is_compatible(dev, "tas3004")) {
+ u32 *addr;
+ printk(KERN_DEBUG PFX "found tas3004\n");
+ addr = (u32 *) get_property(dev, "reg", NULL);
+ if (!addr)
+ continue;
+ return tas_create(adapter, dev, ((*addr) >> 1) & 0x7f);
+ }
+ /* older machines have no 'codec' node with a 'compatible'
+ * property that says 'tas3004', they just have a 'deq'
+ * node without any such property... */
+ if (strcmp(dev->name, "deq") == 0) {
+ u32 *_addr, addr;
+ printk(KERN_DEBUG PFX "found 'deq' node\n");
+ _addr = (u32 *) get_property(dev, "i2c-address", NULL);
+ if (!_addr)
+ continue;
+ addr = ((*_addr) >> 1) & 0x7f;
+ /* now, if the address doesn't match any of the two
+ * that a tas3004 can have, we cannot handle this.
+ * I doubt it ever happens but hey. */
+ if (addr != 0x34 && addr != 0x35)
+ continue;
+ return tas_create(adapter, dev, addr);
+ }
+ }
+ return -ENODEV;
+}
+
+static int tas_i2c_detach(struct i2c_client *client)
+{
+ struct tas *tas = container_of(client, struct tas, i2c);
+ int err;
+ u8 tmp = TAS_ACR_ANALOG_PDOWN;
+
+ if ((err = i2c_detach_client(client)))
+ return err;
+ aoa_codec_unregister(&tas->codec);
+ of_node_put(tas->codec.node);
+
+ /* power down codec chip */
+ tas_write_reg(tas, TAS_REG_ACR, 1, &tmp);
+
+ kfree(tas);
+ return 0;
+}
+
+static struct i2c_driver tas_driver = {
+ .driver = {
+ .name = "aoa_codec_tas",
+ .owner = THIS_MODULE,
+ },
+ .attach_adapter = tas_i2c_attach,
+ .detach_client = tas_i2c_detach,
+};
+
+static int __init tas_init(void)
+{
+ return i2c_add_driver(&tas_driver);
+}
+
+static void __exit tas_exit(void)
+{
+ i2c_del_driver(&tas_driver);
+}
+
+module_init(tas_init);
+module_exit(tas_exit);
--- /dev/null
+/*
+ * Apple Onboard Audio driver for tas codec (header)
+ *
+ * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
+ *
+ * GPL v2, can be found in COPYING.
+ */
+#ifndef __SND_AOA_CODECTASH
+#define __SND_AOA_CODECTASH
+
+#define TAS_REG_MCS 0x01 /* main control */
+# define TAS_MCS_FASTLOAD (1<<7)
+# define TAS_MCS_SCLK64 (1<<6)
+# define TAS_MCS_SPORT_MODE_MASK (3<<4)
+# define TAS_MCS_SPORT_MODE_I2S (2<<4)
+# define TAS_MCS_SPORT_MODE_RJ (1<<4)
+# define TAS_MCS_SPORT_MODE_LJ (0<<4)
+# define TAS_MCS_SPORT_WL_MASK (3<<0)
+# define TAS_MCS_SPORT_WL_16BIT (0<<0)
+# define TAS_MCS_SPORT_WL_18BIT (1<<0)
+# define TAS_MCS_SPORT_WL_20BIT (2<<0)
+# define TAS_MCS_SPORT_WL_24BIT (3<<0)
+
+#define TAS_REG_DRC 0x02
+#define TAS_REG_VOL 0x04
+#define TAS_REG_TREBLE 0x05
+#define TAS_REG_BASS 0x06
+#define TAS_REG_LMIX 0x07
+#define TAS_REG_RMIX 0x08
+
+#define TAS_REG_ACR 0x40 /* analog control */
+# define TAS_ACR_B_MONAUREAL (1<<7)
+# define TAS_ACR_B_MON_SEL_RIGHT (1<<6)
+# define TAS_ACR_DEEMPH_MASK (3<<2)
+# define TAS_ACR_DEEMPH_OFF (0<<2)
+# define TAS_ACR_DEEMPH_48KHz (1<<2)
+# define TAS_ACR_DEEMPH_44KHz (2<<2)
+# define TAS_ACR_INPUT_B (1<<1)
+# define TAS_ACR_ANALOG_PDOWN (1<<0)
+
+#define TAS_REG_MCS2 0x43 /* main control 2 */
+# define TAS_MCS2_ALLPASS (1<<1)
+
+#define TAS_REG_LEFT_BIQUAD6 0x10
+#define TAS_REG_RIGHT_BIQUAD6 0x19
+
+#endif /* __SND_AOA_CODECTASH */
--- /dev/null
+/*
+ * Apple Onboard Audio driver for Toonie codec
+ *
+ * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
+ *
+ * GPL v2, can be found in COPYING.
+ *
+ *
+ * This is a driver for the toonie codec chip. This chip is present
+ * on the Mac Mini and is nothing but a DAC.
+ */
+#include <linux/delay.h>
+#include <linux/module.h>
+MODULE_AUTHOR("Johannes Berg <johannes@sipsolutions.net>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("toonie codec driver for snd-aoa");
+
+#include "../aoa.h"
+#include "../soundbus/soundbus.h"
+
+
+#define PFX "snd-aoa-codec-toonie: "
+
+struct toonie {
+ struct aoa_codec codec;
+};
+#define codec_to_toonie(c) container_of(c, struct toonie, codec)
+
+static int toonie_dev_register(struct snd_device *dev)
+{
+ return 0;
+}
+
+static struct snd_device_ops ops = {
+ .dev_register = toonie_dev_register,
+};
+
+static struct transfer_info toonie_transfers[] = {
+ /* This thing *only* has analog output,
+ * the rates are taken from Info.plist
+ * from Darwin. */
+ {
+ .formats = SNDRV_PCM_FMTBIT_S16_BE |
+ SNDRV_PCM_FMTBIT_S24_BE,
+ .rates = SNDRV_PCM_RATE_32000 |
+ SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_88200 |
+ SNDRV_PCM_RATE_96000,
+ },
+ {}
+};
+
+#ifdef CONFIG_PM
+static int toonie_suspend(struct codec_info_item *cii, pm_message_t state)
+{
+ /* can we turn it off somehow? */
+ return 0;
+}
+
+static int toonie_resume(struct codec_info_item *cii)
+{
+ return 0;
+}
+#endif /* CONFIG_PM */
+
+static struct codec_info toonie_codec_info = {
+ .transfers = toonie_transfers,
+ .sysclock_factor = 256,
+ .bus_factor = 64,
+ .owner = THIS_MODULE,
+#ifdef CONFIG_PM
+ .suspend = toonie_suspend,
+ .resume = toonie_resume,
+#endif
+};
+
+static int toonie_init_codec(struct aoa_codec *codec)
+{
+ struct toonie *toonie = codec_to_toonie(codec);
+
+ if (aoa_snd_device_new(SNDRV_DEV_LOWLEVEL, toonie, &ops)) {
+ printk(KERN_ERR PFX "failed to create toonie snd device!\n");
+ return -ENODEV;
+ }
+
+ /* nothing connected? what a joke! */
+ if (toonie->codec.connected != 1)
+ return -ENOTCONN;
+
+ if (toonie->codec.soundbus_dev->attach_codec(toonie->codec.soundbus_dev,
+ aoa_get_card(),
+ &toonie_codec_info, toonie)) {
+ printk(KERN_ERR PFX "error creating toonie pcm\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static void toonie_exit_codec(struct aoa_codec *codec)
+{
+ struct toonie *toonie = codec_to_toonie(codec);
+
+ if (!toonie->codec.soundbus_dev) {
+ printk(KERN_ERR PFX "toonie_exit_codec called without soundbus_dev!\n");
+ return;
+ }
+ toonie->codec.soundbus_dev->detach_codec(toonie->codec.soundbus_dev, toonie);
+}
+
+static struct toonie *toonie;
+
+static int __init toonie_init(void)
+{
+ toonie = kzalloc(sizeof(struct toonie), GFP_KERNEL);
+
+ if (!toonie)
+ return -ENOMEM;
+
+ strlcpy(toonie->codec.name, "toonie", sizeof(toonie->codec.name));
+ toonie->codec.owner = THIS_MODULE;
+ toonie->codec.init = toonie_init_codec;
+ toonie->codec.exit = toonie_exit_codec;
+
+ if (aoa_codec_register(&toonie->codec)) {
+ kfree(toonie);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void __exit toonie_exit(void)
+{
+ aoa_codec_unregister(&toonie->codec);
+ kfree(toonie);
+}
+
+module_init(toonie_init);
+module_exit(toonie_exit);
--- /dev/null
+obj-$(CONFIG_SND_AOA) += snd-aoa.o
+snd-aoa-objs := snd-aoa-core.o \
+ snd-aoa-alsa.o \
+ snd-aoa-gpio-pmf.o \
+ snd-aoa-gpio-feature.o
--- /dev/null
+/*
+ * Apple Onboard Audio Alsa helpers
+ *
+ * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
+ *
+ * GPL v2, can be found in COPYING.
+ */
+#include <linux/module.h>
+#include "snd-aoa-alsa.h"
+
+static int index = -1;
+module_param(index, int, 0444);
+MODULE_PARM_DESC(index, "index for AOA sound card.");
+
+static struct aoa_card *aoa_card;
+
+int aoa_alsa_init(char *name, struct module *mod)
+{
+ struct snd_card *alsa_card;
+ int err;
+
+ if (aoa_card)
+ /* cannot be EEXIST due to usage in aoa_fabric_register */
+ return -EBUSY;
+
+ alsa_card = snd_card_new(index, name, mod, sizeof(struct aoa_card));
+ if (!alsa_card)
+ return -ENOMEM;
+ aoa_card = alsa_card->private_data;
+ aoa_card->alsa_card = alsa_card;
+ strlcpy(alsa_card->driver, "AppleOnbdAudio", sizeof(alsa_card->driver));
+ strlcpy(alsa_card->shortname, name, sizeof(alsa_card->shortname));
+ strlcpy(alsa_card->longname, name, sizeof(alsa_card->longname));
+ strlcpy(alsa_card->mixername, name, sizeof(alsa_card->mixername));
+ err = snd_card_register(aoa_card->alsa_card);
+ if (err < 0) {
+ printk(KERN_ERR "snd-aoa: couldn't register alsa card\n");
+ snd_card_free(aoa_card->alsa_card);
+ aoa_card = NULL;
+ return err;
+ }
+ return 0;
+}
+
+struct snd_card *aoa_get_card(void)
+{
+ if (aoa_card)
+ return aoa_card->alsa_card;
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(aoa_get_card);
+
+void aoa_alsa_cleanup(void)
+{
+ if (aoa_card) {
+ snd_card_free(aoa_card->alsa_card);
+ aoa_card = NULL;
+ }
+}
+
+int aoa_snd_device_new(snd_device_type_t type,
+ void * device_data, struct snd_device_ops * ops)
+{
+ struct snd_card *card = aoa_get_card();
+ int err;
+
+ if (!card) return -ENOMEM;
+
+ err = snd_device_new(card, type, device_data, ops);
+ if (err) {
+ printk(KERN_ERR "snd-aoa: failed to create snd device (%d)\n", err);
+ return err;
+ }
+ err = snd_device_register(card, device_data);
+ if (err) {
+ printk(KERN_ERR "snd-aoa: failed to register "
+ "snd device (%d)\n", err);
+ printk(KERN_ERR "snd-aoa: have you forgotten the "
+ "dev_register callback?\n");
+ snd_device_free(card, device_data);
+ }
+ return err;
+}
+EXPORT_SYMBOL_GPL(aoa_snd_device_new);
+
+int aoa_snd_ctl_add(struct snd_kcontrol* control)
+{
+ int err;
+
+ if (!aoa_card) return -ENODEV;
+
+ err = snd_ctl_add(aoa_card->alsa_card, control);
+ if (err)
+ printk(KERN_ERR "snd-aoa: failed to add alsa control (%d)\n",
+ err);
+ return err;
+}
+EXPORT_SYMBOL_GPL(aoa_snd_ctl_add);
--- /dev/null
+/*
+ * Apple Onboard Audio Alsa private helpers
+ *
+ * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
+ *
+ * GPL v2, can be found in COPYING.
+ */
+
+#ifndef __SND_AOA_ALSA_H
+#define __SND_AOA_ALSA_H
+#include "../aoa.h"
+
+extern int aoa_alsa_init(char *name, struct module *mod);
+extern void aoa_alsa_cleanup(void);
+
+#endif /* __SND_AOA_ALSA_H */
--- /dev/null
+/*
+ * Apple Onboard Audio driver core
+ *
+ * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
+ *
+ * GPL v2, can be found in COPYING.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/list.h>
+#include "../aoa.h"
+#include "snd-aoa-alsa.h"
+
+MODULE_DESCRIPTION("Apple Onboard Audio Sound Driver");
+MODULE_AUTHOR("Johannes Berg <johannes@sipsolutions.net>");
+MODULE_LICENSE("GPL");
+
+/* We allow only one fabric. This simplifies things,
+ * and more don't really make that much sense */
+static struct aoa_fabric *fabric;
+static LIST_HEAD(codec_list);
+
+static int attach_codec_to_fabric(struct aoa_codec *c)
+{
+ int err;
+
+ if (!try_module_get(c->owner))
+ return -EBUSY;
+ /* found_codec has to be assigned */
+ err = -ENOENT;
+ if (fabric->found_codec)
+ err = fabric->found_codec(c);
+ if (err) {
+ module_put(c->owner);
+ printk(KERN_ERR "snd-aoa: fabric didn't like codec %s\n",
+ c->name);
+ return err;
+ }
+ c->fabric = fabric;
+
+ err = 0;
+ if (c->init)
+ err = c->init(c);
+ if (err) {
+ printk(KERN_ERR "snd-aoa: codec %s didn't init\n", c->name);
+ c->fabric = NULL;
+ if (fabric->remove_codec)
+ fabric->remove_codec(c);
+ module_put(c->owner);
+ return err;
+ }
+ if (fabric->attached_codec)
+ fabric->attached_codec(c);
+ return 0;
+}
+
+int aoa_codec_register(struct aoa_codec *codec)
+{
+ int err = 0;
+
+ /* if there's a fabric already, we can tell if we
+ * will want to have this codec, so propagate error
+ * through. Otherwise, this will happen later... */
+ if (fabric)
+ err = attach_codec_to_fabric(codec);
+ if (!err)
+ list_add(&codec->list, &codec_list);
+ return err;
+}
+EXPORT_SYMBOL_GPL(aoa_codec_register);
+
+void aoa_codec_unregister(struct aoa_codec *codec)
+{
+ list_del(&codec->list);
+ if (codec->fabric && codec->exit)
+ codec->exit(codec);
+ if (fabric && fabric->remove_codec)
+ fabric->remove_codec(codec);
+ codec->fabric = NULL;
+ module_put(codec->owner);
+}
+EXPORT_SYMBOL_GPL(aoa_codec_unregister);
+
+int aoa_fabric_register(struct aoa_fabric *new_fabric)
+{
+ struct aoa_codec *c;
+ int err;
+
+ /* allow querying for presence of fabric
+ * (i.e. do this test first!) */
+ if (new_fabric == fabric) {
+ err = -EALREADY;
+ goto attach;
+ }
+ if (fabric)
+ return -EEXIST;
+ if (!new_fabric)
+ return -EINVAL;
+
+ err = aoa_alsa_init(new_fabric->name, new_fabric->owner);
+ if (err)
+ return err;
+
+ fabric = new_fabric;
+
+ attach:
+ list_for_each_entry(c, &codec_list, list) {
+ if (c->fabric != fabric)
+ attach_codec_to_fabric(c);
+ }
+ return err;
+}
+EXPORT_SYMBOL_GPL(aoa_fabric_register);
+
+void aoa_fabric_unregister(struct aoa_fabric *old_fabric)
+{
+ struct aoa_codec *c;
+
+ if (fabric != old_fabric)
+ return;
+
+ list_for_each_entry(c, &codec_list, list) {
+ if (c->fabric)
+ aoa_fabric_unlink_codec(c);
+ }
+
+ aoa_alsa_cleanup();
+
+ fabric = NULL;
+}
+EXPORT_SYMBOL_GPL(aoa_fabric_unregister);
+
+void aoa_fabric_unlink_codec(struct aoa_codec *codec)
+{
+ if (!codec->fabric) {
+ printk(KERN_ERR "snd-aoa: fabric unassigned "
+ "in aoa_fabric_unlink_codec\n");
+ dump_stack();
+ return;
+ }
+ if (codec->exit)
+ codec->exit(codec);
+ if (codec->fabric->remove_codec)
+ codec->fabric->remove_codec(codec);
+ codec->fabric = NULL;
+ module_put(codec->owner);
+}
+EXPORT_SYMBOL_GPL(aoa_fabric_unlink_codec);
+
+static int __init aoa_init(void)
+{
+ return 0;
+}
+
+static void __exit aoa_exit(void)
+{
+ aoa_alsa_cleanup();
+}
+
+module_init(aoa_init);
+module_exit(aoa_exit);
--- /dev/null
+/*
+ * Apple Onboard Audio feature call GPIO control
+ *
+ * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
+ *
+ * GPL v2, can be found in COPYING.
+ *
+ * This file contains the GPIO control routines for
+ * direct (through feature calls) access to the GPIO
+ * registers.
+ */
+
+#include <asm/pmac_feature.h>
+#include <linux/interrupt.h>
+#include "../aoa.h"
+
+/* TODO: these are 20 global variables
+ * that aren't used on most machines...
+ * Move them into a dynamically allocated
+ * structure and use that.
+ */
+
+/* these are the GPIO numbers (register addresses as offsets into
+ * the GPIO space) */
+static int headphone_mute_gpio;
+static int amp_mute_gpio;
+static int lineout_mute_gpio;
+static int hw_reset_gpio;
+static int lineout_detect_gpio;
+static int headphone_detect_gpio;
+static int linein_detect_gpio;
+
+/* see the SWITCH_GPIO macro */
+static int headphone_mute_gpio_activestate;
+static int amp_mute_gpio_activestate;
+static int lineout_mute_gpio_activestate;
+static int hw_reset_gpio_activestate;
+static int lineout_detect_gpio_activestate;
+static int headphone_detect_gpio_activestate;
+static int linein_detect_gpio_activestate;
+
+/* node pointers that we save when getting the GPIO number
+ * to get the interrupt later */
+static struct device_node *lineout_detect_node;
+static struct device_node *linein_detect_node;
+static struct device_node *headphone_detect_node;
+
+static int lineout_detect_irq;
+static int linein_detect_irq;
+static int headphone_detect_irq;
+
+static struct device_node *get_gpio(char *name,
+ char *altname,
+ int *gpioptr,
+ int *gpioactiveptr)
+{
+ struct device_node *np, *gpio;
+ u32 *reg;
+ char *audio_gpio;
+
+ *gpioptr = -1;
+
+ /* check if we can get it the easy way ... */
+ np = of_find_node_by_name(NULL, name);
+ if (!np) {
+ /* some machines have only gpioX/extint-gpioX nodes,
+ * and an audio-gpio property saying what it is ...
+ * So what we have to do is enumerate all children
+ * of the gpio node and check them all. */
+ gpio = of_find_node_by_name(NULL, "gpio");
+ if (!gpio)
+ return NULL;
+ while ((np = of_get_next_child(gpio, np))) {
+ audio_gpio = get_property(np, "audio-gpio", NULL);
+ if (!audio_gpio)
+ continue;
+ if (strcmp(audio_gpio, name) == 0)
+ break;
+ if (altname && (strcmp(audio_gpio, altname) == 0))
+ break;
+ }
+ /* still not found, assume not there */
+ if (!np)
+ return NULL;
+ }
+
+ reg = (u32 *)get_property(np, "reg", NULL);
+ if (!reg)
+ return NULL;
+
+ *gpioptr = *reg;
+
+ /* this is a hack, usually the GPIOs 'reg' property
+ * should have the offset based from the GPIO space
+ * which is at 0x50, but apparently not always... */
+ if (*gpioptr < 0x50)
+ *gpioptr += 0x50;
+
+ reg = (u32 *)get_property(np, "audio-gpio-active-state", NULL);
+ if (!reg)
+ /* Apple seems to default to 1, but
+ * that doesn't seem right at least on most
+ * machines. So until proven that the opposite
+ * is necessary, we default to 0
+ * (which, incidentally, snd-powermac also does...) */
+ *gpioactiveptr = 0;
+ else
+ *gpioactiveptr = *reg;
+
+ return np;
+}
+
+static void get_irq(struct device_node * np, int *irqptr)
+{
+ *irqptr = -1;
+ if (!np)
+ return;
+ if (np->n_intrs != 1)
+ return;
+ *irqptr = np->intrs[0].line;
+}
+
+/* 0x4 is outenable, 0x1 is out, thus 4 or 5 */
+#define SWITCH_GPIO(name, v, on) \
+ (((v)&~1) | ((on)? \
+ (name##_gpio_activestate==0?4:5): \
+ (name##_gpio_activestate==0?5:4)))
+
+#define FTR_GPIO(name, bit) \
+static void ftr_gpio_set_##name(struct gpio_runtime *rt, int on)\
+{ \
+ int v; \
+ \
+ if (unlikely(!rt)) return; \
+ \
+ if (name##_mute_gpio < 0) \
+ return; \
+ \
+ v = pmac_call_feature(PMAC_FTR_READ_GPIO, NULL, \
+ name##_mute_gpio, \
+ 0); \
+ \
+ /* muted = !on... */ \
+ v = SWITCH_GPIO(name##_mute, v, !on); \
+ \
+ pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, \
+ name##_mute_gpio, v); \
+ \
+ rt->implementation_private &= ~(1<<bit); \
+ rt->implementation_private |= (!!on << bit); \
+} \
+static int ftr_gpio_get_##name(struct gpio_runtime *rt) \
+{ \
+ if (unlikely(!rt)) return 0; \
+ return (rt->implementation_private>>bit)&1; \
+}
+
+FTR_GPIO(headphone, 0);
+FTR_GPIO(amp, 1);
+FTR_GPIO(lineout, 2);
+
+static void ftr_gpio_set_hw_reset(struct gpio_runtime *rt, int on)
+{
+ int v;
+
+ if (unlikely(!rt)) return;
+ if (hw_reset_gpio < 0)
+ return;
+
+ v = pmac_call_feature(PMAC_FTR_READ_GPIO, NULL,
+ hw_reset_gpio, 0);
+ v = SWITCH_GPIO(hw_reset, v, on);
+ pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL,
+ hw_reset_gpio, v);
+}
+
+static void ftr_gpio_all_amps_off(struct gpio_runtime *rt)
+{
+ int saved;
+
+ if (unlikely(!rt)) return;
+ saved = rt->implementation_private;
+ ftr_gpio_set_headphone(rt, 0);
+ ftr_gpio_set_amp(rt, 0);
+ ftr_gpio_set_lineout(rt, 0);
+ rt->implementation_private = saved;
+}
+
+static void ftr_gpio_all_amps_restore(struct gpio_runtime *rt)
+{
+ int s;
+
+ if (unlikely(!rt)) return;
+ s = rt->implementation_private;
+ ftr_gpio_set_headphone(rt, (s>>0)&1);
+ ftr_gpio_set_amp(rt, (s>>1)&1);
+ ftr_gpio_set_lineout(rt, (s>>2)&1);
+}
+
+static void ftr_handle_notify(void *data)
+{
+ struct gpio_notification *notif = data;
+
+ mutex_lock(¬if->mutex);
+ if (notif->notify)
+ notif->notify(notif->data);
+ mutex_unlock(¬if->mutex);
+}
+
+static void ftr_gpio_init(struct gpio_runtime *rt)
+{
+ get_gpio("headphone-mute", NULL,
+ &headphone_mute_gpio,
+ &headphone_mute_gpio_activestate);
+ get_gpio("amp-mute", NULL,
+ &_mute_gpio,
+ &_mute_gpio_activestate);
+ get_gpio("lineout-mute", NULL,
+ &lineout_mute_gpio,
+ &lineout_mute_gpio_activestate);
+ get_gpio("hw-reset", "audio-hw-reset",
+ &hw_reset_gpio,
+ &hw_reset_gpio_activestate);
+
+ headphone_detect_node = get_gpio("headphone-detect", NULL,
+ &headphone_detect_gpio,
+ &headphone_detect_gpio_activestate);
+ /* go Apple, and thanks for giving these different names
+ * across the board... */
+ lineout_detect_node = get_gpio("lineout-detect", "line-output-detect",
+ &lineout_detect_gpio,
+ &lineout_detect_gpio_activestate);
+ linein_detect_node = get_gpio("linein-detect", "line-input-detect",
+ &linein_detect_gpio,
+ &linein_detect_gpio_activestate);
+
+ get_irq(headphone_detect_node, &headphone_detect_irq);
+ get_irq(lineout_detect_node, &lineout_detect_irq);
+ get_irq(linein_detect_node, &linein_detect_irq);
+
+ ftr_gpio_all_amps_off(rt);
+ rt->implementation_private = 0;
+ INIT_WORK(&rt->headphone_notify.work, ftr_handle_notify,
+ &rt->headphone_notify);
+ INIT_WORK(&rt->line_in_notify.work, ftr_handle_notify,
+ &rt->line_in_notify);
+ INIT_WORK(&rt->line_out_notify.work, ftr_handle_notify,
+ &rt->line_out_notify);
+ mutex_init(&rt->headphone_notify.mutex);
+ mutex_init(&rt->line_in_notify.mutex);
+ mutex_init(&rt->line_out_notify.mutex);
+}
+
+static void ftr_gpio_exit(struct gpio_runtime *rt)
+{
+ ftr_gpio_all_amps_off(rt);
+ rt->implementation_private = 0;
+ if (rt->headphone_notify.notify)
+ free_irq(headphone_detect_irq, &rt->headphone_notify);
+ if (rt->line_in_notify.gpio_private)
+ free_irq(linein_detect_irq, &rt->line_in_notify);
+ if (rt->line_out_notify.gpio_private)
+ free_irq(lineout_detect_irq, &rt->line_out_notify);
+ cancel_delayed_work(&rt->headphone_notify.work);
+ cancel_delayed_work(&rt->line_in_notify.work);
+ cancel_delayed_work(&rt->line_out_notify.work);
+ flush_scheduled_work();
+ mutex_destroy(&rt->headphone_notify.mutex);
+ mutex_destroy(&rt->line_in_notify.mutex);
+ mutex_destroy(&rt->line_out_notify.mutex);
+}
+
+static irqreturn_t ftr_handle_notify_irq(int xx,
+ void *data,
+ struct pt_regs *regs)
+{
+ struct gpio_notification *notif = data;
+
+ schedule_work(¬if->work);
+
+ return IRQ_HANDLED;
+}
+
+static int ftr_set_notify(struct gpio_runtime *rt,
+ enum notify_type type,
+ notify_func_t notify,
+ void *data)
+{
+ struct gpio_notification *notif;
+ notify_func_t old;
+ int irq;
+ char *name;
+ int err = -EBUSY;
+
+ switch (type) {
+ case AOA_NOTIFY_HEADPHONE:
+ notif = &rt->headphone_notify;
+ name = "headphone-detect";
+ irq = headphone_detect_irq;
+ break;
+ case AOA_NOTIFY_LINE_IN:
+ notif = &rt->line_in_notify;
+ name = "linein-detect";
+ irq = linein_detect_irq;
+ break;
+ case AOA_NOTIFY_LINE_OUT:
+ notif = &rt->line_out_notify;
+ name = "lineout-detect";
+ irq = lineout_detect_irq;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (irq == -1)
+ return -ENODEV;
+
+ mutex_lock(¬if->mutex);
+
+ old = notif->notify;
+
+ if (!old && !notify) {
+ err = 0;
+ goto out_unlock;
+ }
+
+ if (old && notify) {
+ if (old == notify && notif->data == data)
+ err = 0;
+ goto out_unlock;
+ }
+
+ if (old && !notify)
+ free_irq(irq, notif);
+
+ if (!old && notify) {
+ err = request_irq(irq, ftr_handle_notify_irq, 0, name, notif);
+ if (err)
+ goto out_unlock;
+ }
+
+ notif->notify = notify;
+ notif->data = data;
+
+ err = 0;
+ out_unlock:
+ mutex_unlock(¬if->mutex);
+ return err;
+}
+
+static int ftr_get_detect(struct gpio_runtime *rt,
+ enum notify_type type)
+{
+ int gpio, ret, active;
+
+ switch (type) {
+ case AOA_NOTIFY_HEADPHONE:
+ gpio = headphone_detect_gpio;
+ active = headphone_detect_gpio_activestate;
+ break;
+ case AOA_NOTIFY_LINE_IN:
+ gpio = linein_detect_gpio;
+ active = linein_detect_gpio_activestate;
+ break;
+ case AOA_NOTIFY_LINE_OUT:
+ gpio = lineout_detect_gpio;
+ active = lineout_detect_gpio_activestate;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (gpio == -1)
+ return -ENODEV;
+
+ ret = pmac_call_feature(PMAC_FTR_READ_GPIO, NULL, gpio, 0);
+ if (ret < 0)
+ return ret;
+ return ((ret >> 1) & 1) == active;
+}
+
+static struct gpio_methods methods = {
+ .init = ftr_gpio_init,
+ .exit = ftr_gpio_exit,
+ .all_amps_off = ftr_gpio_all_amps_off,
+ .all_amps_restore = ftr_gpio_all_amps_restore,
+ .set_headphone = ftr_gpio_set_headphone,
+ .set_speakers = ftr_gpio_set_amp,
+ .set_lineout = ftr_gpio_set_lineout,
+ .set_hw_reset = ftr_gpio_set_hw_reset,
+ .get_headphone = ftr_gpio_get_headphone,
+ .get_speakers = ftr_gpio_get_amp,
+ .get_lineout = ftr_gpio_get_lineout,
+ .set_notify = ftr_set_notify,
+ .get_detect = ftr_get_detect,
+};
+
+struct gpio_methods *ftr_gpio_methods = &methods;
+EXPORT_SYMBOL_GPL(ftr_gpio_methods);
--- /dev/null
+/*
+ * Apple Onboard Audio pmf GPIOs
+ *
+ * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
+ *
+ * GPL v2, can be found in COPYING.
+ */
+
+#include <asm/pmac_feature.h>
+#include <asm/pmac_pfunc.h>
+#include "../aoa.h"
+
+#define PMF_GPIO(name, bit) \
+static void pmf_gpio_set_##name(struct gpio_runtime *rt, int on)\
+{ \
+ struct pmf_args args = { .count = 1, .u[0].v = !on }; \
+ \
+ if (unlikely(!rt)) return; \
+ pmf_call_function(rt->node, #name "-mute", &args); \
+ rt->implementation_private &= ~(1<<bit); \
+ rt->implementation_private |= (!!on << bit); \
+} \
+static int pmf_gpio_get_##name(struct gpio_runtime *rt) \
+{ \
+ if (unlikely(!rt)) return 0; \
+ return (rt->implementation_private>>bit)&1; \
+}
+
+PMF_GPIO(headphone, 0);
+PMF_GPIO(amp, 1);
+PMF_GPIO(lineout, 2);
+
+static void pmf_gpio_set_hw_reset(struct gpio_runtime *rt, int on)
+{
+ struct pmf_args args = { .count = 1, .u[0].v = !!on };
+
+ if (unlikely(!rt)) return;
+ pmf_call_function(rt->node, "hw-reset", &args);
+}
+
+static void pmf_gpio_all_amps_off(struct gpio_runtime *rt)
+{
+ int saved;
+
+ if (unlikely(!rt)) return;
+ saved = rt->implementation_private;
+ pmf_gpio_set_headphone(rt, 0);
+ pmf_gpio_set_amp(rt, 0);
+ pmf_gpio_set_lineout(rt, 0);
+ rt->implementation_private = saved;
+}
+
+static void pmf_gpio_all_amps_restore(struct gpio_runtime *rt)
+{
+ int s;
+
+ if (unlikely(!rt)) return;
+ s = rt->implementation_private;
+ pmf_gpio_set_headphone(rt, (s>>0)&1);
+ pmf_gpio_set_amp(rt, (s>>1)&1);
+ pmf_gpio_set_lineout(rt, (s>>2)&1);
+}
+
+static void pmf_handle_notify(void *data)
+{
+ struct gpio_notification *notif = data;
+
+ mutex_lock(¬if->mutex);
+ if (notif->notify)
+ notif->notify(notif->data);
+ mutex_unlock(¬if->mutex);
+}
+
+static void pmf_gpio_init(struct gpio_runtime *rt)
+{
+ pmf_gpio_all_amps_off(rt);
+ rt->implementation_private = 0;
+ INIT_WORK(&rt->headphone_notify.work, pmf_handle_notify,
+ &rt->headphone_notify);
+ INIT_WORK(&rt->line_in_notify.work, pmf_handle_notify,
+ &rt->line_in_notify);
+ INIT_WORK(&rt->line_out_notify.work, pmf_handle_notify,
+ &rt->line_out_notify);
+ mutex_init(&rt->headphone_notify.mutex);
+ mutex_init(&rt->line_in_notify.mutex);
+ mutex_init(&rt->line_out_notify.mutex);
+}
+
+static void pmf_gpio_exit(struct gpio_runtime *rt)
+{
+ pmf_gpio_all_amps_off(rt);
+ rt->implementation_private = 0;
+
+ if (rt->headphone_notify.gpio_private)
+ pmf_unregister_irq_client(rt->headphone_notify.gpio_private);
+ if (rt->line_in_notify.gpio_private)
+ pmf_unregister_irq_client(rt->line_in_notify.gpio_private);
+ if (rt->line_out_notify.gpio_private)
+ pmf_unregister_irq_client(rt->line_out_notify.gpio_private);
+
+ /* make sure no work is pending before freeing
+ * all things */
+ cancel_delayed_work(&rt->headphone_notify.work);
+ cancel_delayed_work(&rt->line_in_notify.work);
+ cancel_delayed_work(&rt->line_out_notify.work);
+ flush_scheduled_work();
+
+ mutex_destroy(&rt->headphone_notify.mutex);
+ mutex_destroy(&rt->line_in_notify.mutex);
+ mutex_destroy(&rt->line_out_notify.mutex);
+
+ if (rt->headphone_notify.gpio_private)
+ kfree(rt->headphone_notify.gpio_private);
+ if (rt->line_in_notify.gpio_private)
+ kfree(rt->line_in_notify.gpio_private);
+ if (rt->line_out_notify.gpio_private)
+ kfree(rt->line_out_notify.gpio_private);
+}
+
+static void pmf_handle_notify_irq(void *data)
+{
+ struct gpio_notification *notif = data;
+
+ schedule_work(¬if->work);
+}
+
+static int pmf_set_notify(struct gpio_runtime *rt,
+ enum notify_type type,
+ notify_func_t notify,
+ void *data)
+{
+ struct gpio_notification *notif;
+ notify_func_t old;
+ struct pmf_irq_client *irq_client;
+ char *name;
+ int err = -EBUSY;
+
+ switch (type) {
+ case AOA_NOTIFY_HEADPHONE:
+ notif = &rt->headphone_notify;
+ name = "headphone-detect";
+ break;
+ case AOA_NOTIFY_LINE_IN:
+ notif = &rt->line_in_notify;
+ name = "linein-detect";
+ break;
+ case AOA_NOTIFY_LINE_OUT:
+ notif = &rt->line_out_notify;
+ name = "lineout-detect";
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ mutex_lock(¬if->mutex);
+
+ old = notif->notify;
+
+ if (!old && !notify) {
+ err = 0;
+ goto out_unlock;
+ }
+
+ if (old && notify) {
+ if (old == notify && notif->data == data)
+ err = 0;
+ goto out_unlock;
+ }
+
+ if (old && !notify) {
+ irq_client = notif->gpio_private;
+ pmf_unregister_irq_client(irq_client);
+ kfree(irq_client);
+ notif->gpio_private = NULL;
+ }
+ if (!old && notify) {
+ irq_client = kzalloc(sizeof(struct pmf_irq_client),
+ GFP_KERNEL);
+ irq_client->data = notif;
+ irq_client->handler = pmf_handle_notify_irq;
+ irq_client->owner = THIS_MODULE;
+ err = pmf_register_irq_client(rt->node,
+ name,
+ irq_client);
+ if (err) {
+ printk(KERN_ERR "snd-aoa: gpio layer failed to"
+ " register %s irq (%d)\n", name, err);
+ kfree(irq_client);
+ goto out_unlock;
+ }
+ notif->gpio_private = irq_client;
+ }
+ notif->notify = notify;
+ notif->data = data;
+
+ err = 0;
+ out_unlock:
+ mutex_unlock(¬if->mutex);
+ return err;
+}
+
+static int pmf_get_detect(struct gpio_runtime *rt,
+ enum notify_type type)
+{
+ char *name;
+ int err = -EBUSY, ret;
+ struct pmf_args args = { .count = 1, .u[0].p = &ret };
+
+ switch (type) {
+ case AOA_NOTIFY_HEADPHONE:
+ name = "headphone-detect";
+ break;
+ case AOA_NOTIFY_LINE_IN:
+ name = "linein-detect";
+ break;
+ case AOA_NOTIFY_LINE_OUT:
+ name = "lineout-detect";
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ err = pmf_call_function(rt->node, name, &args);
+ if (err)
+ return err;
+ return ret;
+}
+
+static struct gpio_methods methods = {
+ .init = pmf_gpio_init,
+ .exit = pmf_gpio_exit,
+ .all_amps_off = pmf_gpio_all_amps_off,
+ .all_amps_restore = pmf_gpio_all_amps_restore,
+ .set_headphone = pmf_gpio_set_headphone,
+ .set_speakers = pmf_gpio_set_amp,
+ .set_lineout = pmf_gpio_set_lineout,
+ .set_hw_reset = pmf_gpio_set_hw_reset,
+ .get_headphone = pmf_gpio_get_headphone,
+ .get_speakers = pmf_gpio_get_amp,
+ .get_lineout = pmf_gpio_get_lineout,
+ .set_notify = pmf_set_notify,
+ .get_detect = pmf_get_detect,
+};
+
+struct gpio_methods *pmf_gpio_methods = &methods;
+EXPORT_SYMBOL_GPL(pmf_gpio_methods);
--- /dev/null
+config SND_AOA_FABRIC_LAYOUT
+ tristate "layout-id fabric"
+ depends SND_AOA
+ select SND_AOA_SOUNDBUS
+ select SND_AOA_SOUNDBUS_I2S
+ ---help---
+ This enables the layout-id fabric for the Apple Onboard
+ Audio driver, the module holding it all together
+ based on the device-tree's layout-id property.
+
+ If you are unsure and have a later Apple machine,
+ compile it as a module.
--- /dev/null
+obj-$(CONFIG_SND_AOA_FABRIC_LAYOUT) += snd-aoa-fabric-layout.o
--- /dev/null
+/*
+ * Apple Onboard Audio driver -- layout fabric
+ *
+ * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
+ *
+ * GPL v2, can be found in COPYING.
+ *
+ *
+ * This fabric module looks for sound codecs
+ * based on the layout-id property in the device tree.
+ *
+ */
+
+#include <asm/prom.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include "../aoa.h"
+#include "../soundbus/soundbus.h"
+
+MODULE_AUTHOR("Johannes Berg <johannes@sipsolutions.net>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Layout-ID fabric for snd-aoa");
+
+#define MAX_CODECS_PER_BUS 2
+
+/* These are the connections the layout fabric
+ * knows about. It doesn't really care about the
+ * input ones, but I thought I'd separate them
+ * to give them proper names. The thing is that
+ * Apple usually will distinguish the active output
+ * by GPIOs, while the active input is set directly
+ * on the codec. Hence we here tell the codec what
+ * we think is connected. This information is hard-
+ * coded below ... */
+#define CC_SPEAKERS (1<<0)
+#define CC_HEADPHONE (1<<1)
+#define CC_LINEOUT (1<<2)
+#define CC_DIGITALOUT (1<<3)
+#define CC_LINEIN (1<<4)
+#define CC_MICROPHONE (1<<5)
+#define CC_DIGITALIN (1<<6)
+/* pretty bogus but users complain...
+ * This is a flag saying that the LINEOUT
+ * should be renamed to HEADPHONE.
+ * be careful with input detection! */
+#define CC_LINEOUT_LABELLED_HEADPHONE (1<<7)
+
+struct codec_connection {
+ /* CC_ flags from above */
+ int connected;
+ /* codec dependent bit to be set in the aoa_codec.connected field.
+ * This intentionally doesn't have any generic flags because the
+ * fabric has to know the codec anyway and all codecs might have
+ * different connectors */
+ int codec_bit;
+};
+
+struct codec_connect_info {
+ char *name;
+ struct codec_connection *connections;
+};
+
+#define LAYOUT_FLAG_COMBO_LINEOUT_SPDIF (1<<0)
+
+struct layout {
+ unsigned int layout_id;
+ struct codec_connect_info codecs[MAX_CODECS_PER_BUS];
+ int flags;
+
+ /* if busname is not assigned, we use 'Master' below,
+ * so that our layout table doesn't need to be filled
+ * too much.
+ * We only assign these two if we expect to find more
+ * than one soundbus, i.e. on those machines with
+ * multiple layout-ids */
+ char *busname;
+ int pcmid;
+};
+
+MODULE_ALIAS("sound-layout-41");
+MODULE_ALIAS("sound-layout-45");
+MODULE_ALIAS("sound-layout-51");
+MODULE_ALIAS("sound-layout-58");
+MODULE_ALIAS("sound-layout-60");
+MODULE_ALIAS("sound-layout-61");
+MODULE_ALIAS("sound-layout-64");
+MODULE_ALIAS("sound-layout-65");
+MODULE_ALIAS("sound-layout-68");
+MODULE_ALIAS("sound-layout-69");
+MODULE_ALIAS("sound-layout-70");
+MODULE_ALIAS("sound-layout-72");
+MODULE_ALIAS("sound-layout-80");
+MODULE_ALIAS("sound-layout-82");
+MODULE_ALIAS("sound-layout-84");
+MODULE_ALIAS("sound-layout-86");
+MODULE_ALIAS("sound-layout-92");
+
+/* onyx with all but microphone connected */
+static struct codec_connection onyx_connections_nomic[] = {
+ {
+ .connected = CC_SPEAKERS | CC_HEADPHONE | CC_LINEOUT,
+ .codec_bit = 0,
+ },
+ {
+ .connected = CC_DIGITALOUT,
+ .codec_bit = 1,
+ },
+ {
+ .connected = CC_LINEIN,
+ .codec_bit = 2,
+ },
+ {} /* terminate array by .connected == 0 */
+};
+
+/* onyx on machines without headphone */
+static struct codec_connection onyx_connections_noheadphones[] = {
+ {
+ .connected = CC_SPEAKERS | CC_LINEOUT |
+ CC_LINEOUT_LABELLED_HEADPHONE,
+ .codec_bit = 0,
+ },
+ {
+ .connected = CC_DIGITALOUT,
+ .codec_bit = 1,
+ },
+ /* FIXME: are these correct? probably not for all the machines
+ * below ... If not this will need separating. */
+ {
+ .connected = CC_LINEIN,
+ .codec_bit = 2,
+ },
+ {
+ .connected = CC_MICROPHONE,
+ .codec_bit = 3,
+ },
+ {} /* terminate array by .connected == 0 */
+};
+
+/* onyx on machines with real line-out */
+static struct codec_connection onyx_connections_reallineout[] = {
+ {
+ .connected = CC_SPEAKERS | CC_LINEOUT | CC_HEADPHONE,
+ .codec_bit = 0,
+ },
+ {
+ .connected = CC_DIGITALOUT,
+ .codec_bit = 1,
+ },
+ {
+ .connected = CC_LINEIN,
+ .codec_bit = 2,
+ },
+ {} /* terminate array by .connected == 0 */
+};
+
+/* tas on machines without line out */
+static struct codec_connection tas_connections_nolineout[] = {
+ {
+ .connected = CC_SPEAKERS | CC_HEADPHONE,
+ .codec_bit = 0,
+ },
+ {
+ .connected = CC_LINEIN,
+ .codec_bit = 2,
+ },
+ {
+ .connected = CC_MICROPHONE,
+ .codec_bit = 3,
+ },
+ {} /* terminate array by .connected == 0 */
+};
+
+/* tas on machines with neither line out nor line in */
+static struct codec_connection tas_connections_noline[] = {
+ {
+ .connected = CC_SPEAKERS | CC_HEADPHONE,
+ .codec_bit = 0,
+ },
+ {
+ .connected = CC_MICROPHONE,
+ .codec_bit = 3,
+ },
+ {} /* terminate array by .connected == 0 */
+};
+
+/* tas on machines without microphone */
+static struct codec_connection tas_connections_nomic[] = {
+ {
+ .connected = CC_SPEAKERS | CC_HEADPHONE | CC_LINEOUT,
+ .codec_bit = 0,
+ },
+ {
+ .connected = CC_LINEIN,
+ .codec_bit = 2,
+ },
+ {} /* terminate array by .connected == 0 */
+};
+
+/* tas on machines with everything connected */
+static struct codec_connection tas_connections_all[] = {
+ {
+ .connected = CC_SPEAKERS | CC_HEADPHONE | CC_LINEOUT,
+ .codec_bit = 0,
+ },
+ {
+ .connected = CC_LINEIN,
+ .codec_bit = 2,
+ },
+ {
+ .connected = CC_MICROPHONE,
+ .codec_bit = 3,
+ },
+ {} /* terminate array by .connected == 0 */
+};
+
+static struct codec_connection toonie_connections[] = {
+ {
+ .connected = CC_SPEAKERS | CC_HEADPHONE,
+ .codec_bit = 0,
+ },
+ {} /* terminate array by .connected == 0 */
+};
+
+static struct codec_connection topaz_input[] = {
+ {
+ .connected = CC_DIGITALIN,
+ .codec_bit = 0,
+ },
+ {} /* terminate array by .connected == 0 */
+};
+
+static struct codec_connection topaz_output[] = {
+ {
+ .connected = CC_DIGITALOUT,
+ .codec_bit = 1,
+ },
+ {} /* terminate array by .connected == 0 */
+};
+
+static struct codec_connection topaz_inout[] = {
+ {
+ .connected = CC_DIGITALIN,
+ .codec_bit = 0,
+ },
+ {
+ .connected = CC_DIGITALOUT,
+ .codec_bit = 1,
+ },
+ {} /* terminate array by .connected == 0 */
+};
+
+static struct layout layouts[] = {
+ /* last PowerBooks (15" Oct 2005) */
+ { .layout_id = 82,
+ .flags = LAYOUT_FLAG_COMBO_LINEOUT_SPDIF,
+ .codecs[0] = {
+ .name = "onyx",
+ .connections = onyx_connections_noheadphones,
+ },
+ .codecs[1] = {
+ .name = "topaz",
+ .connections = topaz_input,
+ },
+ },
+ /* PowerMac9,1 */
+ { .layout_id = 60,
+ .codecs[0] = {
+ .name = "onyx",
+ .connections = onyx_connections_reallineout,
+ },
+ },
+ /* PowerMac9,1 */
+ { .layout_id = 61,
+ .codecs[0] = {
+ .name = "topaz",
+ .connections = topaz_input,
+ },
+ },
+ /* PowerBook5,7 */
+ { .layout_id = 64,
+ .flags = LAYOUT_FLAG_COMBO_LINEOUT_SPDIF,
+ .codecs[0] = {
+ .name = "onyx",
+ .connections = onyx_connections_noheadphones,
+ },
+ },
+ /* PowerBook5,7 */
+ { .layout_id = 65,
+ .codecs[0] = {
+ .name = "topaz",
+ .connections = topaz_input,
+ },
+ },
+ /* PowerBook5,9 [17" Oct 2005] */
+ { .layout_id = 84,
+ .flags = LAYOUT_FLAG_COMBO_LINEOUT_SPDIF,
+ .codecs[0] = {
+ .name = "onyx",
+ .connections = onyx_connections_noheadphones,
+ },
+ .codecs[1] = {
+ .name = "topaz",
+ .connections = topaz_input,
+ },
+ },
+ /* PowerMac8,1 */
+ { .layout_id = 45,
+ .codecs[0] = {
+ .name = "onyx",
+ .connections = onyx_connections_noheadphones,
+ },
+ .codecs[1] = {
+ .name = "topaz",
+ .connections = topaz_input,
+ },
+ },
+ /* Quad PowerMac (analog in, analog/digital out) */
+ { .layout_id = 68,
+ .codecs[0] = {
+ .name = "onyx",
+ .connections = onyx_connections_nomic,
+ },
+ },
+ /* Quad PowerMac (digital in) */
+ { .layout_id = 69,
+ .codecs[0] = {
+ .name = "topaz",
+ .connections = topaz_input,
+ },
+ .busname = "digital in", .pcmid = 1 },
+ /* Early 2005 PowerBook (PowerBook 5,6) */
+ { .layout_id = 70,
+ .codecs[0] = {
+ .name = "tas",
+ .connections = tas_connections_nolineout,
+ },
+ },
+ /* PowerBook 5,4 */
+ { .layout_id = 51,
+ .codecs[0] = {
+ .name = "tas",
+ .connections = tas_connections_nolineout,
+ },
+ },
+ /* PowerBook6,7 */
+ { .layout_id = 80,
+ .codecs[0] = {
+ .name = "tas",
+ .connections = tas_connections_noline,
+ },
+ },
+ /* PowerBook6,8 */
+ { .layout_id = 72,
+ .codecs[0] = {
+ .name = "tas",
+ .connections = tas_connections_nolineout,
+ },
+ },
+ /* PowerMac8,2 */
+ { .layout_id = 86,
+ .codecs[0] = {
+ .name = "onyx",
+ .connections = onyx_connections_nomic,
+ },
+ .codecs[1] = {
+ .name = "topaz",
+ .connections = topaz_input,
+ },
+ },
+ /* PowerBook6,7 */
+ { .layout_id = 92,
+ .codecs[0] = {
+ .name = "tas",
+ .connections = tas_connections_nolineout,
+ },
+ },
+ /* PowerMac10,1 (Mac Mini) */
+ { .layout_id = 58,
+ .codecs[0] = {
+ .name = "toonie",
+ .connections = toonie_connections,
+ },
+ },
+ /* unknown, untested, but this comes from Apple */
+ { .layout_id = 41,
+ .codecs[0] = {
+ .name = "tas",
+ .connections = tas_connections_all,
+ },
+ },
+ { .layout_id = 36,
+ .codecs[0] = {
+ .name = "tas",
+ .connections = tas_connections_nomic,
+ },
+ .codecs[1] = {
+ .name = "topaz",
+ .connections = topaz_inout,
+ },
+ },
+ { .layout_id = 47,
+ .codecs[0] = {
+ .name = "onyx",
+ .connections = onyx_connections_noheadphones,
+ },
+ },
+ { .layout_id = 48,
+ .codecs[0] = {
+ .name = "topaz",
+ .connections = topaz_input,
+ },
+ },
+ { .layout_id = 49,
+ .codecs[0] = {
+ .name = "onyx",
+ .connections = onyx_connections_nomic,
+ },
+ },
+ { .layout_id = 50,
+ .codecs[0] = {
+ .name = "topaz",
+ .connections = topaz_input,
+ },
+ },
+ { .layout_id = 56,
+ .codecs[0] = {
+ .name = "onyx",
+ .connections = onyx_connections_noheadphones,
+ },
+ },
+ { .layout_id = 57,
+ .codecs[0] = {
+ .name = "topaz",
+ .connections = topaz_input,
+ },
+ },
+ { .layout_id = 62,
+ .codecs[0] = {
+ .name = "onyx",
+ .connections = onyx_connections_noheadphones,
+ },
+ .codecs[1] = {
+ .name = "topaz",
+ .connections = topaz_output,
+ },
+ },
+ { .layout_id = 66,
+ .codecs[0] = {
+ .name = "onyx",
+ .connections = onyx_connections_noheadphones,
+ },
+ },
+ { .layout_id = 67,
+ .codecs[0] = {
+ .name = "topaz",
+ .connections = topaz_input,
+ },
+ },
+ { .layout_id = 76,
+ .codecs[0] = {
+ .name = "tas",
+ .connections = tas_connections_nomic,
+ },
+ .codecs[1] = {
+ .name = "topaz",
+ .connections = topaz_inout,
+ },
+ },
+ { .layout_id = 90,
+ .codecs[0] = {
+ .name = "tas",
+ .connections = tas_connections_noline,
+ },
+ },
+ { .layout_id = 94,
+ .codecs[0] = {
+ .name = "onyx",
+ /* but it has an external mic?? how to select? */
+ .connections = onyx_connections_noheadphones,
+ },
+ },
+ { .layout_id = 96,
+ .codecs[0] = {
+ .name = "onyx",
+ .connections = onyx_connections_noheadphones,
+ },
+ },
+ { .layout_id = 98,
+ .codecs[0] = {
+ .name = "toonie",
+ .connections = toonie_connections,
+ },
+ },
+ { .layout_id = 100,
+ .codecs[0] = {
+ .name = "topaz",
+ .connections = topaz_input,
+ },
+ .codecs[1] = {
+ .name = "onyx",
+ .connections = onyx_connections_noheadphones,
+ },
+ },
+ {}
+};
+
+static struct layout *find_layout_by_id(unsigned int id)
+{
+ struct layout *l;
+
+ l = layouts;
+ while (l->layout_id) {
+ if (l->layout_id == id)
+ return l;
+ l++;
+ }
+ return NULL;
+}
+
+static void use_layout(struct layout *l)
+{
+ int i;
+
+ for (i=0; i<MAX_CODECS_PER_BUS; i++) {
+ if (l->codecs[i].name) {
+ request_module("snd-aoa-codec-%s", l->codecs[i].name);
+ }
+ }
+ /* now we wait for the codecs to call us back */
+}
+
+struct layout_dev;
+
+struct layout_dev_ptr {
+ struct layout_dev *ptr;
+};
+
+struct layout_dev {
+ struct list_head list;
+ struct soundbus_dev *sdev;
+ struct device_node *sound;
+ struct aoa_codec *codecs[MAX_CODECS_PER_BUS];
+ struct layout *layout;
+ struct gpio_runtime gpio;
+
+ /* we need these for headphone/lineout detection */
+ struct snd_kcontrol *headphone_ctrl;
+ struct snd_kcontrol *lineout_ctrl;
+ struct snd_kcontrol *speaker_ctrl;
+ struct snd_kcontrol *headphone_detected_ctrl;
+ struct snd_kcontrol *lineout_detected_ctrl;
+
+ struct layout_dev_ptr selfptr_headphone;
+ struct layout_dev_ptr selfptr_lineout;
+
+ u32 have_lineout_detect:1,
+ have_headphone_detect:1,
+ switch_on_headphone:1,
+ switch_on_lineout:1;
+};
+
+static LIST_HEAD(layouts_list);
+static int layouts_list_items;
+/* this can go away but only if we allow multiple cards,
+ * make the fabric handle all the card stuff, etc... */
+static struct layout_dev *layout_device;
+
+static int control_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+#define AMP_CONTROL(n, description) \
+static int n##_control_get(struct snd_kcontrol *kcontrol, \
+ struct snd_ctl_elem_value *ucontrol) \
+{ \
+ struct gpio_runtime *gpio = snd_kcontrol_chip(kcontrol); \
+ if (gpio->methods && gpio->methods->get_##n) \
+ ucontrol->value.integer.value[0] = \
+ gpio->methods->get_##n(gpio); \
+ return 0; \
+} \
+static int n##_control_put(struct snd_kcontrol *kcontrol, \
+ struct snd_ctl_elem_value *ucontrol) \
+{ \
+ struct gpio_runtime *gpio = snd_kcontrol_chip(kcontrol); \
+ if (gpio->methods && gpio->methods->get_##n) \
+ gpio->methods->set_##n(gpio, \
+ ucontrol->value.integer.value[0]); \
+ return 1; \
+} \
+static struct snd_kcontrol_new n##_ctl = { \
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = description, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \
+ .info = control_info, \
+ .get = n##_control_get, \
+ .put = n##_control_put, \
+}
+
+AMP_CONTROL(headphone, "Headphone Switch");
+AMP_CONTROL(speakers, "Speakers Switch");
+AMP_CONTROL(lineout, "Line-Out Switch");
+
+static int detect_choice_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct layout_dev *ldev = snd_kcontrol_chip(kcontrol);
+
+ switch (kcontrol->private_value) {
+ case 0:
+ ucontrol->value.integer.value[0] = ldev->switch_on_headphone;
+ break;
+ case 1:
+ ucontrol->value.integer.value[0] = ldev->switch_on_lineout;
+ break;
+ default:
+ return -ENODEV;
+ }
+ return 0;
+}
+
+static int detect_choice_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct layout_dev *ldev = snd_kcontrol_chip(kcontrol);
+
+ switch (kcontrol->private_value) {
+ case 0:
+ ldev->switch_on_headphone = !!ucontrol->value.integer.value[0];
+ break;
+ case 1:
+ ldev->switch_on_lineout = !!ucontrol->value.integer.value[0];
+ break;
+ default:
+ return -ENODEV;
+ }
+ return 1;
+}
+
+static struct snd_kcontrol_new headphone_detect_choice = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Headphone Detect Autoswitch",
+ .info = control_info,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .get = detect_choice_get,
+ .put = detect_choice_put,
+ .private_value = 0,
+};
+
+static struct snd_kcontrol_new lineout_detect_choice = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Line-Out Detect Autoswitch",
+ .info = control_info,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .get = detect_choice_get,
+ .put = detect_choice_put,
+ .private_value = 1,
+};
+
+static int detected_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct layout_dev *ldev = snd_kcontrol_chip(kcontrol);
+ int v;
+
+ switch (kcontrol->private_value) {
+ case 0:
+ v = ldev->gpio.methods->get_detect(&ldev->gpio,
+ AOA_NOTIFY_HEADPHONE);
+ break;
+ case 1:
+ v = ldev->gpio.methods->get_detect(&ldev->gpio,
+ AOA_NOTIFY_LINE_OUT);
+ break;
+ default:
+ return -ENODEV;
+ }
+ ucontrol->value.integer.value[0] = v;
+ return 0;
+}
+
+static struct snd_kcontrol_new headphone_detected = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Headphone Detected",
+ .info = control_info,
+ .access = SNDRV_CTL_ELEM_ACCESS_READ,
+ .get = detected_get,
+ .private_value = 0,
+};
+
+static struct snd_kcontrol_new lineout_detected = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Line-Out Detected",
+ .info = control_info,
+ .access = SNDRV_CTL_ELEM_ACCESS_READ,
+ .get = detected_get,
+ .private_value = 1,
+};
+
+static int check_codec(struct aoa_codec *codec,
+ struct layout_dev *ldev,
+ struct codec_connect_info *cci)
+{
+ u32 *ref;
+ char propname[32];
+ struct codec_connection *cc;
+
+ /* if the codec has a 'codec' node, we require a reference */
+ if (codec->node && (strcmp(codec->node->name, "codec") == 0)) {
+ snprintf(propname, sizeof(propname),
+ "platform-%s-codec-ref", codec->name);
+ ref = (u32*)get_property(ldev->sound, propname, NULL);
+ if (!ref) {
+ printk(KERN_INFO "snd-aoa-fabric-layout: "
+ "required property %s not present\n", propname);
+ return -ENODEV;
+ }
+ if (*ref != codec->node->linux_phandle) {
+ printk(KERN_INFO "snd-aoa-fabric-layout: "
+ "%s doesn't match!\n", propname);
+ return -ENODEV;
+ }
+ } else {
+ if (layouts_list_items != 1) {
+ printk(KERN_INFO "snd-aoa-fabric-layout: "
+ "more than one soundbus, but no references.\n");
+ return -ENODEV;
+ }
+ }
+ codec->soundbus_dev = ldev->sdev;
+ codec->gpio = &ldev->gpio;
+
+ cc = cci->connections;
+ if (!cc)
+ return -EINVAL;
+
+ printk(KERN_INFO "snd-aoa-fabric-layout: can use this codec\n");
+
+ codec->connected = 0;
+ codec->fabric_data = cc;
+
+ while (cc->connected) {
+ codec->connected |= 1<<cc->codec_bit;
+ cc++;
+ }
+
+ return 0;
+}
+
+static int layout_found_codec(struct aoa_codec *codec)
+{
+ struct layout_dev *ldev;
+ int i;
+
+ list_for_each_entry(ldev, &layouts_list, list) {
+ for (i=0; i<MAX_CODECS_PER_BUS; i++) {
+ if (!ldev->layout->codecs[i].name)
+ continue;
+ if (strcmp(ldev->layout->codecs[i].name, codec->name) == 0) {
+ if (check_codec(codec,
+ ldev,
+ &ldev->layout->codecs[i]) == 0)
+ return 0;
+ }
+ }
+ }
+ return -ENODEV;
+}
+
+static void layout_remove_codec(struct aoa_codec *codec)
+{
+ int i;
+ /* here remove the codec from the layout dev's
+ * codec reference */
+
+ codec->soundbus_dev = NULL;
+ codec->gpio = NULL;
+ for (i=0; i<MAX_CODECS_PER_BUS; i++) {
+ }
+}
+
+static void layout_notify(void *data)
+{
+ struct layout_dev_ptr *dptr = data;
+ struct layout_dev *ldev;
+ int v, update;
+ struct snd_kcontrol *detected, *c;
+ struct snd_card *card = aoa_get_card();
+
+ ldev = dptr->ptr;
+ if (data == &ldev->selfptr_headphone) {
+ v = ldev->gpio.methods->get_detect(&ldev->gpio, AOA_NOTIFY_HEADPHONE);
+ detected = ldev->headphone_detected_ctrl;
+ update = ldev->switch_on_headphone;
+ if (update) {
+ ldev->gpio.methods->set_speakers(&ldev->gpio, !v);
+ ldev->gpio.methods->set_headphone(&ldev->gpio, v);
+ ldev->gpio.methods->set_lineout(&ldev->gpio, 0);
+ }
+ } else if (data == &ldev->selfptr_lineout) {
+ v = ldev->gpio.methods->get_detect(&ldev->gpio, AOA_NOTIFY_LINE_OUT);
+ detected = ldev->lineout_detected_ctrl;
+ update = ldev->switch_on_lineout;
+ if (update) {
+ ldev->gpio.methods->set_speakers(&ldev->gpio, !v);
+ ldev->gpio.methods->set_headphone(&ldev->gpio, 0);
+ ldev->gpio.methods->set_lineout(&ldev->gpio, v);
+ }
+ } else
+ return;
+
+ if (detected)
+ snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &detected->id);
+ if (update) {
+ c = ldev->headphone_ctrl;
+ if (c)
+ snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &c->id);
+ c = ldev->speaker_ctrl;
+ if (c)
+ snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &c->id);
+ c = ldev->lineout_ctrl;
+ if (c)
+ snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &c->id);
+ }
+}
+
+static void layout_attached_codec(struct aoa_codec *codec)
+{
+ struct codec_connection *cc;
+ struct snd_kcontrol *ctl;
+ int headphones, lineout;
+ struct layout_dev *ldev = layout_device;
+
+ /* need to add this codec to our codec array! */
+
+ cc = codec->fabric_data;
+
+ headphones = codec->gpio->methods->get_detect(codec->gpio,
+ AOA_NOTIFY_HEADPHONE);
+ lineout = codec->gpio->methods->get_detect(codec->gpio,
+ AOA_NOTIFY_LINE_OUT);
+
+ while (cc->connected) {
+ if (cc->connected & CC_SPEAKERS) {
+ if (headphones <= 0 && lineout <= 0)
+ ldev->gpio.methods->set_speakers(codec->gpio, 1);
+ ctl = snd_ctl_new1(&speakers_ctl, codec->gpio);
+ ldev->speaker_ctrl = ctl;
+ aoa_snd_ctl_add(ctl);
+ }
+ if (cc->connected & CC_HEADPHONE) {
+ if (headphones == 1)
+ ldev->gpio.methods->set_headphone(codec->gpio, 1);
+ ctl = snd_ctl_new1(&headphone_ctl, codec->gpio);
+ ldev->headphone_ctrl = ctl;
+ aoa_snd_ctl_add(ctl);
+ ldev->have_headphone_detect =
+ !ldev->gpio.methods
+ ->set_notify(&ldev->gpio,
+ AOA_NOTIFY_HEADPHONE,
+ layout_notify,
+ &ldev->selfptr_headphone);
+ if (ldev->have_headphone_detect) {
+ ctl = snd_ctl_new1(&headphone_detect_choice,
+ ldev);
+ aoa_snd_ctl_add(ctl);
+ ctl = snd_ctl_new1(&headphone_detected,
+ ldev);
+ ldev->headphone_detected_ctrl = ctl;
+ aoa_snd_ctl_add(ctl);
+ }
+ }
+ if (cc->connected & CC_LINEOUT) {
+ if (lineout == 1)
+ ldev->gpio.methods->set_lineout(codec->gpio, 1);
+ ctl = snd_ctl_new1(&lineout_ctl, codec->gpio);
+ if (cc->connected & CC_LINEOUT_LABELLED_HEADPHONE)
+ strlcpy(ctl->id.name,
+ "Headphone Switch", sizeof(ctl->id.name));
+ ldev->lineout_ctrl = ctl;
+ aoa_snd_ctl_add(ctl);
+ ldev->have_lineout_detect =
+ !ldev->gpio.methods
+ ->set_notify(&ldev->gpio,
+ AOA_NOTIFY_LINE_OUT,
+ layout_notify,
+ &ldev->selfptr_lineout);
+ if (ldev->have_lineout_detect) {
+ ctl = snd_ctl_new1(&lineout_detect_choice,
+ ldev);
+ if (cc->connected & CC_LINEOUT_LABELLED_HEADPHONE)
+ strlcpy(ctl->id.name,
+ "Headphone Detect Autoswitch",
+ sizeof(ctl->id.name));
+ aoa_snd_ctl_add(ctl);
+ ctl = snd_ctl_new1(&lineout_detected,
+ ldev);
+ if (cc->connected & CC_LINEOUT_LABELLED_HEADPHONE)
+ strlcpy(ctl->id.name,
+ "Headphone Detected",
+ sizeof(ctl->id.name));
+ ldev->lineout_detected_ctrl = ctl;
+ aoa_snd_ctl_add(ctl);
+ }
+ }
+ cc++;
+ }
+ /* now update initial state */
+ if (ldev->have_headphone_detect)
+ layout_notify(&ldev->selfptr_headphone);
+ if (ldev->have_lineout_detect)
+ layout_notify(&ldev->selfptr_lineout);
+}
+
+static struct aoa_fabric layout_fabric = {
+ .name = "SoundByLayout",
+ .owner = THIS_MODULE,
+ .found_codec = layout_found_codec,
+ .remove_codec = layout_remove_codec,
+ .attached_codec = layout_attached_codec,
+};
+
+static int aoa_fabric_layout_probe(struct soundbus_dev *sdev)
+{
+ struct device_node *sound = NULL;
+ unsigned int *layout_id;
+ struct layout *layout;
+ struct layout_dev *ldev = NULL;
+ int err;
+
+ /* hm, currently we can only have one ... */
+ if (layout_device)
+ return -ENODEV;
+
+ /* by breaking out we keep a reference */
+ while ((sound = of_get_next_child(sdev->ofdev.node, sound))) {
+ if (sound->type && strcasecmp(sound->type, "soundchip") == 0)
+ break;
+ }
+ if (!sound) return -ENODEV;
+
+ layout_id = (unsigned int *) get_property(sound, "layout-id", NULL);
+ if (!layout_id)
+ goto outnodev;
+ printk(KERN_INFO "snd-aoa-fabric-layout: found bus with layout %d ", *layout_id);
+
+ layout = find_layout_by_id(*layout_id);
+ if (!layout) {
+ printk("(no idea how to handle)\n");
+ goto outnodev;
+ }
+
+ ldev = kzalloc(sizeof(struct layout_dev), GFP_KERNEL);
+ if (!ldev)
+ goto outnodev;
+
+ layout_device = ldev;
+ ldev->sdev = sdev;
+ ldev->sound = sound;
+ ldev->layout = layout;
+ ldev->gpio.node = sound->parent;
+ switch (layout->layout_id) {
+ case 41: /* that unknown machine no one seems to have */
+ case 51: /* PowerBook5,4 */
+ case 58: /* Mac Mini */
+ ldev->gpio.methods = ftr_gpio_methods;
+ break;
+ default:
+ ldev->gpio.methods = pmf_gpio_methods;
+ }
+ ldev->selfptr_headphone.ptr = ldev;
+ ldev->selfptr_lineout.ptr = ldev;
+ sdev->ofdev.dev.driver_data = ldev;
+
+ printk("(using)\n");
+ list_add(&ldev->list, &layouts_list);
+ layouts_list_items++;
+
+ /* assign these before registering ourselves, so
+ * callbacks that are done during registration
+ * already have the values */
+ sdev->pcmid = ldev->layout->pcmid;
+ if (ldev->layout->busname) {
+ sdev->pcmname = ldev->layout->busname;
+ } else {
+ sdev->pcmname = "Master";
+ }
+
+ ldev->gpio.methods->init(&ldev->gpio);
+
+ err = aoa_fabric_register(&layout_fabric);
+ if (err && err != -EALREADY) {
+ printk(KERN_INFO "snd-aoa-fabric-layout: can't use,"
+ " another fabric is active!\n");
+ goto outlistdel;
+ }
+
+ use_layout(layout);
+ ldev->switch_on_headphone = 1;
+ ldev->switch_on_lineout = 1;
+ return 0;
+ outlistdel:
+ /* we won't be using these then... */
+ ldev->gpio.methods->exit(&ldev->gpio);
+ /* reset if we didn't use it */
+ sdev->pcmname = NULL;
+ sdev->pcmid = -1;
+ list_del(&ldev->list);
+ layouts_list_items--;
+ outnodev:
+ if (sound) of_node_put(sound);
+ layout_device = NULL;
+ if (ldev) kfree(ldev);
+ return -ENODEV;
+}
+
+static int aoa_fabric_layout_remove(struct soundbus_dev *sdev)
+{
+ struct layout_dev *ldev = sdev->ofdev.dev.driver_data;
+ int i;
+
+ for (i=0; i<MAX_CODECS_PER_BUS; i++) {
+ if (ldev->codecs[i]) {
+ aoa_fabric_unlink_codec(ldev->codecs[i]);
+ }
+ ldev->codecs[i] = NULL;
+ }
+ list_del(&ldev->list);
+ layouts_list_items--;
+ of_node_put(ldev->sound);
+
+ ldev->gpio.methods->set_notify(&ldev->gpio,
+ AOA_NOTIFY_HEADPHONE,
+ NULL,
+ NULL);
+ ldev->gpio.methods->set_notify(&ldev->gpio,
+ AOA_NOTIFY_LINE_OUT,
+ NULL,
+ NULL);
+
+ ldev->gpio.methods->exit(&ldev->gpio);
+ layout_device = NULL;
+ kfree(ldev);
+ sdev->pcmid = -1;
+ sdev->pcmname = NULL;
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int aoa_fabric_layout_suspend(struct soundbus_dev *sdev, pm_message_t state)
+{
+ struct layout_dev *ldev = sdev->ofdev.dev.driver_data;
+
+ printk("aoa_fabric_layout_suspend()\n");
+
+ if (ldev->gpio.methods && ldev->gpio.methods->all_amps_off)
+ ldev->gpio.methods->all_amps_off(&ldev->gpio);
+
+ return 0;
+}
+
+static int aoa_fabric_layout_resume(struct soundbus_dev *sdev)
+{
+ struct layout_dev *ldev = sdev->ofdev.dev.driver_data;
+
+ printk("aoa_fabric_layout_resume()\n");
+
+ if (ldev->gpio.methods && ldev->gpio.methods->all_amps_off)
+ ldev->gpio.methods->all_amps_restore(&ldev->gpio);
+
+ return 0;
+}
+#endif
+
+static struct soundbus_driver aoa_soundbus_driver = {
+ .name = "snd_aoa_soundbus_drv",
+ .owner = THIS_MODULE,
+ .probe = aoa_fabric_layout_probe,
+ .remove = aoa_fabric_layout_remove,
+#ifdef CONFIG_PM
+ .suspend = aoa_fabric_layout_suspend,
+ .resume = aoa_fabric_layout_resume,
+#endif
+};
+
+static int __init aoa_fabric_layout_init(void)
+{
+ int err;
+
+ err = soundbus_register_driver(&aoa_soundbus_driver);
+ if (err)
+ return err;
+ return 0;
+}
+
+static void __exit aoa_fabric_layout_exit(void)
+{
+ soundbus_unregister_driver(&aoa_soundbus_driver);
+ aoa_fabric_unregister(&layout_fabric);
+}
+
+module_init(aoa_fabric_layout_init);
+module_exit(aoa_fabric_layout_exit);
--- /dev/null
+config SND_AOA_SOUNDBUS
+ tristate "Apple Soundbus support"
+ depends on SOUND && SND_PCM && EXPERIMENTAL
+ ---help---
+ This option enables the generic driver for the soundbus
+ support on Apple machines.
+
+ It is required for the sound bus implementations.
+
+config SND_AOA_SOUNDBUS_I2S
+ tristate "I2S bus support"
+ depends on SND_AOA_SOUNDBUS && PCI
+ ---help---
+ This option enables support for Apple I2S busses.
--- /dev/null
+obj-$(CONFIG_SND_AOA_SOUNDBUS) += snd-aoa-soundbus.o
+snd-aoa-soundbus-objs := core.o sysfs.o
+obj-$(CONFIG_SND_AOA_SOUNDBUS_I2S) += i2sbus/
--- /dev/null
+/*
+ * soundbus
+ *
+ * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
+ *
+ * GPL v2, can be found in COPYING.
+ */
+
+#include <linux/module.h>
+#include "soundbus.h"
+
+MODULE_AUTHOR("Johannes Berg <johannes@sipsolutions.net>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Apple Soundbus");
+
+struct soundbus_dev *soundbus_dev_get(struct soundbus_dev *dev)
+{
+ struct device *tmp;
+
+ if (!dev)
+ return NULL;
+ tmp = get_device(&dev->ofdev.dev);
+ if (tmp)
+ return to_soundbus_device(tmp);
+ else
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(soundbus_dev_get);
+
+void soundbus_dev_put(struct soundbus_dev *dev)
+{
+ if (dev)
+ put_device(&dev->ofdev.dev);
+}
+EXPORT_SYMBOL_GPL(soundbus_dev_put);
+
+static int soundbus_probe(struct device *dev)
+{
+ int error = -ENODEV;
+ struct soundbus_driver *drv;
+ struct soundbus_dev *soundbus_dev;
+
+ drv = to_soundbus_driver(dev->driver);
+ soundbus_dev = to_soundbus_device(dev);
+
+ if (!drv->probe)
+ return error;
+
+ soundbus_dev_get(soundbus_dev);
+
+ error = drv->probe(soundbus_dev);
+ if (error)
+ soundbus_dev_put(soundbus_dev);
+
+ return error;
+}
+
+
+static int soundbus_uevent(struct device *dev, char **envp, int num_envp,
+ char *buffer, int buffer_size)
+{
+ struct soundbus_dev * soundbus_dev;
+ struct of_device * of;
+ char *scratch, *compat, *compat2;
+ int i = 0;
+ int length, cplen, cplen2, seen = 0;
+
+ if (!dev)
+ return -ENODEV;
+
+ soundbus_dev = to_soundbus_device(dev);
+ if (!soundbus_dev)
+ return -ENODEV;
+
+ of = &soundbus_dev->ofdev;
+
+ /* stuff we want to pass to /sbin/hotplug */
+ envp[i++] = scratch = buffer;
+ length = scnprintf (scratch, buffer_size, "OF_NAME=%s", of->node->name);
+ ++length;
+ buffer_size -= length;
+ if ((buffer_size <= 0) || (i >= num_envp))
+ return -ENOMEM;
+ scratch += length;
+
+ envp[i++] = scratch;
+ length = scnprintf (scratch, buffer_size, "OF_TYPE=%s", of->node->type);
+ ++length;
+ buffer_size -= length;
+ if ((buffer_size <= 0) || (i >= num_envp))
+ return -ENOMEM;
+ scratch += length;
+
+ /* Since the compatible field can contain pretty much anything
+ * it's not really legal to split it out with commas. We split it
+ * up using a number of environment variables instead. */
+
+ compat = (char *) get_property(of->node, "compatible", &cplen);
+ compat2 = compat;
+ cplen2= cplen;
+ while (compat && cplen > 0) {
+ envp[i++] = scratch;
+ length = scnprintf (scratch, buffer_size,
+ "OF_COMPATIBLE_%d=%s", seen, compat);
+ ++length;
+ buffer_size -= length;
+ if ((buffer_size <= 0) || (i >= num_envp))
+ return -ENOMEM;
+ scratch += length;
+ length = strlen (compat) + 1;
+ compat += length;
+ cplen -= length;
+ seen++;
+ }
+
+ envp[i++] = scratch;
+ length = scnprintf (scratch, buffer_size, "OF_COMPATIBLE_N=%d", seen);
+ ++length;
+ buffer_size -= length;
+ if ((buffer_size <= 0) || (i >= num_envp))
+ return -ENOMEM;
+ scratch += length;
+
+ envp[i++] = scratch;
+ length = scnprintf (scratch, buffer_size, "MODALIAS=%s",
+ soundbus_dev->modalias);
+
+ buffer_size -= length;
+ if ((buffer_size <= 0) || (i >= num_envp))
+ return -ENOMEM;
+
+ envp[i] = NULL;
+
+ return 0;
+}
+
+static int soundbus_device_remove(struct device *dev)
+{
+ struct soundbus_dev * soundbus_dev = to_soundbus_device(dev);
+ struct soundbus_driver * drv = to_soundbus_driver(dev->driver);
+
+ if (dev->driver && drv->remove)
+ drv->remove(soundbus_dev);
+ soundbus_dev_put(soundbus_dev);
+
+ return 0;
+}
+
+static void soundbus_device_shutdown(struct device *dev)
+{
+ struct soundbus_dev * soundbus_dev = to_soundbus_device(dev);
+ struct soundbus_driver * drv = to_soundbus_driver(dev->driver);
+
+ if (dev->driver && drv->shutdown)
+ drv->shutdown(soundbus_dev);
+}
+
+#ifdef CONFIG_PM
+
+static int soundbus_device_suspend(struct device *dev, pm_message_t state)
+{
+ struct soundbus_dev * soundbus_dev = to_soundbus_device(dev);
+ struct soundbus_driver * drv = to_soundbus_driver(dev->driver);
+
+ if (dev->driver && drv->suspend)
+ return drv->suspend(soundbus_dev, state);
+ return 0;
+}
+
+static int soundbus_device_resume(struct device * dev)
+{
+ struct soundbus_dev * soundbus_dev = to_soundbus_device(dev);
+ struct soundbus_driver * drv = to_soundbus_driver(dev->driver);
+
+ if (dev->driver && drv->resume)
+ return drv->resume(soundbus_dev);
+ return 0;
+}
+
+#endif /* CONFIG_PM */
+
+extern struct device_attribute soundbus_dev_attrs[];
+
+static struct bus_type soundbus_bus_type = {
+ .name = "aoa-soundbus",
+ .probe = soundbus_probe,
+ .uevent = soundbus_uevent,
+ .remove = soundbus_device_remove,
+ .shutdown = soundbus_device_shutdown,
+#ifdef CONFIG_PM
+ .suspend = soundbus_device_suspend,
+ .resume = soundbus_device_resume,
+#endif
+ .dev_attrs = soundbus_dev_attrs,
+};
+
+static int __init soundbus_init(void)
+{
+ return bus_register(&soundbus_bus_type);
+}
+
+static void __exit soundbus_exit(void)
+{
+ bus_unregister(&soundbus_bus_type);
+}
+
+int soundbus_add_one(struct soundbus_dev *dev)
+{
+ static int devcount;
+
+ /* sanity checks */
+ if (!dev->attach_codec ||
+ !dev->ofdev.node ||
+ dev->pcmname ||
+ dev->pcmid != -1) {
+ printk(KERN_ERR "soundbus: adding device failed sanity check!\n");
+ return -EINVAL;
+ }
+
+ snprintf(dev->ofdev.dev.bus_id, BUS_ID_SIZE, "soundbus:%x", ++devcount);
+ dev->ofdev.dev.bus = &soundbus_bus_type;
+ return of_device_register(&dev->ofdev);
+}
+EXPORT_SYMBOL_GPL(soundbus_add_one);
+
+void soundbus_remove_one(struct soundbus_dev *dev)
+{
+ of_device_unregister(&dev->ofdev);
+}
+EXPORT_SYMBOL_GPL(soundbus_remove_one);
+
+int soundbus_register_driver(struct soundbus_driver *drv)
+{
+ /* initialize common driver fields */
+ drv->driver.name = drv->name;
+ drv->driver.bus = &soundbus_bus_type;
+
+ /* register with core */
+ return driver_register(&drv->driver);
+}
+EXPORT_SYMBOL_GPL(soundbus_register_driver);
+
+void soundbus_unregister_driver(struct soundbus_driver *drv)
+{
+ driver_unregister(&drv->driver);
+}
+EXPORT_SYMBOL_GPL(soundbus_unregister_driver);
+
+module_init(soundbus_init);
+module_exit(soundbus_exit);
--- /dev/null
+obj-$(CONFIG_SND_AOA_SOUNDBUS_I2S) += snd-aoa-i2sbus.o
+snd-aoa-i2sbus-objs := i2sbus-core.o i2sbus-pcm.o i2sbus-control.o
--- /dev/null
+/*
+ * i2sbus driver -- bus control routines
+ *
+ * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
+ *
+ * GPL v2, can be found in COPYING.
+ */
+
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <asm/prom.h>
+#include <asm/macio.h>
+#include <asm/pmac_feature.h>
+#include <asm/pmac_pfunc.h>
+#include "i2sbus.h"
+
+int i2sbus_control_init(struct macio_dev* dev, struct i2sbus_control **c)
+{
+ *c = kzalloc(sizeof(struct i2sbus_control), GFP_KERNEL);
+ if (!*c)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&(*c)->list);
+
+ if (of_address_to_resource(dev->ofdev.node, 0, &(*c)->rsrc))
+ goto err;
+ /* we really should be using feature calls instead of mapping
+ * these registers. It's safe for now since no one else is
+ * touching them... */
+ (*c)->controlregs = ioremap((*c)->rsrc.start,
+ sizeof(struct i2s_control_regs));
+ if (!(*c)->controlregs)
+ goto err;
+
+ return 0;
+ err:
+ kfree(*c);
+ *c = NULL;
+ return -ENODEV;
+}
+
+void i2sbus_control_destroy(struct i2sbus_control *c)
+{
+ iounmap(c->controlregs);
+ kfree(c);
+}
+
+/* this is serialised externally */
+int i2sbus_control_add_dev(struct i2sbus_control *c,
+ struct i2sbus_dev *i2sdev)
+{
+ struct device_node *np;
+
+ np = i2sdev->sound.ofdev.node;
+ i2sdev->enable = pmf_find_function(np, "enable");
+ i2sdev->cell_enable = pmf_find_function(np, "cell-enable");
+ i2sdev->clock_enable = pmf_find_function(np, "clock-enable");
+ i2sdev->cell_disable = pmf_find_function(np, "cell-disable");
+ i2sdev->clock_disable = pmf_find_function(np, "clock-disable");
+
+ /* if the bus number is not 0 or 1 we absolutely need to use
+ * the platform functions -- there's nothing in Darwin that
+ * would allow seeing a system behind what the FCRs are then,
+ * and I don't want to go parsing a bunch of platform functions
+ * by hand to try finding a system... */
+ if (i2sdev->bus_number != 0 && i2sdev->bus_number != 1 &&
+ (!i2sdev->enable ||
+ !i2sdev->cell_enable || !i2sdev->clock_enable ||
+ !i2sdev->cell_disable || !i2sdev->clock_disable)) {
+ pmf_put_function(i2sdev->enable);
+ pmf_put_function(i2sdev->cell_enable);
+ pmf_put_function(i2sdev->clock_enable);
+ pmf_put_function(i2sdev->cell_disable);
+ pmf_put_function(i2sdev->clock_disable);
+ return -ENODEV;
+ }
+
+ list_add(&i2sdev->item, &c->list);
+
+ return 0;
+}
+
+void i2sbus_control_remove_dev(struct i2sbus_control *c,
+ struct i2sbus_dev *i2sdev)
+{
+ /* this is serialised externally */
+ list_del(&i2sdev->item);
+ if (list_empty(&c->list))
+ i2sbus_control_destroy(c);
+}
+
+int i2sbus_control_enable(struct i2sbus_control *c,
+ struct i2sbus_dev *i2sdev)
+{
+ struct pmf_args args = { .count = 0 };
+ int cc;
+
+ if (i2sdev->enable)
+ return pmf_call_one(i2sdev->enable, &args);
+
+ switch (i2sdev->bus_number) {
+ case 0:
+ cc = in_le32(&c->controlregs->cell_control);
+ out_le32(&c->controlregs->cell_control, cc | CTRL_CLOCK_INTF_0_ENABLE);
+ break;
+ case 1:
+ cc = in_le32(&c->controlregs->cell_control);
+ out_le32(&c->controlregs->cell_control, cc | CTRL_CLOCK_INTF_1_ENABLE);
+ break;
+ default:
+ return -ENODEV;
+ }
+ return 0;
+}
+
+int i2sbus_control_cell(struct i2sbus_control *c,
+ struct i2sbus_dev *i2sdev,
+ int enable)
+{
+ struct pmf_args args = { .count = 0 };
+ int cc;
+
+ switch (enable) {
+ case 0:
+ if (i2sdev->cell_disable)
+ return pmf_call_one(i2sdev->cell_disable, &args);
+ break;
+ case 1:
+ if (i2sdev->cell_enable)
+ return pmf_call_one(i2sdev->cell_enable, &args);
+ break;
+ default:
+ printk(KERN_ERR "i2sbus: INVALID CELL ENABLE VALUE\n");
+ return -ENODEV;
+ }
+ switch (i2sdev->bus_number) {
+ case 0:
+ cc = in_le32(&c->controlregs->cell_control);
+ cc &= ~CTRL_CLOCK_CELL_0_ENABLE;
+ cc |= enable * CTRL_CLOCK_CELL_0_ENABLE;
+ out_le32(&c->controlregs->cell_control, cc);
+ break;
+ case 1:
+ cc = in_le32(&c->controlregs->cell_control);
+ cc &= ~CTRL_CLOCK_CELL_1_ENABLE;
+ cc |= enable * CTRL_CLOCK_CELL_1_ENABLE;
+ out_le32(&c->controlregs->cell_control, cc);
+ break;
+ default:
+ return -ENODEV;
+ }
+ return 0;
+}
+
+int i2sbus_control_clock(struct i2sbus_control *c,
+ struct i2sbus_dev *i2sdev,
+ int enable)
+{
+ struct pmf_args args = { .count = 0 };
+ int cc;
+
+ switch (enable) {
+ case 0:
+ if (i2sdev->clock_disable)
+ return pmf_call_one(i2sdev->clock_disable, &args);
+ break;
+ case 1:
+ if (i2sdev->clock_enable)
+ return pmf_call_one(i2sdev->clock_enable, &args);
+ break;
+ default:
+ printk(KERN_ERR "i2sbus: INVALID CLOCK ENABLE VALUE\n");
+ return -ENODEV;
+ }
+ switch (i2sdev->bus_number) {
+ case 0:
+ cc = in_le32(&c->controlregs->cell_control);
+ cc &= ~CTRL_CLOCK_CLOCK_0_ENABLE;
+ cc |= enable * CTRL_CLOCK_CLOCK_0_ENABLE;
+ out_le32(&c->controlregs->cell_control, cc);
+ break;
+ case 1:
+ cc = in_le32(&c->controlregs->cell_control);
+ cc &= ~CTRL_CLOCK_CLOCK_1_ENABLE;
+ cc |= enable * CTRL_CLOCK_CLOCK_1_ENABLE;
+ out_le32(&c->controlregs->cell_control, cc);
+ break;
+ default:
+ return -ENODEV;
+ }
+ return 0;
+}
--- /dev/null
+/*
+ * i2sbus driver -- bus register definitions
+ *
+ * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
+ *
+ * GPL v2, can be found in COPYING.
+ */
+#ifndef __I2SBUS_CONTROLREGS_H
+#define __I2SBUS_CONTROLREGS_H
+
+/* i2s control registers, at least what we know about them */
+
+#define __PAD(m,n) u8 __pad##m[n]
+#define _PAD(line, n) __PAD(line, n)
+#define PAD(n) _PAD(__LINE__, (n))
+struct i2s_control_regs {
+ PAD(0x38);
+ __le32 fcr0; /* 0x38 (unknown) */
+ __le32 cell_control; /* 0x3c (fcr1) */
+ __le32 fcr2; /* 0x40 (unknown) */
+ __le32 fcr3; /* 0x44 (fcr3) */
+ __le32 clock_control; /* 0x48 (unknown) */
+ PAD(4);
+ /* total size: 0x50 bytes */
+} __attribute__((__packed__));
+
+#define CTRL_CLOCK_CELL_0_ENABLE (1<<10)
+#define CTRL_CLOCK_CLOCK_0_ENABLE (1<<12)
+#define CTRL_CLOCK_SWRESET_0 (1<<11)
+#define CTRL_CLOCK_INTF_0_ENABLE (1<<13)
+
+#define CTRL_CLOCK_CELL_1_ENABLE (1<<17)
+#define CTRL_CLOCK_CLOCK_1_ENABLE (1<<18)
+#define CTRL_CLOCK_SWRESET_1 (1<<19)
+#define CTRL_CLOCK_INTF_1_ENABLE (1<<20)
+
+#endif /* __I2SBUS_CONTROLREGS_H */
--- /dev/null
+/*
+ * i2sbus driver
+ *
+ * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
+ *
+ * GPL v2, can be found in COPYING.
+ */
+
+#include <linux/module.h>
+#include <asm/macio.h>
+#include <asm/dbdma.h>
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+#include <sound/driver.h>
+#include <sound/core.h>
+#include <linux/dma-mapping.h>
+#include "../soundbus.h"
+#include "i2sbus.h"
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Johannes Berg <johannes@sipsolutions.net>");
+MODULE_DESCRIPTION("Apple Soundbus: I2S support");
+/* for auto-loading, declare that we handle this weird
+ * string that macio puts into the relevant device */
+MODULE_ALIAS("of:Ni2sTi2sC");
+
+static struct of_device_id i2sbus_match[] = {
+ { .name = "i2s" },
+ { }
+};
+
+static int alloc_dbdma_descriptor_ring(struct i2sbus_dev *i2sdev,
+ struct dbdma_command_mem *r,
+ int numcmds)
+{
+ /* one more for rounding */
+ r->size = (numcmds+1) * sizeof(struct dbdma_cmd);
+ /* We use the PCI APIs for now until the generic one gets fixed
+ * enough or until we get some macio-specific versions
+ */
+ r->space = dma_alloc_coherent(
+ &macio_get_pci_dev(i2sdev->macio)->dev,
+ r->size,
+ &r->bus_addr,
+ GFP_KERNEL);
+
+ if (!r->space) return -ENOMEM;
+
+ memset(r->space, 0, r->size);
+ r->cmds = (void*)DBDMA_ALIGN(r->space);
+ r->bus_cmd_start = r->bus_addr +
+ (dma_addr_t)((char*)r->cmds - (char*)r->space);
+
+ return 0;
+}
+
+static void free_dbdma_descriptor_ring(struct i2sbus_dev *i2sdev,
+ struct dbdma_command_mem *r)
+{
+ if (!r->space) return;
+
+ dma_free_coherent(&macio_get_pci_dev(i2sdev->macio)->dev,
+ r->size, r->space, r->bus_addr);
+}
+
+static void i2sbus_release_dev(struct device *dev)
+{
+ struct i2sbus_dev *i2sdev;
+ int i;
+
+ i2sdev = container_of(dev, struct i2sbus_dev, sound.ofdev.dev);
+
+ if (i2sdev->intfregs) iounmap(i2sdev->intfregs);
+ if (i2sdev->out.dbdma) iounmap(i2sdev->out.dbdma);
+ if (i2sdev->in.dbdma) iounmap(i2sdev->in.dbdma);
+ for (i=0;i<3;i++)
+ if (i2sdev->allocated_resource[i])
+ release_and_free_resource(i2sdev->allocated_resource[i]);
+ free_dbdma_descriptor_ring(i2sdev, &i2sdev->out.dbdma_ring);
+ free_dbdma_descriptor_ring(i2sdev, &i2sdev->in.dbdma_ring);
+ for (i=0;i<3;i++)
+ free_irq(i2sdev->interrupts[i], i2sdev);
+ i2sbus_control_remove_dev(i2sdev->control, i2sdev);
+ mutex_destroy(&i2sdev->lock);
+ kfree(i2sdev);
+}
+
+static irqreturn_t i2sbus_bus_intr(int irq, void *devid, struct pt_regs *regs)
+{
+ struct i2sbus_dev *dev = devid;
+ u32 intreg;
+
+ spin_lock(&dev->low_lock);
+ intreg = in_le32(&dev->intfregs->intr_ctl);
+
+ /* acknowledge interrupt reasons */
+ out_le32(&dev->intfregs->intr_ctl, intreg);
+
+ spin_unlock(&dev->low_lock);
+
+ return IRQ_HANDLED;
+}
+
+static int force;
+module_param(force, int, 0444);
+MODULE_PARM_DESC(force, "Force loading i2sbus even when"
+ " no layout-id property is present");
+
+/* FIXME: look at device node refcounting */
+static int i2sbus_add_dev(struct macio_dev *macio,
+ struct i2sbus_control *control,
+ struct device_node *np)
+{
+ struct i2sbus_dev *dev;
+ struct device_node *child = NULL, *sound = NULL;
+ int i;
+ static const char *rnames[] = { "i2sbus: %s (control)",
+ "i2sbus: %s (tx)",
+ "i2sbus: %s (rx)" };
+ static irqreturn_t (*ints[])(int irq, void *devid,
+ struct pt_regs *regs) = {
+ i2sbus_bus_intr,
+ i2sbus_tx_intr,
+ i2sbus_rx_intr
+ };
+
+ if (strlen(np->name) != 5)
+ return 0;
+ if (strncmp(np->name, "i2s-", 4))
+ return 0;
+
+ if (np->n_intrs != 3)
+ return 0;
+
+ dev = kzalloc(sizeof(struct i2sbus_dev), GFP_KERNEL);
+ if (!dev)
+ return 0;
+
+ i = 0;
+ while ((child = of_get_next_child(np, child))) {
+ if (strcmp(child->name, "sound") == 0) {
+ i++;
+ sound = child;
+ }
+ }
+ if (i == 1) {
+ u32 *layout_id;
+ layout_id = (u32*) get_property(sound, "layout-id", NULL);
+ if (layout_id) {
+ snprintf(dev->sound.modalias, 32,
+ "sound-layout-%d", *layout_id);
+ force = 1;
+ }
+ }
+ /* for the time being, until we can handle non-layout-id
+ * things in some fabric, refuse to attach if there is no
+ * layout-id property or we haven't been forced to attach.
+ * When there are two i2s busses and only one has a layout-id,
+ * then this depends on the order, but that isn't important
+ * either as the second one in that case is just a modem. */
+ if (!force) {
+ kfree(dev);
+ return -ENODEV;
+ }
+
+ mutex_init(&dev->lock);
+ spin_lock_init(&dev->low_lock);
+ dev->sound.ofdev.node = np;
+ dev->sound.ofdev.dma_mask = macio->ofdev.dma_mask;
+ dev->sound.ofdev.dev.dma_mask = &dev->sound.ofdev.dma_mask;
+ dev->sound.ofdev.dev.parent = &macio->ofdev.dev;
+ dev->sound.ofdev.dev.release = i2sbus_release_dev;
+ dev->sound.attach_codec = i2sbus_attach_codec;
+ dev->sound.detach_codec = i2sbus_detach_codec;
+ dev->sound.pcmid = -1;
+ dev->macio = macio;
+ dev->control = control;
+ dev->bus_number = np->name[4] - 'a';
+ INIT_LIST_HEAD(&dev->sound.codec_list);
+
+ for (i=0;i<3;i++) {
+ dev->interrupts[i] = -1;
+ snprintf(dev->rnames[i], sizeof(dev->rnames[i]), rnames[i], np->name);
+ }
+ for (i=0;i<3;i++) {
+ if (request_irq(np->intrs[i].line, ints[i], 0, dev->rnames[i], dev))
+ goto err;
+ dev->interrupts[i] = np->intrs[i].line;
+ }
+
+ for (i=0;i<3;i++) {
+ if (of_address_to_resource(np, i, &dev->resources[i]))
+ goto err;
+ /* if only we could use our resource dev->resources[i]...
+ * but request_resource doesn't know about parents and
+ * contained resources... */
+ dev->allocated_resource[i] =
+ request_mem_region(dev->resources[i].start,
+ dev->resources[i].end -
+ dev->resources[i].start + 1,
+ dev->rnames[i]);
+ if (!dev->allocated_resource[i]) {
+ printk(KERN_ERR "i2sbus: failed to claim resource %d!\n", i);
+ goto err;
+ }
+ }
+ /* should do sanity checking here about length of them */
+ dev->intfregs = ioremap(dev->resources[0].start,
+ dev->resources[0].end-dev->resources[0].start+1);
+ dev->out.dbdma = ioremap(dev->resources[1].start,
+ dev->resources[1].end-dev->resources[1].start+1);
+ dev->in.dbdma = ioremap(dev->resources[2].start,
+ dev->resources[2].end-dev->resources[2].start+1);
+ if (!dev->intfregs || !dev->out.dbdma || !dev->in.dbdma)
+ goto err;
+
+ if (alloc_dbdma_descriptor_ring(dev, &dev->out.dbdma_ring,
+ MAX_DBDMA_COMMANDS))
+ goto err;
+ if (alloc_dbdma_descriptor_ring(dev, &dev->in.dbdma_ring,
+ MAX_DBDMA_COMMANDS))
+ goto err;
+
+ if (i2sbus_control_add_dev(dev->control, dev)) {
+ printk(KERN_ERR "i2sbus: control layer didn't like bus\n");
+ goto err;
+ }
+
+ if (soundbus_add_one(&dev->sound)) {
+ printk(KERN_DEBUG "i2sbus: device registration error!\n");
+ goto err;
+ }
+
+ /* enable this cell */
+ i2sbus_control_cell(dev->control, dev, 1);
+ i2sbus_control_enable(dev->control, dev);
+ i2sbus_control_clock(dev->control, dev, 1);
+
+ return 1;
+ err:
+ for (i=0;i<3;i++)
+ if (dev->interrupts[i] != -1)
+ free_irq(dev->interrupts[i], dev);
+ free_dbdma_descriptor_ring(dev, &dev->out.dbdma_ring);
+ free_dbdma_descriptor_ring(dev, &dev->in.dbdma_ring);
+ if (dev->intfregs) iounmap(dev->intfregs);
+ if (dev->out.dbdma) iounmap(dev->out.dbdma);
+ if (dev->in.dbdma) iounmap(dev->in.dbdma);
+ for (i=0;i<3;i++)
+ if (dev->allocated_resource[i])
+ release_and_free_resource(dev->allocated_resource[i]);
+ mutex_destroy(&dev->lock);
+ kfree(dev);
+ return 0;
+}
+
+static int i2sbus_probe(struct macio_dev* dev, const struct of_device_id *match)
+{
+ struct device_node *np = NULL;
+ int got = 0, err;
+ struct i2sbus_control *control = NULL;
+
+ err = i2sbus_control_init(dev, &control);
+ if (err)
+ return err;
+ if (!control) {
+ printk(KERN_ERR "i2sbus_control_init API breakage\n");
+ return -ENODEV;
+ }
+
+ while ((np = of_get_next_child(dev->ofdev.node, np))) {
+ if (device_is_compatible(np, "i2sbus") ||
+ device_is_compatible(np, "i2s-modem")) {
+ got += i2sbus_add_dev(dev, control, np);
+ }
+ }
+
+ if (!got) {
+ /* found none, clean up */
+ i2sbus_control_destroy(control);
+ return -ENODEV;
+ }
+
+ dev->ofdev.dev.driver_data = control;
+
+ return 0;
+}
+
+static int i2sbus_remove(struct macio_dev* dev)
+{
+ struct i2sbus_control *control = dev->ofdev.dev.driver_data;
+ struct i2sbus_dev *i2sdev, *tmp;
+
+ list_for_each_entry_safe(i2sdev, tmp, &control->list, item)
+ soundbus_remove_one(&i2sdev->sound);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int i2sbus_suspend(struct macio_dev* dev, pm_message_t state)
+{
+ struct i2sbus_control *control = dev->ofdev.dev.driver_data;
+ struct codec_info_item *cii;
+ struct i2sbus_dev* i2sdev;
+ int err, ret = 0;
+
+ list_for_each_entry(i2sdev, &control->list, item) {
+ /* Notify Alsa */
+ if (i2sdev->sound.pcm) {
+ /* Suspend PCM streams */
+ snd_pcm_suspend_all(i2sdev->sound.pcm);
+ /* Probably useless as we handle
+ * power transitions ourselves */
+ snd_power_change_state(i2sdev->sound.pcm->card,
+ SNDRV_CTL_POWER_D3hot);
+ }
+ /* Notify codecs */
+ list_for_each_entry(cii, &i2sdev->sound.codec_list, list) {
+ err = 0;
+ if (cii->codec->suspend)
+ err = cii->codec->suspend(cii, state);
+ if (err)
+ ret = err;
+ }
+ }
+ return ret;
+}
+
+static int i2sbus_resume(struct macio_dev* dev)
+{
+ struct i2sbus_control *control = dev->ofdev.dev.driver_data;
+ struct codec_info_item *cii;
+ struct i2sbus_dev* i2sdev;
+ int err, ret = 0;
+
+ list_for_each_entry(i2sdev, &control->list, item) {
+ /* Notify codecs so they can re-initialize */
+ list_for_each_entry(cii, &i2sdev->sound.codec_list, list) {
+ err = 0;
+ if (cii->codec->resume)
+ err = cii->codec->resume(cii);
+ if (err)
+ ret = err;
+ }
+ /* Notify Alsa */
+ if (i2sdev->sound.pcm) {
+ /* Same comment as above, probably useless */
+ snd_power_change_state(i2sdev->sound.pcm->card,
+ SNDRV_CTL_POWER_D0);
+ }
+ }
+
+ return ret;
+}
+#endif /* CONFIG_PM */
+
+static int i2sbus_shutdown(struct macio_dev* dev)
+{
+ return 0;
+}
+
+static struct macio_driver i2sbus_drv = {
+ .name = "soundbus-i2s",
+ .owner = THIS_MODULE,
+ .match_table = i2sbus_match,
+ .probe = i2sbus_probe,
+ .remove = i2sbus_remove,
+#ifdef CONFIG_PM
+ .suspend = i2sbus_suspend,
+ .resume = i2sbus_resume,
+#endif
+ .shutdown = i2sbus_shutdown,
+};
+
+static int __init soundbus_i2sbus_init(void)
+{
+ return macio_register_driver(&i2sbus_drv);
+}
+
+static void __exit soundbus_i2sbus_exit(void)
+{
+ macio_unregister_driver(&i2sbus_drv);
+}
+
+module_init(soundbus_i2sbus_init);
+module_exit(soundbus_i2sbus_exit);
--- /dev/null
+/*
+ * i2sbus driver -- interface register definitions
+ *
+ * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
+ *
+ * GPL v2, can be found in COPYING.
+ */
+#ifndef __I2SBUS_INTERFACE_H
+#define __I2SBUS_INTERFACE_H
+
+/* i2s bus control registers, at least what we know about them */
+
+#define __PAD(m,n) u8 __pad##m[n]
+#define _PAD(line, n) __PAD(line, n)
+#define PAD(n) _PAD(__LINE__, (n))
+struct i2s_interface_regs {
+ __le32 intr_ctl; /* 0x00 */
+ PAD(12);
+ __le32 serial_format; /* 0x10 */
+ PAD(12);
+ __le32 codec_msg_out; /* 0x20 */
+ PAD(12);
+ __le32 codec_msg_in; /* 0x30 */
+ PAD(12);
+ __le32 frame_count; /* 0x40 */
+ PAD(12);
+ __le32 frame_match; /* 0x50 */
+ PAD(12);
+ __le32 data_word_sizes; /* 0x60 */
+ PAD(12);
+ __le32 peak_level_sel; /* 0x70 */
+ PAD(12);
+ __le32 peak_level_in0; /* 0x80 */
+ PAD(12);
+ __le32 peak_level_in1; /* 0x90 */
+ PAD(12);
+ /* total size: 0x100 bytes */
+} __attribute__((__packed__));
+
+/* interrupt register is just a bitfield with
+ * interrupt enable and pending bits */
+#define I2S_REG_INTR_CTL 0x00
+# define I2S_INT_FRAME_COUNT (1<<31)
+# define I2S_PENDING_FRAME_COUNT (1<<30)
+# define I2S_INT_MESSAGE_FLAG (1<<29)
+# define I2S_PENDING_MESSAGE_FLAG (1<<28)
+# define I2S_INT_NEW_PEAK (1<<27)
+# define I2S_PENDING_NEW_PEAK (1<<26)
+# define I2S_INT_CLOCKS_STOPPED (1<<25)
+# define I2S_PENDING_CLOCKS_STOPPED (1<<24)
+# define I2S_INT_EXTERNAL_SYNC_ERROR (1<<23)
+# define I2S_PENDING_EXTERNAL_SYNC_ERROR (1<<22)
+# define I2S_INT_EXTERNAL_SYNC_OK (1<<21)
+# define I2S_PENDING_EXTERNAL_SYNC_OK (1<<20)
+# define I2S_INT_NEW_SAMPLE_RATE (1<<19)
+# define I2S_PENDING_NEW_SAMPLE_RATE (1<<18)
+# define I2S_INT_STATUS_FLAG (1<<17)
+# define I2S_PENDING_STATUS_FLAG (1<<16)
+
+/* serial format register is more interesting :)
+ * It contains:
+ * - clock source
+ * - MClk divisor
+ * - SClk divisor
+ * - SClk master flag
+ * - serial format (sony, i2s 64x, i2s 32x, dav, silabs)
+ * - external sample frequency interrupt (don't understand)
+ * - external sample frequency
+ */
+#define I2S_REG_SERIAL_FORMAT 0x10
+/* clock source. You get either 18.432, 45.1584 or 49.1520 MHz */
+# define I2S_SF_CLOCK_SOURCE_SHIFT 30
+# define I2S_SF_CLOCK_SOURCE_MASK (3<<I2S_SF_CLOCK_SOURCE_SHIFT)
+# define I2S_SF_CLOCK_SOURCE_18MHz (0<<I2S_SF_CLOCK_SOURCE_SHIFT)
+# define I2S_SF_CLOCK_SOURCE_45MHz (1<<I2S_SF_CLOCK_SOURCE_SHIFT)
+# define I2S_SF_CLOCK_SOURCE_49MHz (2<<I2S_SF_CLOCK_SOURCE_SHIFT)
+/* also, let's define the exact clock speeds here, in Hz */
+#define I2S_CLOCK_SPEED_18MHz 18432000
+#define I2S_CLOCK_SPEED_45MHz 45158400
+#define I2S_CLOCK_SPEED_49MHz 49152000
+/* MClk is the clock that drives the codec, usually called its 'system clock'.
+ * It is derived by taking only every 'divisor' tick of the clock.
+ */
+# define I2S_SF_MCLKDIV_SHIFT 24
+# define I2S_SF_MCLKDIV_MASK (0x1F<<I2S_SF_MCLKDIV_SHIFT)
+# define I2S_SF_MCLKDIV_1 (0x14<<I2S_SF_MCLKDIV_SHIFT)
+# define I2S_SF_MCLKDIV_3 (0x13<<I2S_SF_MCLKDIV_SHIFT)
+# define I2S_SF_MCLKDIV_5 (0x12<<I2S_SF_MCLKDIV_SHIFT)
+# define I2S_SF_MCLKDIV_14 (0x0E<<I2S_SF_MCLKDIV_SHIFT)
+# define I2S_SF_MCLKDIV_OTHER(div) (((div/2-1)<<I2S_SF_MCLKDIV_SHIFT)&I2S_SF_MCLKDIV_MASK)
+static inline int i2s_sf_mclkdiv(int div, int *out)
+{
+ int d;
+
+ switch(div) {
+ case 1: *out |= I2S_SF_MCLKDIV_1; return 0;
+ case 3: *out |= I2S_SF_MCLKDIV_3; return 0;
+ case 5: *out |= I2S_SF_MCLKDIV_5; return 0;
+ case 14: *out |= I2S_SF_MCLKDIV_14; return 0;
+ default:
+ if (div%2) return -1;
+ d = div/2-1;
+ if (d == 0x14 || d == 0x13 || d == 0x12 || d == 0x0E)
+ return -1;
+ *out |= I2S_SF_MCLKDIV_OTHER(div);
+ return 0;
+ }
+}
+/* SClk is the clock that drives the i2s wire bus. Note that it is
+ * derived from the MClk above by taking only every 'divisor' tick
+ * of MClk.
+ */
+# define I2S_SF_SCLKDIV_SHIFT 20
+# define I2S_SF_SCLKDIV_MASK (0xF<<I2S_SF_SCLKDIV_SHIFT)
+# define I2S_SF_SCLKDIV_1 (8<<I2S_SF_SCLKDIV_SHIFT)
+# define I2S_SF_SCLKDIV_3 (9<<I2S_SF_SCLKDIV_SHIFT)
+# define I2S_SF_SCLKDIV_OTHER(div) (((div/2-1)<<I2S_SF_SCLKDIV_SHIFT)&I2S_SF_SCLKDIV_MASK)
+static inline int i2s_sf_sclkdiv(int div, int *out)
+{
+ int d;
+
+ switch(div) {
+ case 1: *out |= I2S_SF_SCLKDIV_1; return 0;
+ case 3: *out |= I2S_SF_SCLKDIV_3; return 0;
+ default:
+ if (div%2) return -1;
+ d = div/2-1;
+ if (d == 8 || d == 9) return -1;
+ *out |= I2S_SF_SCLKDIV_OTHER(div);
+ return 0;
+ }
+}
+# define I2S_SF_SCLK_MASTER (1<<19)
+/* serial format is the way the data is put to the i2s wire bus */
+# define I2S_SF_SERIAL_FORMAT_SHIFT 16
+# define I2S_SF_SERIAL_FORMAT_MASK (7<<I2S_SF_SERIAL_FORMAT_SHIFT)
+# define I2S_SF_SERIAL_FORMAT_SONY (0<<I2S_SF_SERIAL_FORMAT_SHIFT)
+# define I2S_SF_SERIAL_FORMAT_I2S_64X (1<<I2S_SF_SERIAL_FORMAT_SHIFT)
+# define I2S_SF_SERIAL_FORMAT_I2S_32X (2<<I2S_SF_SERIAL_FORMAT_SHIFT)
+# define I2S_SF_SERIAL_FORMAT_I2S_DAV (4<<I2S_SF_SERIAL_FORMAT_SHIFT)
+# define I2S_SF_SERIAL_FORMAT_I2S_SILABS (5<<I2S_SF_SERIAL_FORMAT_SHIFT)
+/* unknown */
+# define I2S_SF_EXT_SAMPLE_FREQ_INT_SHIFT 12
+# define I2S_SF_EXT_SAMPLE_FREQ_INT_MASK (0xF<<I2S_SF_SAMPLE_FREQ_INT_SHIFT)
+/* probably gives external frequency? */
+# define I2S_SF_EXT_SAMPLE_FREQ_MASK 0xFFF
+
+/* used to send codec messages, but how isn't clear */
+#define I2S_REG_CODEC_MSG_OUT 0x20
+
+/* used to receive codec messages, but how isn't clear */
+#define I2S_REG_CODEC_MSG_IN 0x30
+
+/* frame count reg isn't clear to me yet, but probably useful */
+#define I2S_REG_FRAME_COUNT 0x40
+
+/* program to some value, and get interrupt if frame count reaches it */
+#define I2S_REG_FRAME_MATCH 0x50
+
+/* this register describes how the bus transfers data */
+#define I2S_REG_DATA_WORD_SIZES 0x60
+/* number of interleaved input channels */
+# define I2S_DWS_NUM_CHANNELS_IN_SHIFT 24
+# define I2S_DWS_NUM_CHANNELS_IN_MASK (0x1F<<I2S_DWS_NUM_CHANNELS_IN_SHIFT)
+/* word size of input data */
+# define I2S_DWS_DATA_IN_SIZE_SHIFT 16
+# define I2S_DWS_DATA_IN_16BIT (0<<I2S_DWS_DATA_IN_SIZE_SHIFT)
+# define I2S_DWS_DATA_IN_24BIT (3<<I2S_DWS_DATA_IN_SIZE_SHIFT)
+/* number of interleaved output channels */
+# define I2S_DWS_NUM_CHANNELS_OUT_SHIFT 8
+# define I2S_DWS_NUM_CHANNELS_OUT_MASK (0x1F<<I2S_DWS_NUM_CHANNELS_OUT_SHIFT)
+/* word size of output data */
+# define I2S_DWS_DATA_OUT_SIZE_SHIFT 0
+# define I2S_DWS_DATA_OUT_16BIT (0<<I2S_DWS_DATA_OUT_SIZE_SHIFT)
+# define I2S_DWS_DATA_OUT_24BIT (3<<I2S_DWS_DATA_OUT_SIZE_SHIFT)
+
+
+/* unknown */
+#define I2S_REG_PEAK_LEVEL_SEL 0x70
+
+/* unknown */
+#define I2S_REG_PEAK_LEVEL_IN0 0x80
+
+/* unknown */
+#define I2S_REG_PEAK_LEVEL_IN1 0x90
+
+#endif /* __I2SBUS_INTERFACE_H */
--- /dev/null
+/*
+ * i2sbus driver -- pcm routines
+ *
+ * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
+ *
+ * GPL v2, can be found in COPYING.
+ */
+
+#include <asm/io.h>
+#include <linux/delay.h>
+/* So apparently there's a reason for requiring driver.h
+ * to be included first, even if I don't know it... */
+#include <sound/driver.h>
+#include <sound/core.h>
+#include <asm/macio.h>
+#include <linux/pci.h>
+#include "../soundbus.h"
+#include "i2sbus.h"
+
+static inline void get_pcm_info(struct i2sbus_dev *i2sdev, int in,
+ struct pcm_info **pi, struct pcm_info **other)
+{
+ if (in) {
+ if (pi)
+ *pi = &i2sdev->in;
+ if (other)
+ *other = &i2sdev->out;
+ } else {
+ if (pi)
+ *pi = &i2sdev->out;
+ if (other)
+ *other = &i2sdev->in;
+ }
+}
+
+static int clock_and_divisors(int mclk, int sclk, int rate, int *out)
+{
+ /* sclk must be derived from mclk! */
+ if (mclk % sclk)
+ return -1;
+ /* derive sclk register value */
+ if (i2s_sf_sclkdiv(mclk / sclk, out))
+ return -1;
+
+ if (I2S_CLOCK_SPEED_18MHz % (rate * mclk) == 0) {
+ if (!i2s_sf_mclkdiv(I2S_CLOCK_SPEED_18MHz / (rate * mclk), out)) {
+ *out |= I2S_SF_CLOCK_SOURCE_18MHz;
+ return 0;
+ }
+ }
+ if (I2S_CLOCK_SPEED_45MHz % (rate * mclk) == 0) {
+ if (!i2s_sf_mclkdiv(I2S_CLOCK_SPEED_45MHz / (rate * mclk), out)) {
+ *out |= I2S_SF_CLOCK_SOURCE_45MHz;
+ return 0;
+ }
+ }
+ if (I2S_CLOCK_SPEED_49MHz % (rate * mclk) == 0) {
+ if (!i2s_sf_mclkdiv(I2S_CLOCK_SPEED_49MHz / (rate * mclk), out)) {
+ *out |= I2S_SF_CLOCK_SOURCE_49MHz;
+ return 0;
+ }
+ }
+ return -1;
+}
+
+#define CHECK_RATE(rate) \
+ do { if (rates & SNDRV_PCM_RATE_ ##rate) { \
+ int dummy; \
+ if (clock_and_divisors(sysclock_factor, \
+ bus_factor, rate, &dummy)) \
+ rates &= ~SNDRV_PCM_RATE_ ##rate; \
+ } } while (0)
+
+static int i2sbus_pcm_open(struct i2sbus_dev *i2sdev, int in)
+{
+ struct pcm_info *pi, *other;
+ struct soundbus_dev *sdev;
+ int masks_inited = 0, err;
+ struct codec_info_item *cii, *rev;
+ struct snd_pcm_hardware *hw;
+ u64 formats = 0;
+ unsigned int rates = 0;
+ struct transfer_info v;
+ int result = 0;
+ int bus_factor = 0, sysclock_factor = 0;
+ int found_this;
+
+ mutex_lock(&i2sdev->lock);
+
+ get_pcm_info(i2sdev, in, &pi, &other);
+
+ hw = &pi->substream->runtime->hw;
+ sdev = &i2sdev->sound;
+
+ if (pi->active) {
+ /* alsa messed up */
+ result = -EBUSY;
+ goto out_unlock;
+ }
+
+ /* we now need to assign the hw */
+ list_for_each_entry(cii, &sdev->codec_list, list) {
+ struct transfer_info *ti = cii->codec->transfers;
+ bus_factor = cii->codec->bus_factor;
+ sysclock_factor = cii->codec->sysclock_factor;
+ while (ti->formats && ti->rates) {
+ v = *ti;
+ if (ti->transfer_in == in
+ && cii->codec->usable(cii, ti, &v)) {
+ if (masks_inited) {
+ formats &= v.formats;
+ rates &= v.rates;
+ } else {
+ formats = v.formats;
+ rates = v.rates;
+ masks_inited = 1;
+ }
+ }
+ ti++;
+ }
+ }
+ if (!masks_inited || !bus_factor || !sysclock_factor) {
+ result = -ENODEV;
+ goto out_unlock;
+ }
+ /* bus dependent stuff */
+ hw->info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_RESUME;
+
+ CHECK_RATE(5512);
+ CHECK_RATE(8000);
+ CHECK_RATE(11025);
+ CHECK_RATE(16000);
+ CHECK_RATE(22050);
+ CHECK_RATE(32000);
+ CHECK_RATE(44100);
+ CHECK_RATE(48000);
+ CHECK_RATE(64000);
+ CHECK_RATE(88200);
+ CHECK_RATE(96000);
+ CHECK_RATE(176400);
+ CHECK_RATE(192000);
+ hw->rates = rates;
+
+ /* well. the codec might want 24 bits only, and we'll
+ * ever only transfer 24 bits, but they are top-aligned!
+ * So for alsa, we claim that we're doing full 32 bit
+ * while in reality we'll ignore the lower 8 bits of
+ * that when doing playback (they're transferred as 0
+ * as far as I know, no codecs we have are 32-bit capable
+ * so I can't really test) and when doing recording we'll
+ * always have those lower 8 bits recorded as 0 */
+ if (formats & SNDRV_PCM_FMTBIT_S24_BE)
+ formats |= SNDRV_PCM_FMTBIT_S32_BE;
+ if (formats & SNDRV_PCM_FMTBIT_U24_BE)
+ formats |= SNDRV_PCM_FMTBIT_U32_BE;
+ /* now mask off what we can support. I suppose we could
+ * also support S24_3LE and some similar formats, but I
+ * doubt there's a codec that would be able to use that,
+ * so we don't support it here. */
+ hw->formats = formats & (SNDRV_PCM_FMTBIT_S16_BE |
+ SNDRV_PCM_FMTBIT_U16_BE |
+ SNDRV_PCM_FMTBIT_S32_BE |
+ SNDRV_PCM_FMTBIT_U32_BE);
+
+ /* we need to set the highest and lowest rate possible.
+ * These are the highest and lowest rates alsa can
+ * support properly in its bitfield.
+ * Below, we'll use that to restrict to the rate
+ * currently in use (if any). */
+ hw->rate_min = 5512;
+ hw->rate_max = 192000;
+ /* if the other stream is active, then we can only
+ * support what it is currently using.
+ * FIXME: I lied. This comment is wrong. We can support
+ * anything that works with the same serial format, ie.
+ * when recording 24 bit sound we can well play 16 bit
+ * sound at the same time iff using the same transfer mode.
+ */
+ if (other->active) {
+ /* FIXME: is this guaranteed by the alsa api? */
+ hw->formats &= (1ULL << i2sdev->format);
+ /* see above, restrict rates to the one we already have */
+ hw->rate_min = i2sdev->rate;
+ hw->rate_max = i2sdev->rate;
+ }
+
+ hw->channels_min = 2;
+ hw->channels_max = 2;
+ /* these are somewhat arbitrary */
+ hw->buffer_bytes_max = 131072;
+ hw->period_bytes_min = 256;
+ hw->period_bytes_max = 16384;
+ hw->periods_min = 3;
+ hw->periods_max = MAX_DBDMA_COMMANDS;
+ list_for_each_entry(cii, &sdev->codec_list, list) {
+ if (cii->codec->open) {
+ err = cii->codec->open(cii, pi->substream);
+ if (err) {
+ result = err;
+ /* unwind */
+ found_this = 0;
+ list_for_each_entry_reverse(rev,
+ &sdev->codec_list, list) {
+ if (found_this && rev->codec->close) {
+ rev->codec->close(rev,
+ pi->substream);
+ }
+ if (rev == cii)
+ found_this = 1;
+ }
+ goto out_unlock;
+ }
+ }
+ }
+
+ out_unlock:
+ mutex_unlock(&i2sdev->lock);
+ return result;
+}
+
+#undef CHECK_RATE
+
+static int i2sbus_pcm_close(struct i2sbus_dev *i2sdev, int in)
+{
+ struct codec_info_item *cii;
+ struct pcm_info *pi;
+ int err = 0, tmp;
+
+ mutex_lock(&i2sdev->lock);
+
+ get_pcm_info(i2sdev, in, &pi, NULL);
+
+ list_for_each_entry(cii, &i2sdev->sound.codec_list, list) {
+ if (cii->codec->close) {
+ tmp = cii->codec->close(cii, pi->substream);
+ if (tmp)
+ err = tmp;
+ }
+ }
+
+ pi->substream = NULL;
+ pi->active = 0;
+ mutex_unlock(&i2sdev->lock);
+ return err;
+}
+
+static int i2sbus_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
+}
+
+static int i2sbus_hw_free(struct snd_pcm_substream *substream)
+{
+ snd_pcm_lib_free_pages(substream);
+ return 0;
+}
+
+static int i2sbus_pcm_prepare(struct i2sbus_dev *i2sdev, int in)
+{
+ /* whee. Hard work now. The user has selected a bitrate
+ * and bit format, so now we have to program our
+ * I2S controller appropriately. */
+ struct snd_pcm_runtime *runtime;
+ struct dbdma_cmd *command;
+ int i, periodsize;
+ dma_addr_t offset;
+ struct bus_info bi;
+ struct codec_info_item *cii;
+ int sfr = 0; /* serial format register */
+ int dws = 0; /* data word sizes reg */
+ int input_16bit;
+ struct pcm_info *pi, *other;
+ int cnt;
+ int result = 0;
+
+ mutex_lock(&i2sdev->lock);
+
+ get_pcm_info(i2sdev, in, &pi, &other);
+
+ if (pi->dbdma_ring.running) {
+ result = -EBUSY;
+ goto out_unlock;
+ }
+
+ runtime = pi->substream->runtime;
+ pi->active = 1;
+ if (other->active &&
+ ((i2sdev->format != runtime->format)
+ || (i2sdev->rate != runtime->rate))) {
+ result = -EINVAL;
+ goto out_unlock;
+ }
+
+ i2sdev->format = runtime->format;
+ i2sdev->rate = runtime->rate;
+
+ periodsize = snd_pcm_lib_period_bytes(pi->substream);
+ pi->current_period = 0;
+
+ /* generate dbdma command ring first */
+ command = pi->dbdma_ring.cmds;
+ offset = runtime->dma_addr;
+ for (i = 0; i < pi->substream->runtime->periods;
+ i++, command++, offset += periodsize) {
+ memset(command, 0, sizeof(struct dbdma_cmd));
+ command->command =
+ cpu_to_le16((in ? INPUT_MORE : OUTPUT_MORE) | INTR_ALWAYS);
+ command->phy_addr = cpu_to_le32(offset);
+ command->req_count = cpu_to_le16(periodsize);
+ command->xfer_status = cpu_to_le16(0);
+ }
+ /* last one branches back to first */
+ command--;
+ command->command |= cpu_to_le16(BR_ALWAYS);
+ command->cmd_dep = cpu_to_le32(pi->dbdma_ring.bus_cmd_start);
+
+ /* ok, let's set the serial format and stuff */
+ switch (runtime->format) {
+ /* 16 bit formats */
+ case SNDRV_PCM_FORMAT_S16_BE:
+ case SNDRV_PCM_FORMAT_U16_BE:
+ /* FIXME: if we add different bus factors we need to
+ * do more here!! */
+ bi.bus_factor = 0;
+ list_for_each_entry(cii, &i2sdev->sound.codec_list, list) {
+ bi.bus_factor = cii->codec->bus_factor;
+ break;
+ }
+ if (!bi.bus_factor) {
+ result = -ENODEV;
+ goto out_unlock;
+ }
+ input_16bit = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S32_BE:
+ case SNDRV_PCM_FORMAT_U32_BE:
+ /* force 64x bus speed, otherwise the data cannot be
+ * transferred quickly enough! */
+ bi.bus_factor = 64;
+ input_16bit = 0;
+ break;
+ default:
+ result = -EINVAL;
+ goto out_unlock;
+ }
+ /* we assume all sysclocks are the same! */
+ list_for_each_entry(cii, &i2sdev->sound.codec_list, list) {
+ bi.sysclock_factor = cii->codec->sysclock_factor;
+ break;
+ }
+
+ if (clock_and_divisors(bi.sysclock_factor,
+ bi.bus_factor,
+ runtime->rate,
+ &sfr) < 0) {
+ result = -EINVAL;
+ goto out_unlock;
+ }
+ switch (bi.bus_factor) {
+ case 32:
+ sfr |= I2S_SF_SERIAL_FORMAT_I2S_32X;
+ break;
+ case 64:
+ sfr |= I2S_SF_SERIAL_FORMAT_I2S_64X;
+ break;
+ }
+ /* FIXME: THIS ASSUMES MASTER ALL THE TIME */
+ sfr |= I2S_SF_SCLK_MASTER;
+
+ list_for_each_entry(cii, &i2sdev->sound.codec_list, list) {
+ int err = 0;
+ if (cii->codec->prepare)
+ err = cii->codec->prepare(cii, &bi, pi->substream);
+ if (err) {
+ result = err;
+ goto out_unlock;
+ }
+ }
+ /* codecs are fine with it, so set our clocks */
+ if (input_16bit)
+ dws = (2 << I2S_DWS_NUM_CHANNELS_IN_SHIFT) |
+ (2 << I2S_DWS_NUM_CHANNELS_OUT_SHIFT) |
+ I2S_DWS_DATA_IN_16BIT | I2S_DWS_DATA_OUT_16BIT;
+ else
+ dws = (2 << I2S_DWS_NUM_CHANNELS_IN_SHIFT) |
+ (2 << I2S_DWS_NUM_CHANNELS_OUT_SHIFT) |
+ I2S_DWS_DATA_IN_24BIT | I2S_DWS_DATA_OUT_24BIT;
+
+ /* early exit if already programmed correctly */
+ /* not locking these is fine since we touch them only in this function */
+ if (in_le32(&i2sdev->intfregs->serial_format) == sfr
+ && in_le32(&i2sdev->intfregs->data_word_sizes) == dws)
+ goto out_unlock;
+
+ /* let's notify the codecs about clocks going away.
+ * For now we only do mastering on the i2s cell... */
+ list_for_each_entry(cii, &i2sdev->sound.codec_list, list)
+ if (cii->codec->switch_clock)
+ cii->codec->switch_clock(cii, CLOCK_SWITCH_PREPARE_SLAVE);
+
+ i2sbus_control_enable(i2sdev->control, i2sdev);
+ i2sbus_control_cell(i2sdev->control, i2sdev, 1);
+
+ out_le32(&i2sdev->intfregs->intr_ctl, I2S_PENDING_CLOCKS_STOPPED);
+
+ i2sbus_control_clock(i2sdev->control, i2sdev, 0);
+
+ msleep(1);
+
+ /* wait for clock stopped. This can apparently take a while... */
+ cnt = 100;
+ while (cnt-- &&
+ !(in_le32(&i2sdev->intfregs->intr_ctl) & I2S_PENDING_CLOCKS_STOPPED)) {
+ msleep(5);
+ }
+ out_le32(&i2sdev->intfregs->intr_ctl, I2S_PENDING_CLOCKS_STOPPED);
+
+ /* not locking these is fine since we touch them only in this function */
+ out_le32(&i2sdev->intfregs->serial_format, sfr);
+ out_le32(&i2sdev->intfregs->data_word_sizes, dws);
+
+ i2sbus_control_enable(i2sdev->control, i2sdev);
+ i2sbus_control_cell(i2sdev->control, i2sdev, 1);
+ i2sbus_control_clock(i2sdev->control, i2sdev, 1);
+ msleep(1);
+
+ list_for_each_entry(cii, &i2sdev->sound.codec_list, list)
+ if (cii->codec->switch_clock)
+ cii->codec->switch_clock(cii, CLOCK_SWITCH_SLAVE);
+
+ out_unlock:
+ mutex_unlock(&i2sdev->lock);
+ return result;
+}
+
+static struct dbdma_cmd STOP_CMD = {
+ .command = __constant_cpu_to_le16(DBDMA_STOP),
+};
+
+static int i2sbus_pcm_trigger(struct i2sbus_dev *i2sdev, int in, int cmd)
+{
+ struct codec_info_item *cii;
+ struct pcm_info *pi;
+ int timeout;
+ struct dbdma_cmd tmp;
+ int result = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&i2sdev->low_lock, flags);
+
+ get_pcm_info(i2sdev, in, &pi, NULL);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ if (pi->dbdma_ring.running) {
+ result = -EALREADY;
+ goto out_unlock;
+ }
+ list_for_each_entry(cii, &i2sdev->sound.codec_list, list)
+ if (cii->codec->start)
+ cii->codec->start(cii, pi->substream);
+ pi->dbdma_ring.running = 1;
+
+ /* reset dma engine */
+ out_le32(&pi->dbdma->control,
+ 0 | (RUN | PAUSE | FLUSH | WAKE) << 16);
+ timeout = 100;
+ while (in_le32(&pi->dbdma->status) & RUN && timeout--)
+ udelay(1);
+ if (timeout <= 0) {
+ printk(KERN_ERR
+ "i2sbus: error waiting for dma reset\n");
+ result = -ENXIO;
+ goto out_unlock;
+ }
+
+ /* write dma command buffer address to the dbdma chip */
+ out_le32(&pi->dbdma->cmdptr, pi->dbdma_ring.bus_cmd_start);
+ /* post PCI write */
+ mb();
+ (void)in_le32(&pi->dbdma->status);
+
+ /* change first command to STOP */
+ tmp = *pi->dbdma_ring.cmds;
+ *pi->dbdma_ring.cmds = STOP_CMD;
+
+ /* set running state, remember that the first command is STOP */
+ out_le32(&pi->dbdma->control, RUN | (RUN << 16));
+ timeout = 100;
+ /* wait for STOP to be executed */
+ while (in_le32(&pi->dbdma->status) & ACTIVE && timeout--)
+ udelay(1);
+ if (timeout <= 0) {
+ printk(KERN_ERR "i2sbus: error waiting for dma stop\n");
+ result = -ENXIO;
+ goto out_unlock;
+ }
+ /* again, write dma command buffer address to the dbdma chip,
+ * this time of the first real command */
+ *pi->dbdma_ring.cmds = tmp;
+ out_le32(&pi->dbdma->cmdptr, pi->dbdma_ring.bus_cmd_start);
+ /* post write */
+ mb();
+ (void)in_le32(&pi->dbdma->status);
+
+ /* reset dma engine again */
+ out_le32(&pi->dbdma->control,
+ 0 | (RUN | PAUSE | FLUSH | WAKE) << 16);
+ timeout = 100;
+ while (in_le32(&pi->dbdma->status) & RUN && timeout--)
+ udelay(1);
+ if (timeout <= 0) {
+ printk(KERN_ERR
+ "i2sbus: error waiting for dma reset\n");
+ result = -ENXIO;
+ goto out_unlock;
+ }
+
+ /* wake up the chip with the next descriptor */
+ out_le32(&pi->dbdma->control,
+ (RUN | WAKE) | ((RUN | WAKE) << 16));
+ /* get the frame count */
+ pi->frame_count = in_le32(&i2sdev->intfregs->frame_count);
+
+ /* off you go! */
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ if (!pi->dbdma_ring.running) {
+ result = -EALREADY;
+ goto out_unlock;
+ }
+
+ /* turn off all relevant bits */
+ out_le32(&pi->dbdma->control,
+ (RUN | WAKE | FLUSH | PAUSE) << 16);
+ {
+ /* FIXME: move to own function */
+ int timeout = 5000;
+ while ((in_le32(&pi->dbdma->status) & RUN)
+ && --timeout > 0)
+ udelay(1);
+ if (!timeout)
+ printk(KERN_ERR
+ "i2sbus: timed out turning "
+ "off dbdma engine!\n");
+ }
+
+ pi->dbdma_ring.running = 0;
+ list_for_each_entry(cii, &i2sdev->sound.codec_list, list)
+ if (cii->codec->stop)
+ cii->codec->stop(cii, pi->substream);
+ break;
+ default:
+ result = -EINVAL;
+ goto out_unlock;
+ }
+
+ out_unlock:
+ spin_unlock_irqrestore(&i2sdev->low_lock, flags);
+ return result;
+}
+
+static snd_pcm_uframes_t i2sbus_pcm_pointer(struct i2sbus_dev *i2sdev, int in)
+{
+ struct pcm_info *pi;
+ u32 fc;
+
+ get_pcm_info(i2sdev, in, &pi, NULL);
+
+ fc = in_le32(&i2sdev->intfregs->frame_count);
+ fc = fc - pi->frame_count;
+
+ return (bytes_to_frames(pi->substream->runtime,
+ pi->current_period *
+ snd_pcm_lib_period_bytes(pi->substream))
+ + fc) % pi->substream->runtime->buffer_size;
+}
+
+static inline void handle_interrupt(struct i2sbus_dev *i2sdev, int in)
+{
+ struct pcm_info *pi;
+ u32 fc;
+ u32 delta;
+
+ spin_lock(&i2sdev->low_lock);
+ get_pcm_info(i2sdev, in, &pi, NULL);
+
+ if (!pi->dbdma_ring.running) {
+ /* there was still an interrupt pending
+ * while we stopped. or maybe another
+ * processor (not the one that was stopping
+ * the DMA engine) was spinning above
+ * waiting for the lock. */
+ goto out_unlock;
+ }
+
+ fc = in_le32(&i2sdev->intfregs->frame_count);
+ /* a counter overflow does not change the calculation. */
+ delta = fc - pi->frame_count;
+
+ /* update current_period */
+ while (delta >= pi->substream->runtime->period_size) {
+ pi->current_period++;
+ delta = delta - pi->substream->runtime->period_size;
+ }
+
+ if (unlikely(delta)) {
+ /* Some interrupt came late, so check the dbdma.
+ * This special case exists to syncronize the frame_count with
+ * the dbdma transfer, but is hit every once in a while. */
+ int period;
+
+ period = (in_le32(&pi->dbdma->cmdptr)
+ - pi->dbdma_ring.bus_cmd_start)
+ / sizeof(struct dbdma_cmd);
+ pi->current_period = pi->current_period
+ % pi->substream->runtime->periods;
+
+ while (pi->current_period != period) {
+ pi->current_period++;
+ pi->current_period %= pi->substream->runtime->periods;
+ /* Set delta to zero, as the frame_count value is too
+ * high (otherwise the code path will not be executed).
+ * This corrects the fact that the frame_count is too
+ * low at the beginning due to buffering. */
+ delta = 0;
+ }
+ }
+
+ pi->frame_count = fc - delta;
+ pi->current_period %= pi->substream->runtime->periods;
+
+ spin_unlock(&i2sdev->low_lock);
+ /* may call _trigger again, hence needs to be unlocked */
+ snd_pcm_period_elapsed(pi->substream);
+ return;
+ out_unlock:
+ spin_unlock(&i2sdev->low_lock);
+}
+
+irqreturn_t i2sbus_tx_intr(int irq, void *devid, struct pt_regs *regs)
+{
+ handle_interrupt((struct i2sbus_dev *)devid, 0);
+ return IRQ_HANDLED;
+}
+
+irqreturn_t i2sbus_rx_intr(int irq, void *devid, struct pt_regs * regs)
+{
+ handle_interrupt((struct i2sbus_dev *)devid, 1);
+ return IRQ_HANDLED;
+}
+
+static int i2sbus_playback_open(struct snd_pcm_substream *substream)
+{
+ struct i2sbus_dev *i2sdev = snd_pcm_substream_chip(substream);
+
+ if (!i2sdev)
+ return -EINVAL;
+ i2sdev->out.substream = substream;
+ return i2sbus_pcm_open(i2sdev, 0);
+}
+
+static int i2sbus_playback_close(struct snd_pcm_substream *substream)
+{
+ struct i2sbus_dev *i2sdev = snd_pcm_substream_chip(substream);
+ int err;
+
+ if (!i2sdev)
+ return -EINVAL;
+ if (i2sdev->out.substream != substream)
+ return -EINVAL;
+ err = i2sbus_pcm_close(i2sdev, 0);
+ if (!err)
+ i2sdev->out.substream = NULL;
+ return err;
+}
+
+static int i2sbus_playback_prepare(struct snd_pcm_substream *substream)
+{
+ struct i2sbus_dev *i2sdev = snd_pcm_substream_chip(substream);
+
+ if (!i2sdev)
+ return -EINVAL;
+ if (i2sdev->out.substream != substream)
+ return -EINVAL;
+ return i2sbus_pcm_prepare(i2sdev, 0);
+}
+
+static int i2sbus_playback_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct i2sbus_dev *i2sdev = snd_pcm_substream_chip(substream);
+
+ if (!i2sdev)
+ return -EINVAL;
+ if (i2sdev->out.substream != substream)
+ return -EINVAL;
+ return i2sbus_pcm_trigger(i2sdev, 0, cmd);
+}
+
+static snd_pcm_uframes_t i2sbus_playback_pointer(struct snd_pcm_substream
+ *substream)
+{
+ struct i2sbus_dev *i2sdev = snd_pcm_substream_chip(substream);
+
+ if (!i2sdev)
+ return -EINVAL;
+ if (i2sdev->out.substream != substream)
+ return 0;
+ return i2sbus_pcm_pointer(i2sdev, 0);
+}
+
+static struct snd_pcm_ops i2sbus_playback_ops = {
+ .open = i2sbus_playback_open,
+ .close = i2sbus_playback_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = i2sbus_hw_params,
+ .hw_free = i2sbus_hw_free,
+ .prepare = i2sbus_playback_prepare,
+ .trigger = i2sbus_playback_trigger,
+ .pointer = i2sbus_playback_pointer,
+};
+
+static int i2sbus_record_open(struct snd_pcm_substream *substream)
+{
+ struct i2sbus_dev *i2sdev = snd_pcm_substream_chip(substream);
+
+ if (!i2sdev)
+ return -EINVAL;
+ i2sdev->in.substream = substream;
+ return i2sbus_pcm_open(i2sdev, 1);
+}
+
+static int i2sbus_record_close(struct snd_pcm_substream *substream)
+{
+ struct i2sbus_dev *i2sdev = snd_pcm_substream_chip(substream);
+ int err;
+
+ if (!i2sdev)
+ return -EINVAL;
+ if (i2sdev->in.substream != substream)
+ return -EINVAL;
+ err = i2sbus_pcm_close(i2sdev, 1);
+ if (!err)
+ i2sdev->in.substream = NULL;
+ return err;
+}
+
+static int i2sbus_record_prepare(struct snd_pcm_substream *substream)
+{
+ struct i2sbus_dev *i2sdev = snd_pcm_substream_chip(substream);
+
+ if (!i2sdev)
+ return -EINVAL;
+ if (i2sdev->in.substream != substream)
+ return -EINVAL;
+ return i2sbus_pcm_prepare(i2sdev, 1);
+}
+
+static int i2sbus_record_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct i2sbus_dev *i2sdev = snd_pcm_substream_chip(substream);
+
+ if (!i2sdev)
+ return -EINVAL;
+ if (i2sdev->in.substream != substream)
+ return -EINVAL;
+ return i2sbus_pcm_trigger(i2sdev, 1, cmd);
+}
+
+static snd_pcm_uframes_t i2sbus_record_pointer(struct snd_pcm_substream
+ *substream)
+{
+ struct i2sbus_dev *i2sdev = snd_pcm_substream_chip(substream);
+
+ if (!i2sdev)
+ return -EINVAL;
+ if (i2sdev->in.substream != substream)
+ return 0;
+ return i2sbus_pcm_pointer(i2sdev, 1);
+}
+
+static struct snd_pcm_ops i2sbus_record_ops = {
+ .open = i2sbus_record_open,
+ .close = i2sbus_record_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = i2sbus_hw_params,
+ .hw_free = i2sbus_hw_free,
+ .prepare = i2sbus_record_prepare,
+ .trigger = i2sbus_record_trigger,
+ .pointer = i2sbus_record_pointer,
+};
+
+static void i2sbus_private_free(struct snd_pcm *pcm)
+{
+ struct i2sbus_dev *i2sdev = snd_pcm_chip(pcm);
+ struct codec_info_item *p, *tmp;
+
+ i2sdev->sound.pcm = NULL;
+ i2sdev->out.created = 0;
+ i2sdev->in.created = 0;
+ list_for_each_entry_safe(p, tmp, &i2sdev->sound.codec_list, list) {
+ printk(KERN_ERR "i2sbus: a codec didn't unregister!\n");
+ list_del(&p->list);
+ module_put(p->codec->owner);
+ kfree(p);
+ }
+ soundbus_dev_put(&i2sdev->sound);
+ module_put(THIS_MODULE);
+}
+
+/* FIXME: this function needs an error handling strategy with labels */
+int
+i2sbus_attach_codec(struct soundbus_dev *dev, struct snd_card *card,
+ struct codec_info *ci, void *data)
+{
+ int err, in = 0, out = 0;
+ struct transfer_info *tmp;
+ struct i2sbus_dev *i2sdev = soundbus_dev_to_i2sbus_dev(dev);
+ struct codec_info_item *cii;
+
+ if (!dev->pcmname || dev->pcmid == -1) {
+ printk(KERN_ERR "i2sbus: pcm name and id must be set!\n");
+ return -EINVAL;
+ }
+
+ list_for_each_entry(cii, &dev->codec_list, list) {
+ if (cii->codec_data == data)
+ return -EALREADY;
+ }
+
+ if (!ci->transfers || !ci->transfers->formats
+ || !ci->transfers->rates || !ci->usable)
+ return -EINVAL;
+
+ /* we currently code the i2s transfer on the clock, and support only
+ * 32 and 64 */
+ if (ci->bus_factor != 32 && ci->bus_factor != 64)
+ return -EINVAL;
+
+ /* If you want to fix this, you need to keep track of what transport infos
+ * are to be used, which codecs they belong to, and then fix all the
+ * sysclock/busclock stuff above to depend on which is usable */
+ list_for_each_entry(cii, &dev->codec_list, list) {
+ if (cii->codec->sysclock_factor != ci->sysclock_factor) {
+ printk(KERN_DEBUG
+ "cannot yet handle multiple different sysclocks!\n");
+ return -EINVAL;
+ }
+ if (cii->codec->bus_factor != ci->bus_factor) {
+ printk(KERN_DEBUG
+ "cannot yet handle multiple different bus clocks!\n");
+ return -EINVAL;
+ }
+ }
+
+ tmp = ci->transfers;
+ while (tmp->formats && tmp->rates) {
+ if (tmp->transfer_in)
+ in = 1;
+ else
+ out = 1;
+ tmp++;
+ }
+
+ cii = kzalloc(sizeof(struct codec_info_item), GFP_KERNEL);
+ if (!cii) {
+ printk(KERN_DEBUG "i2sbus: failed to allocate cii\n");
+ return -ENOMEM;
+ }
+
+ /* use the private data to point to the codec info */
+ cii->sdev = soundbus_dev_get(dev);
+ cii->codec = ci;
+ cii->codec_data = data;
+
+ if (!cii->sdev) {
+ printk(KERN_DEBUG
+ "i2sbus: failed to get soundbus dev reference\n");
+ kfree(cii);
+ return -ENODEV;
+ }
+
+ if (!try_module_get(THIS_MODULE)) {
+ printk(KERN_DEBUG "i2sbus: failed to get module reference!\n");
+ soundbus_dev_put(dev);
+ kfree(cii);
+ return -EBUSY;
+ }
+
+ if (!try_module_get(ci->owner)) {
+ printk(KERN_DEBUG
+ "i2sbus: failed to get module reference to codec owner!\n");
+ module_put(THIS_MODULE);
+ soundbus_dev_put(dev);
+ kfree(cii);
+ return -EBUSY;
+ }
+
+ if (!dev->pcm) {
+ err = snd_pcm_new(card,
+ dev->pcmname,
+ dev->pcmid,
+ 0,
+ 0,
+ &dev->pcm);
+ if (err) {
+ printk(KERN_DEBUG "i2sbus: failed to create pcm\n");
+ kfree(cii);
+ module_put(ci->owner);
+ soundbus_dev_put(dev);
+ module_put(THIS_MODULE);
+ return err;
+ }
+ }
+
+ /* ALSA yet again sucks.
+ * If it is ever fixed, remove this line. See below. */
+ out = in = 1;
+
+ if (!i2sdev->out.created && out) {
+ if (dev->pcm->card != card) {
+ /* eh? */
+ printk(KERN_ERR
+ "Can't attach same bus to different cards!\n");
+ module_put(ci->owner);
+ kfree(cii);
+ soundbus_dev_put(dev);
+ module_put(THIS_MODULE);
+ return -EINVAL;
+ }
+ if ((err =
+ snd_pcm_new_stream(dev->pcm, SNDRV_PCM_STREAM_PLAYBACK, 1))) {
+ module_put(ci->owner);
+ kfree(cii);
+ soundbus_dev_put(dev);
+ module_put(THIS_MODULE);
+ return err;
+ }
+ snd_pcm_set_ops(dev->pcm, SNDRV_PCM_STREAM_PLAYBACK,
+ &i2sbus_playback_ops);
+ i2sdev->out.created = 1;
+ }
+
+ if (!i2sdev->in.created && in) {
+ if (dev->pcm->card != card) {
+ printk(KERN_ERR
+ "Can't attach same bus to different cards!\n");
+ module_put(ci->owner);
+ kfree(cii);
+ soundbus_dev_put(dev);
+ module_put(THIS_MODULE);
+ return -EINVAL;
+ }
+ if ((err =
+ snd_pcm_new_stream(dev->pcm, SNDRV_PCM_STREAM_CAPTURE, 1))) {
+ module_put(ci->owner);
+ kfree(cii);
+ soundbus_dev_put(dev);
+ module_put(THIS_MODULE);
+ return err;
+ }
+ snd_pcm_set_ops(dev->pcm, SNDRV_PCM_STREAM_CAPTURE,
+ &i2sbus_record_ops);
+ i2sdev->in.created = 1;
+ }
+
+ /* so we have to register the pcm after adding any substream
+ * to it because alsa doesn't create the devices for the
+ * substreams when we add them later.
+ * Therefore, force in and out on both busses (above) and
+ * register the pcm now instead of just after creating it.
+ */
+ err = snd_device_register(card, dev->pcm);
+ if (err) {
+ printk(KERN_ERR "i2sbus: error registering new pcm\n");
+ module_put(ci->owner);
+ kfree(cii);
+ soundbus_dev_put(dev);
+ module_put(THIS_MODULE);
+ return err;
+ }
+ /* no errors any more, so let's add this to our list */
+ list_add(&cii->list, &dev->codec_list);
+
+ dev->pcm->private_data = i2sdev;
+ dev->pcm->private_free = i2sbus_private_free;
+
+ /* well, we really should support scatter/gather DMA */
+ snd_pcm_lib_preallocate_pages_for_all(
+ dev->pcm, SNDRV_DMA_TYPE_DEV,
+ snd_dma_pci_data(macio_get_pci_dev(i2sdev->macio)),
+ 64 * 1024, 64 * 1024);
+
+ return 0;
+}
+
+void i2sbus_detach_codec(struct soundbus_dev *dev, void *data)
+{
+ struct codec_info_item *cii = NULL, *i;
+
+ list_for_each_entry(i, &dev->codec_list, list) {
+ if (i->codec_data == data) {
+ cii = i;
+ break;
+ }
+ }
+ if (cii) {
+ list_del(&cii->list);
+ module_put(cii->codec->owner);
+ kfree(cii);
+ }
+ /* no more codecs, but still a pcm? */
+ if (list_empty(&dev->codec_list) && dev->pcm) {
+ /* the actual cleanup is done by the callback above! */
+ snd_device_free(dev->pcm->card, dev->pcm);
+ }
+}
--- /dev/null
+/*
+ * i2sbus driver -- private definitions
+ *
+ * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
+ *
+ * GPL v2, can be found in COPYING.
+ */
+#ifndef __I2SBUS_H
+#define __I2SBUS_H
+#include <asm/dbdma.h>
+#include <linux/interrupt.h>
+#include <sound/pcm.h>
+#include <linux/spinlock.h>
+#include <linux/mutex.h>
+#include <asm/prom.h>
+#include "i2sbus-interface.h"
+#include "i2sbus-control.h"
+#include "../soundbus.h"
+
+struct i2sbus_control {
+ volatile struct i2s_control_regs __iomem *controlregs;
+ struct resource rsrc;
+ struct list_head list;
+};
+
+#define MAX_DBDMA_COMMANDS 32
+
+struct dbdma_command_mem {
+ dma_addr_t bus_addr;
+ dma_addr_t bus_cmd_start;
+ struct dbdma_cmd *cmds;
+ void *space;
+ int size;
+ u32 running:1;
+};
+
+struct pcm_info {
+ u32 created:1, /* has this direction been created with alsa? */
+ active:1; /* is this stream active? */
+ /* runtime information */
+ struct snd_pcm_substream *substream;
+ int current_period;
+ u32 frame_count;
+ struct dbdma_command_mem dbdma_ring;
+ volatile struct dbdma_regs __iomem *dbdma;
+};
+
+struct i2sbus_dev {
+ struct soundbus_dev sound;
+ struct macio_dev *macio;
+ struct i2sbus_control *control;
+ volatile struct i2s_interface_regs __iomem *intfregs;
+
+ struct resource resources[3];
+ struct resource *allocated_resource[3];
+ int interrupts[3];
+ char rnames[3][32];
+
+ /* info about currently active substreams */
+ struct pcm_info out, in;
+ snd_pcm_format_t format;
+ unsigned int rate;
+
+ /* list for a single controller */
+ struct list_head item;
+ /* number of bus on controller */
+ int bus_number;
+ /* for use by control layer */
+ struct pmf_function *enable,
+ *cell_enable,
+ *cell_disable,
+ *clock_enable,
+ *clock_disable;
+
+ /* locks */
+ /* spinlock for low-level interrupt locking */
+ spinlock_t low_lock;
+ /* mutex for high-level consistency */
+ struct mutex lock;
+};
+
+#define soundbus_dev_to_i2sbus_dev(sdev) \
+ container_of(sdev, struct i2sbus_dev, sound)
+
+/* pcm specific functions */
+extern int
+i2sbus_attach_codec(struct soundbus_dev *dev, struct snd_card *card,
+ struct codec_info *ci, void *data);
+extern void
+i2sbus_detach_codec(struct soundbus_dev *dev, void *data);
+extern irqreturn_t
+i2sbus_tx_intr(int irq, void *devid, struct pt_regs *regs);
+extern irqreturn_t
+i2sbus_rx_intr(int irq, void *devid, struct pt_regs *regs);
+
+/* control specific functions */
+extern int i2sbus_control_init(struct macio_dev* dev,
+ struct i2sbus_control **c);
+extern void i2sbus_control_destroy(struct i2sbus_control *c);
+extern int i2sbus_control_add_dev(struct i2sbus_control *c,
+ struct i2sbus_dev *i2sdev);
+extern void i2sbus_control_remove_dev(struct i2sbus_control *c,
+ struct i2sbus_dev *i2sdev);
+extern int i2sbus_control_enable(struct i2sbus_control *c,
+ struct i2sbus_dev *i2sdev);
+extern int i2sbus_control_cell(struct i2sbus_control *c,
+ struct i2sbus_dev *i2sdev,
+ int enable);
+extern int i2sbus_control_clock(struct i2sbus_control *c,
+ struct i2sbus_dev *i2sdev,
+ int enable);
+#endif /* __I2SBUS_H */
--- /dev/null
+/*
+ * soundbus generic definitions
+ *
+ * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
+ *
+ * GPL v2, can be found in COPYING.
+ */
+#ifndef __SOUNDBUS_H
+#define __SOUNDBUS_H
+
+#include <asm/of_device.h>
+#include <sound/pcm.h>
+#include <linux/list.h>
+
+
+/* When switching from master to slave or the other way around,
+ * you don't want to have the codec chip acting as clock source
+ * while the bus still is.
+ * More importantly, while switch from slave to master, you need
+ * to turn off the chip's master function first, but then there's
+ * no clock for a while and other chips might reset, so we notify
+ * their drivers after having switched.
+ * The constants here are codec-point of view, so when we switch
+ * the soundbus to master we tell the codec we're going to switch
+ * and give it CLOCK_SWITCH_PREPARE_SLAVE!
+ */
+enum clock_switch {
+ CLOCK_SWITCH_PREPARE_SLAVE,
+ CLOCK_SWITCH_PREPARE_MASTER,
+ CLOCK_SWITCH_SLAVE,
+ CLOCK_SWITCH_MASTER,
+ CLOCK_SWITCH_NOTIFY,
+};
+
+/* information on a transfer the codec can take */
+struct transfer_info {
+ u64 formats; /* SNDRV_PCM_FMTBIT_* */
+ unsigned int rates; /* SNDRV_PCM_RATE_* */
+ /* flags */
+ u32 transfer_in:1, /* input = 1, output = 0 */
+ must_be_clock_source:1;
+ /* for codecs to distinguish among their TIs */
+ int tag;
+};
+
+struct codec_info_item {
+ struct codec_info *codec;
+ void *codec_data;
+ struct soundbus_dev *sdev;
+ /* internal, to be used by the soundbus provider */
+ struct list_head list;
+};
+
+/* for prepare, where the codecs need to know
+ * what we're going to drive the bus with */
+struct bus_info {
+ /* see below */
+ int sysclock_factor;
+ int bus_factor;
+};
+
+/* information on the codec itself, plus function pointers */
+struct codec_info {
+ /* the module this lives in */
+ struct module *owner;
+
+ /* supported transfer possibilities, array terminated by
+ * formats or rates being 0. */
+ struct transfer_info *transfers;
+
+ /* Master clock speed factor
+ * to be used (master clock speed = sysclock_factor * sampling freq)
+ * Unused if the soundbus provider has no such notion.
+ */
+ int sysclock_factor;
+
+ /* Bus factor, bus clock speed = bus_factor * sampling freq)
+ * Unused if the soundbus provider has no such notion.
+ */
+ int bus_factor;
+
+ /* operations */
+ /* clock switching, see above */
+ int (*switch_clock)(struct codec_info_item *cii,
+ enum clock_switch clock);
+
+ /* called for each transfer_info when the user
+ * opens the pcm device to determine what the
+ * hardware can support at this point in time.
+ * That can depend on other user-switchable controls.
+ * Return 1 if usable, 0 if not.
+ * out points to another instance of a transfer_info
+ * which is initialised to the values in *ti, and
+ * it's format and rate values can be modified by
+ * the callback if it is necessary to further restrict
+ * the formats that can be used at the moment, for
+ * example when one codec has multiple logical codec
+ * info structs for multiple inputs.
+ */
+ int (*usable)(struct codec_info_item *cii,
+ struct transfer_info *ti,
+ struct transfer_info *out);
+
+ /* called when pcm stream is opened, probably not implemented
+ * most of the time since it isn't too useful */
+ int (*open)(struct codec_info_item *cii,
+ struct snd_pcm_substream *substream);
+
+ /* called when the pcm stream is closed, at this point
+ * the user choices can all be unlocked (see below) */
+ int (*close)(struct codec_info_item *cii,
+ struct snd_pcm_substream *substream);
+
+ /* if the codec must forbid some user choices because
+ * they are not valid with the substream/transfer info,
+ * it must do so here. Example: no digital output for
+ * incompatible framerate, say 8KHz, on Onyx.
+ * If the selected stuff in the substream is NOT
+ * compatible, you have to reject this call! */
+ int (*prepare)(struct codec_info_item *cii,
+ struct bus_info *bi,
+ struct snd_pcm_substream *substream);
+
+ /* start() is called before data is pushed to the codec.
+ * Note that start() must be atomic! */
+ int (*start)(struct codec_info_item *cii,
+ struct snd_pcm_substream *substream);
+
+ /* stop() is called after data is no longer pushed to the codec.
+ * Note that stop() must be atomic! */
+ int (*stop)(struct codec_info_item *cii,
+ struct snd_pcm_substream *substream);
+
+ int (*suspend)(struct codec_info_item *cii, pm_message_t state);
+ int (*resume)(struct codec_info_item *cii);
+};
+
+/* information on a soundbus device */
+struct soundbus_dev {
+ /* the bus it belongs to */
+ struct list_head onbuslist;
+
+ /* the of device it represents */
+ struct of_device ofdev;
+
+ /* what modules go by */
+ char modalias[32];
+
+ /* These fields must be before attach_codec can be called.
+ * They should be set by the owner of the alsa card object
+ * that is needed, and whoever sets them must make sure
+ * that they are unique within that alsa card object. */
+ char *pcmname;
+ int pcmid;
+
+ /* this is assigned by the soundbus provider in attach_codec */
+ struct snd_pcm *pcm;
+
+ /* operations */
+ /* attach a codec to this soundbus, give the alsa
+ * card object the PCMs for this soundbus should be in.
+ * The 'data' pointer must be unique, it is used as the
+ * key for detach_codec(). */
+ int (*attach_codec)(struct soundbus_dev *dev, struct snd_card *card,
+ struct codec_info *ci, void *data);
+ void (*detach_codec)(struct soundbus_dev *dev, void *data);
+ /* TODO: suspend/resume */
+
+ /* private for the soundbus provider */
+ struct list_head codec_list;
+ u32 have_out:1, have_in:1;
+};
+#define to_soundbus_device(d) container_of(d, struct soundbus_dev, ofdev.dev)
+#define of_to_soundbus_device(d) container_of(d, struct soundbus_dev, ofdev)
+
+extern int soundbus_add_one(struct soundbus_dev *dev);
+extern void soundbus_remove_one(struct soundbus_dev *dev);
+
+extern struct soundbus_dev *soundbus_dev_get(struct soundbus_dev *dev);
+extern void soundbus_dev_put(struct soundbus_dev *dev);
+
+struct soundbus_driver {
+ char *name;
+ struct module *owner;
+
+ /* we don't implement any matching at all */
+
+ int (*probe)(struct soundbus_dev* dev);
+ int (*remove)(struct soundbus_dev* dev);
+
+ int (*suspend)(struct soundbus_dev* dev, pm_message_t state);
+ int (*resume)(struct soundbus_dev* dev);
+ int (*shutdown)(struct soundbus_dev* dev);
+
+ struct device_driver driver;
+};
+#define to_soundbus_driver(drv) container_of(drv,struct soundbus_driver, driver)
+
+extern int soundbus_register_driver(struct soundbus_driver *drv);
+extern void soundbus_unregister_driver(struct soundbus_driver *drv);
+
+#endif /* __SOUNDBUS_H */
--- /dev/null
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/stat.h>
+/* FIX UP */
+#include "soundbus.h"
+
+#define soundbus_config_of_attr(field, format_string) \
+static ssize_t \
+field##_show (struct device *dev, struct device_attribute *attr, \
+ char *buf) \
+{ \
+ struct soundbus_dev *mdev = to_soundbus_device (dev); \
+ return sprintf (buf, format_string, mdev->ofdev.node->field); \
+}
+
+static ssize_t modalias_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct soundbus_dev *sdev = to_soundbus_device(dev);
+ struct of_device *of = &sdev->ofdev;
+ int length;
+
+ if (*sdev->modalias) {
+ strlcpy(buf, sdev->modalias, sizeof(sdev->modalias) + 1);
+ strcat(buf, "\n");
+ length = strlen(buf);
+ } else {
+ length = sprintf(buf, "of:N%sT%s\n",
+ of->node->name, of->node->type);
+ }
+
+ return length;
+}
+
+soundbus_config_of_attr (name, "%s\n");
+soundbus_config_of_attr (type, "%s\n");
+
+struct device_attribute soundbus_dev_attrs[] = {
+ __ATTR_RO(name),
+ __ATTR_RO(type),
+ __ATTR_RO(modalias),
+ __ATTR_NULL
+};
MODULE_DESCRIPTION("SA1100/SA1111 + UDA1341TS driver for ALSA");
MODULE_SUPPORTED_DEVICE("{{UDA1341,iPAQ H3600 UDA1341TS}}");
-static char *id = NULL; /* ID for this card */
+static char *id; /* ID for this card */
module_param(id, charp, 0444);
MODULE_PARM_DESC(id, "ID string for SA1100/SA1111 + UDA1341TS soundcard.");
if ((err = platform_driver_register(&sa11xx_uda1341_driver)) < 0)
return err;
device = platform_device_register_simple(SA11XX_UDA1341_DRIVER, -1, NULL, 0);
- if (IS_ERR(device)) {
- platform_driver_unregister(&sa11xx_uda1341_driver);
- return PTR_ERR(device);
- }
- return 0;
+ if (!IS_ERR(device)) {
+ if (platform_get_drvdata(device))
+ return 0;
+ platform_device_unregister(device);
+ err = -ENODEV
+ } else
+ err = PTR_ERR(device);
+ platform_driver_unregister(&sa11xx_uda1341_driver);
+ return err;
}
static void __exit sa11xx_uda1341_exit(void)
read_unlock(&card->ctl_files_rwlock);
}
+EXPORT_SYMBOL(snd_ctl_notify);
+
/**
* snd_ctl_new - create a control instance from the template
* @control: the control template
return kctl;
}
+EXPORT_SYMBOL(snd_ctl_new);
+
/**
* snd_ctl_new1 - create a control instance from the template
* @ncontrol: the initialization record
return snd_ctl_new(&kctl, access);
}
+EXPORT_SYMBOL(snd_ctl_new1);
+
/**
* snd_ctl_free_one - release the control instance
* @kcontrol: the control instance
}
}
+EXPORT_SYMBOL(snd_ctl_free_one);
+
static unsigned int snd_ctl_hole_check(struct snd_card *card,
unsigned int count)
{
return err;
}
+EXPORT_SYMBOL(snd_ctl_add);
+
/**
* snd_ctl_remove - remove the control from the card and release it
* @card: the card instance
return 0;
}
+EXPORT_SYMBOL(snd_ctl_remove);
+
/**
* snd_ctl_remove_id - remove the control of the given id and release it
* @card: the card instance
return ret;
}
+EXPORT_SYMBOL(snd_ctl_remove_id);
+
/**
* snd_ctl_remove_unlocked_id - remove the unlocked control of the given id and release it
* @file: active control handle
return 0;
}
+EXPORT_SYMBOL(snd_ctl_rename_id);
+
/**
* snd_ctl_find_numid - find the control instance with the given number-id
* @card: the card instance
return NULL;
}
+EXPORT_SYMBOL(snd_ctl_find_numid);
+
/**
* snd_ctl_find_id - find the control instance with the given id
* @card: the card instance
return NULL;
}
+EXPORT_SYMBOL(snd_ctl_find_id);
+
static int snd_ctl_card_info(struct snd_card *card, struct snd_ctl_file * ctl,
unsigned int cmd, void __user *arg)
{
return result;
}
+EXPORT_SYMBOL(snd_ctl_elem_read);
+
static int snd_ctl_elem_read_user(struct snd_card *card,
struct snd_ctl_elem_value __user *_control)
{
return result;
}
+EXPORT_SYMBOL(snd_ctl_elem_write);
+
static int snd_ctl_elem_write_user(struct snd_ctl_file *file,
struct snd_ctl_elem_value __user *_control)
{
return _snd_ctl_register_ioctl(fcn, &snd_control_ioctls);
}
+EXPORT_SYMBOL(snd_ctl_register_ioctl);
+
#ifdef CONFIG_COMPAT
int snd_ctl_register_ioctl_compat(snd_kctl_ioctl_func_t fcn)
{
return _snd_ctl_register_ioctl(fcn, &snd_control_compat_ioctls);
}
+
+EXPORT_SYMBOL(snd_ctl_register_ioctl_compat);
#endif
/*
return _snd_ctl_unregister_ioctl(fcn, &snd_control_ioctls);
}
+EXPORT_SYMBOL(snd_ctl_unregister_ioctl);
+
#ifdef CONFIG_COMPAT
int snd_ctl_unregister_ioctl_compat(snd_kctl_ioctl_func_t fcn)
{
return _snd_ctl_unregister_ioctl(fcn, &snd_control_compat_ioctls);
}
+EXPORT_SYMBOL(snd_ctl_unregister_ioctl_compat);
#endif
static int snd_ctl_fasync(int fd, struct file * file, int on)
return 0;
}
+EXPORT_SYMBOL(snd_device_new);
+
/**
* snd_device_free - release the device from the card
* @card: the card instance
return -ENXIO;
}
+EXPORT_SYMBOL(snd_device_free);
+
/**
* snd_device_disconnect - disconnect the device
* @card: the card instance
return -ENXIO;
}
+EXPORT_SYMBOL(snd_device_register);
+
/*
* register all the devices on the card.
* called from init.c
struct snd_info_entry *entry;
if ((entry = snd_info_create_module_entry(THIS_MODULE, "hwdep", NULL)) != NULL) {
- entry->c.text.read_size = PAGE_SIZE;
entry->c.text.read = snd_hwdep_proc_read;
if (snd_info_register(entry) < 0) {
snd_info_free_entry(entry);
#include <sound/driver.h>
#include <linux/init.h>
-#include <linux/vmalloc.h>
#include <linux/time.h>
#include <linux/smp_lock.h>
#include <linux/string.h>
static int snd_info_version_done(void);
+/* resize the proc r/w buffer */
+static int resize_info_buffer(struct snd_info_buffer *buffer,
+ unsigned int nsize)
+{
+ char *nbuf;
+
+ nsize = PAGE_ALIGN(nsize);
+ nbuf = kmalloc(nsize, GFP_KERNEL);
+ if (! nbuf)
+ return -ENOMEM;
+
+ memcpy(nbuf, buffer->buffer, buffer->len);
+ kfree(buffer->buffer);
+ buffer->buffer = nbuf;
+ buffer->len = nsize;
+ return 0;
+}
+
/**
* snd_iprintf - printf on the procfs buffer
* @buffer: the procfs buffer
{
va_list args;
int len, res;
+ int err = 0;
+ might_sleep();
if (buffer->stop || buffer->error)
return 0;
len = buffer->len - buffer->size;
va_start(args, fmt);
- res = vsnprintf(buffer->curr, len, fmt, args);
- va_end(args);
- if (res >= len) {
- buffer->stop = 1;
- return 0;
+ for (;;) {
+ res = vsnprintf(buffer->buffer + buffer->curr, len, fmt, args);
+ if (res < len)
+ break;
+ err = resize_info_buffer(buffer, buffer->len + PAGE_SIZE);
+ if (err < 0)
+ break;
+ len = buffer->len - buffer->size;
}
+ va_end(args);
+
+ if (err < 0)
+ return err;
buffer->curr += res;
buffer->size += res;
return res;
}
+EXPORT_SYMBOL(snd_iprintf);
+
/*
*/
-static struct proc_dir_entry *snd_proc_root = NULL;
-struct snd_info_entry *snd_seq_root = NULL;
+static struct proc_dir_entry *snd_proc_root;
+struct snd_info_entry *snd_seq_root;
+EXPORT_SYMBOL(snd_seq_root);
+
#ifdef CONFIG_SND_OSSEMUL
-struct snd_info_entry *snd_oss_root = NULL;
+struct snd_info_entry *snd_oss_root;
#endif
static inline void snd_info_entry_prepare(struct proc_dir_entry *de)
struct snd_info_private_data *data;
struct snd_info_entry *entry;
struct snd_info_buffer *buf;
- size_t size = 0;
+ ssize_t size = 0;
loff_t pos;
data = file->private_data;
buf = data->wbuffer;
if (buf == NULL)
return -EIO;
- if (pos >= buf->len)
- return -ENOMEM;
- size = buf->len - pos;
- size = min(count, size);
- if (copy_from_user(buf->buffer + pos, buffer, size))
+ mutex_lock(&entry->access);
+ if (pos + count >= buf->len) {
+ if (resize_info_buffer(buf, pos + count)) {
+ mutex_unlock(&entry->access);
+ return -ENOMEM;
+ }
+ }
+ if (copy_from_user(buf->buffer + pos, buffer, count)) {
+ mutex_unlock(&entry->access);
return -EFAULT;
- if ((long)buf->size < pos + size)
- buf->size = pos + size;
+ }
+ buf->size = pos + count;
+ mutex_unlock(&entry->access);
+ size = count;
break;
case SNDRV_INFO_CONTENT_DATA:
if (entry->c.ops->write)
}
mode = file->f_flags & O_ACCMODE;
if (mode == O_RDONLY || mode == O_RDWR) {
- if ((entry->content == SNDRV_INFO_CONTENT_TEXT &&
- !entry->c.text.read_size) ||
- (entry->content == SNDRV_INFO_CONTENT_DATA &&
+ if ((entry->content == SNDRV_INFO_CONTENT_DATA &&
entry->c.ops->read == NULL)) {
err = -ENODEV;
goto __error;
}
}
if (mode == O_WRONLY || mode == O_RDWR) {
- if ((entry->content == SNDRV_INFO_CONTENT_TEXT &&
- !entry->c.text.write_size) ||
- (entry->content == SNDRV_INFO_CONTENT_DATA &&
+ if ((entry->content == SNDRV_INFO_CONTENT_DATA &&
entry->c.ops->write == NULL)) {
err = -ENODEV;
goto __error;
case SNDRV_INFO_CONTENT_TEXT:
if (mode == O_RDONLY || mode == O_RDWR) {
buffer = kzalloc(sizeof(*buffer), GFP_KERNEL);
- if (buffer == NULL) {
- kfree(data);
- err = -ENOMEM;
- goto __error;
- }
- buffer->len = (entry->c.text.read_size +
- (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1);
- buffer->buffer = vmalloc(buffer->len);
- if (buffer->buffer == NULL) {
- kfree(buffer);
- kfree(data);
- err = -ENOMEM;
- goto __error;
- }
- buffer->curr = buffer->buffer;
+ if (buffer == NULL)
+ goto __nomem;
data->rbuffer = buffer;
+ buffer->len = PAGE_SIZE;
+ buffer->buffer = kmalloc(buffer->len, GFP_KERNEL);
+ if (buffer->buffer == NULL)
+ goto __nomem;
}
if (mode == O_WRONLY || mode == O_RDWR) {
buffer = kzalloc(sizeof(*buffer), GFP_KERNEL);
- if (buffer == NULL) {
- if (mode == O_RDWR) {
- vfree(data->rbuffer->buffer);
- kfree(data->rbuffer);
- }
- kfree(data);
- err = -ENOMEM;
- goto __error;
- }
- buffer->len = (entry->c.text.write_size +
- (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1);
- buffer->buffer = vmalloc(buffer->len);
- if (buffer->buffer == NULL) {
- if (mode == O_RDWR) {
- vfree(data->rbuffer->buffer);
- kfree(data->rbuffer);
- }
- kfree(buffer);
- kfree(data);
- err = -ENOMEM;
- goto __error;
- }
- buffer->curr = buffer->buffer;
+ if (buffer == NULL)
+ goto __nomem;
data->wbuffer = buffer;
+ buffer->len = PAGE_SIZE;
+ buffer->buffer = kmalloc(buffer->len, GFP_KERNEL);
+ if (buffer->buffer == NULL)
+ goto __nomem;
}
break;
case SNDRV_INFO_CONTENT_DATA: /* data */
}
return 0;
+ __nomem:
+ if (data->rbuffer) {
+ kfree(data->rbuffer->buffer);
+ kfree(data->rbuffer);
+ }
+ if (data->wbuffer) {
+ kfree(data->wbuffer->buffer);
+ kfree(data->wbuffer);
+ }
+ kfree(data);
+ err = -ENOMEM;
__error:
module_put(entry->module);
__error1:
entry = data->entry;
switch (entry->content) {
case SNDRV_INFO_CONTENT_TEXT:
- if (mode == O_RDONLY || mode == O_RDWR) {
- vfree(data->rbuffer->buffer);
+ if (data->rbuffer) {
+ kfree(data->rbuffer->buffer);
kfree(data->rbuffer);
}
- if (mode == O_WRONLY || mode == O_RDWR) {
+ if (data->wbuffer) {
if (entry->c.text.write) {
entry->c.text.write(entry, data->wbuffer);
if (data->wbuffer->error) {
data->wbuffer->error);
}
}
- vfree(data->wbuffer->buffer);
+ kfree(data->wbuffer->buffer);
kfree(data->wbuffer);
}
break;
if (len <= 0 || buffer->stop || buffer->error)
return 1;
while (--len > 0) {
- c = *buffer->curr++;
+ c = buffer->buffer[buffer->curr++];
if (c == '\n') {
- if ((buffer->curr - buffer->buffer) >= (long)buffer->size) {
+ if (buffer->curr >= buffer->size)
buffer->stop = 1;
- }
break;
}
*line++ = c;
- if ((buffer->curr - buffer->buffer) >= (long)buffer->size) {
+ if (buffer->curr >= buffer->size) {
buffer->stop = 1;
break;
}
}
while (c != '\n' && !buffer->stop) {
- c = *buffer->curr++;
- if ((buffer->curr - buffer->buffer) >= (long)buffer->size) {
+ c = buffer->buffer[buffer->curr++];
+ if (buffer->curr >= buffer->size)
buffer->stop = 1;
- }
}
*line = '\0';
return 0;
}
+EXPORT_SYMBOL(snd_info_get_line);
+
/**
* snd_info_get_str - parse a string token
* @dest: the buffer to store the string token
return src;
}
+EXPORT_SYMBOL(snd_info_get_str);
+
/**
* snd_info_create_entry - create an info entry
* @name: the proc file name
return entry;
}
+EXPORT_SYMBOL(snd_info_create_module_entry);
+
/**
* snd_info_create_card_entry - create an info entry for the given card
* @card: the card instance
return entry;
}
+EXPORT_SYMBOL(snd_info_create_card_entry);
+
static int snd_info_dev_free_entry(struct snd_device *device)
{
struct snd_info_entry *entry = device->device_data;
return 0;
}
+EXPORT_SYMBOL(snd_card_proc_new);
+
/**
* snd_info_free_entry - release the info entry
* @entry: the info entry
kfree(entry);
}
+EXPORT_SYMBOL(snd_info_free_entry);
+
/**
* snd_info_register - register the info entry
* @entry: the info entry
return 0;
}
+EXPORT_SYMBOL(snd_info_register);
+
/**
* snd_info_unregister - de-register the info entry
* @entry: the info entry
return 0;
}
+EXPORT_SYMBOL(snd_info_unregister);
+
/*
*/
-static struct snd_info_entry *snd_info_version_entry = NULL;
+static struct snd_info_entry *snd_info_version_entry;
static void snd_info_version_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
{
entry = snd_info_create_module_entry(THIS_MODULE, "version", NULL);
if (entry == NULL)
return -ENOMEM;
- entry->c.text.read_size = 256;
entry->c.text.read = snd_info_version_read;
if (snd_info_register(entry) < 0) {
snd_info_free_entry(entry);
return 0;
}
+EXPORT_SYMBOL(snd_oss_info_register);
+
extern void snd_card_info_read_oss(struct snd_info_buffer *buffer);
static int snd_sndstat_show_strings(struct snd_info_buffer *buf, char *id, int dev)
memset(snd_sndstat_strings, 0, sizeof(snd_sndstat_strings));
if ((entry = snd_info_create_module_entry(THIS_MODULE, "sndstat", snd_oss_root)) != NULL) {
- entry->c.text.read_size = 2048;
entry->c.text.read = snd_sndstat_proc_read;
if (snd_info_register(entry) < 0) {
snd_info_free_entry(entry);
struct snd_shutdown_f_ops *next;
};
-unsigned int snd_cards_lock = 0; /* locked for registering/using */
-struct snd_card *snd_cards[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = NULL};
-DEFINE_RWLOCK(snd_card_rwlock);
+static unsigned int snd_cards_lock; /* locked for registering/using */
+struct snd_card *snd_cards[SNDRV_CARDS];
+EXPORT_SYMBOL(snd_cards);
+
+static DEFINE_MUTEX(snd_card_mutex);
#if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE)
int (*snd_mixer_oss_notify_callback)(struct snd_card *card, int free_flag);
+EXPORT_SYMBOL(snd_mixer_oss_notify_callback);
#endif
#ifdef CONFIG_PROC_FS
snd_printd("unable to create card entry\n");
return err;
}
- entry->c.text.read_size = PAGE_SIZE;
entry->c.text.read = snd_card_id_read;
if (snd_info_register(entry) < 0) {
snd_info_free_entry(entry);
strlcpy(card->id, xid, sizeof(card->id));
}
err = 0;
- write_lock(&snd_card_rwlock);
+ mutex_lock(&snd_card_mutex);
if (idx < 0) {
int idx2;
for (idx2 = 0; idx2 < SNDRV_CARDS; idx2++)
else
err = -ENODEV;
if (idx < 0 || err < 0) {
- write_unlock(&snd_card_rwlock);
+ mutex_unlock(&snd_card_mutex);
snd_printk(KERN_ERR "cannot find the slot for index %d (range 0-%i)\n", idx, snd_ecards_limit - 1);
goto __error;
}
snd_cards_lock |= 1 << idx; /* lock it */
- write_unlock(&snd_card_rwlock);
+ mutex_unlock(&snd_card_mutex);
card->number = idx;
card->module = module;
INIT_LIST_HEAD(&card->devices);
return NULL;
}
+EXPORT_SYMBOL(snd_card_new);
+
+/* return non-zero if a card is already locked */
+int snd_card_locked(int card)
+{
+ int locked;
+
+ mutex_lock(&snd_card_mutex);
+ locked = snd_cards_lock & (1 << card);
+ mutex_unlock(&snd_card_mutex);
+ return locked;
+}
+
static loff_t snd_disconnect_llseek(struct file *file, loff_t offset, int orig)
{
return -ENODEV;
spin_unlock(&card->files_lock);
/* phase 1: disable fops (user space) operations for ALSA API */
- write_lock(&snd_card_rwlock);
+ mutex_lock(&snd_card_mutex);
snd_cards[card->number] = NULL;
- write_unlock(&snd_card_rwlock);
+ mutex_unlock(&snd_card_mutex);
/* phase 2: replace file->f_op with special dummy operations */
return 0;
}
+EXPORT_SYMBOL(snd_card_disconnect);
+
/**
* snd_card_free - frees given soundcard structure
* @card: soundcard structure
if (card == NULL)
return -EINVAL;
- write_lock(&snd_card_rwlock);
+ mutex_lock(&snd_card_mutex);
snd_cards[card->number] = NULL;
- write_unlock(&snd_card_rwlock);
+ mutex_unlock(&snd_card_mutex);
#ifdef CONFIG_PM
wake_up(&card->power_sleep);
card->s_f_ops = s_f_ops->next;
kfree(s_f_ops);
}
- write_lock(&snd_card_rwlock);
+ mutex_lock(&snd_card_mutex);
snd_cards_lock &= ~(1 << card->number);
- write_unlock(&snd_card_rwlock);
+ mutex_unlock(&snd_card_mutex);
kfree(card);
return 0;
}
+EXPORT_SYMBOL(snd_card_free);
+
static void snd_card_free_thread(void * __card)
{
struct snd_card *card = __card;
return -EFAULT;
}
+EXPORT_SYMBOL(snd_card_free_in_thread);
+
static void choose_default_id(struct snd_card *card)
{
int i, len, idx_flag = 0, loops = SNDRV_CARDS;
snd_assert(card != NULL, return -EINVAL);
if ((err = snd_device_register_all(card)) < 0)
return err;
- write_lock(&snd_card_rwlock);
+ mutex_lock(&snd_card_mutex);
if (snd_cards[card->number]) {
/* already registered */
- write_unlock(&snd_card_rwlock);
+ mutex_unlock(&snd_card_mutex);
return 0;
}
if (card->id[0] == '\0')
choose_default_id(card);
snd_cards[card->number] = card;
- write_unlock(&snd_card_rwlock);
+ mutex_unlock(&snd_card_mutex);
init_info_for_card(card);
#if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE)
if (snd_mixer_oss_notify_callback)
return 0;
}
+EXPORT_SYMBOL(snd_card_register);
+
#ifdef CONFIG_PROC_FS
-static struct snd_info_entry *snd_card_info_entry = NULL;
+static struct snd_info_entry *snd_card_info_entry;
static void snd_card_info_read(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
struct snd_card *card;
for (idx = count = 0; idx < SNDRV_CARDS; idx++) {
- read_lock(&snd_card_rwlock);
+ mutex_lock(&snd_card_mutex);
if ((card = snd_cards[idx]) != NULL) {
count++;
snd_iprintf(buffer, "%2i [%-15s]: %s - %s\n",
snd_iprintf(buffer, " %s\n",
card->longname);
}
- read_unlock(&snd_card_rwlock);
+ mutex_unlock(&snd_card_mutex);
}
if (!count)
snd_iprintf(buffer, "--- no soundcards ---\n");
struct snd_card *card;
for (idx = count = 0; idx < SNDRV_CARDS; idx++) {
- read_lock(&snd_card_rwlock);
+ mutex_lock(&snd_card_mutex);
if ((card = snd_cards[idx]) != NULL) {
count++;
snd_iprintf(buffer, "%s\n", card->longname);
}
- read_unlock(&snd_card_rwlock);
+ mutex_unlock(&snd_card_mutex);
}
if (!count) {
snd_iprintf(buffer, "--- no soundcards ---\n");
struct snd_card *card;
for (idx = 0; idx < SNDRV_CARDS; idx++) {
- read_lock(&snd_card_rwlock);
+ mutex_lock(&snd_card_mutex);
if ((card = snd_cards[idx]) != NULL)
snd_iprintf(buffer, "%2i %s\n",
idx, card->module->name);
- read_unlock(&snd_card_rwlock);
+ mutex_unlock(&snd_card_mutex);
}
}
#endif
entry = snd_info_create_module_entry(THIS_MODULE, "cards", NULL);
if (! entry)
return -ENOMEM;
- entry->c.text.read_size = PAGE_SIZE;
entry->c.text.read = snd_card_info_read;
if (snd_info_register(entry) < 0) {
snd_info_free_entry(entry);
#ifdef MODULE
entry = snd_info_create_module_entry(THIS_MODULE, "modules", NULL);
if (entry) {
- entry->c.text.read_size = PAGE_SIZE;
entry->c.text.read = snd_card_module_info_read;
if (snd_info_register(entry) < 0)
snd_info_free_entry(entry);
return 0;
}
+EXPORT_SYMBOL(snd_component_add);
+
/**
* snd_card_file_add - add the file to the file list of the card
* @card: soundcard structure
return 0;
}
+EXPORT_SYMBOL(snd_card_file_add);
+
/**
* snd_card_file_remove - remove the file from the file list
* @card: soundcard structure
return 0;
}
+EXPORT_SYMBOL(snd_card_file_remove);
+
#ifdef CONFIG_PM
/**
* snd_power_wait - wait until the power-state is changed.
return result;
}
+EXPORT_SYMBOL(snd_power_wait);
#endif /* CONFIG_PM */
release_dma_lock(flags);
}
+EXPORT_SYMBOL(snd_dma_program);
+
/**
* snd_dma_disable - stop the ISA DMA transfer
* @dma: the dma number
release_dma_lock(flags);
}
+EXPORT_SYMBOL(snd_dma_disable);
+
/**
* snd_dma_pointer - return the current pointer to DMA transfer buffer in bytes
* @dma: the dma number
else
return size - result;
}
+
+EXPORT_SYMBOL(snd_dma_pointer);
*/
#include <linux/config.h>
+#include <linux/module.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#endif
}
+EXPORT_SYMBOL(copy_to_user_fromio);
+
/**
* copy_from_user_toio - copy data from user-space to mmio-space
* @dst: the destination pointer on mmio-space
return 0;
#endif
}
+
+EXPORT_SYMBOL(copy_from_user_toio);
}
}
+EXPORT_SYMBOL(release_and_free_resource);
+
#ifdef CONFIG_SND_VERBOSE_PRINTK
void snd_verbose_printk(const char *file, int line, const char *format, ...)
{
vprintk(format, args);
va_end(args);
}
+
+EXPORT_SYMBOL(snd_verbose_printk);
#endif
#if defined(CONFIG_SND_DEBUG) && defined(CONFIG_SND_VERBOSE_PRINTK)
va_end(args);
}
+
+EXPORT_SYMBOL(snd_verbose_printd);
#endif
return;
entry->content = SNDRV_INFO_CONTENT_TEXT;
entry->mode = S_IFREG | S_IRUGO | S_IWUSR;
- entry->c.text.read_size = 8192;
entry->c.text.read = snd_mixer_oss_proc_read;
- entry->c.text.write_size = 8192;
entry->c.text.write = snd_mixer_oss_proc_write;
entry->private_data = mixer;
if (snd_info_register(entry) < 0) {
#define OSS_ALSAEMULVER _SIOR ('M', 249, int)
-static int dsp_map[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = 0};
+static int dsp_map[SNDRV_CARDS];
static int adsp_map[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = 1};
static int nonblock_open = 1;
set_fs(fs);
}
+/*
+ * helper functions to process hw_params
+ */
+static int snd_interval_refine_min(struct snd_interval *i, unsigned int min, int openmin)
+{
+ int changed = 0;
+ if (i->min < min) {
+ i->min = min;
+ i->openmin = openmin;
+ changed = 1;
+ } else if (i->min == min && !i->openmin && openmin) {
+ i->openmin = 1;
+ changed = 1;
+ }
+ if (i->integer) {
+ if (i->openmin) {
+ i->min++;
+ i->openmin = 0;
+ }
+ }
+ if (snd_interval_checkempty(i)) {
+ snd_interval_none(i);
+ return -EINVAL;
+ }
+ return changed;
+}
+
+static int snd_interval_refine_max(struct snd_interval *i, unsigned int max, int openmax)
+{
+ int changed = 0;
+ if (i->max > max) {
+ i->max = max;
+ i->openmax = openmax;
+ changed = 1;
+ } else if (i->max == max && !i->openmax && openmax) {
+ i->openmax = 1;
+ changed = 1;
+ }
+ if (i->integer) {
+ if (i->openmax) {
+ i->max--;
+ i->openmax = 0;
+ }
+ }
+ if (snd_interval_checkempty(i)) {
+ snd_interval_none(i);
+ return -EINVAL;
+ }
+ return changed;
+}
+
+static int snd_interval_refine_set(struct snd_interval *i, unsigned int val)
+{
+ struct snd_interval t;
+ t.empty = 0;
+ t.min = t.max = val;
+ t.openmin = t.openmax = 0;
+ t.integer = 1;
+ return snd_interval_refine(i, &t);
+}
+
+/**
+ * snd_pcm_hw_param_value_min
+ * @params: the hw_params instance
+ * @var: parameter to retrieve
+ * @dir: pointer to the direction (-1,0,1) or NULL
+ *
+ * Return the minimum value for field PAR.
+ */
+static unsigned int
+snd_pcm_hw_param_value_min(const struct snd_pcm_hw_params *params,
+ snd_pcm_hw_param_t var, int *dir)
+{
+ if (hw_is_mask(var)) {
+ if (dir)
+ *dir = 0;
+ return snd_mask_min(hw_param_mask_c(params, var));
+ }
+ if (hw_is_interval(var)) {
+ const struct snd_interval *i = hw_param_interval_c(params, var);
+ if (dir)
+ *dir = i->openmin;
+ return snd_interval_min(i);
+ }
+ return -EINVAL;
+}
+
+/**
+ * snd_pcm_hw_param_value_max
+ * @params: the hw_params instance
+ * @var: parameter to retrieve
+ * @dir: pointer to the direction (-1,0,1) or NULL
+ *
+ * Return the maximum value for field PAR.
+ */
+static unsigned int
+snd_pcm_hw_param_value_max(const struct snd_pcm_hw_params *params,
+ snd_pcm_hw_param_t var, int *dir)
+{
+ if (hw_is_mask(var)) {
+ if (dir)
+ *dir = 0;
+ return snd_mask_max(hw_param_mask_c(params, var));
+ }
+ if (hw_is_interval(var)) {
+ const struct snd_interval *i = hw_param_interval_c(params, var);
+ if (dir)
+ *dir = - (int) i->openmax;
+ return snd_interval_max(i);
+ }
+ return -EINVAL;
+}
+
+static int _snd_pcm_hw_param_mask(struct snd_pcm_hw_params *params,
+ snd_pcm_hw_param_t var,
+ const struct snd_mask *val)
+{
+ int changed;
+ changed = snd_mask_refine(hw_param_mask(params, var), val);
+ if (changed) {
+ params->cmask |= 1 << var;
+ params->rmask |= 1 << var;
+ }
+ return changed;
+}
+
+static int snd_pcm_hw_param_mask(struct snd_pcm_substream *pcm,
+ struct snd_pcm_hw_params *params,
+ snd_pcm_hw_param_t var,
+ const struct snd_mask *val)
+{
+ int changed = _snd_pcm_hw_param_mask(params, var, val);
+ if (changed < 0)
+ return changed;
+ if (params->rmask) {
+ int err = snd_pcm_hw_refine(pcm, params);
+ if (err < 0)
+ return err;
+ }
+ return 0;
+}
+
+static int _snd_pcm_hw_param_min(struct snd_pcm_hw_params *params,
+ snd_pcm_hw_param_t var, unsigned int val,
+ int dir)
+{
+ int changed;
+ int open = 0;
+ if (dir) {
+ if (dir > 0) {
+ open = 1;
+ } else if (dir < 0) {
+ if (val > 0) {
+ open = 1;
+ val--;
+ }
+ }
+ }
+ if (hw_is_mask(var))
+ changed = snd_mask_refine_min(hw_param_mask(params, var),
+ val + !!open);
+ else if (hw_is_interval(var))
+ changed = snd_interval_refine_min(hw_param_interval(params, var),
+ val, open);
+ else
+ return -EINVAL;
+ if (changed) {
+ params->cmask |= 1 << var;
+ params->rmask |= 1 << var;
+ }
+ return changed;
+}
+
+/**
+ * snd_pcm_hw_param_min
+ * @pcm: PCM instance
+ * @params: the hw_params instance
+ * @var: parameter to retrieve
+ * @val: minimal value
+ * @dir: pointer to the direction (-1,0,1) or NULL
+ *
+ * Inside configuration space defined by PARAMS remove from PAR all
+ * values < VAL. Reduce configuration space accordingly.
+ * Return new minimum or -EINVAL if the configuration space is empty
+ */
+static int snd_pcm_hw_param_min(struct snd_pcm_substream *pcm,
+ struct snd_pcm_hw_params *params,
+ snd_pcm_hw_param_t var, unsigned int val,
+ int *dir)
+{
+ int changed = _snd_pcm_hw_param_min(params, var, val, dir ? *dir : 0);
+ if (changed < 0)
+ return changed;
+ if (params->rmask) {
+ int err = snd_pcm_hw_refine(pcm, params);
+ if (err < 0)
+ return err;
+ }
+ return snd_pcm_hw_param_value_min(params, var, dir);
+}
+
+static int _snd_pcm_hw_param_max(struct snd_pcm_hw_params *params,
+ snd_pcm_hw_param_t var, unsigned int val,
+ int dir)
+{
+ int changed;
+ int open = 0;
+ if (dir) {
+ if (dir < 0) {
+ open = 1;
+ } else if (dir > 0) {
+ open = 1;
+ val++;
+ }
+ }
+ if (hw_is_mask(var)) {
+ if (val == 0 && open) {
+ snd_mask_none(hw_param_mask(params, var));
+ changed = -EINVAL;
+ } else
+ changed = snd_mask_refine_max(hw_param_mask(params, var),
+ val - !!open);
+ } else if (hw_is_interval(var))
+ changed = snd_interval_refine_max(hw_param_interval(params, var),
+ val, open);
+ else
+ return -EINVAL;
+ if (changed) {
+ params->cmask |= 1 << var;
+ params->rmask |= 1 << var;
+ }
+ return changed;
+}
+
+/**
+ * snd_pcm_hw_param_max
+ * @pcm: PCM instance
+ * @params: the hw_params instance
+ * @var: parameter to retrieve
+ * @val: maximal value
+ * @dir: pointer to the direction (-1,0,1) or NULL
+ *
+ * Inside configuration space defined by PARAMS remove from PAR all
+ * values >= VAL + 1. Reduce configuration space accordingly.
+ * Return new maximum or -EINVAL if the configuration space is empty
+ */
+static int snd_pcm_hw_param_max(struct snd_pcm_substream *pcm,
+ struct snd_pcm_hw_params *params,
+ snd_pcm_hw_param_t var, unsigned int val,
+ int *dir)
+{
+ int changed = _snd_pcm_hw_param_max(params, var, val, dir ? *dir : 0);
+ if (changed < 0)
+ return changed;
+ if (params->rmask) {
+ int err = snd_pcm_hw_refine(pcm, params);
+ if (err < 0)
+ return err;
+ }
+ return snd_pcm_hw_param_value_max(params, var, dir);
+}
+
+static int boundary_sub(int a, int adir,
+ int b, int bdir,
+ int *c, int *cdir)
+{
+ adir = adir < 0 ? -1 : (adir > 0 ? 1 : 0);
+ bdir = bdir < 0 ? -1 : (bdir > 0 ? 1 : 0);
+ *c = a - b;
+ *cdir = adir - bdir;
+ if (*cdir == -2) {
+ (*c)--;
+ } else if (*cdir == 2) {
+ (*c)++;
+ }
+ return 0;
+}
+
+static int boundary_lt(unsigned int a, int adir,
+ unsigned int b, int bdir)
+{
+ if (adir < 0) {
+ a--;
+ adir = 1;
+ } else if (adir > 0)
+ adir = 1;
+ if (bdir < 0) {
+ b--;
+ bdir = 1;
+ } else if (bdir > 0)
+ bdir = 1;
+ return a < b || (a == b && adir < bdir);
+}
+
+/* Return 1 if min is nearer to best than max */
+static int boundary_nearer(int min, int mindir,
+ int best, int bestdir,
+ int max, int maxdir)
+{
+ int dmin, dmindir;
+ int dmax, dmaxdir;
+ boundary_sub(best, bestdir, min, mindir, &dmin, &dmindir);
+ boundary_sub(max, maxdir, best, bestdir, &dmax, &dmaxdir);
+ return boundary_lt(dmin, dmindir, dmax, dmaxdir);
+}
+
+/**
+ * snd_pcm_hw_param_near
+ * @pcm: PCM instance
+ * @params: the hw_params instance
+ * @var: parameter to retrieve
+ * @best: value to set
+ * @dir: pointer to the direction (-1,0,1) or NULL
+ *
+ * Inside configuration space defined by PARAMS set PAR to the available value
+ * nearest to VAL. Reduce configuration space accordingly.
+ * This function cannot be called for SNDRV_PCM_HW_PARAM_ACCESS,
+ * SNDRV_PCM_HW_PARAM_FORMAT, SNDRV_PCM_HW_PARAM_SUBFORMAT.
+ * Return the value found.
+ */
+static int snd_pcm_hw_param_near(struct snd_pcm_substream *pcm,
+ struct snd_pcm_hw_params *params,
+ snd_pcm_hw_param_t var, unsigned int best,
+ int *dir)
+{
+ struct snd_pcm_hw_params *save = NULL;
+ int v;
+ unsigned int saved_min;
+ int last = 0;
+ int min, max;
+ int mindir, maxdir;
+ int valdir = dir ? *dir : 0;
+ /* FIXME */
+ if (best > INT_MAX)
+ best = INT_MAX;
+ min = max = best;
+ mindir = maxdir = valdir;
+ if (maxdir > 0)
+ maxdir = 0;
+ else if (maxdir == 0)
+ maxdir = -1;
+ else {
+ maxdir = 1;
+ max--;
+ }
+ save = kmalloc(sizeof(*save), GFP_KERNEL);
+ if (save == NULL)
+ return -ENOMEM;
+ *save = *params;
+ saved_min = min;
+ min = snd_pcm_hw_param_min(pcm, params, var, min, &mindir);
+ if (min >= 0) {
+ struct snd_pcm_hw_params *params1;
+ if (max < 0)
+ goto _end;
+ if ((unsigned int)min == saved_min && mindir == valdir)
+ goto _end;
+ params1 = kmalloc(sizeof(*params1), GFP_KERNEL);
+ if (params1 == NULL) {
+ kfree(save);
+ return -ENOMEM;
+ }
+ *params1 = *save;
+ max = snd_pcm_hw_param_max(pcm, params1, var, max, &maxdir);
+ if (max < 0) {
+ kfree(params1);
+ goto _end;
+ }
+ if (boundary_nearer(max, maxdir, best, valdir, min, mindir)) {
+ *params = *params1;
+ last = 1;
+ }
+ kfree(params1);
+ } else {
+ *params = *save;
+ max = snd_pcm_hw_param_max(pcm, params, var, max, &maxdir);
+ snd_assert(max >= 0, return -EINVAL);
+ last = 1;
+ }
+ _end:
+ kfree(save);
+ if (last)
+ v = snd_pcm_hw_param_last(pcm, params, var, dir);
+ else
+ v = snd_pcm_hw_param_first(pcm, params, var, dir);
+ snd_assert(v >= 0, return -EINVAL);
+ return v;
+}
+
+static int _snd_pcm_hw_param_set(struct snd_pcm_hw_params *params,
+ snd_pcm_hw_param_t var, unsigned int val,
+ int dir)
+{
+ int changed;
+ if (hw_is_mask(var)) {
+ struct snd_mask *m = hw_param_mask(params, var);
+ if (val == 0 && dir < 0) {
+ changed = -EINVAL;
+ snd_mask_none(m);
+ } else {
+ if (dir > 0)
+ val++;
+ else if (dir < 0)
+ val--;
+ changed = snd_mask_refine_set(hw_param_mask(params, var), val);
+ }
+ } else if (hw_is_interval(var)) {
+ struct snd_interval *i = hw_param_interval(params, var);
+ if (val == 0 && dir < 0) {
+ changed = -EINVAL;
+ snd_interval_none(i);
+ } else if (dir == 0)
+ changed = snd_interval_refine_set(i, val);
+ else {
+ struct snd_interval t;
+ t.openmin = 1;
+ t.openmax = 1;
+ t.empty = 0;
+ t.integer = 0;
+ if (dir < 0) {
+ t.min = val - 1;
+ t.max = val;
+ } else {
+ t.min = val;
+ t.max = val+1;
+ }
+ changed = snd_interval_refine(i, &t);
+ }
+ } else
+ return -EINVAL;
+ if (changed) {
+ params->cmask |= 1 << var;
+ params->rmask |= 1 << var;
+ }
+ return changed;
+}
+
+/**
+ * snd_pcm_hw_param_set
+ * @pcm: PCM instance
+ * @params: the hw_params instance
+ * @var: parameter to retrieve
+ * @val: value to set
+ * @dir: pointer to the direction (-1,0,1) or NULL
+ *
+ * Inside configuration space defined by PARAMS remove from PAR all
+ * values != VAL. Reduce configuration space accordingly.
+ * Return VAL or -EINVAL if the configuration space is empty
+ */
+static int snd_pcm_hw_param_set(struct snd_pcm_substream *pcm,
+ struct snd_pcm_hw_params *params,
+ snd_pcm_hw_param_t var, unsigned int val,
+ int dir)
+{
+ int changed = _snd_pcm_hw_param_set(params, var, val, dir);
+ if (changed < 0)
+ return changed;
+ if (params->rmask) {
+ int err = snd_pcm_hw_refine(pcm, params);
+ if (err < 0)
+ return err;
+ }
+ return snd_pcm_hw_param_value(params, var, NULL);
+}
+
+static int _snd_pcm_hw_param_setinteger(struct snd_pcm_hw_params *params,
+ snd_pcm_hw_param_t var)
+{
+ int changed;
+ changed = snd_interval_setinteger(hw_param_interval(params, var));
+ if (changed) {
+ params->cmask |= 1 << var;
+ params->rmask |= 1 << var;
+ }
+ return changed;
+}
+
+/*
+ * plugin
+ */
+
#ifdef CONFIG_SND_PCM_OSS_PLUGINS
static int snd_pcm_oss_plugin_clear(struct snd_pcm_substream *substream)
{
oss_buffer_size = snd_pcm_plug_client_size(substream,
snd_pcm_hw_param_value_max(slave_params, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, NULL)) * oss_frame_size;
oss_buffer_size = 1 << ld2(oss_buffer_size);
- if (atomic_read(&runtime->mmap_count)) {
+ if (atomic_read(&substream->mmap_count)) {
if (oss_buffer_size > runtime->oss.mmap_bytes)
oss_buffer_size = runtime->oss.mmap_bytes;
}
goto failure;
}
- if (atomic_read(&runtime->mmap_count))
+ if (atomic_read(&substream->mmap_count))
direct = 1;
else
direct = substream->oss.setup.direct;
_snd_pcm_hw_param_setinteger(sparams, SNDRV_PCM_HW_PARAM_PERIODS);
_snd_pcm_hw_param_min(sparams, SNDRV_PCM_HW_PARAM_PERIODS, 2, 0);
snd_mask_none(&mask);
- if (atomic_read(&runtime->mmap_count))
+ if (atomic_read(&substream->mmap_count))
snd_mask_set(&mask, SNDRV_PCM_ACCESS_MMAP_INTERLEAVED);
else {
snd_mask_set(&mask, SNDRV_PCM_ACCESS_RW_INTERLEAVED);
} else {
sw_params->start_threshold = runtime->boundary;
}
- if (atomic_read(&runtime->mmap_count) || substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ if (atomic_read(&substream->mmap_count) ||
+ substream->stream == SNDRV_PCM_STREAM_CAPTURE)
sw_params->stop_threshold = runtime->boundary;
else
sw_params->stop_threshold = runtime->buffer_size;
sw_params->avail_min = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
1 : runtime->period_size;
sw_params->xfer_align = 1;
- if (atomic_read(&runtime->mmap_count) ||
+ if (atomic_read(&substream->mmap_count) ||
substream->oss.setup.nosilence) {
sw_params->silence_threshold = 0;
sw_params->silence_size = 0;
ssize_t tmp;
struct snd_pcm_runtime *runtime = substream->runtime;
- if (atomic_read(&runtime->mmap_count))
+ if (atomic_read(&substream->mmap_count))
return -ENXIO;
if ((tmp = snd_pcm_oss_make_ready(substream)) < 0)
if (runtime->oss.period_ptr == 0 ||
runtime->oss.period_ptr == runtime->oss.buffer_used)
runtime->oss.buffer_used = 0;
- else if ((substream->ffile->f_flags & O_NONBLOCK) != 0)
+ else if ((substream->f_flags & O_NONBLOCK) != 0)
return xfer > 0 ? xfer : -EAGAIN;
}
} else {
buf += tmp;
bytes -= tmp;
xfer += tmp;
- if ((substream->ffile->f_flags & O_NONBLOCK) != 0 &&
+ if ((substream->f_flags & O_NONBLOCK) != 0 &&
tmp != runtime->oss.period_bytes)
break;
}
ssize_t tmp;
struct snd_pcm_runtime *runtime = substream->runtime;
- if (atomic_read(&runtime->mmap_count))
+ if (atomic_read(&substream->mmap_count))
return -ENXIO;
if ((tmp = snd_pcm_oss_make_ready(substream)) < 0)
substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK];
if (substream != NULL) {
runtime = substream->runtime;
- if (atomic_read(&runtime->mmap_count))
+ if (atomic_read(&substream->mmap_count))
goto __direct;
if ((err = snd_pcm_oss_make_ready(substream)) < 0)
return err;
* finish sync: drain the buffer
*/
__direct:
- saved_f_flags = substream->ffile->f_flags;
- substream->ffile->f_flags &= ~O_NONBLOCK;
+ saved_f_flags = substream->f_flags;
+ substream->f_flags &= ~O_NONBLOCK;
err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DRAIN, NULL);
- substream->ffile->f_flags = saved_f_flags;
+ substream->f_flags = saved_f_flags;
if (err < 0)
return err;
runtime->oss.prepare = 1;
if ((err = snd_pcm_oss_get_active_substream(pcm_oss_file, &substream)) < 0)
return err;
- if (atomic_read(&substream->runtime->mmap_count))
+ if (atomic_read(&substream->mmap_count))
direct = 1;
else
direct = substream->oss.setup.direct;
if (trigger & PCM_ENABLE_OUTPUT) {
if (runtime->oss.trigger)
goto _skip1;
- if (atomic_read(&psubstream->runtime->mmap_count))
+ if (atomic_read(&psubstream->mmap_count))
snd_pcm_oss_simulate_fill(psubstream, runtime->hw_ptr_interrupt);
runtime->oss.trigger = 1;
runtime->start_threshold = 1;
if (err < 0)
return err;
info.ptr = snd_pcm_oss_bytes(substream, runtime->status->hw_ptr % runtime->buffer_size);
- if (atomic_read(&runtime->mmap_count)) {
+ if (atomic_read(&substream->mmap_count)) {
snd_pcm_sframes_t n;
n = (delay = runtime->hw_ptr_interrupt) - runtime->oss.prev_hw_ptr_interrupt;
if (n < 0)
substream->oss.oss = 1;
substream->oss.setup = *setup;
if (setup->nonblock)
- substream->ffile->f_flags |= O_NONBLOCK;
+ substream->f_flags |= O_NONBLOCK;
else if (setup->block)
- substream->ffile->f_flags &= ~O_NONBLOCK;
+ substream->f_flags &= ~O_NONBLOCK;
runtime = substream->runtime;
runtime->oss.params = 1;
runtime->oss.trigger = 1;
(pcm->info_flags & SNDRV_PCM_INFO_HALF_DUPLEX))
f_mode = FMODE_WRITE;
+ file->f_flags &= ~O_APPEND;
for (idx = 0; idx < 2; idx++) {
if (setup[idx].disable)
continue;
substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE];
if (substream == NULL)
return -ENXIO;
+ substream->f_flags = file->f_flags & O_NONBLOCK;
#ifndef OSS_DEBUG
return snd_pcm_oss_read1(substream, buf, count);
#else
substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK];
if (substream == NULL)
return -ENXIO;
+ substream->f_flags = file->f_flags & O_NONBLOCK;
result = snd_pcm_oss_write1(substream, buf, count);
#ifdef OSS_DEBUG
printk("pcm_oss: write %li bytes (wrote %li bytes)\n", (long)count, (long)result);
static int snd_pcm_oss_playback_ready(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
- if (atomic_read(&runtime->mmap_count))
+ if (atomic_read(&substream->mmap_count))
return runtime->oss.prev_hw_ptr_interrupt != runtime->hw_ptr_interrupt;
else
return snd_pcm_playback_avail(runtime) >= runtime->oss.period_frames;
static int snd_pcm_oss_capture_ready(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
- if (atomic_read(&runtime->mmap_count))
+ if (atomic_read(&substream->mmap_count))
return runtime->oss.prev_hw_ptr_interrupt != runtime->hw_ptr_interrupt;
else
return snd_pcm_capture_avail(runtime) >= runtime->oss.period_frames;
if ((entry = snd_info_create_card_entry(pcm->card, "oss", pstr->proc_root)) != NULL) {
entry->content = SNDRV_INFO_CONTENT_TEXT;
entry->mode = S_IFREG | S_IRUGO | S_IWUSR;
- entry->c.text.read_size = 8192;
entry->c.text.read = snd_pcm_oss_proc_read;
- entry->c.text.write_size = 8192;
entry->c.text.write = snd_pcm_oss_proc_write;
entry->private_data = pstr;
if (snd_info_register(entry) < 0) {
snd_iprintf(buffer, "closed\n");
return;
}
- snd_pcm_stream_lock_irq(substream);
if (runtime->status->state == SNDRV_PCM_STATE_OPEN) {
snd_iprintf(buffer, "no setup\n");
- snd_pcm_stream_unlock_irq(substream);
return;
}
snd_iprintf(buffer, "access: %s\n", snd_pcm_access_name(runtime->access));
snd_iprintf(buffer, "OSS period frames: %lu\n", (unsigned long)runtime->oss.period_frames);
}
#endif
- snd_pcm_stream_unlock_irq(substream);
}
static void snd_pcm_substream_proc_sw_params_read(struct snd_info_entry *entry,
snd_iprintf(buffer, "closed\n");
return;
}
- snd_pcm_stream_lock_irq(substream);
if (runtime->status->state == SNDRV_PCM_STATE_OPEN) {
snd_iprintf(buffer, "no setup\n");
- snd_pcm_stream_unlock_irq(substream);
return;
}
snd_iprintf(buffer, "tstamp_mode: %s\n", snd_pcm_tstamp_mode_name(runtime->tstamp_mode));
snd_iprintf(buffer, "silence_threshold: %lu\n", runtime->silence_threshold);
snd_iprintf(buffer, "silence_size: %lu\n", runtime->silence_size);
snd_iprintf(buffer, "boundary: %lu\n", runtime->boundary);
- snd_pcm_stream_unlock_irq(substream);
}
static void snd_pcm_substream_proc_status_read(struct snd_info_entry *entry,
pstr->proc_root = entry;
if ((entry = snd_info_create_card_entry(pcm->card, "info", pstr->proc_root)) != NULL) {
- snd_info_set_text_ops(entry, pstr, 256, snd_pcm_stream_proc_info_read);
+ snd_info_set_text_ops(entry, pstr, snd_pcm_stream_proc_info_read);
if (snd_info_register(entry) < 0) {
snd_info_free_entry(entry);
entry = NULL;
#ifdef CONFIG_SND_PCM_XRUN_DEBUG
if ((entry = snd_info_create_card_entry(pcm->card, "xrun_debug",
pstr->proc_root)) != NULL) {
- entry->c.text.read_size = 64;
entry->c.text.read = snd_pcm_xrun_debug_read;
- entry->c.text.write_size = 64;
entry->c.text.write = snd_pcm_xrun_debug_write;
entry->mode |= S_IWUSR;
entry->private_data = pstr;
substream->proc_root = entry;
if ((entry = snd_info_create_card_entry(card, "info", substream->proc_root)) != NULL) {
- snd_info_set_text_ops(entry, substream, 256, snd_pcm_substream_proc_info_read);
+ snd_info_set_text_ops(entry, substream,
+ snd_pcm_substream_proc_info_read);
if (snd_info_register(entry) < 0) {
snd_info_free_entry(entry);
entry = NULL;
substream->proc_info_entry = entry;
if ((entry = snd_info_create_card_entry(card, "hw_params", substream->proc_root)) != NULL) {
- snd_info_set_text_ops(entry, substream, 256, snd_pcm_substream_proc_hw_params_read);
+ snd_info_set_text_ops(entry, substream,
+ snd_pcm_substream_proc_hw_params_read);
if (snd_info_register(entry) < 0) {
snd_info_free_entry(entry);
entry = NULL;
substream->proc_hw_params_entry = entry;
if ((entry = snd_info_create_card_entry(card, "sw_params", substream->proc_root)) != NULL) {
- snd_info_set_text_ops(entry, substream, 256, snd_pcm_substream_proc_sw_params_read);
+ snd_info_set_text_ops(entry, substream,
+ snd_pcm_substream_proc_sw_params_read);
if (snd_info_register(entry) < 0) {
snd_info_free_entry(entry);
entry = NULL;
substream->proc_sw_params_entry = entry;
if ((entry = snd_info_create_card_entry(card, "status", substream->proc_root)) != NULL) {
- snd_info_set_text_ops(entry, substream, 256, snd_pcm_substream_proc_status_read);
+ snd_info_set_text_ops(entry, substream,
+ snd_pcm_substream_proc_status_read);
if (snd_info_register(entry) < 0) {
snd_info_free_entry(entry);
entry = NULL;
INIT_LIST_HEAD(&substream->self_group.substreams);
list_add_tail(&substream->link_list, &substream->self_group.substreams);
spin_lock_init(&substream->timer_lock);
+ atomic_set(&substream->mmap_count, 0);
prev = substream;
}
return 0;
}
+EXPORT_SYMBOL(snd_pcm_new_stream);
+
/**
* snd_pcm_new - create a new PCM instance
* @card: the card instance
return 0;
}
+EXPORT_SYMBOL(snd_pcm_new);
+
static void snd_pcm_free_stream(struct snd_pcm_str * pstr)
{
struct snd_pcm_substream *substream, *substream_next;
return -EINVAL;
}
+ if (file->f_flags & O_APPEND) {
+ if (prefer_subdevice < 0) {
+ if (pstr->substream_count > 1)
+ return -EINVAL; /* must be unique */
+ substream = pstr->substream;
+ } else {
+ for (substream = pstr->substream; substream;
+ substream = substream->next)
+ if (substream->number == prefer_subdevice)
+ break;
+ }
+ if (! substream)
+ return -ENODEV;
+ if (! SUBSTREAM_BUSY(substream))
+ return -EBADFD;
+ substream->ref_count++;
+ *rsubstream = substream;
+ return 0;
+ }
+
if (prefer_subdevice >= 0) {
for (substream = pstr->substream; substream; substream = substream->next)
if (!SUBSTREAM_BUSY(substream) && substream->number == prefer_subdevice)
memset((void*)runtime->control, 0, size);
init_waitqueue_head(&runtime->sleep);
- atomic_set(&runtime->mmap_count, 0);
init_timer(&runtime->tick_timer);
runtime->tick_timer.function = snd_pcm_tick_timer_func;
runtime->tick_timer.data = (unsigned long) substream;
substream->runtime = runtime;
substream->private_data = pcm->private_data;
- substream->ffile = file;
+ substream->ref_count = 1;
+ substream->f_flags = file->f_flags;
pstr->substream_opened++;
*rsubstream = substream;
return 0;
void snd_pcm_detach_substream(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime;
- substream->file = NULL;
+
runtime = substream->runtime;
snd_assert(runtime != NULL, return);
if (runtime->private_free != NULL)
return 0;
}
+EXPORT_SYMBOL(snd_pcm_notify);
+
#ifdef CONFIG_PROC_FS
/*
* Info interface
mutex_unlock(®ister_mutex);
}
-static struct snd_info_entry *snd_pcm_proc_entry = NULL;
+static struct snd_info_entry *snd_pcm_proc_entry;
static void snd_pcm_proc_init(void)
{
struct snd_info_entry *entry;
if ((entry = snd_info_create_module_entry(THIS_MODULE, "pcm", NULL)) != NULL) {
- snd_info_set_text_ops(entry, NULL, SNDRV_CARDS * SNDRV_PCM_DEVICES * 128,
- snd_pcm_proc_read);
+ snd_info_set_text_ops(entry, NULL, snd_pcm_proc_read);
if (snd_info_register(entry) < 0) {
snd_info_free_entry(entry);
entry = NULL;
module_init(alsa_pcm_init)
module_exit(alsa_pcm_exit)
-
-EXPORT_SYMBOL(snd_pcm_new);
-EXPORT_SYMBOL(snd_pcm_new_stream);
-EXPORT_SYMBOL(snd_pcm_notify);
-EXPORT_SYMBOL(snd_pcm_open_substream);
-EXPORT_SYMBOL(snd_pcm_release_substream);
- /* pcm_native.c */
-EXPORT_SYMBOL(snd_pcm_link_rwlock);
-#ifdef CONFIG_PM
-EXPORT_SYMBOL(snd_pcm_suspend);
-EXPORT_SYMBOL(snd_pcm_suspend_all);
-#endif
-EXPORT_SYMBOL(snd_pcm_kernel_ioctl);
-EXPORT_SYMBOL(snd_pcm_mmap_data);
-#if SNDRV_PCM_INFO_MMAP_IOMEM
-EXPORT_SYMBOL(snd_pcm_lib_mmap_iomem);
-#endif
- /* pcm_misc.c */
-EXPORT_SYMBOL(snd_pcm_format_signed);
-EXPORT_SYMBOL(snd_pcm_format_unsigned);
-EXPORT_SYMBOL(snd_pcm_format_linear);
-EXPORT_SYMBOL(snd_pcm_format_little_endian);
-EXPORT_SYMBOL(snd_pcm_format_big_endian);
-EXPORT_SYMBOL(snd_pcm_format_width);
-EXPORT_SYMBOL(snd_pcm_format_physical_width);
-EXPORT_SYMBOL(snd_pcm_format_size);
-EXPORT_SYMBOL(snd_pcm_format_silence_64);
-EXPORT_SYMBOL(snd_pcm_format_set_silence);
-EXPORT_SYMBOL(snd_pcm_build_linear_format);
-EXPORT_SYMBOL(snd_pcm_limit_hw_rates);
case SNDRV_PCM_IOCTL_LINK:
case SNDRV_PCM_IOCTL_UNLINK:
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- return snd_pcm_playback_ioctl1(substream, cmd, argp);
+ return snd_pcm_playback_ioctl1(file, substream, cmd, argp);
else
- return snd_pcm_capture_ioctl1(substream, cmd, argp);
+ return snd_pcm_capture_ioctl1(file, substream, cmd, argp);
case SNDRV_PCM_IOCTL_HW_REFINE32:
return snd_pcm_ioctl_hw_params_compat(substream, 1, argp);
case SNDRV_PCM_IOCTL_HW_PARAMS32:
substream->ops = ops;
}
+EXPORT_SYMBOL(snd_pcm_set_ops);
/**
* snd_pcm_sync - set the PCM sync id
runtime->sync.id32[3] = -1;
}
+EXPORT_SYMBOL(snd_pcm_set_sync);
+
/*
* Standard ioctl routine
*/
-/* Code taken from alsa-lib */
-#define assert(a) snd_assert((a), return -EINVAL)
-
static inline unsigned int div32(unsigned int a, unsigned int b,
unsigned int *r)
{
return n;
}
-static int snd_interval_refine_min(struct snd_interval *i, unsigned int min, int openmin)
-{
- int changed = 0;
- assert(!snd_interval_empty(i));
- if (i->min < min) {
- i->min = min;
- i->openmin = openmin;
- changed = 1;
- } else if (i->min == min && !i->openmin && openmin) {
- i->openmin = 1;
- changed = 1;
- }
- if (i->integer) {
- if (i->openmin) {
- i->min++;
- i->openmin = 0;
- }
- }
- if (snd_interval_checkempty(i)) {
- snd_interval_none(i);
- return -EINVAL;
- }
- return changed;
-}
-
-static int snd_interval_refine_max(struct snd_interval *i, unsigned int max, int openmax)
-{
- int changed = 0;
- assert(!snd_interval_empty(i));
- if (i->max > max) {
- i->max = max;
- i->openmax = openmax;
- changed = 1;
- } else if (i->max == max && !i->openmax && openmax) {
- i->openmax = 1;
- changed = 1;
- }
- if (i->integer) {
- if (i->openmax) {
- i->max--;
- i->openmax = 0;
- }
- }
- if (snd_interval_checkempty(i)) {
- snd_interval_none(i);
- return -EINVAL;
- }
- return changed;
-}
-
/**
* snd_interval_refine - refine the interval value of configurator
* @i: the interval value to refine
int snd_interval_refine(struct snd_interval *i, const struct snd_interval *v)
{
int changed = 0;
- assert(!snd_interval_empty(i));
+ snd_assert(!snd_interval_empty(i), return -EINVAL);
if (i->min < v->min) {
i->min = v->min;
i->openmin = v->openmin;
return changed;
}
+EXPORT_SYMBOL(snd_interval_refine);
+
static int snd_interval_refine_first(struct snd_interval *i)
{
- assert(!snd_interval_empty(i));
+ snd_assert(!snd_interval_empty(i), return -EINVAL);
if (snd_interval_single(i))
return 0;
i->max = i->min;
static int snd_interval_refine_last(struct snd_interval *i)
{
- assert(!snd_interval_empty(i));
+ snd_assert(!snd_interval_empty(i), return -EINVAL);
if (snd_interval_single(i))
return 0;
i->min = i->max;
return 1;
}
-static int snd_interval_refine_set(struct snd_interval *i, unsigned int val)
-{
- struct snd_interval t;
- t.empty = 0;
- t.min = t.max = val;
- t.openmin = t.openmax = 0;
- t.integer = 1;
- return snd_interval_refine(i, &t);
-}
-
void snd_interval_mul(const struct snd_interval *a, const struct snd_interval *b, struct snd_interval *c)
{
if (a->empty || b->empty) {
c->integer = 0;
}
-#undef assert
/* ---- */
return err;
}
+EXPORT_SYMBOL(snd_interval_ratnum);
+
/**
* snd_interval_ratden - refine the interval value
* @i: interval to refine
return changed;
}
+EXPORT_SYMBOL(snd_interval_list);
+
static int snd_interval_step(struct snd_interval *i, unsigned int min, unsigned int step)
{
unsigned int n;
return 0;
}
+EXPORT_SYMBOL(snd_pcm_hw_rule_add);
+
/**
* snd_pcm_hw_constraint_mask
* @runtime: PCM runtime instance
return snd_interval_setinteger(constrs_interval(constrs, var));
}
+EXPORT_SYMBOL(snd_pcm_hw_constraint_integer);
+
/**
* snd_pcm_hw_constraint_minmax
* @runtime: PCM runtime instance
return snd_interval_refine(constrs_interval(constrs, var), &t);
}
+EXPORT_SYMBOL(snd_pcm_hw_constraint_minmax);
+
static int snd_pcm_hw_rule_list(struct snd_pcm_hw_params *params,
struct snd_pcm_hw_rule *rule)
{
var, -1);
}
+EXPORT_SYMBOL(snd_pcm_hw_constraint_list);
+
static int snd_pcm_hw_rule_ratnums(struct snd_pcm_hw_params *params,
struct snd_pcm_hw_rule *rule)
{
var, -1);
}
+EXPORT_SYMBOL(snd_pcm_hw_constraint_ratnums);
+
static int snd_pcm_hw_rule_ratdens(struct snd_pcm_hw_params *params,
struct snd_pcm_hw_rule *rule)
{
var, -1);
}
+EXPORT_SYMBOL(snd_pcm_hw_constraint_ratdens);
+
static int snd_pcm_hw_rule_msbits(struct snd_pcm_hw_params *params,
struct snd_pcm_hw_rule *rule)
{
SNDRV_PCM_HW_PARAM_SAMPLE_BITS, -1);
}
+EXPORT_SYMBOL(snd_pcm_hw_constraint_msbits);
+
static int snd_pcm_hw_rule_step(struct snd_pcm_hw_params *params,
struct snd_pcm_hw_rule *rule)
{
var, -1);
}
+EXPORT_SYMBOL(snd_pcm_hw_constraint_step);
+
static int snd_pcm_hw_rule_pow2(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule)
{
static int pow2_sizes[] = {
var, -1);
}
-/* To use the same code we have in alsa-lib */
-#define assert(i) snd_assert((i), return -EINVAL)
-#ifndef INT_MIN
-#define INT_MIN ((int)((unsigned int)INT_MAX+1))
-#endif
+EXPORT_SYMBOL(snd_pcm_hw_constraint_pow2);
static void _snd_pcm_hw_param_any(struct snd_pcm_hw_params *params,
snd_pcm_hw_param_t var)
snd_BUG();
}
-#if 0
-/*
- * snd_pcm_hw_param_any
- */
-int snd_pcm_hw_param_any(struct snd_pcm_substream *pcm, struct snd_pcm_hw_params *params,
- snd_pcm_hw_param_t var)
-{
- _snd_pcm_hw_param_any(params, var);
- return snd_pcm_hw_refine(pcm, params);
-}
-#endif /* 0 */
-
void _snd_pcm_hw_params_any(struct snd_pcm_hw_params *params)
{
unsigned int k;
params->info = ~0U;
}
-#if 0
-/*
- * snd_pcm_hw_params_any
- *
- * Fill PARAMS with full configuration space boundaries
- */
-int snd_pcm_hw_params_any(struct snd_pcm_substream *pcm, struct snd_pcm_hw_params *params)
-{
- _snd_pcm_hw_params_any(params);
- return snd_pcm_hw_refine(pcm, params);
-}
-#endif /* 0 */
+EXPORT_SYMBOL(_snd_pcm_hw_params_any);
/**
* snd_pcm_hw_param_value
* Return the value for field PAR if it's fixed in configuration space
* defined by PARAMS. Return -EINVAL otherwise
*/
-static int snd_pcm_hw_param_value(const struct snd_pcm_hw_params *params,
- snd_pcm_hw_param_t var, int *dir)
+int snd_pcm_hw_param_value(const struct snd_pcm_hw_params *params,
+ snd_pcm_hw_param_t var, int *dir)
{
if (hw_is_mask(var)) {
const struct snd_mask *mask = hw_param_mask_c(params, var);
*dir = i->openmin;
return snd_interval_value(i);
}
- assert(0);
- return -EINVAL;
-}
-
-/**
- * snd_pcm_hw_param_value_min
- * @params: the hw_params instance
- * @var: parameter to retrieve
- * @dir: pointer to the direction (-1,0,1) or NULL
- *
- * Return the minimum value for field PAR.
- */
-unsigned int snd_pcm_hw_param_value_min(const struct snd_pcm_hw_params *params,
- snd_pcm_hw_param_t var, int *dir)
-{
- if (hw_is_mask(var)) {
- if (dir)
- *dir = 0;
- return snd_mask_min(hw_param_mask_c(params, var));
- }
- if (hw_is_interval(var)) {
- const struct snd_interval *i = hw_param_interval_c(params, var);
- if (dir)
- *dir = i->openmin;
- return snd_interval_min(i);
- }
- assert(0);
return -EINVAL;
}
-/**
- * snd_pcm_hw_param_value_max
- * @params: the hw_params instance
- * @var: parameter to retrieve
- * @dir: pointer to the direction (-1,0,1) or NULL
- *
- * Return the maximum value for field PAR.
- */
-unsigned int snd_pcm_hw_param_value_max(const struct snd_pcm_hw_params *params,
- snd_pcm_hw_param_t var, int *dir)
-{
- if (hw_is_mask(var)) {
- if (dir)
- *dir = 0;
- return snd_mask_max(hw_param_mask_c(params, var));
- }
- if (hw_is_interval(var)) {
- const struct snd_interval *i = hw_param_interval_c(params, var);
- if (dir)
- *dir = - (int) i->openmax;
- return snd_interval_max(i);
- }
- assert(0);
- return -EINVAL;
-}
+EXPORT_SYMBOL(snd_pcm_hw_param_value);
void _snd_pcm_hw_param_setempty(struct snd_pcm_hw_params *params,
snd_pcm_hw_param_t var)
}
}
-int _snd_pcm_hw_param_setinteger(struct snd_pcm_hw_params *params,
- snd_pcm_hw_param_t var)
-{
- int changed;
- assert(hw_is_interval(var));
- changed = snd_interval_setinteger(hw_param_interval(params, var));
- if (changed) {
- params->cmask |= 1 << var;
- params->rmask |= 1 << var;
- }
- return changed;
-}
-
-#if 0
-/*
- * snd_pcm_hw_param_setinteger
- *
- * Inside configuration space defined by PARAMS remove from PAR all
- * non integer values. Reduce configuration space accordingly.
- * Return -EINVAL if the configuration space is empty
- */
-int snd_pcm_hw_param_setinteger(struct snd_pcm_substream *pcm,
- struct snd_pcm_hw_params *params,
- snd_pcm_hw_param_t var)
-{
- int changed = _snd_pcm_hw_param_setinteger(params, var);
- if (changed < 0)
- return changed;
- if (params->rmask) {
- int err = snd_pcm_hw_refine(pcm, params);
- if (err < 0)
- return err;
- }
- return 0;
-}
-#endif /* 0 */
+EXPORT_SYMBOL(_snd_pcm_hw_param_setempty);
static int _snd_pcm_hw_param_first(struct snd_pcm_hw_params *params,
snd_pcm_hw_param_t var)
changed = snd_mask_refine_first(hw_param_mask(params, var));
else if (hw_is_interval(var))
changed = snd_interval_refine_first(hw_param_interval(params, var));
- else {
- assert(0);
+ else
return -EINVAL;
- }
if (changed) {
params->cmask |= 1 << var;
params->rmask |= 1 << var;
* values > minimum. Reduce configuration space accordingly.
* Return the minimum.
*/
-static int snd_pcm_hw_param_first(struct snd_pcm_substream *pcm,
- struct snd_pcm_hw_params *params,
- snd_pcm_hw_param_t var, int *dir)
+int snd_pcm_hw_param_first(struct snd_pcm_substream *pcm,
+ struct snd_pcm_hw_params *params,
+ snd_pcm_hw_param_t var, int *dir)
{
int changed = _snd_pcm_hw_param_first(params, var);
if (changed < 0)
return changed;
if (params->rmask) {
int err = snd_pcm_hw_refine(pcm, params);
- assert(err >= 0);
+ snd_assert(err >= 0, return err);
}
return snd_pcm_hw_param_value(params, var, dir);
}
+EXPORT_SYMBOL(snd_pcm_hw_param_first);
+
static int _snd_pcm_hw_param_last(struct snd_pcm_hw_params *params,
snd_pcm_hw_param_t var)
{
changed = snd_mask_refine_last(hw_param_mask(params, var));
else if (hw_is_interval(var))
changed = snd_interval_refine_last(hw_param_interval(params, var));
- else {
- assert(0);
+ else
return -EINVAL;
- }
if (changed) {
params->cmask |= 1 << var;
params->rmask |= 1 << var;
* values < maximum. Reduce configuration space accordingly.
* Return the maximum.
*/
-static int snd_pcm_hw_param_last(struct snd_pcm_substream *pcm,
- struct snd_pcm_hw_params *params,
- snd_pcm_hw_param_t var, int *dir)
+int snd_pcm_hw_param_last(struct snd_pcm_substream *pcm,
+ struct snd_pcm_hw_params *params,
+ snd_pcm_hw_param_t var, int *dir)
{
int changed = _snd_pcm_hw_param_last(params, var);
if (changed < 0)
return changed;
if (params->rmask) {
int err = snd_pcm_hw_refine(pcm, params);
- assert(err >= 0);
+ snd_assert(err >= 0, return err);
}
return snd_pcm_hw_param_value(params, var, dir);
}
-int _snd_pcm_hw_param_min(struct snd_pcm_hw_params *params,
- snd_pcm_hw_param_t var, unsigned int val, int dir)
-{
- int changed;
- int open = 0;
- if (dir) {
- if (dir > 0) {
- open = 1;
- } else if (dir < 0) {
- if (val > 0) {
- open = 1;
- val--;
- }
- }
- }
- if (hw_is_mask(var))
- changed = snd_mask_refine_min(hw_param_mask(params, var), val + !!open);
- else if (hw_is_interval(var))
- changed = snd_interval_refine_min(hw_param_interval(params, var), val, open);
- else {
- assert(0);
- return -EINVAL;
- }
- if (changed) {
- params->cmask |= 1 << var;
- params->rmask |= 1 << var;
- }
- return changed;
-}
-
-/**
- * snd_pcm_hw_param_min
- * @pcm: PCM instance
- * @params: the hw_params instance
- * @var: parameter to retrieve
- * @val: minimal value
- * @dir: pointer to the direction (-1,0,1) or NULL
- *
- * Inside configuration space defined by PARAMS remove from PAR all
- * values < VAL. Reduce configuration space accordingly.
- * Return new minimum or -EINVAL if the configuration space is empty
- */
-static int snd_pcm_hw_param_min(struct snd_pcm_substream *pcm, struct snd_pcm_hw_params *params,
- snd_pcm_hw_param_t var, unsigned int val,
- int *dir)
-{
- int changed = _snd_pcm_hw_param_min(params, var, val, dir ? *dir : 0);
- if (changed < 0)
- return changed;
- if (params->rmask) {
- int err = snd_pcm_hw_refine(pcm, params);
- if (err < 0)
- return err;
- }
- return snd_pcm_hw_param_value_min(params, var, dir);
-}
-
-static int _snd_pcm_hw_param_max(struct snd_pcm_hw_params *params,
- snd_pcm_hw_param_t var, unsigned int val,
- int dir)
-{
- int changed;
- int open = 0;
- if (dir) {
- if (dir < 0) {
- open = 1;
- } else if (dir > 0) {
- open = 1;
- val++;
- }
- }
- if (hw_is_mask(var)) {
- if (val == 0 && open) {
- snd_mask_none(hw_param_mask(params, var));
- changed = -EINVAL;
- } else
- changed = snd_mask_refine_max(hw_param_mask(params, var), val - !!open);
- } else if (hw_is_interval(var))
- changed = snd_interval_refine_max(hw_param_interval(params, var), val, open);
- else {
- assert(0);
- return -EINVAL;
- }
- if (changed) {
- params->cmask |= 1 << var;
- params->rmask |= 1 << var;
- }
- return changed;
-}
-
-/**
- * snd_pcm_hw_param_max
- * @pcm: PCM instance
- * @params: the hw_params instance
- * @var: parameter to retrieve
- * @val: maximal value
- * @dir: pointer to the direction (-1,0,1) or NULL
- *
- * Inside configuration space defined by PARAMS remove from PAR all
- * values >= VAL + 1. Reduce configuration space accordingly.
- * Return new maximum or -EINVAL if the configuration space is empty
- */
-static int snd_pcm_hw_param_max(struct snd_pcm_substream *pcm, struct snd_pcm_hw_params *params,
- snd_pcm_hw_param_t var, unsigned int val,
- int *dir)
-{
- int changed = _snd_pcm_hw_param_max(params, var, val, dir ? *dir : 0);
- if (changed < 0)
- return changed;
- if (params->rmask) {
- int err = snd_pcm_hw_refine(pcm, params);
- if (err < 0)
- return err;
- }
- return snd_pcm_hw_param_value_max(params, var, dir);
-}
-
-int _snd_pcm_hw_param_set(struct snd_pcm_hw_params *params,
- snd_pcm_hw_param_t var, unsigned int val, int dir)
-{
- int changed;
- if (hw_is_mask(var)) {
- struct snd_mask *m = hw_param_mask(params, var);
- if (val == 0 && dir < 0) {
- changed = -EINVAL;
- snd_mask_none(m);
- } else {
- if (dir > 0)
- val++;
- else if (dir < 0)
- val--;
- changed = snd_mask_refine_set(hw_param_mask(params, var), val);
- }
- } else if (hw_is_interval(var)) {
- struct snd_interval *i = hw_param_interval(params, var);
- if (val == 0 && dir < 0) {
- changed = -EINVAL;
- snd_interval_none(i);
- } else if (dir == 0)
- changed = snd_interval_refine_set(i, val);
- else {
- struct snd_interval t;
- t.openmin = 1;
- t.openmax = 1;
- t.empty = 0;
- t.integer = 0;
- if (dir < 0) {
- t.min = val - 1;
- t.max = val;
- } else {
- t.min = val;
- t.max = val+1;
- }
- changed = snd_interval_refine(i, &t);
- }
- } else {
- assert(0);
- return -EINVAL;
- }
- if (changed) {
- params->cmask |= 1 << var;
- params->rmask |= 1 << var;
- }
- return changed;
-}
-
-/**
- * snd_pcm_hw_param_set
- * @pcm: PCM instance
- * @params: the hw_params instance
- * @var: parameter to retrieve
- * @val: value to set
- * @dir: pointer to the direction (-1,0,1) or NULL
- *
- * Inside configuration space defined by PARAMS remove from PAR all
- * values != VAL. Reduce configuration space accordingly.
- * Return VAL or -EINVAL if the configuration space is empty
- */
-int snd_pcm_hw_param_set(struct snd_pcm_substream *pcm, struct snd_pcm_hw_params *params,
- snd_pcm_hw_param_t var, unsigned int val, int dir)
-{
- int changed = _snd_pcm_hw_param_set(params, var, val, dir);
- if (changed < 0)
- return changed;
- if (params->rmask) {
- int err = snd_pcm_hw_refine(pcm, params);
- if (err < 0)
- return err;
- }
- return snd_pcm_hw_param_value(params, var, NULL);
-}
-
-static int _snd_pcm_hw_param_mask(struct snd_pcm_hw_params *params,
- snd_pcm_hw_param_t var, const struct snd_mask *val)
-{
- int changed;
- assert(hw_is_mask(var));
- changed = snd_mask_refine(hw_param_mask(params, var), val);
- if (changed) {
- params->cmask |= 1 << var;
- params->rmask |= 1 << var;
- }
- return changed;
-}
-
-/**
- * snd_pcm_hw_param_mask
- * @pcm: PCM instance
- * @params: the hw_params instance
- * @var: parameter to retrieve
- * @val: mask to apply
- *
- * Inside configuration space defined by PARAMS remove from PAR all values
- * not contained in MASK. Reduce configuration space accordingly.
- * This function can be called only for SNDRV_PCM_HW_PARAM_ACCESS,
- * SNDRV_PCM_HW_PARAM_FORMAT, SNDRV_PCM_HW_PARAM_SUBFORMAT.
- * Return 0 on success or -EINVAL
- * if the configuration space is empty
- */
-int snd_pcm_hw_param_mask(struct snd_pcm_substream *pcm, struct snd_pcm_hw_params *params,
- snd_pcm_hw_param_t var, const struct snd_mask *val)
-{
- int changed = _snd_pcm_hw_param_mask(params, var, val);
- if (changed < 0)
- return changed;
- if (params->rmask) {
- int err = snd_pcm_hw_refine(pcm, params);
- if (err < 0)
- return err;
- }
- return 0;
-}
-
-static int boundary_sub(int a, int adir,
- int b, int bdir,
- int *c, int *cdir)
-{
- adir = adir < 0 ? -1 : (adir > 0 ? 1 : 0);
- bdir = bdir < 0 ? -1 : (bdir > 0 ? 1 : 0);
- *c = a - b;
- *cdir = adir - bdir;
- if (*cdir == -2) {
- assert(*c > INT_MIN);
- (*c)--;
- } else if (*cdir == 2) {
- assert(*c < INT_MAX);
- (*c)++;
- }
- return 0;
-}
-
-static int boundary_lt(unsigned int a, int adir,
- unsigned int b, int bdir)
-{
- assert(a > 0 || adir >= 0);
- assert(b > 0 || bdir >= 0);
- if (adir < 0) {
- a--;
- adir = 1;
- } else if (adir > 0)
- adir = 1;
- if (bdir < 0) {
- b--;
- bdir = 1;
- } else if (bdir > 0)
- bdir = 1;
- return a < b || (a == b && adir < bdir);
-}
-
-/* Return 1 if min is nearer to best than max */
-static int boundary_nearer(int min, int mindir,
- int best, int bestdir,
- int max, int maxdir)
-{
- int dmin, dmindir;
- int dmax, dmaxdir;
- boundary_sub(best, bestdir, min, mindir, &dmin, &dmindir);
- boundary_sub(max, maxdir, best, bestdir, &dmax, &dmaxdir);
- return boundary_lt(dmin, dmindir, dmax, dmaxdir);
-}
-
-/**
- * snd_pcm_hw_param_near
- * @pcm: PCM instance
- * @params: the hw_params instance
- * @var: parameter to retrieve
- * @best: value to set
- * @dir: pointer to the direction (-1,0,1) or NULL
- *
- * Inside configuration space defined by PARAMS set PAR to the available value
- * nearest to VAL. Reduce configuration space accordingly.
- * This function cannot be called for SNDRV_PCM_HW_PARAM_ACCESS,
- * SNDRV_PCM_HW_PARAM_FORMAT, SNDRV_PCM_HW_PARAM_SUBFORMAT.
- * Return the value found.
- */
-int snd_pcm_hw_param_near(struct snd_pcm_substream *pcm, struct snd_pcm_hw_params *params,
- snd_pcm_hw_param_t var, unsigned int best, int *dir)
-{
- struct snd_pcm_hw_params *save = NULL;
- int v;
- unsigned int saved_min;
- int last = 0;
- int min, max;
- int mindir, maxdir;
- int valdir = dir ? *dir : 0;
- /* FIXME */
- if (best > INT_MAX)
- best = INT_MAX;
- min = max = best;
- mindir = maxdir = valdir;
- if (maxdir > 0)
- maxdir = 0;
- else if (maxdir == 0)
- maxdir = -1;
- else {
- maxdir = 1;
- max--;
- }
- save = kmalloc(sizeof(*save), GFP_KERNEL);
- if (save == NULL)
- return -ENOMEM;
- *save = *params;
- saved_min = min;
- min = snd_pcm_hw_param_min(pcm, params, var, min, &mindir);
- if (min >= 0) {
- struct snd_pcm_hw_params *params1;
- if (max < 0)
- goto _end;
- if ((unsigned int)min == saved_min && mindir == valdir)
- goto _end;
- params1 = kmalloc(sizeof(*params1), GFP_KERNEL);
- if (params1 == NULL) {
- kfree(save);
- return -ENOMEM;
- }
- *params1 = *save;
- max = snd_pcm_hw_param_max(pcm, params1, var, max, &maxdir);
- if (max < 0) {
- kfree(params1);
- goto _end;
- }
- if (boundary_nearer(max, maxdir, best, valdir, min, mindir)) {
- *params = *params1;
- last = 1;
- }
- kfree(params1);
- } else {
- *params = *save;
- max = snd_pcm_hw_param_max(pcm, params, var, max, &maxdir);
- assert(max >= 0);
- last = 1;
- }
- _end:
- kfree(save);
- if (last)
- v = snd_pcm_hw_param_last(pcm, params, var, dir);
- else
- v = snd_pcm_hw_param_first(pcm, params, var, dir);
- assert(v >= 0);
- return v;
-}
+EXPORT_SYMBOL(snd_pcm_hw_param_last);
/**
* snd_pcm_hw_param_choose
* first access, first format, first subformat, min channels,
* min rate, min period time, max buffer size, min tick time
*/
-int snd_pcm_hw_params_choose(struct snd_pcm_substream *pcm, struct snd_pcm_hw_params *params)
-{
- int err;
-
- err = snd_pcm_hw_param_first(pcm, params, SNDRV_PCM_HW_PARAM_ACCESS, NULL);
- assert(err >= 0);
-
- err = snd_pcm_hw_param_first(pcm, params, SNDRV_PCM_HW_PARAM_FORMAT, NULL);
- assert(err >= 0);
-
- err = snd_pcm_hw_param_first(pcm, params, SNDRV_PCM_HW_PARAM_SUBFORMAT, NULL);
- assert(err >= 0);
-
- err = snd_pcm_hw_param_first(pcm, params, SNDRV_PCM_HW_PARAM_CHANNELS, NULL);
- assert(err >= 0);
-
- err = snd_pcm_hw_param_first(pcm, params, SNDRV_PCM_HW_PARAM_RATE, NULL);
- assert(err >= 0);
-
- err = snd_pcm_hw_param_first(pcm, params, SNDRV_PCM_HW_PARAM_PERIOD_TIME, NULL);
- assert(err >= 0);
-
- err = snd_pcm_hw_param_last(pcm, params, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, NULL);
- assert(err >= 0);
-
- err = snd_pcm_hw_param_first(pcm, params, SNDRV_PCM_HW_PARAM_TICK_TIME, NULL);
- assert(err >= 0);
+int snd_pcm_hw_params_choose(struct snd_pcm_substream *pcm,
+ struct snd_pcm_hw_params *params)
+{
+ static int vars[] = {
+ SNDRV_PCM_HW_PARAM_ACCESS,
+ SNDRV_PCM_HW_PARAM_FORMAT,
+ SNDRV_PCM_HW_PARAM_SUBFORMAT,
+ SNDRV_PCM_HW_PARAM_CHANNELS,
+ SNDRV_PCM_HW_PARAM_RATE,
+ SNDRV_PCM_HW_PARAM_PERIOD_TIME,
+ SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
+ SNDRV_PCM_HW_PARAM_TICK_TIME,
+ -1
+ };
+ int err, *v;
+ for (v = vars; *v != -1; v++) {
+ if (*v != SNDRV_PCM_HW_PARAM_BUFFER_SIZE)
+ err = snd_pcm_hw_param_first(pcm, params, *v, NULL);
+ else
+ err = snd_pcm_hw_param_last(pcm, params, *v, NULL);
+ snd_assert(err >= 0, return err);
+ }
return 0;
}
-#undef assert
-
static int snd_pcm_lib_ioctl_reset(struct snd_pcm_substream *substream,
void *arg)
{
return -ENXIO;
}
+EXPORT_SYMBOL(snd_pcm_lib_ioctl);
+
/*
* Conditions
*/
kill_fasync(&runtime->fasync, SIGIO, POLL_IN);
}
+EXPORT_SYMBOL(snd_pcm_period_elapsed);
+
static int snd_pcm_lib_write_transfer(struct snd_pcm_substream *substream,
unsigned int hwoff,
unsigned long data, unsigned int off,
if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
return -EBADFD;
- nonblock = !!(substream->ffile->f_flags & O_NONBLOCK);
+ nonblock = !!(substream->f_flags & O_NONBLOCK);
if (runtime->access != SNDRV_PCM_ACCESS_RW_INTERLEAVED &&
runtime->channels > 1)
snd_pcm_lib_write_transfer);
}
+EXPORT_SYMBOL(snd_pcm_lib_write);
+
static int snd_pcm_lib_writev_transfer(struct snd_pcm_substream *substream,
unsigned int hwoff,
unsigned long data, unsigned int off,
if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
return -EBADFD;
- nonblock = !!(substream->ffile->f_flags & O_NONBLOCK);
+ nonblock = !!(substream->f_flags & O_NONBLOCK);
if (runtime->access != SNDRV_PCM_ACCESS_RW_NONINTERLEAVED)
return -EINVAL;
nonblock, snd_pcm_lib_writev_transfer);
}
+EXPORT_SYMBOL(snd_pcm_lib_writev);
+
static int snd_pcm_lib_read_transfer(struct snd_pcm_substream *substream,
unsigned int hwoff,
unsigned long data, unsigned int off,
if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
return -EBADFD;
- nonblock = !!(substream->ffile->f_flags & O_NONBLOCK);
+ nonblock = !!(substream->f_flags & O_NONBLOCK);
if (runtime->access != SNDRV_PCM_ACCESS_RW_INTERLEAVED)
return -EINVAL;
return snd_pcm_lib_read1(substream, (unsigned long)buf, size, nonblock, snd_pcm_lib_read_transfer);
}
+EXPORT_SYMBOL(snd_pcm_lib_read);
+
static int snd_pcm_lib_readv_transfer(struct snd_pcm_substream *substream,
unsigned int hwoff,
unsigned long data, unsigned int off,
if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
return -EBADFD;
- nonblock = !!(substream->ffile->f_flags & O_NONBLOCK);
+ nonblock = !!(substream->f_flags & O_NONBLOCK);
if (runtime->access != SNDRV_PCM_ACCESS_RW_NONINTERLEAVED)
return -EINVAL;
return snd_pcm_lib_read1(substream, (unsigned long)bufs, frames, nonblock, snd_pcm_lib_readv_transfer);
}
-/*
- * Exported symbols
- */
-
-EXPORT_SYMBOL(snd_interval_refine);
-EXPORT_SYMBOL(snd_interval_list);
-EXPORT_SYMBOL(snd_interval_ratnum);
-EXPORT_SYMBOL(_snd_pcm_hw_params_any);
-EXPORT_SYMBOL(_snd_pcm_hw_param_min);
-EXPORT_SYMBOL(_snd_pcm_hw_param_set);
-EXPORT_SYMBOL(_snd_pcm_hw_param_setempty);
-EXPORT_SYMBOL(_snd_pcm_hw_param_setinteger);
-EXPORT_SYMBOL(snd_pcm_hw_param_value_min);
-EXPORT_SYMBOL(snd_pcm_hw_param_value_max);
-EXPORT_SYMBOL(snd_pcm_hw_param_mask);
-EXPORT_SYMBOL(snd_pcm_hw_param_first);
-EXPORT_SYMBOL(snd_pcm_hw_param_last);
-EXPORT_SYMBOL(snd_pcm_hw_param_near);
-EXPORT_SYMBOL(snd_pcm_hw_param_set);
-EXPORT_SYMBOL(snd_pcm_hw_refine);
-EXPORT_SYMBOL(snd_pcm_hw_constraints_init);
-EXPORT_SYMBOL(snd_pcm_hw_constraints_complete);
-EXPORT_SYMBOL(snd_pcm_hw_constraint_list);
-EXPORT_SYMBOL(snd_pcm_hw_constraint_step);
-EXPORT_SYMBOL(snd_pcm_hw_constraint_ratnums);
-EXPORT_SYMBOL(snd_pcm_hw_constraint_ratdens);
-EXPORT_SYMBOL(snd_pcm_hw_constraint_msbits);
-EXPORT_SYMBOL(snd_pcm_hw_constraint_minmax);
-EXPORT_SYMBOL(snd_pcm_hw_constraint_integer);
-EXPORT_SYMBOL(snd_pcm_hw_constraint_pow2);
-EXPORT_SYMBOL(snd_pcm_hw_rule_add);
-EXPORT_SYMBOL(snd_pcm_set_ops);
-EXPORT_SYMBOL(snd_pcm_set_sync);
-EXPORT_SYMBOL(snd_pcm_lib_ioctl);
-EXPORT_SYMBOL(snd_pcm_stop);
-EXPORT_SYMBOL(snd_pcm_period_elapsed);
-EXPORT_SYMBOL(snd_pcm_lib_write);
-EXPORT_SYMBOL(snd_pcm_lib_read);
-EXPORT_SYMBOL(snd_pcm_lib_writev);
EXPORT_SYMBOL(snd_pcm_lib_readv);
-EXPORT_SYMBOL(snd_pcm_lib_buffer_bytes);
-EXPORT_SYMBOL(snd_pcm_lib_period_bytes);
-/* pcm_memory.c */
-EXPORT_SYMBOL(snd_pcm_lib_preallocate_free_for_all);
-EXPORT_SYMBOL(snd_pcm_lib_preallocate_pages);
-EXPORT_SYMBOL(snd_pcm_lib_preallocate_pages_for_all);
-EXPORT_SYMBOL(snd_pcm_sgbuf_ops_page);
-EXPORT_SYMBOL(snd_pcm_lib_malloc_pages);
-EXPORT_SYMBOL(snd_pcm_lib_free_pages);
return 0;
}
+EXPORT_SYMBOL(snd_pcm_lib_preallocate_free_for_all);
+
#ifdef CONFIG_SND_VERBOSE_PROCFS
/*
* read callback for prealloc proc file
struct snd_info_entry *entry;
if ((entry = snd_info_create_card_entry(substream->pcm->card, "prealloc", substream->proc_root)) != NULL) {
- entry->c.text.read_size = 64;
entry->c.text.read = snd_pcm_lib_preallocate_proc_read;
- entry->c.text.write_size = 64;
entry->c.text.write = snd_pcm_lib_preallocate_proc_write;
entry->mode |= S_IWUSR;
entry->private_data = substream;
return snd_pcm_lib_preallocate_pages1(substream, size, max);
}
+EXPORT_SYMBOL(snd_pcm_lib_preallocate_pages);
+
/**
* snd_pcm_lib_preallocate_pages_for_all - pre-allocation for continous memory type (all substreams)
* @pcm: the pcm instance
return 0;
}
+EXPORT_SYMBOL(snd_pcm_lib_preallocate_pages_for_all);
+
/**
* snd_pcm_sgbuf_ops_page - get the page struct at the given offset
* @substream: the pcm substream instance
return sgbuf->page_table[idx];
}
+EXPORT_SYMBOL(snd_pcm_sgbuf_ops_page);
+
/**
* snd_pcm_lib_malloc_pages - allocate the DMA buffer
* @substream: the substream to allocate the DMA buffer to
return 1; /* area was changed */
}
+EXPORT_SYMBOL(snd_pcm_lib_malloc_pages);
+
/**
* snd_pcm_lib_free_pages - release the allocated DMA buffer.
* @substream: the substream to release the DMA buffer
snd_pcm_set_runtime_buffer(substream, NULL);
return 0;
}
+
+EXPORT_SYMBOL(snd_pcm_lib_free_pages);
return val;
}
+EXPORT_SYMBOL(snd_pcm_format_signed);
+
/**
* snd_pcm_format_unsigned - Check the PCM format is unsigned linear
* @format: the format to check
return !val;
}
+EXPORT_SYMBOL(snd_pcm_format_unsigned);
+
/**
* snd_pcm_format_linear - Check the PCM format is linear
* @format: the format to check
return snd_pcm_format_signed(format) >= 0;
}
+EXPORT_SYMBOL(snd_pcm_format_linear);
+
/**
* snd_pcm_format_little_endian - Check the PCM format is little-endian
* @format: the format to check
return val;
}
+EXPORT_SYMBOL(snd_pcm_format_little_endian);
+
/**
* snd_pcm_format_big_endian - Check the PCM format is big-endian
* @format: the format to check
return !val;
}
+EXPORT_SYMBOL(snd_pcm_format_big_endian);
+
/**
* snd_pcm_format_width - return the bit-width of the format
* @format: the format to check
return val;
}
+EXPORT_SYMBOL(snd_pcm_format_width);
+
/**
* snd_pcm_format_physical_width - return the physical bit-width of the format
* @format: the format to check
return val;
}
+EXPORT_SYMBOL(snd_pcm_format_physical_width);
+
/**
* snd_pcm_format_size - return the byte size of samples on the given format
* @format: the format to check
return samples * phys_width / 8;
}
+EXPORT_SYMBOL(snd_pcm_format_size);
+
/**
* snd_pcm_format_silence_64 - return the silent data in 8 bytes array
* @format: the format to check
return pcm_formats[format].silence;
}
+EXPORT_SYMBOL(snd_pcm_format_silence_64);
+
/**
* snd_pcm_format_set_silence - set the silence data on the buffer
* @format: the PCM format
return 0;
}
+EXPORT_SYMBOL(snd_pcm_format_set_silence);
+
/* [width][unsigned][bigendian] */
static int linear_formats[4][2][2] = {
{{ SNDRV_PCM_FORMAT_S8, SNDRV_PCM_FORMAT_S8},
return linear_formats[width][!!unsignd][!!big_endian];
}
+EXPORT_SYMBOL(snd_pcm_build_linear_format);
+
/**
* snd_pcm_limit_hw_rates - determine rate_min/rate_max fields
* @runtime: the runtime instance
}
return 0;
}
+
+EXPORT_SYMBOL(snd_pcm_limit_hw_rates);
*/
DEFINE_RWLOCK(snd_pcm_link_rwlock);
-static DECLARE_RWSEM(snd_pcm_link_rwsem);
+EXPORT_SYMBOL(snd_pcm_link_rwlock);
+static DECLARE_RWSEM(snd_pcm_link_rwsem);
static inline mm_segment_t snd_enter_user(void)
{
return 0;
}
+EXPORT_SYMBOL(snd_pcm_hw_refine);
+
static int snd_pcm_hw_refine_user(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params __user * _params)
{
#if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE)
if (!substream->oss.oss)
#endif
- if (atomic_read(&runtime->mmap_count))
+ if (atomic_read(&substream->mmap_count))
return -EBADFD;
params->rmask = ~0U;
return -EBADFD;
}
snd_pcm_stream_unlock_irq(substream);
- if (atomic_read(&runtime->mmap_count))
+ if (atomic_read(&substream->mmap_count))
return -EBADFD;
if (substream->ops->hw_free)
result = substream->ops->hw_free(substream);
return snd_pcm_action(&snd_pcm_action_stop, substream, state);
}
+EXPORT_SYMBOL(snd_pcm_stop);
+
/**
* snd_pcm_drain_done
* @substream: the PCM substream
return err;
}
+EXPORT_SYMBOL(snd_pcm_suspend);
+
/**
* snd_pcm_suspend_all
* @pcm: the PCM instance
return 0;
}
+EXPORT_SYMBOL(snd_pcm_suspend_all);
+
/* resume */
static int snd_pcm_pre_resume(struct snd_pcm_substream *substream, int state)
/*
* prepare ioctl
*/
-static int snd_pcm_pre_prepare(struct snd_pcm_substream *substream, int state)
+/* we use the second argument for updating f_flags */
+static int snd_pcm_pre_prepare(struct snd_pcm_substream *substream,
+ int f_flags)
{
struct snd_pcm_runtime *runtime = substream->runtime;
if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
return -EBADFD;
if (snd_pcm_running(substream))
return -EBUSY;
+ substream->f_flags = f_flags;
return 0;
}
/**
* snd_pcm_prepare
* @substream: the PCM substream instance
+ * @file: file to refer f_flags
*
* Prepare the PCM substream to be triggerable.
*/
-static int snd_pcm_prepare(struct snd_pcm_substream *substream)
+static int snd_pcm_prepare(struct snd_pcm_substream *substream,
+ struct file *file)
{
int res;
struct snd_card *card = substream->pcm->card;
+ int f_flags;
+
+ if (file)
+ f_flags = file->f_flags;
+ else
+ f_flags = substream->f_flags;
snd_power_lock(card);
if ((res = snd_power_wait(card, SNDRV_CTL_POWER_D0)) >= 0)
- res = snd_pcm_action_nonatomic(&snd_pcm_action_prepare, substream, 0);
+ res = snd_pcm_action_nonatomic(&snd_pcm_action_prepare,
+ substream, f_flags);
snd_power_unlock(card);
return res;
}
static int snd_pcm_pre_drain_init(struct snd_pcm_substream *substream, int state)
{
- if (substream->ffile->f_flags & O_NONBLOCK)
+ if (substream->f_flags & O_NONBLOCK)
return -EAGAIN;
substream->runtime->trigger_master = substream;
return 0;
}
}
up_read(&snd_pcm_link_rwsem);
- if (! num_drecs)
- goto _error;
snd_pcm_stream_lock_irq(substream);
/* resume pause */
void snd_pcm_release_substream(struct snd_pcm_substream *substream)
{
+ substream->ref_count--;
+ if (substream->ref_count > 0)
+ return;
+
snd_pcm_drop(substream);
if (substream->hw_opened) {
if (substream->ops->hw_free != NULL)
snd_pcm_detach_substream(substream);
}
+EXPORT_SYMBOL(snd_pcm_release_substream);
+
int snd_pcm_open_substream(struct snd_pcm *pcm, int stream,
struct file *file,
struct snd_pcm_substream **rsubstream)
err = snd_pcm_attach_substream(pcm, stream, file, &substream);
if (err < 0)
return err;
+ if (substream->ref_count > 1) {
+ *rsubstream = substream;
+ return 0;
+ }
+
substream->no_mmap_ctrl = 0;
err = snd_pcm_hw_constraints_init(substream);
if (err < 0) {
return err;
}
+EXPORT_SYMBOL(snd_pcm_open_substream);
+
static int snd_pcm_open_file(struct file *file,
struct snd_pcm *pcm,
int stream,
if (err < 0)
return err;
- pcm_file = kzalloc(sizeof(*pcm_file), GFP_KERNEL);
- if (pcm_file == NULL) {
- snd_pcm_release_substream(substream);
- return -ENOMEM;
+ if (substream->ref_count > 1)
+ pcm_file = substream->file;
+ else {
+ pcm_file = kzalloc(sizeof(*pcm_file), GFP_KERNEL);
+ if (pcm_file == NULL) {
+ snd_pcm_release_substream(substream);
+ return -ENOMEM;
+ }
+ str = substream->pstr;
+ substream->file = pcm_file;
+ substream->pcm_release = pcm_release_private;
+ pcm_file->substream = substream;
+ snd_pcm_add_file(str, pcm_file);
}
- str = substream->pstr;
- substream->file = pcm_file;
- substream->pcm_release = pcm_release_private;
- pcm_file->substream = substream;
- snd_pcm_add_file(str, pcm_file);
-
file->private_data = pcm_file;
*rpcm_file = pcm_file;
return 0;
pcm_file = file->private_data;
substream = pcm_file->substream;
snd_assert(substream != NULL, return -ENXIO);
- snd_assert(!atomic_read(&substream->runtime->mmap_count), );
pcm = substream->pcm;
fasync_helper(-1, file, 0, &substream->runtime->fasync);
mutex_lock(&pcm->open_mutex);
return 0;
}
-static int snd_pcm_common_ioctl1(struct snd_pcm_substream *substream,
+static int snd_pcm_common_ioctl1(struct file *file,
+ struct snd_pcm_substream *substream,
unsigned int cmd, void __user *arg)
{
snd_assert(substream != NULL, return -ENXIO);
case SNDRV_PCM_IOCTL_CHANNEL_INFO:
return snd_pcm_channel_info_user(substream, arg);
case SNDRV_PCM_IOCTL_PREPARE:
- return snd_pcm_prepare(substream);
+ return snd_pcm_prepare(substream, file);
case SNDRV_PCM_IOCTL_RESET:
return snd_pcm_reset(substream);
case SNDRV_PCM_IOCTL_START:
return -ENOTTY;
}
-static int snd_pcm_playback_ioctl1(struct snd_pcm_substream *substream,
+static int snd_pcm_playback_ioctl1(struct file *file,
+ struct snd_pcm_substream *substream,
unsigned int cmd, void __user *arg)
{
snd_assert(substream != NULL, return -ENXIO);
return result < 0 ? result : 0;
}
}
- return snd_pcm_common_ioctl1(substream, cmd, arg);
+ return snd_pcm_common_ioctl1(file, substream, cmd, arg);
}
-static int snd_pcm_capture_ioctl1(struct snd_pcm_substream *substream,
+static int snd_pcm_capture_ioctl1(struct file *file,
+ struct snd_pcm_substream *substream,
unsigned int cmd, void __user *arg)
{
snd_assert(substream != NULL, return -ENXIO);
return result < 0 ? result : 0;
}
}
- return snd_pcm_common_ioctl1(substream, cmd, arg);
+ return snd_pcm_common_ioctl1(file, substream, cmd, arg);
}
static long snd_pcm_playback_ioctl(struct file *file, unsigned int cmd,
if (((cmd >> 8) & 0xff) != 'A')
return -ENOTTY;
- return snd_pcm_playback_ioctl1(pcm_file->substream, cmd, (void __user *)arg);
+ return snd_pcm_playback_ioctl1(file, pcm_file->substream, cmd,
+ (void __user *)arg);
}
static long snd_pcm_capture_ioctl(struct file *file, unsigned int cmd,
if (((cmd >> 8) & 0xff) != 'A')
return -ENOTTY;
- return snd_pcm_capture_ioctl1(pcm_file->substream, cmd, (void __user *)arg);
+ return snd_pcm_capture_ioctl1(file, pcm_file->substream, cmd,
+ (void __user *)arg);
}
int snd_pcm_kernel_ioctl(struct snd_pcm_substream *substream,
fs = snd_enter_user();
switch (substream->stream) {
case SNDRV_PCM_STREAM_PLAYBACK:
- result = snd_pcm_playback_ioctl1(substream,
- cmd, (void __user *)arg);
+ result = snd_pcm_playback_ioctl1(NULL, substream, cmd,
+ (void __user *)arg);
break;
case SNDRV_PCM_STREAM_CAPTURE:
- result = snd_pcm_capture_ioctl1(substream,
- cmd, (void __user *)arg);
+ result = snd_pcm_capture_ioctl1(NULL, substream, cmd,
+ (void __user *)arg);
break;
default:
result = -EINVAL;
return result;
}
+EXPORT_SYMBOL(snd_pcm_kernel_ioctl);
+
static ssize_t snd_pcm_read(struct file *file, char __user *buf, size_t count,
loff_t * offset)
{
area->vm_ops = &snd_pcm_vm_ops_data;
area->vm_private_data = substream;
area->vm_flags |= VM_RESERVED;
- atomic_inc(&substream->runtime->mmap_count);
+ atomic_inc(&substream->mmap_count);
return 0;
}
(substream->runtime->dma_addr + offset) >> PAGE_SHIFT,
size, area->vm_page_prot))
return -EAGAIN;
- atomic_inc(&substream->runtime->mmap_count);
+ atomic_inc(&substream->mmap_count);
return 0;
}
+
+EXPORT_SYMBOL(snd_pcm_lib_mmap_iomem);
#endif /* SNDRV_PCM_INFO_MMAP */
/*
return snd_pcm_default_mmap(substream, area);
}
+EXPORT_SYMBOL(snd_pcm_mmap_data);
+
static int snd_pcm_mmap(struct file *file, struct vm_area_struct *area)
{
struct snd_pcm_file * pcm_file;
MODULE_LICENSE("GPL");
#ifdef CONFIG_SND_OSSEMUL
-static int midi_map[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = 0};
+static int midi_map[SNDRV_CARDS];
static int amidi_map[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = 1};
module_param_array(midi_map, int, NULL, 0444);
MODULE_PARM_DESC(midi_map, "Raw MIDI device number assigned to 1st OSS device.");
entry = snd_info_create_card_entry(rmidi->card, name, rmidi->card->proc_root);
if (entry) {
entry->private_data = rmidi;
- entry->c.text.read_size = 1024;
entry->c.text.read = snd_rawmidi_proc_info_read;
if (snd_info_register(entry) < 0) {
snd_info_free_entry(entry);
entry->content = SNDRV_INFO_CONTENT_TEXT;
entry->private_data = NULL;
- entry->c.text.read_size = 1024;
entry->c.text.read = info_read;
if (snd_info_register(entry) < 0) {
snd_info_free_entry(entry);
module_init(alsa_seq_init)
module_exit(alsa_seq_exit)
-
- /* seq_clientmgr.c */
-EXPORT_SYMBOL(snd_seq_create_kernel_client);
-EXPORT_SYMBOL(snd_seq_delete_kernel_client);
-EXPORT_SYMBOL(snd_seq_kernel_client_enqueue);
-EXPORT_SYMBOL(snd_seq_kernel_client_enqueue_blocking);
-EXPORT_SYMBOL(snd_seq_kernel_client_dispatch);
-EXPORT_SYMBOL(snd_seq_kernel_client_ctl);
-EXPORT_SYMBOL(snd_seq_kernel_client_write_poll);
-EXPORT_SYMBOL(snd_seq_set_queue_tempo);
- /* seq_memory.c */
-EXPORT_SYMBOL(snd_seq_expand_var_event);
-EXPORT_SYMBOL(snd_seq_dump_var_event);
- /* seq_ports.c */
-EXPORT_SYMBOL(snd_seq_event_port_attach);
-EXPORT_SYMBOL(snd_seq_event_port_detach);
- /* seq_lock.c */
-#if defined(CONFIG_SMP) || defined(CONFIG_SND_DEBUG)
-/*EXPORT_SYMBOL(snd_seq_sleep_in_lock);*/
-/*EXPORT_SYMBOL(snd_seq_sleep_timeout_in_lock);*/
-EXPORT_SYMBOL(snd_use_lock_sync_helper);
-#endif
return snd_seq_queue_timer_set_tempo(tempo->queue, client, tempo);
}
+EXPORT_SYMBOL(snd_seq_set_queue_tempo);
+
static int snd_seq_ioctl_set_queue_tempo(struct snd_seq_client *client,
void __user *arg)
{
return client->number;
}
+EXPORT_SYMBOL(snd_seq_create_kernel_client);
+
/* exported to kernel modules */
int snd_seq_delete_kernel_client(int client)
{
return 0;
}
+EXPORT_SYMBOL(snd_seq_delete_kernel_client);
/* skeleton to enqueue event, called from snd_seq_kernel_client_enqueue
* and snd_seq_kernel_client_enqueue_blocking
return kernel_client_enqueue(client, ev, NULL, 0, atomic, hop);
}
+EXPORT_SYMBOL(snd_seq_kernel_client_enqueue);
+
/*
* exported, called by kernel clients to enqueue events (with blocking)
*
return kernel_client_enqueue(client, ev, file, 1, atomic, hop);
}
+EXPORT_SYMBOL(snd_seq_kernel_client_enqueue_blocking);
/*
* exported, called by kernel clients to dispatch events directly to other
return result;
}
+EXPORT_SYMBOL(snd_seq_kernel_client_dispatch);
/*
* exported, called by kernel clients to perform same functions as with
return result;
}
+EXPORT_SYMBOL(snd_seq_kernel_client_ctl);
/* exported (for OSS emulator) */
int snd_seq_kernel_client_write_poll(int clientid, struct file *file, poll_table *wait)
return 0;
}
+EXPORT_SYMBOL(snd_seq_kernel_client_write_poll);
+
/*---------------------------------------------------------------------------*/
#ifdef CONFIG_PROC_FS
static int num_ops;
static DEFINE_MUTEX(ops_mutex);
#ifdef CONFIG_PROC_FS
-static struct snd_info_entry *info_entry = NULL;
+static struct snd_info_entry *info_entry;
#endif
/*
if (info_entry == NULL)
return -ENOMEM;
info_entry->content = SNDRV_INFO_CONTENT_TEXT;
- info_entry->c.text.read_size = 2048;
info_entry->c.text.read = snd_seq_device_info;
if (snd_info_register(info_entry) < 0) {
snd_info_free_entry(info_entry);
MODULE_ALIAS("snd-seq-client-" __stringify(SNDRV_SEQ_CLIENT_DUMMY));
static int ports = 1;
-static int duplex = 0;
+static int duplex;
module_param(ports, int, 0444);
MODULE_PARM_DESC(ports, "number of ports to be created");
pinfo.capability |= SNDRV_SEQ_PORT_CAP_WRITE | SNDRV_SEQ_PORT_CAP_SUBS_WRITE;
if (duplex)
pinfo.capability |= SNDRV_SEQ_PORT_CAP_DUPLEX;
- pinfo.type = SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC;
+ pinfo.type = SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC
+ | SNDRV_SEQ_PORT_TYPE_SOFTWARE
+ | SNDRV_SEQ_PORT_TYPE_PORT;
memset(&pcb, 0, sizeof(pcb));
pcb.owner = THIS_MODULE;
pcb.unuse = dummy_unuse;
static struct snd_info_entry * __init
-create_info_entry(char *name, int size, void (*read)(struct snd_info_entry *,
- struct snd_info_buffer *))
+create_info_entry(char *name, void (*read)(struct snd_info_entry *,
+ struct snd_info_buffer *))
{
struct snd_info_entry *entry;
if (entry == NULL)
return NULL;
entry->content = SNDRV_INFO_CONTENT_TEXT;
- entry->c.text.read_size = size;
entry->c.text.read = read;
if (snd_info_register(entry) < 0) {
snd_info_free_entry(entry);
/* create all our /proc entries */
int __init snd_seq_info_init(void)
{
- queues_entry = create_info_entry("queues", 512 + (256 * SNDRV_SEQ_MAX_QUEUES),
+ queues_entry = create_info_entry("queues",
snd_seq_info_queues_read);
- clients_entry = create_info_entry("clients", 512 + (256 * SNDRV_SEQ_MAX_CLIENTS),
+ clients_entry = create_info_entry("clients",
snd_seq_info_clients_read);
- timer_entry = create_info_entry("timer", 1024, snd_seq_info_timer_read);
+ timer_entry = create_info_entry("timer", snd_seq_info_timer_read);
return 0;
}
}
}
+EXPORT_SYMBOL(snd_use_lock_sync_helper);
+
#endif
return 0;
}
+EXPORT_SYMBOL(snd_seq_dump_var_event);
+
/*
* exported:
return err < 0 ? err : newlen;
}
+EXPORT_SYMBOL(snd_seq_expand_var_event);
/*
* release this cell, free extended data if available
struct seq_midisynth *msynth, *ms;
struct snd_seq_port_info *port;
struct snd_rawmidi_info *info;
+ struct snd_rawmidi *rmidi = dev->private_data;
int newclient = 0;
unsigned int p, ports;
struct snd_seq_port_callback pcallbacks;
}
client->seq_client =
snd_seq_create_kernel_client(
- card, 0, "%s", info->name[0] ?
- (const char *)info->name : "External MIDI");
+ card, 0, "%s", card->shortname[0] ?
+ (const char *)card->shortname : "External MIDI");
if (client->seq_client < 0) {
kfree(client);
mutex_unlock(®ister_mutex);
if ((port->capability & (SNDRV_SEQ_PORT_CAP_WRITE|SNDRV_SEQ_PORT_CAP_READ)) == (SNDRV_SEQ_PORT_CAP_WRITE|SNDRV_SEQ_PORT_CAP_READ) &&
info->flags & SNDRV_RAWMIDI_INFO_DUPLEX)
port->capability |= SNDRV_SEQ_PORT_CAP_DUPLEX;
- port->type = SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC;
+ port->type = SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC
+ | SNDRV_SEQ_PORT_TYPE_HARDWARE
+ | SNDRV_SEQ_PORT_TYPE_PORT;
port->midi_channels = 16;
memset(&pcallbacks, 0, sizeof(pcallbacks));
pcallbacks.owner = THIS_MODULE;
pcallbacks.unuse = midisynth_unuse;
pcallbacks.event_input = event_process_midi;
port->kernel = &pcallbacks;
+ if (rmidi->ops && rmidi->ops->get_port_info)
+ rmidi->ops->get_port_info(rmidi, p, port);
if (snd_seq_kernel_client_ctl(client->seq_client, SNDRV_SEQ_IOCTL_CREATE_PORT, port)<0)
goto __nomem;
ms->seq_client = client->seq_client;
{
struct list_head *p, *n;
- down_write(&grp->list_mutex);
list_for_each_safe(p, n, &grp->list_head) {
struct snd_seq_subscribers *subs;
struct snd_seq_client *c;
snd_seq_client_unlock(c);
}
}
- up_write(&grp->list_mutex);
}
/* delete port data */
return ret;
}
+EXPORT_SYMBOL(snd_seq_event_port_attach);
/*
* Detach the driver from a port.
return err;
}
+
+EXPORT_SYMBOL(snd_seq_event_port_detach);
pinfo->capability |= SNDRV_SEQ_PORT_CAP_WRITE | SNDRV_SEQ_PORT_CAP_SYNC_WRITE | SNDRV_SEQ_PORT_CAP_SUBS_WRITE;
pinfo->capability |= SNDRV_SEQ_PORT_CAP_READ | SNDRV_SEQ_PORT_CAP_SYNC_READ | SNDRV_SEQ_PORT_CAP_SUBS_READ;
pinfo->capability |= SNDRV_SEQ_PORT_CAP_DUPLEX;
- pinfo->type = SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC;
+ pinfo->type = SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC
+ | SNDRV_SEQ_PORT_TYPE_SOFTWARE
+ | SNDRV_SEQ_PORT_TYPE_PORT;
pinfo->midi_channels = 16;
memset(&pcallbacks, 0, sizeof(pcallbacks));
pcallbacks.owner = THIS_MODULE;
static int major = CONFIG_SND_MAJOR;
int snd_major;
+EXPORT_SYMBOL(snd_major);
+
static int cards_limit = 1;
static int device_mode = S_IFCHR | S_IRUGO | S_IWUGO;
* modules are loaded manually, this limit number increases, too.
*/
int snd_ecards_limit;
+EXPORT_SYMBOL(snd_ecards_limit);
static struct snd_minor *snd_minors[SNDRV_OS_MINORS];
static DEFINE_MUTEX(sound_mutex);
*/
void snd_request_card(int card)
{
- int locked;
-
if (! current->fs->root)
return;
- read_lock(&snd_card_rwlock);
- locked = snd_cards_lock & (1 << card);
- read_unlock(&snd_card_rwlock);
- if (locked)
+ if (snd_card_locked(card))
return;
if (card < 0 || card >= cards_limit)
return;
request_module("snd-card-%i", card);
}
+EXPORT_SYMBOL(snd_request_card);
+
static void snd_request_other(int minor)
{
char *str;
return private_data;
}
+EXPORT_SYMBOL(snd_lookup_minor_data);
+
static int snd_open(struct inode *inode, struct file *file)
{
unsigned int minor = iminor(inode);
return 0;
}
+EXPORT_SYMBOL(snd_register_device);
+
/**
* snd_unregister_device - unregister the device on the given card
* @type: the device type, SNDRV_DEVICE_TYPE_XXX
return 0;
}
+EXPORT_SYMBOL(snd_unregister_device);
+
#ifdef CONFIG_PROC_FS
/*
* INFO PART
*/
-static struct snd_info_entry *snd_minor_info_entry = NULL;
+static struct snd_info_entry *snd_minor_info_entry;
static const char *snd_device_type_name(int type)
{
entry = snd_info_create_module_entry(THIS_MODULE, "devices", NULL);
if (entry) {
- entry->c.text.read_size = PAGE_SIZE;
entry->c.text.read = snd_minor_info_read;
if (snd_info_register(entry) < 0) {
snd_info_free_entry(entry);
module_init(alsa_sound_init)
module_exit(alsa_sound_exit)
-
- /* sound.c */
-EXPORT_SYMBOL(snd_major);
-EXPORT_SYMBOL(snd_ecards_limit);
-#if defined(CONFIG_KMOD)
-EXPORT_SYMBOL(snd_request_card);
-#endif
-EXPORT_SYMBOL(snd_register_device);
-EXPORT_SYMBOL(snd_unregister_device);
-EXPORT_SYMBOL(snd_lookup_minor_data);
-#if defined(CONFIG_SND_OSSEMUL)
-EXPORT_SYMBOL(snd_register_oss_device);
-EXPORT_SYMBOL(snd_unregister_oss_device);
-EXPORT_SYMBOL(snd_lookup_oss_minor_data);
-#endif
- /* memory.c */
-EXPORT_SYMBOL(copy_to_user_fromio);
-EXPORT_SYMBOL(copy_from_user_toio);
- /* init.c */
-EXPORT_SYMBOL(snd_cards);
-#if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE)
-EXPORT_SYMBOL(snd_mixer_oss_notify_callback);
-#endif
-EXPORT_SYMBOL(snd_card_new);
-EXPORT_SYMBOL(snd_card_disconnect);
-EXPORT_SYMBOL(snd_card_free);
-EXPORT_SYMBOL(snd_card_free_in_thread);
-EXPORT_SYMBOL(snd_card_register);
-EXPORT_SYMBOL(snd_component_add);
-EXPORT_SYMBOL(snd_card_file_add);
-EXPORT_SYMBOL(snd_card_file_remove);
-#ifdef CONFIG_PM
-EXPORT_SYMBOL(snd_power_wait);
-#endif
- /* device.c */
-EXPORT_SYMBOL(snd_device_new);
-EXPORT_SYMBOL(snd_device_register);
-EXPORT_SYMBOL(snd_device_free);
- /* isadma.c */
-#ifdef CONFIG_ISA_DMA_API
-EXPORT_SYMBOL(snd_dma_program);
-EXPORT_SYMBOL(snd_dma_disable);
-EXPORT_SYMBOL(snd_dma_pointer);
-#endif
- /* info.c */
-#ifdef CONFIG_PROC_FS
-EXPORT_SYMBOL(snd_seq_root);
-EXPORT_SYMBOL(snd_iprintf);
-EXPORT_SYMBOL(snd_info_get_line);
-EXPORT_SYMBOL(snd_info_get_str);
-EXPORT_SYMBOL(snd_info_create_module_entry);
-EXPORT_SYMBOL(snd_info_create_card_entry);
-EXPORT_SYMBOL(snd_info_free_entry);
-EXPORT_SYMBOL(snd_info_register);
-EXPORT_SYMBOL(snd_info_unregister);
-EXPORT_SYMBOL(snd_card_proc_new);
-#endif
- /* info_oss.c */
-#if defined(CONFIG_SND_OSSEMUL) && defined(CONFIG_PROC_FS)
-EXPORT_SYMBOL(snd_oss_info_register);
-#endif
- /* control.c */
-EXPORT_SYMBOL(snd_ctl_new);
-EXPORT_SYMBOL(snd_ctl_new1);
-EXPORT_SYMBOL(snd_ctl_free_one);
-EXPORT_SYMBOL(snd_ctl_add);
-EXPORT_SYMBOL(snd_ctl_remove);
-EXPORT_SYMBOL(snd_ctl_remove_id);
-EXPORT_SYMBOL(snd_ctl_rename_id);
-EXPORT_SYMBOL(snd_ctl_find_numid);
-EXPORT_SYMBOL(snd_ctl_find_id);
-EXPORT_SYMBOL(snd_ctl_notify);
-EXPORT_SYMBOL(snd_ctl_register_ioctl);
-EXPORT_SYMBOL(snd_ctl_unregister_ioctl);
-#ifdef CONFIG_COMPAT
-EXPORT_SYMBOL(snd_ctl_register_ioctl_compat);
-EXPORT_SYMBOL(snd_ctl_unregister_ioctl_compat);
-#endif
-EXPORT_SYMBOL(snd_ctl_elem_read);
-EXPORT_SYMBOL(snd_ctl_elem_write);
- /* misc.c */
-EXPORT_SYMBOL(release_and_free_resource);
-#ifdef CONFIG_SND_VERBOSE_PRINTK
-EXPORT_SYMBOL(snd_verbose_printk);
-#endif
-#if defined(CONFIG_SND_DEBUG) && defined(CONFIG_SND_VERBOSE_PRINTK)
-EXPORT_SYMBOL(snd_verbose_printd);
-#endif
return private_data;
}
+EXPORT_SYMBOL(snd_lookup_oss_minor_data);
+
static int snd_oss_kernel_minor(int type, struct snd_card *card, int dev)
{
int minor;
return -EBUSY;
}
+EXPORT_SYMBOL(snd_register_oss_device);
+
int snd_unregister_oss_device(int type, struct snd_card *card, int dev)
{
int minor = snd_oss_kernel_minor(type, card, dev);
return 0;
}
+EXPORT_SYMBOL(snd_unregister_oss_device);
+
/*
* INFO PART
*/
#ifdef CONFIG_PROC_FS
-static struct snd_info_entry *snd_minor_info_oss_entry = NULL;
+static struct snd_info_entry *snd_minor_info_oss_entry;
static const char *snd_oss_device_type_name(int type)
{
entry = snd_info_create_module_entry(THIS_MODULE, "devices", snd_oss_root);
if (entry) {
- entry->c.text.read_size = PAGE_SIZE;
entry->c.text.read = snd_minor_info_oss_read;
if (snd_info_register(entry) < 0) {
snd_info_free_entry(entry);
static void snd_timer_proc_read(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
- unsigned long flags;
struct snd_timer *timer;
struct snd_timer_instance *ti;
struct list_head *p, *q;
if (timer->hw.flags & SNDRV_TIMER_HW_SLAVE)
snd_iprintf(buffer, " SLAVE");
snd_iprintf(buffer, "\n");
- spin_lock_irqsave(&timer->lock, flags);
list_for_each(q, &timer->open_list_head) {
ti = list_entry(q, struct snd_timer_instance, open_list);
snd_iprintf(buffer, " Client %s : %s\n",
SNDRV_TIMER_IFLG_RUNNING)
? "running" : "stopped");
}
- spin_unlock_irqrestore(&timer->lock, flags);
}
mutex_unlock(®ister_mutex);
}
-static struct snd_info_entry *snd_timer_proc_entry = NULL;
+static struct snd_info_entry *snd_timer_proc_entry;
static void __init snd_timer_proc_init(void)
{
entry = snd_info_create_module_entry(THIS_MODULE, "timers", NULL);
if (entry != NULL) {
- entry->c.text.read_size = SNDRV_TIMER_DEVICES * 128;
entry->c.text.read = snd_timer_proc_read;
if (snd_info_register(entry) < 0) {
snd_info_free_entry(entry);
i, NULL, 0);
if (IS_ERR(device))
continue;
+ if (!platform_get_drvdata(device)) {
+ platform_device_unregister(device);
+ continue;
+ }
devices[i] = device;
cards++;
}
i, NULL, 0);
if (IS_ERR(device))
continue;
+ if (!platform_get_drvdata(device)) {
+ platform_device_unregister(device);
+ continue;
+ }
platform_devices[i] = device;
snd_mpu401_devices++;
}
#define MPU401_ACK 0xfe
/* Build in lowlevel io */
-static void mpu401_write_port(struct snd_mpu401 *mpu, unsigned char data, unsigned long addr)
+static void mpu401_write_port(struct snd_mpu401 *mpu, unsigned char data,
+ unsigned long addr)
{
outb(data, addr);
}
-static unsigned char mpu401_read_port(struct snd_mpu401 *mpu, unsigned long addr)
+static unsigned char mpu401_read_port(struct snd_mpu401 *mpu,
+ unsigned long addr)
{
return inb(addr);
}
-static void mpu401_write_mmio(struct snd_mpu401 *mpu, unsigned char data, unsigned long addr)
+static void mpu401_write_mmio(struct snd_mpu401 *mpu, unsigned char data,
+ unsigned long addr)
{
writeb(data, (void __iomem *)addr);
}
-static unsigned char mpu401_read_mmio(struct snd_mpu401 *mpu, unsigned long addr)
+static unsigned char mpu401_read_mmio(struct snd_mpu401 *mpu,
+ unsigned long addr)
{
return readb((void __iomem *)addr);
}
mpu->read(mpu, MPU401D(mpu));
#ifdef CONFIG_SND_DEBUG
if (timeout <= 0)
- snd_printk("cmd: clear rx timeout (status = 0x%x)\n", mpu->read(mpu, MPU401C(mpu)));
+ snd_printk(KERN_ERR "cmd: clear rx timeout (status = 0x%x)\n",
+ mpu->read(mpu, MPU401C(mpu)));
#endif
}
-static void _snd_mpu401_uart_interrupt(struct snd_mpu401 *mpu)
+static void uart_interrupt_tx(struct snd_mpu401 *mpu)
{
- spin_lock(&mpu->input_lock);
- if (test_bit(MPU401_MODE_BIT_INPUT, &mpu->mode)) {
- snd_mpu401_uart_input_read(mpu);
- } else {
- snd_mpu401_uart_clear_rx(mpu);
- }
- spin_unlock(&mpu->input_lock);
- /* ok. for better Tx performance try do some output when input is done */
if (test_bit(MPU401_MODE_BIT_OUTPUT, &mpu->mode) &&
test_bit(MPU401_MODE_BIT_OUTPUT_TRIGGER, &mpu->mode)) {
spin_lock(&mpu->output_lock);
}
}
+static void _snd_mpu401_uart_interrupt(struct snd_mpu401 *mpu)
+{
+ if (mpu->info_flags & MPU401_INFO_INPUT) {
+ spin_lock(&mpu->input_lock);
+ if (test_bit(MPU401_MODE_BIT_INPUT, &mpu->mode))
+ snd_mpu401_uart_input_read(mpu);
+ else
+ snd_mpu401_uart_clear_rx(mpu);
+ spin_unlock(&mpu->input_lock);
+ }
+ if (! (mpu->info_flags & MPU401_INFO_TX_IRQ))
+ /* ok. for better Tx performance try do some output
+ when input is done */
+ uart_interrupt_tx(mpu);
+}
+
/**
* snd_mpu401_uart_interrupt - generic MPU401-UART interrupt handler
* @irq: the irq number
*
* Processes the interrupt for MPU401-UART i/o.
*/
-irqreturn_t snd_mpu401_uart_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+irqreturn_t snd_mpu401_uart_interrupt(int irq, void *dev_id,
+ struct pt_regs *regs)
{
struct snd_mpu401 *mpu = dev_id;
return IRQ_HANDLED;
}
+EXPORT_SYMBOL(snd_mpu401_uart_interrupt);
+
+/**
+ * snd_mpu401_uart_interrupt_tx - generic MPU401-UART transmit irq handler
+ * @irq: the irq number
+ * @dev_id: mpu401 instance
+ * @regs: the reigster
+ *
+ * Processes the interrupt for MPU401-UART output.
+ */
+irqreturn_t snd_mpu401_uart_interrupt_tx(int irq, void *dev_id,
+ struct pt_regs *regs)
+{
+ struct snd_mpu401 *mpu = dev_id;
+
+ if (mpu == NULL)
+ return IRQ_NONE;
+ uart_interrupt_tx(mpu);
+ return IRQ_HANDLED;
+}
+
+EXPORT_SYMBOL(snd_mpu401_uart_interrupt_tx);
+
/*
* timer callback
* reprogram the timer and call the interrupt job
mpu->timer.expires = 1 + jiffies;
add_timer(&mpu->timer);
}
- mpu->timer_invoked |= input ? MPU401_MODE_INPUT_TIMER : MPU401_MODE_OUTPUT_TIMER;
+ mpu->timer_invoked |= input ? MPU401_MODE_INPUT_TIMER :
+ MPU401_MODE_OUTPUT_TIMER;
spin_unlock_irqrestore (&mpu->timer_lock, flags);
}
spin_lock_irqsave (&mpu->timer_lock, flags);
if (mpu->timer_invoked) {
- mpu->timer_invoked &= input ? ~MPU401_MODE_INPUT_TIMER : ~MPU401_MODE_OUTPUT_TIMER;
+ mpu->timer_invoked &= input ? ~MPU401_MODE_INPUT_TIMER :
+ ~MPU401_MODE_OUTPUT_TIMER;
if (! mpu->timer_invoked)
del_timer(&mpu->timer);
}
}
/*
-
+ * send a UART command
+ * return zero if successful, non-zero for some errors
*/
static int snd_mpu401_uart_cmd(struct snd_mpu401 * mpu, unsigned char cmd,
- int ack)
+ int ack)
{
unsigned long flags;
int timeout, ok;
}
/* ok. standard MPU-401 initialization */
if (mpu->hardware != MPU401_HW_SB) {
- for (timeout = 1000; timeout > 0 && !snd_mpu401_output_ready(mpu); timeout--)
+ for (timeout = 1000; timeout > 0 &&
+ !snd_mpu401_output_ready(mpu); timeout--)
udelay(10);
#ifdef CONFIG_SND_DEBUG
if (!timeout)
- snd_printk("cmd: tx timeout (status = 0x%x)\n", mpu->read(mpu, MPU401C(mpu)));
+ snd_printk(KERN_ERR "cmd: tx timeout (status = 0x%x)\n",
+ mpu->read(mpu, MPU401C(mpu)));
#endif
}
mpu->write(mpu, cmd, MPU401C(mpu));
}
if (!ok && mpu->read(mpu, MPU401D(mpu)) == MPU401_ACK)
ok = 1;
- } else {
+ } else
ok = 1;
- }
spin_unlock_irqrestore(&mpu->input_lock, flags);
if (!ok) {
- snd_printk("cmd: 0x%x failed at 0x%lx (status = 0x%x, data = 0x%x)\n", cmd, mpu->port, mpu->read(mpu, MPU401C(mpu)), mpu->read(mpu, MPU401D(mpu)));
+ snd_printk(KERN_ERR "cmd: 0x%x failed at 0x%lx "
+ "(status = 0x%x, data = 0x%x)\n", cmd, mpu->port,
+ mpu->read(mpu, MPU401C(mpu)),
+ mpu->read(mpu, MPU401D(mpu)));
return 1;
}
return 0;
/*
* trigger input callback
*/
-static void snd_mpu401_uart_input_trigger(struct snd_rawmidi_substream *substream, int up)
+static void
+snd_mpu401_uart_input_trigger(struct snd_rawmidi_substream *substream, int up)
{
unsigned long flags;
struct snd_mpu401 *mpu;
mpu = substream->rmidi->private_data;
if (up) {
- if (! test_and_set_bit(MPU401_MODE_BIT_INPUT_TRIGGER, &mpu->mode)) {
+ if (! test_and_set_bit(MPU401_MODE_BIT_INPUT_TRIGGER,
+ &mpu->mode)) {
/* first time - flush FIFO */
while (max-- > 0)
mpu->read(mpu, MPU401D(mpu));
unsigned char byte;
while (max-- > 0) {
- if (snd_mpu401_input_avail(mpu)) {
- byte = mpu->read(mpu, MPU401D(mpu));
- if (test_bit(MPU401_MODE_BIT_INPUT_TRIGGER, &mpu->mode))
- snd_rawmidi_receive(mpu->substream_input, &byte, 1);
- } else {
+ if (! snd_mpu401_input_avail(mpu))
break; /* input not available */
- }
+ byte = mpu->read(mpu, MPU401D(mpu));
+ if (test_bit(MPU401_MODE_BIT_INPUT_TRIGGER, &mpu->mode))
+ snd_rawmidi_receive(mpu->substream_input, &byte, 1);
}
}
int max = 256, timeout;
do {
- if (snd_rawmidi_transmit_peek(mpu->substream_output, &byte, 1) == 1) {
+ if (snd_rawmidi_transmit_peek(mpu->substream_output,
+ &byte, 1) == 1) {
for (timeout = 100; timeout > 0; timeout--) {
- if (snd_mpu401_output_ready(mpu)) {
- mpu->write(mpu, byte, MPU401D(mpu));
- snd_rawmidi_transmit_ack(mpu->substream_output, 1);
+ if (snd_mpu401_output_ready(mpu))
break;
- }
}
if (timeout == 0)
break; /* Tx FIFO full - try again later */
+ mpu->write(mpu, byte, MPU401D(mpu));
+ snd_rawmidi_transmit_ack(mpu->substream_output, 1);
} else {
snd_mpu401_uart_remove_timer (mpu, 0);
break; /* no other data - leave the tx loop */
/*
* output trigger callback
*/
-static void snd_mpu401_uart_output_trigger(struct snd_rawmidi_substream *substream, int up)
+static void
+snd_mpu401_uart_output_trigger(struct snd_rawmidi_substream *substream, int up)
{
unsigned long flags;
struct snd_mpu401 *mpu;
* since the output timer might have been removed in
* snd_mpu401_uart_output_write().
*/
- snd_mpu401_uart_add_timer(mpu, 0);
+ if (! (mpu->info_flags & MPU401_INFO_TX_IRQ))
+ snd_mpu401_uart_add_timer(mpu, 0);
/* output pending data */
spin_lock_irqsave(&mpu->output_lock, flags);
snd_mpu401_uart_output_write(mpu);
spin_unlock_irqrestore(&mpu->output_lock, flags);
} else {
- snd_mpu401_uart_remove_timer(mpu, 0);
+ if (! (mpu->info_flags & MPU401_INFO_TX_IRQ))
+ snd_mpu401_uart_remove_timer(mpu, 0);
clear_bit(MPU401_MODE_BIT_OUTPUT_TRIGGER, &mpu->mode);
}
}
* @device: the device index, zero-based
* @hardware: the hardware type, MPU401_HW_XXXX
* @port: the base address of MPU401 port
- * @integrated: non-zero if the port was already reserved by the chip
+ * @info_flags: bitflags MPU401_INFO_XXX
* @irq: the irq number, -1 if no interrupt for mpu
* @irq_flags: the irq request flags (SA_XXX), 0 if irq was already reserved.
* @rrawmidi: the pointer to store the new rawmidi instance
*/
int snd_mpu401_uart_new(struct snd_card *card, int device,
unsigned short hardware,
- unsigned long port, int integrated,
+ unsigned long port,
+ unsigned int info_flags,
int irq, int irq_flags,
struct snd_rawmidi ** rrawmidi)
{
struct snd_mpu401 *mpu;
struct snd_rawmidi *rmidi;
+ int in_enable, out_enable;
int err;
if (rrawmidi)
*rrawmidi = NULL;
- if ((err = snd_rawmidi_new(card, "MPU-401U", device, 1, 1, &rmidi)) < 0)
+ if (! (info_flags & (MPU401_INFO_INPUT | MPU401_INFO_OUTPUT)))
+ info_flags |= MPU401_INFO_INPUT | MPU401_INFO_OUTPUT;
+ in_enable = (info_flags & MPU401_INFO_INPUT) ? 1 : 0;
+ out_enable = (info_flags & MPU401_INFO_OUTPUT) ? 1 : 0;
+ if ((err = snd_rawmidi_new(card, "MPU-401U", device,
+ out_enable, in_enable, &rmidi)) < 0)
return err;
mpu = kzalloc(sizeof(*mpu), GFP_KERNEL);
if (mpu == NULL) {
spin_lock_init(&mpu->output_lock);
spin_lock_init(&mpu->timer_lock);
mpu->hardware = hardware;
- if (!integrated) {
+ if (! (info_flags & MPU401_INFO_INTEGRATED)) {
int res_size = hardware == MPU401_HW_PC98II ? 4 : 2;
- if ((mpu->res = request_region(port, res_size, "MPU401 UART")) == NULL) {
- snd_printk(KERN_ERR "mpu401_uart: unable to grab port 0x%lx size %d\n", port, res_size);
+ mpu->res = request_region(port, res_size, "MPU401 UART");
+ if (mpu->res == NULL) {
+ snd_printk(KERN_ERR "mpu401_uart: "
+ "unable to grab port 0x%lx size %d\n",
+ port, res_size);
snd_device_free(card, rmidi);
return -EBUSY;
}
}
- switch (hardware) {
- case MPU401_HW_AUREAL:
+ if (info_flags & MPU401_INFO_MMIO) {
mpu->write = mpu401_write_mmio;
mpu->read = mpu401_read_mmio;
- break;
- default:
+ } else {
mpu->write = mpu401_write_port;
mpu->read = mpu401_read_port;
- break;
}
mpu->port = port;
if (hardware == MPU401_HW_PC98II)
else
mpu->cport = port + 1;
if (irq >= 0 && irq_flags) {
- if (request_irq(irq, snd_mpu401_uart_interrupt, irq_flags, "MPU401 UART", (void *) mpu)) {
- snd_printk(KERN_ERR "mpu401_uart: unable to grab IRQ %d\n", irq);
+ if (request_irq(irq, snd_mpu401_uart_interrupt, irq_flags,
+ "MPU401 UART", (void *) mpu)) {
+ snd_printk(KERN_ERR "mpu401_uart: "
+ "unable to grab IRQ %d\n", irq);
snd_device_free(card, rmidi);
return -EBUSY;
}
}
+ mpu->info_flags = info_flags;
mpu->irq = irq;
mpu->irq_flags = irq_flags;
if (card->shortname[0])
- snprintf(rmidi->name, sizeof(rmidi->name), "%s MIDI", card->shortname);
+ snprintf(rmidi->name, sizeof(rmidi->name), "%s MIDI",
+ card->shortname);
else
- sprintf(rmidi->name, "MPU-401 MIDI %d-%d", card->number, device);
- snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_mpu401_uart_output);
- snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_mpu401_uart_input);
- rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT |
- SNDRV_RAWMIDI_INFO_INPUT |
- SNDRV_RAWMIDI_INFO_DUPLEX;
+ sprintf(rmidi->name, "MPU-401 MIDI %d-%d",card->number, device);
+ if (out_enable) {
+ snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT,
+ &snd_mpu401_uart_output);
+ rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT;
+ }
+ if (in_enable) {
+ snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT,
+ &snd_mpu401_uart_input);
+ rmidi->info_flags |= SNDRV_RAWMIDI_INFO_INPUT;
+ if (out_enable)
+ rmidi->info_flags |= SNDRV_RAWMIDI_INFO_DUPLEX;
+ }
mpu->rmidi = rmidi;
if (rrawmidi)
*rrawmidi = rmidi;
return 0;
}
-EXPORT_SYMBOL(snd_mpu401_uart_interrupt);
EXPORT_SYMBOL(snd_mpu401_uart_new);
/*
return err;
device = platform_device_register_simple(SND_MTPAV_DRIVER, -1, NULL, 0);
- if (IS_ERR(device)) {
- platform_driver_unregister(&snd_mtpav_driver);
- return PTR_ERR(device);
- }
- return 0;
+ if (!IS_ERR(device)) {
+ if (platform_get_drvdata(device))
+ return 0;
+ platform_device_unregister(device);
+ err = -ENODEV;
+ } else
+ err = PTR_ERR(device);
+ platform_driver_unregister(&snd_mtpav_driver);
+ return err;
}
static void __exit alsa_card_mtpav_exit(void)
}
}
+EXPORT_SYMBOL(snd_opl3_interrupt);
+
/*
*/
return 0;
}
+EXPORT_SYMBOL(snd_opl3_new);
+
int snd_opl3_init(struct snd_opl3 *opl3)
{
if (! opl3->command) {
return 0;
}
+EXPORT_SYMBOL(snd_opl3_init);
+
int snd_opl3_create(struct snd_card *card,
unsigned long l_port,
unsigned long r_port,
return 0;
}
+EXPORT_SYMBOL(snd_opl3_create);
+
int snd_opl3_timer_new(struct snd_opl3 * opl3, int timer1_dev, int timer2_dev)
{
int err;
return 0;
}
+EXPORT_SYMBOL(snd_opl3_timer_new);
+
int snd_opl3_hwdep_new(struct snd_opl3 * opl3,
int device, int seq_device,
struct snd_hwdep ** rhwdep)
return 0;
}
-EXPORT_SYMBOL(snd_opl3_interrupt);
-EXPORT_SYMBOL(snd_opl3_new);
-EXPORT_SYMBOL(snd_opl3_init);
-EXPORT_SYMBOL(snd_opl3_create);
-EXPORT_SYMBOL(snd_opl3_timer_new);
EXPORT_SYMBOL(snd_opl3_hwdep_new);
-/* opl3_synth.c */
-EXPORT_SYMBOL(snd_opl3_regmap);
-EXPORT_SYMBOL(snd_opl3_reset);
-
/*
* INIT part
*/
SNDRV_SEQ_PORT_CAP_WRITE,
SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC |
SNDRV_SEQ_PORT_TYPE_MIDI_GM |
- SNDRV_SEQ_PORT_TYPE_SYNTH,
+ SNDRV_SEQ_PORT_TYPE_HARDWARE |
+ SNDRV_SEQ_PORT_TYPE_SYNTHESIZER,
voices, voices,
name);
if (opl3->oss_chset->port < 0) {
SNDRV_SEQ_PORT_CAP_SUBS_WRITE,
SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC |
SNDRV_SEQ_PORT_TYPE_MIDI_GM |
- SNDRV_SEQ_PORT_TYPE_SYNTH,
+ SNDRV_SEQ_PORT_TYPE_DIRECT_SAMPLE |
+ SNDRV_SEQ_PORT_TYPE_HARDWARE |
+ SNDRV_SEQ_PORT_TYPE_SYNTHESIZER,
16, voices,
name);
if (opl3->chset->port < 0) {
{ 0x12, 0x15, 0x00, 0x00 } /* is selected (only left reg block) */
};
+EXPORT_SYMBOL(snd_opl3_regmap);
+
/*
* prototypes
*/
opl3->rhythm = 0;
}
+EXPORT_SYMBOL(snd_opl3_reset);
static int snd_opl3_play_note(struct snd_opl3 * opl3, struct snd_dm_fm_note * note)
{
return 0;
}
+
outb(value, opl4->pcm_port + 1);
}
+EXPORT_SYMBOL(snd_opl4_write);
+
u8 snd_opl4_read(struct snd_opl4 *opl4, u8 reg)
{
snd_opl4_wait(opl4);
return inb(opl4->pcm_port + 1);
}
+EXPORT_SYMBOL(snd_opl4_read);
+
void snd_opl4_read_memory(struct snd_opl4 *opl4, char *buf, int offset, int size)
{
unsigned long flags;
spin_unlock_irqrestore(&opl4->reg_lock, flags);
}
+EXPORT_SYMBOL(snd_opl4_read_memory);
+
void snd_opl4_write_memory(struct snd_opl4 *opl4, const char *buf, int offset, int size)
{
unsigned long flags;
spin_unlock_irqrestore(&opl4->reg_lock, flags);
}
+EXPORT_SYMBOL(snd_opl4_write_memory);
+
static void snd_opl4_enable_opl4(struct snd_opl4 *opl4)
{
outb(OPL3_REG_MODE, opl4->fm_port + 2);
return 0;
}
-EXPORT_SYMBOL(snd_opl4_write);
-EXPORT_SYMBOL(snd_opl4_read);
-EXPORT_SYMBOL(snd_opl4_write_memory);
-EXPORT_SYMBOL(snd_opl4_read_memory);
EXPORT_SYMBOL(snd_opl4_create);
static int __init alsa_opl4_init(void)
SNDRV_SEQ_PORT_CAP_WRITE |
SNDRV_SEQ_PORT_CAP_SUBS_WRITE,
SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC |
- SNDRV_SEQ_PORT_TYPE_MIDI_GM,
+ SNDRV_SEQ_PORT_TYPE_MIDI_GM |
+ SNDRV_SEQ_PORT_TYPE_HARDWARE |
+ SNDRV_SEQ_PORT_TYPE_SYNTHESIZER,
16, 24,
"OPL4 Wavetable Port");
if (opl4->chset->port < 0) {
i, NULL, 0);
if (IS_ERR(device))
continue;
+ if (!platform_get_drvdata(device)) {
+ platform_device_unregister(device);
+ continue;
+ }
devices[i] = device;
cards++;
}
i, NULL, 0);
if (IS_ERR(device))
continue;
+ if (!platform_get_drvdata(device)) {
+ platform_device_unregister(device);
+ continue;
+ }
devices[i] = device;
cards++;
}
return -EIO;
}
+EXPORT_SYMBOL(snd_vx_check_reg_bit);
+
/*
* vx_send_irq_dsp - set command irq bit
* @num: the requested IRQ type, IRQ_XXX
return 0;
}
+EXPORT_SYMBOL(snd_vx_load_boot_image);
+
/*
* vx_test_irq_src - query the source of interrupts
*
return IRQ_HANDLED;
}
+EXPORT_SYMBOL(snd_vx_irq_handler);
/*
*/
struct snd_info_entry *entry;
if (! snd_card_proc_new(chip->card, "vx-status", &entry))
- snd_info_set_text_ops(entry, chip, 1024, vx_proc_read);
+ snd_info_set_text_ops(entry, chip, vx_proc_read);
}
return 0;
}
+EXPORT_SYMBOL(snd_vx_dsp_boot);
+
/**
* snd_vx_dsp_load - load the DSP image
*/
return 0;
}
+EXPORT_SYMBOL(snd_vx_dsp_load);
+
#ifdef CONFIG_PM
/*
* suspend
return 0;
}
+EXPORT_SYMBOL(snd_vx_suspend);
+
/*
* resume
*/
return 0;
}
+EXPORT_SYMBOL(snd_vx_resume);
#endif
/**
return chip;
}
+EXPORT_SYMBOL(snd_vx_create);
+
/*
* module entries
*/
module_init(alsa_vx_core_init)
module_exit(alsa_vx_core_exit)
-
-/*
- * exports
- */
-EXPORT_SYMBOL(snd_vx_check_reg_bit);
-EXPORT_SYMBOL(snd_vx_create);
-EXPORT_SYMBOL(snd_vx_setup_firmware);
-EXPORT_SYMBOL(snd_vx_free_firmware);
-EXPORT_SYMBOL(snd_vx_irq_handler);
-EXPORT_SYMBOL(snd_vx_dsp_boot);
-EXPORT_SYMBOL(snd_vx_dsp_load);
-EXPORT_SYMBOL(snd_vx_load_boot_image);
-#ifdef CONFIG_PM
-EXPORT_SYMBOL(snd_vx_suspend);
-EXPORT_SYMBOL(snd_vx_resume);
-#endif
}
#endif /* SND_VX_FW_LOADER */
+
+EXPORT_SYMBOL(snd_vx_setup_firmware);
+EXPORT_SYMBOL(snd_vx_free_firmware);
return 0;
}
+EXPORT_SYMBOL(snd_i2c_bus_create);
+
int snd_i2c_device_create(struct snd_i2c_bus *bus, const char *name,
unsigned char addr, struct snd_i2c_device **rdevice)
{
return 0;
}
+EXPORT_SYMBOL(snd_i2c_device_create);
+
int snd_i2c_device_free(struct snd_i2c_device *device)
{
if (device->bus)
return 0;
}
+EXPORT_SYMBOL(snd_i2c_device_free);
+
int snd_i2c_sendbytes(struct snd_i2c_device *device, unsigned char *bytes, int count)
{
return device->bus->ops->sendbytes(device, bytes, count);
}
+EXPORT_SYMBOL(snd_i2c_sendbytes);
int snd_i2c_readbytes(struct snd_i2c_device *device, unsigned char *bytes, int count)
{
return device->bus->ops->readbytes(device, bytes, count);
}
+EXPORT_SYMBOL(snd_i2c_readbytes);
+
int snd_i2c_probeaddr(struct snd_i2c_bus *bus, unsigned short addr)
{
return bus->ops->probeaddr(bus, addr);
}
+EXPORT_SYMBOL(snd_i2c_probeaddr);
+
/*
* bit-operations
*/
return err;
}
-EXPORT_SYMBOL(snd_i2c_bus_create);
-EXPORT_SYMBOL(snd_i2c_device_create);
-EXPORT_SYMBOL(snd_i2c_device_free);
-EXPORT_SYMBOL(snd_i2c_sendbytes);
-EXPORT_SYMBOL(snd_i2c_readbytes);
-EXPORT_SYMBOL(snd_i2c_probeaddr);
static int __init alsa_i2c_init(void)
{
struct snd_info_entry *entry;
if (! snd_card_proc_new(card, "uda1341", &entry))
- snd_info_set_text_ops(entry, clnt, 1024, snd_uda1341_proc_read);
+ snd_info_set_text_ops(entry, clnt, snd_uda1341_proc_read);
if (! snd_card_proc_new(card, "uda1341-regs", &entry))
- snd_info_set_text_ops(entry, clnt, 1024, snd_uda1341_proc_regs_read);
+ snd_info_set_text_ops(entry, clnt, snd_uda1341_proc_regs_read);
}
/* }}} */
struct snd_info_entry *entry;
if (! snd_card_proc_new(gus->card, "gusirq", &entry))
- snd_info_set_text_ops(entry, gus, 1024, snd_gus_irq_info_read);
+ snd_info_set_text_ops(entry, gus, snd_gus_irq_info_read);
}
#endif
if (snd_gf1_mem_xalloc(alloc, &block) == NULL)
return -ENOMEM;
#ifdef CONFIG_SND_DEBUG
- if (! snd_card_proc_new(gus->card, "gusmem", &entry)) {
- snd_info_set_text_ops(entry, gus, 1024, snd_gf1_mem_info_read);
- entry->c.text.read_size = 256 * 1024;
- }
+ if (! snd_card_proc_new(gus->card, "gusmem", &entry))
+ snd_info_set_text_ops(entry, gus, snd_gf1_mem_info_read);
#endif
return 0;
}
&callbacks,
SNDRV_SEQ_PORT_CAP_WRITE | SNDRV_SEQ_PORT_CAP_SUBS_WRITE,
SNDRV_SEQ_PORT_TYPE_DIRECT_SAMPLE |
- SNDRV_SEQ_PORT_TYPE_SYNTH,
+ SNDRV_SEQ_PORT_TYPE_SYNTH |
+ SNDRV_SEQ_PORT_TYPE_HARDWARE |
+ SNDRV_SEQ_PORT_TYPE_SYNTHESIZER,
16, 0,
name);
if (p->chset->port < 0) {
static int dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 0,1,3,5,6,7 */
static int joystick_dac[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 29};
/* 0 to 31, (0.59V-4.52V or 0.389V-2.98V) */
-static int midi[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0};
+static int midi[SNDRV_CARDS];
static int pcm_channels[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 2};
-static int effect[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0};
+static int effect[SNDRV_CARDS];
#ifdef SNDRV_STB
#define PFX "interwave-stb: "
static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 0,1,3,5,9,11,12,15 */
static int dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 1,3,5,6,7 */
static int dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 1,3,5,6,7 */
-static int opl3sa3_ymode[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS-1)] = 0 }; /* 0,1,2,3 */ /*SL Added*/
+static int opl3sa3_ymode[SNDRV_CARDS]; /* 0,1,2,3 */ /*SL Added*/
module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Index value for OPL3-SA soundcard.");
spin_unlock_irqrestore(&chip->reg_lock, flags);
}
-static int __init snd_opl3sa2_detect(struct snd_opl3sa2 *chip)
+static int __devinit snd_opl3sa2_detect(struct snd_opl3sa2 *chip)
{
struct snd_card *card;
unsigned long port;
chip->master_volume = NULL;
}
-static int __init snd_opl3sa2_mixer(struct snd_opl3sa2 *chip)
+static int __devinit snd_opl3sa2_mixer(struct snd_opl3sa2 *chip)
{
struct snd_card *card = chip->card;
struct snd_ctl_elem_id id1, id2;
#endif /* CONFIG_PM */
#ifdef CONFIG_PNP
-static int __init snd_opl3sa2_pnp(int dev, struct snd_opl3sa2 *chip,
- struct pnp_dev *pdev)
+static int __devinit snd_opl3sa2_pnp(int dev, struct snd_opl3sa2 *chip,
+ struct pnp_dev *pdev)
{
struct pnp_resource_table * cfg;
int err;
};
#endif /* CONFIG_PNP */
-static int __init snd_opl3sa2_nonpnp_probe(struct platform_device *pdev)
+static int __devinit snd_opl3sa2_nonpnp_probe(struct platform_device *pdev)
{
struct snd_card *card;
int err;
struct snd_info_entry *entry;
if (! snd_card_proc_new(miro->card, "miro", &entry))
- snd_info_set_text_ops(entry, miro, 1024, snd_miro_proc_read);
+ snd_info_set_text_ops(entry, miro, snd_miro_proc_read);
}
/*
/*
*/
-static void __init
+static void __devinit
snd_emu8000_read_wait(struct snd_emu8000 *emu)
{
while ((EMU8000_SMALR_READ(emu) & 0x80000000) != 0) {
/*
*/
-static void __init
+static void __devinit
snd_emu8000_write_wait(struct snd_emu8000 *emu)
{
while ((EMU8000_SMALW_READ(emu) & 0x80000000) != 0) {
/*
* detect a card at the given port
*/
-static int __init
+static int __devinit
snd_emu8000_detect(struct snd_emu8000 *emu)
{
/* Initialise */
/*
* intiailize audio channels
*/
-static void __init
+static void __devinit
init_audio(struct snd_emu8000 *emu)
{
int ch;
/*
* initialize DMA address
*/
-static void __init
+static void __devinit
init_dma(struct snd_emu8000 *emu)
{
EMU8000_SMALR_WRITE(emu, 0);
* Taken from the oss driver, not obvious from the doc how this
* is meant to work
*/
-static void __init
+static void __devinit
send_array(struct snd_emu8000 *emu, unsigned short *data, int size)
{
int i;
* Send initialization arrays to start up, this just follows the
* initialisation sequence in the adip.
*/
-static void __init
+static void __devinit
init_arrays(struct snd_emu8000 *emu)
{
send_array(emu, init1, ARRAY_SIZE(init1)/4);
* seems that the only way to do this is to use the one channel and keep
* reallocating between read and write.
*/
-static void __init
+static void __devinit
size_dram(struct snd_emu8000 *emu)
{
int i, size;
/*
* The main initialization routine.
*/
-static void __init
+static void __devinit
snd_emu8000_init_hw(struct snd_emu8000 *emu)
{
int i;
/*
* create and attach mixer elements for WaveTable treble/bass controls
*/
-static int __init
+static int __devinit
snd_emu8000_create_mixer(struct snd_card *card, struct snd_emu8000 *emu)
{
int i, err = 0;
/*
* initialize and register emu8000 synth device.
*/
-int __init
+int __devinit
snd_emu8000_new(struct snd_card *card, int index, long port, int seq_ports,
struct snd_seq_device **awe_ret)
{
#include <asm/uaccess.h>
#include <linux/moduleparam.h>
-static int emu8000_reset_addr = 0;
+static int emu8000_reset_addr;
module_param(emu8000_reset_addr, int, 0444);
MODULE_PARM_DESC(emu8000_reset_addr, "reset write address at each time (makes slowdown)");
static int dma16[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 5,6,7 */
static int mic_agc[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1};
#ifdef CONFIG_SND_SB16_CSP
-static int csp[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0};
+static int csp[SNDRV_CARDS];
#endif
#ifdef SNDRV_SBAWE_EMU8000
static int seq_ports[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 4};
struct snd_info_entry *entry;
sprintf(name, "cspD%d", device);
if (! snd_card_proc_new(p->chip->card, name, &entry))
- snd_info_set_text_ops(entry, p, 1024, info_read);
+ snd_info_set_text_ops(entry, p, info_read);
return 0;
}
#include <sound/core.h>
#include <sound/sb.h>
-/*
-
- */
-irqreturn_t snd_sb8dsp_midi_interrupt(struct snd_sb * chip)
+irqreturn_t snd_sb8dsp_midi_interrupt(struct snd_sb *chip)
{
struct snd_rawmidi *rmidi;
int max = 64;
char byte;
- if (chip == NULL || (rmidi = chip->rmidi) == NULL) {
+ if (!chip)
+ return IRQ_NONE;
+
+ rmidi = chip->rmidi;
+ if (!rmidi) {
inb(SBP(chip, DATA_AVAIL)); /* ack interrupt */
return IRQ_NONE;
}
+
spin_lock(&chip->midi_input_lock);
while (max-- > 0) {
if (inb(SBP(chip, DATA_AVAIL)) & 0x80) {
return IRQ_HANDLED;
}
-/*
-
- */
-
static int snd_sb8dsp_midi_input_open(struct snd_rawmidi_substream *substream)
{
unsigned long flags;
snd_sb8dsp_midi_output_write(substream);
}
-/*
-
- */
-
static struct snd_rawmidi_ops snd_sb8dsp_midi_output =
{
.open = snd_sb8dsp_midi_output_open,
struct snd_rawmidi *rawmidi;
int err;
-#define MPU401_SHARE_HARDWARE 1
if ((err = snd_mpu401_uart_new(card, devnum,
MPU401_HW_MPU401,
- port, MPU401_SHARE_HARDWARE,
+ port, MPU401_INFO_INTEGRATED,
irq, SA_INTERRUPT,
&rawmidi)) == 0) {
struct snd_mpu401 *mpu = (struct snd_mpu401 *) rawmidi->private_data;
static long fm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */
static int dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 0,1,3,5,6,7 */
static int dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 0,1,3,5,6,7 */
-static int use_cs4232_midi[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0};
+static int use_cs4232_midi[SNDRV_CARDS];
module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Index value for WaveFront soundcard.");
This works better than the old code, so say Y.
config SND_CS5535AUDIO
- tristate "CS5535 Audio"
+ tristate "CS5535/CS5536 Audio"
depends on SND && X86 && !X86_64
select SND_PCM
select SND_AC97_CODEC
help
Say Y here to include support for audio on CS5535 chips. It is
referred to as NS CS5535 IO or AMD CS5535 IO companion in
- various literature.
+ various literature. This driver also supports the CS5536 audio
+ device. However, for both chips, on certain boards, you may
+ need to use ac97_quirk=hp_only if your board has physically
+ mapped headphone out to master output. If that works for you,
+ send lspci -vvv output to the mailing list so that your board
+ can be identified in the quirks list.
To compile this driver as a module, choose M here: the module
will be called snd-cs5535audio.
ac97->bus->ops->write(ac97, reg, value);
}
+EXPORT_SYMBOL(snd_ac97_write);
+
/**
* snd_ac97_read - read a value from the given register
*
return ac97->regs[reg];
}
+EXPORT_SYMBOL(snd_ac97_read);
+
/**
* snd_ac97_write_cache - write a value on the given register and update the cache
* @ac97: the ac97 instance
mutex_unlock(&ac97->reg_mutex);
}
+EXPORT_SYMBOL(snd_ac97_write_cache);
+
/**
* snd_ac97_update - update the value on the given register
* @ac97: the ac97 instance
return change;
}
+EXPORT_SYMBOL(snd_ac97_update);
+
/**
* snd_ac97_update_bits - update the bits on the given register
* @ac97: the ac97 instance
return change;
}
+EXPORT_SYMBOL(snd_ac97_update_bits);
+
/* no lock version - see snd_ac97_updat_bits() */
int snd_ac97_update_bits_nolock(struct snd_ac97 *ac97, unsigned short reg,
unsigned short mask, unsigned short value)
};
static const struct snd_kcontrol_new snd_ac97_controls_mic_boost =
- AC97_SINGLE("Mic Boost (+20dB)", AC97_MIC, 6, 1, 0);
+ AC97_SINGLE("Mic Boost (+20dB) Switch", AC97_MIC, 6, 1, 0);
static const char* std_rec_sel[] = {"Mic", "CD", "Video", "Aux", "Line", "Mix", "Mix Mono", "Phone"};
AC97_SINGLE("3D Control - Switch", AC97_GENERAL_PURPOSE, 13, 1, 0),
AC97_SINGLE("Loudness (bass boost)", AC97_GENERAL_PURPOSE, 12, 1, 0),
AC97_ENUM("Mono Output Select", std_enum[2]),
-AC97_ENUM("Mic Select", std_enum[3]),
+AC97_ENUM("Mic Select Capture Switch", std_enum[3]),
AC97_SINGLE("ADC/DAC Loopback", AC97_GENERAL_PURPOSE, 7, 1, 0)
};
ac97->regs[AC97_CENTER_LFE_MASTER] = 0x8080;
/* build center controls */
- if (snd_ac97_try_volume_mix(ac97, AC97_CENTER_LFE_MASTER)) {
+ if ((snd_ac97_try_volume_mix(ac97, AC97_CENTER_LFE_MASTER))
+ && !(ac97->flags & AC97_AD_MULTI)) {
if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_center[0], ac97))) < 0)
return err;
if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_center[1], ac97))) < 0)
}
/* build LFE controls */
- if (snd_ac97_try_volume_mix(ac97, AC97_CENTER_LFE_MASTER+1)) {
+ if ((snd_ac97_try_volume_mix(ac97, AC97_CENTER_LFE_MASTER+1))
+ && !(ac97->flags & AC97_AD_MULTI)) {
if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_lfe[0], ac97))) < 0)
return err;
if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_lfe[1], ac97))) < 0)
}
/* build surround controls */
- if (snd_ac97_try_volume_mix(ac97, AC97_SURROUND_MASTER)) {
+ if ((snd_ac97_try_volume_mix(ac97, AC97_SURROUND_MASTER))
+ && !(ac97->flags & AC97_AD_MULTI)) {
/* Surround Master (0x38) is with stereo mutes */
if ((err = snd_ac97_cmix_new_stereo(card, "Surround Playback", AC97_SURROUND_MASTER, 1, ac97)) < 0)
return err;
}
/* build Aux controls */
- if (snd_ac97_try_volume_mix(ac97, AC97_AUX)) {
- if ((err = snd_ac97_cmix_new(card, "Aux Playback", AC97_AUX, ac97)) < 0)
- return err;
+ if (!(ac97->flags & AC97_HAS_NO_AUX)) {
+ if (snd_ac97_try_volume_mix(ac97, AC97_AUX)) {
+ if ((err = snd_ac97_cmix_new(card, "Aux Playback", AC97_AUX, ac97)) < 0)
+ return err;
+ }
}
/* build PCM controls */
return "unknown codec";
}
+EXPORT_SYMBOL(snd_ac97_get_short_name);
/* wait for a while until registers are accessible after RESET
* return 0 if ok, negative not ready
return 0;
}
+EXPORT_SYMBOL(snd_ac97_bus);
+
/* stop no dev release warning */
static void ac97_device_release(struct device * dev)
{
return 0;
}
+EXPORT_SYMBOL(snd_ac97_mixer);
/*
* Power down the chip.
snd_ac97_powerdown(ac97);
}
+EXPORT_SYMBOL(snd_ac97_suspend);
+
/*
* restore ac97 status
*/
snd_ac97_restore_iec958(ac97);
}
}
+
+EXPORT_SYMBOL(snd_ac97_resume);
#endif
return 0;
}
-
-/*
- * Exported symbols
- */
-
-EXPORT_SYMBOL(snd_ac97_write);
-EXPORT_SYMBOL(snd_ac97_read);
-EXPORT_SYMBOL(snd_ac97_write_cache);
-EXPORT_SYMBOL(snd_ac97_update);
-EXPORT_SYMBOL(snd_ac97_update_bits);
-EXPORT_SYMBOL(snd_ac97_get_short_name);
-EXPORT_SYMBOL(snd_ac97_bus);
-EXPORT_SYMBOL(snd_ac97_mixer);
-EXPORT_SYMBOL(snd_ac97_pcm_assign);
-EXPORT_SYMBOL(snd_ac97_pcm_open);
-EXPORT_SYMBOL(snd_ac97_pcm_close);
-EXPORT_SYMBOL(snd_ac97_pcm_double_rate_rules);
EXPORT_SYMBOL(snd_ac97_tune_hardware);
-EXPORT_SYMBOL(snd_ac97_set_rate);
-#ifdef CONFIG_PM
-EXPORT_SYMBOL(snd_ac97_resume);
-EXPORT_SYMBOL(snd_ac97_suspend);
-#endif
/*
* INIT part
{
/* WM9705, WM9710 */
ac97->build_ops = &patch_wolfson_wm9705_ops;
+#ifdef CONFIG_TOUCHSCREEN_WM9705
+ /* WM9705 touchscreen uses AUX and VIDEO for touch */
+ ac97->flags |=3D AC97_HAS_NO_VIDEO | AC97_HAS_NO_AUX;
+#endif
return 0;
}
snd_ac97_restore_iec958(ac97);
}
+
+static void ad1888_resume(struct snd_ac97 *ac97)
+{
+ ad18xx_resume(ac97);
+ snd_ac97_write_cache(ac97, AC97_CODEC_CLASS_REV, 0x8080);
+}
+
#endif
int patch_ad1819(struct snd_ac97 * ac97)
* (SS vendor << 16 | device)
*/
static unsigned int ad1981_jacks_blacklist[] = {
+ 0x10140537, /* Thinkpad T41p */
0x10140554, /* Thinkpad T42p/R50p */
0 /* end */
};
.build_post_spdif = patch_ad198x_post_spdif,
.build_specific = patch_ad1888_specific,
#ifdef CONFIG_PM
- .resume = ad18xx_resume,
+ .resume = ad1888_resume,
#endif
.update_jacks = ad1888_update_jacks,
};
/* Enable SPDIF-IN only on Rev.E and above */
val = snd_ac97_read(ac97, AC97_ALC650_CLOCK);
/* SPDIF IN with pin 47 */
- if (ac97->spec.dev_flags)
+ if (ac97->spec.dev_flags &&
+ /* ASUS A6KM requires EAPD */
+ ! (ac97->subsystem_vendor == 0x1043 &&
+ ac97->subsystem_device == 0x1103))
val |= 0x03; /* enable */
else
val &= ~0x03; /* disable */
return 0;
}
+EXPORT_SYMBOL(snd_ac97_set_rate);
+
static unsigned short get_pslots(struct snd_ac97 *ac97, unsigned char *rate_table, unsigned short *spdif_slots)
{
if (!ac97_is_audio(ac97))
return 0;
}
+EXPORT_SYMBOL(snd_ac97_pcm_assign);
+
/**
* snd_ac97_pcm_open - opens the given AC97 pcm
* @pcm: the ac97 pcm instance
return err;
}
+EXPORT_SYMBOL(snd_ac97_pcm_open);
+
/**
* snd_ac97_pcm_close - closes the given AC97 pcm
* @pcm: the ac97 pcm instance
return 0;
}
+EXPORT_SYMBOL(snd_ac97_pcm_close);
+
static int double_rate_hw_constraint_rate(struct snd_pcm_hw_params *params,
struct snd_pcm_hw_rule *rule)
{
SNDRV_PCM_HW_PARAM_RATE, -1);
return err;
}
+
+EXPORT_SYMBOL(snd_ac97_pcm_double_rate_rules);
prefix = ac97_is_audio(ac97) ? "ac97" : "mc97";
sprintf(name, "%s#%d-%d", prefix, ac97->addr, ac97->num);
if ((entry = snd_info_create_card_entry(ac97->bus->card, name, ac97->bus->proc)) != NULL) {
- snd_info_set_text_ops(entry, ac97, 1024, snd_ac97_proc_read);
+ snd_info_set_text_ops(entry, ac97, snd_ac97_proc_read);
if (snd_info_register(entry) < 0) {
snd_info_free_entry(entry);
entry = NULL;
ac97->proc = entry;
sprintf(name, "%s#%d-%d+regs", prefix, ac97->addr, ac97->num);
if ((entry = snd_info_create_card_entry(ac97->bus->card, name, ac97->bus->proc)) != NULL) {
- snd_info_set_text_ops(entry, ac97, 1024, snd_ac97_proc_regs_read);
+ snd_info_set_text_ops(entry, ac97, snd_ac97_proc_regs_read);
#ifdef CONFIG_SND_DEBUG
entry->mode |= S_IWUSR;
- entry->c.text.write_size = 1024;
entry->c.text.write = snd_ac97_proc_regs_write;
#endif
if (snd_info_register(entry) < 0) {
struct snd_info_entry *entry;
if (! snd_card_proc_new(card, "ak4531", &entry))
- snd_info_set_text_ops(entry, ak4531, 1024, snd_ak4531_proc_read);
+ snd_info_set_text_ops(entry, ak4531, snd_ak4531_proc_read);
}
#endif
struct snd_info_entry *entry;
if (!snd_card_proc_new(chip->card, chip->card->driver, &entry))
- snd_info_set_text_ops(entry, chip, 1024, snd_ad1889_proc_read);
+ snd_info_set_text_ops(entry, chip, snd_ad1889_proc_read);
}
static struct ac97_quirk ac97_quirks[] = {
static int index = SNDRV_DEFAULT_IDX1; /* Index */
static char *id = SNDRV_DEFAULT_STR1; /* ID for this card */
static int pcm_channels = 32;
-static int spdif = 0;
+static int spdif;
module_param(index, int, 0444);
MODULE_PARM_DESC(index, "Index value for ALI M5451 PCI Audio.");
{
struct snd_info_entry *entry;
if(!snd_card_proc_new(codec->card, "ali5451", &entry))
- snd_info_set_text_ops(entry, codec, 1024, snd_ali_proc_read);
+ snd_info_set_text_ops(entry, codec, snd_ali_proc_read);
}
static int __devinit snd_ali_resources(struct snd_ali *codec)
card->shortname, chip->alt_port, chip->irq);
if ((err = snd_mpu401_uart_new( card, 0, MPU401_HW_ALS4000,
- gcr+0x30, 1, pci->irq, 0,
- &chip->rmidi)) < 0) {
+ gcr+0x30, MPU401_INFO_INTEGRATED,
+ pci->irq, 0, &chip->rmidi)) < 0) {
printk(KERN_ERR "als4000: no MPU-401 device at 0x%lx?\n", gcr+0x30);
goto out_err;
}
struct snd_info_entry *entry;
if (! snd_card_proc_new(chip->card, "atiixp", &entry))
- snd_info_set_text_ops(entry, chip, 1024, snd_atiixp_proc_read);
+ snd_info_set_text_ops(entry, chip, snd_atiixp_proc_read);
}
#else /* !CONFIG_PROC_FS */
#define snd_atiixp_proc_init(chip)
struct snd_info_entry *entry;
if (! snd_card_proc_new(chip->card, "atiixp-modem", &entry))
- snd_info_set_text_ops(entry, chip, 1024, snd_atiixp_proc_read);
+ snd_info_set_text_ops(entry, chip, snd_atiixp_proc_read);
}
#else
#define snd_atiixp_proc_init(chip)
return err;
}
snd_vortex_workaround(pci, pcifix[dev]);
+
+ // Card details needed in snd_vortex_midi
+ strcpy(card->driver, CARD_NAME_SHORT);
+ sprintf(card->shortname, "Aureal Vortex %s", CARD_NAME_SHORT);
+ sprintf(card->longname, "%s at 0x%lx irq %i",
+ card->shortname, chip->io, chip->irq);
+
// (4) Alloc components.
// ADB pcm.
if ((err = snd_vortex_new_pcm(chip, VORTEX_PCM_ADB, NR_ADB)) < 0) {
#endif
// (5)
- strcpy(card->driver, CARD_NAME_SHORT);
- strcpy(card->shortname, CARD_NAME_SHORT);
- sprintf(card->longname, "%s at 0x%lx irq %i",
- card->shortname, chip->io, chip->irq);
-
if ((err = pci_read_config_word(pci, PCI_DEVICE_ID,
&(chip->device))) < 0) {
snd_card_free(card);
struct snd_rawmidi *rmidi;
int temp, mode;
struct snd_mpu401 *mpu;
- int port;
+ unsigned long port;
#ifdef VORTEX_MPU401_LEGACY
/* EnableHardCodedMPU401Port() */
temp |= (MIDI_CLOCK_DIV << 8) | ((mode >> 24) & 0xff) << 4;
hwwrite(vortex->mmio, VORTEX_CTRL2, temp);
hwwrite(vortex->mmio, VORTEX_MIDI_CMD, MPU401_RESET);
- /* Set some kind of mode */
- if (mode)
- hwwrite(vortex->mmio, VORTEX_MIDI_CMD, MPU401_ENTER_UART);
/* Check if anything is OK. */
temp = hwread(vortex->mmio, VORTEX_MIDI_DATA);
port = (unsigned long)(vortex->mmio + VORTEX_MIDI_DATA);
if ((temp =
snd_mpu401_uart_new(vortex->card, 0, MPU401_HW_AUREAL, port,
- 1, 0, 0, &rmidi)) != 0) {
+ MPU401_INFO_INTEGRATED | MPU401_INFO_MMIO,
+ 0, 0, &rmidi)) != 0) {
hwwrite(vortex->mmio, VORTEX_CTRL,
(hwread(vortex->mmio, VORTEX_CTRL) &
~CTRL_MIDI_PORT) & ~CTRL_MIDI_EN);
mpu = rmidi->private_data;
mpu->cport = (unsigned long)(vortex->mmio + VORTEX_MIDI_CMD);
#endif
+ /* Overwrite MIDI name */
+ snprintf(rmidi->name, sizeof(rmidi->name), "%s MIDI %d", CARD_NAME_SHORT , vortex->card->number);
+
vortex->rmidi = rmidi;
return 0;
}
0
//0x7FFF,0x7FFF,0x7FFF,0x7FFF,0x7fff,0x7FFF,0x7FFF,0x7FFF,0x7FFF,0x7fff
};
-static xtalk_gains_t const asXtalkGainsZeros = {
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
-};
+static xtalk_gains_t const asXtalkGainsZeros;
-static xtalk_dline_t const alXtalkDlineZeros = {
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0
-};
+static xtalk_dline_t const alXtalkDlineZeros;
static xtalk_dline_t const alXtalkDlineTest = {
0xFC18, 0x03E8FFFF, 0x186A0, 0x7960FFFE, 1, 0xFFFFFFFF,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0
};
-static xtalk_instate_t const asXtalkInStateZeros = { 0, 0, 0, 0 };
+static xtalk_instate_t const asXtalkInStateZeros;
static xtalk_instate_t const asXtalkInStateTest =
{ 0xFF80, 0x0080, 0xFFFF, 0x0001 };
-static xtalk_state_t const asXtalkOutStateZeros = {
- {0, 0, 0, 0},
- {0, 0, 0, 0},
- {0, 0, 0, 0},
- {0, 0, 0, 0},
- {0, 0, 0, 0}
-};
+static xtalk_state_t const asXtalkOutStateZeros;
+
static short const sDiamondKLeftEq = 0x401d;
static short const sDiamondKRightEq = 0x401d;
static short const sDiamondKLeftXt = 0xF90E;
{0, 0, 0, 0, 0}
};
-static xtalk_coefs_t const asXtalkCoefsZeros = {
- {0, 0, 0, 0, 0},
- {0, 0, 0, 0, 0},
- {0, 0, 0, 0, 0},
- {0, 0, 0, 0, 0},
- {0, 0, 0, 0, 0}
-};
+static xtalk_coefs_t const asXtalkCoefsZeros;
static xtalk_coefs_t const asXtalkCoefsPipe = {
{0, 0, 0x0FA0, 0, 0},
{0, 0, 0x0FA0, 0, 0},
* in the first place >:-P}),
* I was forced to base this driver on reverse engineering
* (3 weeks' worth of evenings filled with driver work).
- * (and no, I did NOT go the easy way: to pick up a PCI128 for 9 Euros)
+ * (and no, I did NOT go the easy way: to pick up a SB PCI128 for 9 Euros)
*
* The AZF3328 chip (note: AZF3328, *not* AZT3328, that's just the driver name
* for compatibility reasons) has the following features:
*
* - builtin AC97 conformant codec (SNR over 80dB)
- * (really AC97 compliant?? I really doubt it when looking
- * at the mixer register layout)
+ * Note that "conformant" != "compliant"!! this chip's mixer register layout
+ * *differs* from the standard AC97 layout:
+ * they chose to not implement the headphone register (which is not a
+ * problem since it's merely optional), yet when doing this, they committed
+ * the grave sin of letting other registers follow immediately instead of
+ * keeping a headphone dummy register, thereby shifting the mixer register
+ * addresses illegally. So far unfortunately it looks like the very flexible
+ * ALSA AC97 support is still not enough to easily compensate for such a
+ * grave layout violation despite all tweaks and quirks mechanisms it offers.
* - builtin genuine OPL3
* - full duplex 16bit playback/record at independent sampling rate
* - MPU401 (+ legacy address support) FIXME: how to enable legacy addr??
*
* TODO
* - test MPU401 MIDI playback etc.
- * - power management. See e.g. intel8x0 or cs4281.
- * This would be nice since the chip runs a bit hot, and it's *required*
- * anyway for proper ACPI power management.
+ * - add some power micro-management (disable various units of the card
+ * as long as they're unused). However this requires I/O ports which I
+ * haven't figured out yet and which thus might not even exist...
+ * The standard suspend/resume functionality could probably make use of
+ * some improvement, too...
* - figure out what all unknown port bits are responsible for
+ * - figure out some cleverly evil scheme to possibly make ALSA AC97 code
+ * fully accept our quite incompatible ""AC97"" mixer and thus save some
+ * code (but I'm not too optimistic that doing this is possible at all)
*/
#include <sound/driver.h>
struct pci_dev *pci;
int irq;
+
+#ifdef CONFIG_PM
+ /* register value containers for power management
+ * Note: not always full I/O range preserved (just like Win driver!) */
+ u16 saved_regs_codec [AZF_IO_SIZE_CODEC_PM / 2];
+ u16 saved_regs_io2 [AZF_IO_SIZE_IO2_PM / 2];
+ u16 saved_regs_mpu [AZF_IO_SIZE_MPU_PM / 2];
+ u16 saved_regs_synth[AZF_IO_SIZE_SYNTH_PM / 2];
+ u16 saved_regs_mixer[AZF_IO_SIZE_MIXER_PM / 2];
+#endif
};
static const struct pci_device_id snd_azf3328_ids[] __devinitdata = {
else
dst_vol_left &= ~0x80;
- do
- {
- if (!left_done)
- {
+ do {
+ if (!left_done) {
if (curr_vol_left > dst_vol_left)
curr_vol_left--;
else
left_done = 1;
outb(curr_vol_left, portbase + 1);
}
- if (!right_done)
- {
+ if (!right_done) {
if (curr_vol_right > dst_vol_right)
curr_vol_right--;
else
}
if (delay)
mdelay(delay);
- }
- while ((!left_done) || (!right_done));
+ } while ((!left_done) || (!right_done));
snd_azf3328_dbgcallleave();
}
struct snd_ctl_elem_info *uinfo)
{
static const char * const texts1[] = {
- "ModemOut1", "ModemOut2"
+ "Mic1", "Mic2"
};
static const char * const texts2[] = {
- "MonoSelectSource1", "MonoSelectSource2"
+ "Mix", "Mic"
};
static const char * const texts3[] = {
"Mic", "CD", "Video", "Aux",
"Line", "Mix", "Mix Mono", "Phone"
};
+ static const char * const texts4[] = {
+ "pre 3D", "post 3D"
+ };
struct azf3328_mixer_reg reg;
snd_azf3328_mixer_reg_decode(®, kcontrol->private_value);
uinfo->value.enumerated.items = reg.enum_c;
if (uinfo->value.enumerated.item > reg.enum_c - 1U)
uinfo->value.enumerated.item = reg.enum_c - 1U;
- if (reg.reg == IDX_MIXER_ADVCTL2)
- {
- if (reg.lchan_shift == 8) /* modem out sel */
+ if (reg.reg == IDX_MIXER_ADVCTL2) {
+ switch(reg.lchan_shift) {
+ case 8: /* modem out sel */
strcpy(uinfo->value.enumerated.name, texts1[uinfo->value.enumerated.item]);
- else /* mono sel source */
+ break;
+ case 9: /* mono sel source */
strcpy(uinfo->value.enumerated.name, texts2[uinfo->value.enumerated.item]);
- }
- else
+ break;
+ case 15: /* PCM Out Path */
+ strcpy(uinfo->value.enumerated.name, texts4[uinfo->value.enumerated.item]);
+ break;
+ }
+ } else
strcpy(uinfo->value.enumerated.name, texts3[uinfo->value.enumerated.item]
);
return 0;
snd_azf3328_mixer_reg_decode(®, kcontrol->private_value);
val = snd_azf3328_mixer_inw(chip, reg.reg);
- if (reg.reg == IDX_MIXER_REC_SELECT)
- {
+ if (reg.reg == IDX_MIXER_REC_SELECT) {
ucontrol->value.enumerated.item[0] = (val >> 8) & (reg.enum_c - 1);
ucontrol->value.enumerated.item[1] = (val >> 0) & (reg.enum_c - 1);
- }
- else
+ } else
ucontrol->value.enumerated.item[0] = (val >> reg.lchan_shift) & (reg.enum_c - 1);
snd_azf3328_dbgmixer("get_enum: %02x is %04x -> %d|%d (shift %02d, enum_c %d)\n",
snd_azf3328_mixer_reg_decode(®, kcontrol->private_value);
oreg = snd_azf3328_mixer_inw(chip, reg.reg);
val = oreg;
- if (reg.reg == IDX_MIXER_REC_SELECT)
- {
+ if (reg.reg == IDX_MIXER_REC_SELECT) {
if (ucontrol->value.enumerated.item[0] > reg.enum_c - 1U ||
ucontrol->value.enumerated.item[1] > reg.enum_c - 1U)
return -EINVAL;
val = (ucontrol->value.enumerated.item[0] << 8) |
(ucontrol->value.enumerated.item[1] << 0);
- }
- else
- {
+ } else {
if (ucontrol->value.enumerated.item[0] > reg.enum_c - 1U)
return -EINVAL;
val &= ~((reg.enum_c - 1) << reg.lchan_shift);
AZF3328_MIXER_VOL_MONO("Modem Playback Volume", IDX_MIXER_MODEMOUT, 0x1f, 1),
AZF3328_MIXER_SWITCH("Modem Capture Switch", IDX_MIXER_MODEMIN, 15, 1),
AZF3328_MIXER_VOL_MONO("Modem Capture Volume", IDX_MIXER_MODEMIN, 0x1f, 1),
- AZF3328_MIXER_ENUM("Modem Out Select", IDX_MIXER_ADVCTL2, 2, 8),
- AZF3328_MIXER_ENUM("Mono Select Source", IDX_MIXER_ADVCTL2, 2, 9),
+ AZF3328_MIXER_ENUM("Mic Select", IDX_MIXER_ADVCTL2, 2, 8),
+ AZF3328_MIXER_ENUM("Mono Output Select", IDX_MIXER_ADVCTL2, 2, 9),
+ AZF3328_MIXER_ENUM("PCM", IDX_MIXER_ADVCTL2, 2, 15), /* PCM Out Path, place in front since it controls *both* 3D and Bass/Treble! */
AZF3328_MIXER_VOL_SPECIAL("Tone Control - Treble", IDX_MIXER_BASSTREBLE, 0x07, 1, 0),
AZF3328_MIXER_VOL_SPECIAL("Tone Control - Bass", IDX_MIXER_BASSTREBLE, 0x07, 9, 0),
AZF3328_MIXER_SWITCH("3D Control - Switch", IDX_MIXER_ADVCTL2, 13, 0),
- AZF3328_MIXER_VOL_SPECIAL("3D Control - Wide", IDX_MIXER_ADVCTL1, 0x07, 1, 0), /* "3D Width" */
- AZF3328_MIXER_VOL_SPECIAL("3D Control - Space", IDX_MIXER_ADVCTL1, 0x03, 8, 0), /* "Hifi 3D" */
+ AZF3328_MIXER_VOL_SPECIAL("3D Control - Width", IDX_MIXER_ADVCTL1, 0x07, 1, 0), /* "3D Width" */
+ AZF3328_MIXER_VOL_SPECIAL("3D Control - Depth", IDX_MIXER_ADVCTL1, 0x03, 8, 0), /* "Hifi 3D" */
#if MIXER_TESTING
AZF3328_MIXER_SWITCH("0", IDX_MIXER_ADVCTL2, 0, 0),
AZF3328_MIXER_SWITCH("1", IDX_MIXER_ADVCTL2, 1, 0),
unsigned int is_running;
snd_azf3328_dbgcallenter();
- if (do_recording)
- {
+ if (do_recording) {
/* access capture registers, i.e. skip playback reg section */
portbase = chip->codec_port + 0x20;
is_running = chip->is_recording;
- }
- else
- {
+ } else {
/* access the playback register section */
portbase = chip->codec_port + 0x00;
is_running = chip->is_playing;
}
/* AZF3328 uses a two buffer pointer DMA playback approach */
- if (!is_running)
- {
+ if (!is_running) {
unsigned long addr_area2;
unsigned long count_areas, count_tmp; /* width 32bit -- overflow!! */
count_areas = size/2;
chip->is_playing = 1;
snd_azf3328_dbgplay("STARTED PLAYBACK\n");
break;
+ case SNDRV_PCM_TRIGGER_RESUME:
+ snd_azf3328_dbgplay("RESUME PLAYBACK\n");
+ /* resume playback if we were active */
+ if (chip->is_playing)
+ snd_azf3328_codec_outw(chip, IDX_IO_PLAY_FLAGS,
+ snd_azf3328_codec_inw(chip, IDX_IO_PLAY_FLAGS) | DMA_RESUME);
+ break;
case SNDRV_PCM_TRIGGER_STOP:
snd_azf3328_dbgplay("STOP PLAYBACK\n");
chip->is_playing = 0;
snd_azf3328_dbgplay("STOPPED PLAYBACK\n");
break;
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ snd_azf3328_dbgplay("SUSPEND PLAYBACK\n");
+ /* make sure playback is stopped */
+ snd_azf3328_codec_outw(chip, IDX_IO_PLAY_FLAGS,
+ snd_azf3328_codec_inw(chip, IDX_IO_PLAY_FLAGS) & ~DMA_RESUME);
+ break;
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
snd_printk(KERN_ERR "FIXME: SNDRV_PCM_TRIGGER_PAUSE_PUSH NIY!\n");
break;
snd_printk(KERN_ERR "FIXME: SNDRV_PCM_TRIGGER_PAUSE_RELEASE NIY!\n");
break;
default:
+ printk(KERN_ERR "FIXME: unknown trigger mode!\n");
return -EINVAL;
}
chip->is_recording = 1;
snd_azf3328_dbgplay("STARTED CAPTURE\n");
break;
+ case SNDRV_PCM_TRIGGER_RESUME:
+ snd_azf3328_dbgplay("RESUME CAPTURE\n");
+ /* resume recording if we were active */
+ if (chip->is_recording)
+ snd_azf3328_codec_outw(chip, IDX_IO_REC_FLAGS,
+ snd_azf3328_codec_inw(chip, IDX_IO_REC_FLAGS) | DMA_RESUME);
+ break;
case SNDRV_PCM_TRIGGER_STOP:
snd_azf3328_dbgplay("STOP CAPTURE\n");
chip->is_recording = 0;
snd_azf3328_dbgplay("STOPPED CAPTURE\n");
break;
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ snd_azf3328_dbgplay("SUSPEND CAPTURE\n");
+ /* make sure recording is stopped */
+ snd_azf3328_codec_outw(chip, IDX_IO_REC_FLAGS,
+ snd_azf3328_codec_inw(chip, IDX_IO_REC_FLAGS) & ~DMA_RESUME);
+ break;
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
snd_printk(KERN_ERR "FIXME: SNDRV_PCM_TRIGGER_PAUSE_PUSH NIY!\n");
break;
snd_printk(KERN_ERR "FIXME: SNDRV_PCM_TRIGGER_PAUSE_RELEASE NIY!\n");
break;
default:
+ printk(KERN_ERR "FIXME: unknown trigger mode!\n");
return -EINVAL;
}
snd_azf3328_codec_inw(chip, IDX_IO_PLAY_IRQTYPE),
status);
- if (status & IRQ_TIMER)
- {
+ if (status & IRQ_TIMER) {
/* snd_azf3328_dbgplay("timer %ld\n", inl(chip->codec_port+IDX_IO_TIMER_VALUE) & TIMER_VALUE_MASK); */
if (chip->timer)
snd_timer_interrupt(chip->timer, chip->timer->sticks);
spin_unlock(&chip->reg_lock);
snd_azf3328_dbgplay("azt3328: timer IRQ\n");
}
- if (status & IRQ_PLAYBACK)
- {
+ if (status & IRQ_PLAYBACK) {
spin_lock(&chip->reg_lock);
which = snd_azf3328_codec_inb(chip, IDX_IO_PLAY_IRQTYPE);
/* ack all IRQ types immediately */
snd_azf3328_codec_outb(chip, IDX_IO_PLAY_IRQTYPE, which);
spin_unlock(&chip->reg_lock);
- if (chip->pcm && chip->playback_substream)
- {
+ if (chip->pcm && chip->playback_substream) {
snd_pcm_period_elapsed(chip->playback_substream);
snd_azf3328_dbgplay("PLAY period done (#%x), @ %x\n",
which,
inl(chip->codec_port+IDX_IO_PLAY_DMA_CURRPOS));
- }
- else
+ } else
snd_azf3328_dbgplay("azt3328: ouch, irq handler problem!\n");
if (which & IRQ_PLAY_SOMETHING)
snd_azf3328_dbgplay("azt3328: unknown play IRQ type occurred, please report!\n");
}
- if (status & IRQ_RECORDING)
- {
+ if (status & IRQ_RECORDING) {
spin_lock(&chip->reg_lock);
which = snd_azf3328_codec_inb(chip, IDX_IO_REC_IRQTYPE);
/* ack all IRQ types immediately */
snd_azf3328_codec_outb(chip, IDX_IO_REC_IRQTYPE, which);
spin_unlock(&chip->reg_lock);
- if (chip->pcm && chip->capture_substream)
- {
+ if (chip->pcm && chip->capture_substream) {
snd_pcm_period_elapsed(chip->capture_substream);
snd_azf3328_dbgplay("REC period done (#%x), @ %x\n",
which,
inl(chip->codec_port+IDX_IO_REC_DMA_CURRPOS));
- }
- else
+ } else
snd_azf3328_dbgplay("azt3328: ouch, irq handler problem!\n");
if (which & IRQ_REC_SOMETHING)
snd_azf3328_dbgplay("azt3328: unknown rec IRQ type occurred, please report!\n");
}
/* MPU401 has less critical IRQ requirements
* than timer and playback/recording, right? */
- if (status & IRQ_MPU401)
- {
+ if (status & IRQ_MPU401) {
snd_mpu401_uart_interrupt(irq, chip->rmidi->private_data, regs);
/* hmm, do we have to ack the IRQ here somehow?
snd_azf3328_dbgcallenter();
chip = snd_timer_chip(timer);
delay = ((timer->sticks * seqtimer_scaling) - 1) & TIMER_VALUE_MASK;
- if (delay < 49)
- {
+ if (delay < 49) {
/* uhoh, that's not good, since user-space won't know about
* this timing tweak
* (we need to do it to avoid a lockup, though) */
goto out_err;
}
+ card->private_data = chip;
+
if ((err = snd_mpu401_uart_new( card, 0, MPU401_HW_MPU401,
- chip->mpu_port, 1, pci->irq, 0,
- &chip->rmidi)) < 0) {
+ chip->mpu_port, MPU401_INFO_INTEGRATED,
+ pci->irq, 0, &chip->rmidi)) < 0) {
snd_printk(KERN_ERR "azf3328: no MPU-401 device at 0x%lx?\n", chip->mpu_port);
goto out_err;
}
}
}
+ opl3->private_data = chip;
+
sprintf(card->longname, "%s at 0x%lx, irq %i",
card->shortname, chip->codec_port, chip->irq);
snd_azf3328_dbgcallleave();
}
+#ifdef CONFIG_PM
+static int
+snd_azf3328_suspend(struct pci_dev *pci, pm_message_t state)
+{
+ struct snd_card *card = pci_get_drvdata(pci);
+ struct snd_azf3328 *chip = card->private_data;
+ int reg;
+
+ snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
+
+ snd_pcm_suspend_all(chip->pcm);
+
+ for (reg = 0; reg < AZF_IO_SIZE_MIXER_PM / 2; reg++)
+ chip->saved_regs_mixer[reg] = inw(chip->mixer_port + reg * 2);
+
+ /* make sure to disable master volume etc. to prevent looping sound */
+ snd_azf3328_mixer_set_mute(chip, IDX_MIXER_PLAY_MASTER, 1);
+ snd_azf3328_mixer_set_mute(chip, IDX_MIXER_WAVEOUT, 1);
+
+ for (reg = 0; reg < AZF_IO_SIZE_CODEC_PM / 2; reg++)
+ chip->saved_regs_codec[reg] = inw(chip->codec_port + reg * 2);
+ for (reg = 0; reg < AZF_IO_SIZE_IO2_PM / 2; reg++)
+ chip->saved_regs_io2[reg] = inw(chip->io2_port + reg * 2);
+ for (reg = 0; reg < AZF_IO_SIZE_MPU_PM / 2; reg++)
+ chip->saved_regs_mpu[reg] = inw(chip->mpu_port + reg * 2);
+ for (reg = 0; reg < AZF_IO_SIZE_SYNTH_PM / 2; reg++)
+ chip->saved_regs_synth[reg] = inw(chip->synth_port + reg * 2);
+
+ pci_set_power_state(pci, PCI_D3hot);
+ pci_disable_device(pci);
+ pci_save_state(pci);
+ return 0;
+}
+
+static int
+snd_azf3328_resume(struct pci_dev *pci)
+{
+ struct snd_card *card = pci_get_drvdata(pci);
+ struct snd_azf3328 *chip = card->private_data;
+ int reg;
+
+ pci_restore_state(pci);
+ pci_enable_device(pci);
+ pci_set_power_state(pci, PCI_D0);
+ pci_set_master(pci);
+
+ for (reg = 0; reg < AZF_IO_SIZE_IO2_PM / 2; reg++)
+ outw(chip->saved_regs_io2[reg], chip->io2_port + reg * 2);
+ for (reg = 0; reg < AZF_IO_SIZE_MPU_PM / 2; reg++)
+ outw(chip->saved_regs_mpu[reg], chip->mpu_port + reg * 2);
+ for (reg = 0; reg < AZF_IO_SIZE_SYNTH_PM / 2; reg++)
+ outw(chip->saved_regs_synth[reg], chip->synth_port + reg * 2);
+ for (reg = 0; reg < AZF_IO_SIZE_MIXER_PM / 2; reg++)
+ outw(chip->saved_regs_mixer[reg], chip->mixer_port + reg * 2);
+ for (reg = 0; reg < AZF_IO_SIZE_CODEC_PM / 2; reg++)
+ outw(chip->saved_regs_codec[reg], chip->codec_port + reg * 2);
+
+ snd_power_change_state(card, SNDRV_CTL_POWER_D0);
+ return 0;
+}
+#endif
+
+
+
+
static struct pci_driver driver = {
.name = "AZF3328",
.id_table = snd_azf3328_ids,
.probe = snd_azf3328_probe,
.remove = __devexit_p(snd_azf3328_remove),
+#ifdef CONFIG_PM
+ .suspend = snd_azf3328_suspend,
+ .resume = snd_azf3328_resume,
+#endif
};
static int __init
/*** main I/O area port indices ***/
/* (only 0x70 of 0x80 bytes saved/restored by Windows driver) */
+#define AZF_IO_SIZE_CODEC 0x80
+#define AZF_IO_SIZE_CODEC_PM 0x70
+
/* the driver initialisation suggests a layout of 4 main areas:
* from 0x00 (playback), from 0x20 (recording) and from 0x40 (maybe MPU401??).
* And another area from 0x60 to 0x6f (DirectX timer, IRQ management,
#define IDX_IO_REC_DMA_CURROFS 0x34 /* PU:0x00000000 */
#define IDX_IO_REC_SOUNDFORMAT 0x36 /* PU:0x0000 */
-/** hmm, what is this I/O area for? MPU401?? (after playback, recording, ???, timer) **/
+/** hmm, what is this I/O area for? MPU401?? or external DAC via I2S?? (after playback, recording, ???, timer) **/
#define IDX_IO_SOMETHING_FLAGS 0x40 /* gets set to 0x34 just like port 0x0 and 0x20 on card init, PU:0x0000 */
/* general */
#define IDX_IO_42H 0x42 /* PU:0x0001 */
#define IRQ_UNKNOWN2 0x0080 /* probably unused */
#define IDX_IO_66H 0x66 /* writing 0xffff returns 0x0000 */
#define IDX_IO_SOME_VALUE 0x68 /* this is set to e.g. 0x3ff or 0x300, and writable; maybe some buffer limit, but I couldn't find out more, PU:0x00ff */
-#define IDX_IO_6AH 0x6A /* this WORD can be set to have bits 0x0028 activated; actually inhibits PCM playback!!! maybe power management?? */
+#define IDX_IO_6AH 0x6A /* this WORD can be set to have bits 0x0028 activated (FIXME: correct??); actually inhibits PCM playback!!! maybe power management?? */
+ #define IO_6A_PAUSE_PLAYBACK 0x0200 /* bit 9; sure, this pauses playback, but what the heck is this really about?? */
#define IDX_IO_6CH 0x6C
#define IDX_IO_6EH 0x6E /* writing 0xffff returns 0x83fe */
/* further I/O indices not saved/restored, so probably not used */
/*** I/O 2 area port indices ***/
/* (only 0x06 of 0x08 bytes saved/restored by Windows driver) */
+#define AZF_IO_SIZE_IO2 0x08
+#define AZF_IO_SIZE_IO2_PM 0x06
+
#define IDX_IO2_LEGACY_ADDR 0x04
#define LEGACY_SOMETHING 0x01 /* OPL3?? */
#define LEGACY_JOY 0x08
+#define AZF_IO_SIZE_MPU 0x04
+#define AZF_IO_SIZE_MPU_PM 0x04
+
+#define AZF_IO_SIZE_SYNTH 0x08
+#define AZF_IO_SIZE_SYNTH_PM 0x06
/*** mixer I/O area port indices ***/
/* (only 0x22 of 0x40 bytes saved/restored by Windows driver)
- * generally spoken: AC97 register index = AZF3328 mixer reg index + 2
- * (in other words: AZF3328 NOT fully AC97 compliant) */
+ * UNFORTUNATELY azf3328 is NOT truly AC97 compliant: see main file intro */
+#define AZF_IO_SIZE_MIXER 0x40
+#define AZF_IO_SIZE_MIXER_PM 0x22
+
#define MIXER_VOLUME_RIGHT_MASK 0x001f
#define MIXER_VOLUME_LEFT_MASK 0x1f00
#define MIXER_MUTE_MASK 0x8000
#define IDX_MIXER_ADVCTL1 0x1e
/* unlisted bits are unmodifiable */
#define MIXER_ADVCTL1_3DWIDTH_MASK 0x000e
- #define MIXER_ADVCTL1_HIFI3D_MASK 0x0300
-#define IDX_MIXER_ADVCTL2 0x20 /* resembles AC97_GENERAL_PURPOSE reg! */
+ #define MIXER_ADVCTL1_HIFI3D_MASK 0x0300 /* yup, this is missing the high bit that official AC97 contains, plus it doesn't have linear bit value range behaviour but instead acts weirdly (possibly we're dealing with two *different* 3D settings here??) */
+#define IDX_MIXER_ADVCTL2 0x20 /* subset of AC97_GENERAL_PURPOSE reg! */
/* unlisted bits are unmodifiable */
- #define MIXER_ADVCTL2_BIT7 0x0080 /* WaveOut 3D Bypass? mutes WaveOut at LineOut */
- #define MIXER_ADVCTL2_BIT8 0x0100 /* is this Modem Out Select? */
- #define MIXER_ADVCTL2_BIT9 0x0200 /* Mono Select Source? */
- #define MIXER_ADVCTL2_BIT13 0x2000 /* 3D enable? */
- #define MIXER_ADVCTL2_BIT15 0x8000 /* unknown */
+ #define MIXER_ADVCTL2_LPBK 0x0080 /* Loopback mode -- Win driver: "WaveOut3DBypass"? mutes WaveOut at LineOut */
+ #define MIXER_ADVCTL2_MS 0x0100 /* Mic Select 0=Mic1, 1=Mic2 -- Win driver: "ModemOutSelect"?? */
+ #define MIXER_ADVCTL2_MIX 0x0200 /* Mono output select 0=Mix, 1=Mic; Win driver: "MonoSelectSource"?? */
+ #define MIXER_ADVCTL2_3D 0x2000 /* 3D Enhancement 1=on */
+ #define MIXER_ADVCTL2_POP 0x8000 /* Pcm Out Path, 0=pre 3D, 1=post 3D */
#define IDX_MIXER_SOMETHING30H 0x30 /* used, but unknown??? */
static int index[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = -2}; /* Exclude the first card */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
-static int digital_rate[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS-1)] = 0 }; /* digital input rate */
+static int digital_rate[SNDRV_CARDS]; /* digital input rate */
static int load_all; /* allow to load the non-whitelisted cards */
module_param_array(index, int, NULL, 0444);
BT_DEVICE(PCI_DEVICE_ID_BROOKTREE_879, 0x0070, 0x13eb, 32000),
/* Viewcast Osprey 200 */
BT_DEVICE(PCI_DEVICE_ID_BROOKTREE_878, 0x0070, 0xff01, 44100),
- /* AVerMedia Studio No. 103, 203, ...? */
- BT_DEVICE(PCI_DEVICE_ID_BROOKTREE_878, 0x1461, 0x0003, 48000),
/* Leadtek Winfast tv 2000xp delux */
BT_DEVICE(PCI_DEVICE_ID_BROOKTREE_878, 0x107d, 0x6606, 32000),
+ /* Voodoo TV 200 */
+ BT_DEVICE(PCI_DEVICE_ID_BROOKTREE_878, 0x121a, 0x3000, 32000),
+ /* AVerMedia Studio No. 103, 203, ...? */
+ BT_DEVICE(PCI_DEVICE_ID_BROOKTREE_878, 0x1461, 0x0003, 48000),
{ }
};
MODULE_DEVICE_TABLE(pci, snd_bt87x_ids);
#endif
#define ADC_MUX_MASK 0x0000000f //Mask for ADC Mux
+#define ADC_MUX_PHONE 0x00000001 //Value to select TAD at ADC Mux (Not used)
#define ADC_MUX_MIC 0x00000002 //Value to select Mic at ADC Mux
#define ADC_MUX_LINEIN 0x00000004 //Value to select LineIn at ADC Mux
-#define ADC_MUX_PHONE 0x00000001 //Value to select TAD at ADC Mux (Not used)
#define ADC_MUX_AUX 0x00000008 //Value to select Aux at ADC Mux
#define SET_CHANNEL 0 /* Testing channel outputs 0=Front, 1=Center/LFE, 2=Unknown, 3=Rear */
u32 spdif_bits[4]; /* s/pdif out setup */
int spdif_enable;
int capture_source;
+ int i2c_capture_source;
+ u8 i2c_capture_volume[4][2];
int capture_mic_line_in;
struct snd_dma_buffer buffer;
/* New Audigy SE. Has a different DAC. */
/* SB0570:
* CTRL:CA0106-DAT
- * ADC: WM8768GEDS
- * DAC: WM8775EDS
+ * ADC: WM8775EDS
+ * DAC: WM8768GEDS
*/
{ .serial = 0x100a1102,
.name = "Audigy SE [SB0570]",
.i2c_adc = 1,
.spi_dac = 1 } ,
/* MSI K8N Diamond Motherboard with onboard SB Live 24bit without AC97 */
+ /* SB0438
+ * CTRL:CA0106-DAT
+ * ADC: WM8775SEDS
+ * DAC: CS4382-KQZ
+ */
{ .serial = 0x10091462,
.name = "MSI K8N Diamond MB [SB0438]",
- .gpio_type = 1,
+ .gpio_type = 2,
.i2c_adc = 1 } ,
/* Shuttle XPC SD31P which has an onboard Creative Labs
* Sound Blaster Live! 24-bit EAX
return 0;
}
+/* The ADC does not support i2c read, so only write is implemented */
int snd_ca0106_i2c_write(struct snd_ca0106 *emu,
u32 reg,
u32 value)
}
tmp = reg << 25 | value << 16;
+ // snd_printk("I2C-write:reg=0x%x, value=0x%x\n", reg, value);
/* Not sure what this I2C channel controls. */
/* snd_ca0106_ptr_write(emu, I2C_D0, 0, tmp); */
for (retry = 0; retry < 10; retry++) {
/* Send the data to i2c */
- tmp = snd_ca0106_ptr_read(emu, I2C_A, 0);
- tmp = tmp & ~(I2C_A_ADC_READ|I2C_A_ADC_LAST|I2C_A_ADC_START|I2C_A_ADC_ADD_MASK);
+ //tmp = snd_ca0106_ptr_read(emu, I2C_A, 0);
+ //tmp = tmp & ~(I2C_A_ADC_READ|I2C_A_ADC_LAST|I2C_A_ADC_START|I2C_A_ADC_ADD_MASK);
+ tmp = 0;
tmp = tmp | (I2C_A_ADC_LAST|I2C_A_ADC_START|I2C_A_ADC_ADD);
snd_ca0106_ptr_write(emu, I2C_A, 0, tmp);
0x02ff,
0x0400,
0x0520,
- 0x0600,
+ 0x0620, /* Set 24 bit. Was 0x0600 */
0x08ff,
0x0aff,
0x0cff,
0x1400,
};
+static unsigned int i2c_adc_init[][2] = {
+ { 0x17, 0x00 }, /* Reset */
+ { 0x07, 0x00 }, /* Timeout */
+ { 0x0b, 0x22 }, /* Interface control */
+ { 0x0c, 0x22 }, /* Master mode control */
+ { 0x0d, 0x08 }, /* Powerdown control */
+ { 0x0e, 0xcf }, /* Attenuation Left 0x01 = -103dB, 0xff = 24dB */
+ { 0x0f, 0xcf }, /* Attenuation Right 0.5dB steps */
+ { 0x10, 0x7b }, /* ALC Control 1 */
+ { 0x11, 0x00 }, /* ALC Control 2 */
+ { 0x12, 0x32 }, /* ALC Control 3 */
+ { 0x13, 0x00 }, /* Noise gate control */
+ { 0x14, 0xa6 }, /* Limiter control */
+ { 0x15, ADC_MUX_LINEIN }, /* ADC Mixer control */
+};
+
static int __devinit snd_ca0106_create(struct snd_card *card,
struct pci_dev *pci,
struct snd_ca0106 **rchip)
snd_ca0106_ptr_write(chip, CAPTURE_SOURCE, 0x0, 0x333300e4); /* Select MIC, Line in, TAD in, AUX in */
chip->capture_source = 3; /* Set CAPTURE_SOURCE */
- if (chip->details->gpio_type == 1) { /* The SB0410 and SB0413 use GPIO differently. */
+ if (chip->details->gpio_type == 2) { /* The SB0438 use GPIO differently. */
+ /* FIXME: Still need to find out what the other GPIO bits do. E.g. For digital spdif out. */
+ outl(0x0, chip->port+GPIO);
+ //outl(0x00f0e000, chip->port+GPIO); /* Analog */
+ outl(0x005f5301, chip->port+GPIO); /* Analog */
+ } else if (chip->details->gpio_type == 1) { /* The SB0410 and SB0413 use GPIO differently. */
/* FIXME: Still need to find out what the other GPIO bits do. E.g. For digital spdif out. */
outl(0x0, chip->port+GPIO);
//outl(0x00f0e000, chip->port+GPIO); /* Analog */
outl(HCFG_AC97 | HCFG_AUDIOENABLE, chip->port+HCFG); /* AC97 2.0, Enable outputs. */
if (chip->details->i2c_adc == 1) { /* The SB0410 and SB0413 use I2C to control ADC. */
- snd_ca0106_i2c_write(chip, ADC_MUX, ADC_MUX_LINEIN); /* Enable Line-in capture. MIC in currently untested. */
+ int size, n;
+
+ size = ARRAY_SIZE(i2c_adc_init);
+ //snd_printk("I2C:array size=0x%x\n", size);
+ for (n=0; n < size; n++) {
+ snd_ca0106_i2c_write(chip, i2c_adc_init[n][0], i2c_adc_init[n][1]);
+ }
+ for (n=0; n < 4; n++) {
+ chip->i2c_capture_volume[n][0]= 0xcf;
+ chip->i2c_capture_volume[n][1]= 0xcf;
+ }
+ chip->i2c_capture_source=2; /* Line in */
+ //snd_ca0106_i2c_write(chip, ADC_MUX, ADC_MUX_LINEIN); /* Enable Line-in capture. MIC in currently untested. */
}
if (chip->details->spi_dac == 1) { /* The SB0570 use SPI to control DAC. */
int size, n;
return change;
}
+static int snd_ca0106_i2c_capture_source_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ static char *texts[6] = {
+ "Phone", "Mic", "Line in", "Aux"
+ };
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = 4;
+ if (uinfo->value.enumerated.item > 3)
+ uinfo->value.enumerated.item = 3;
+ strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+ return 0;
+}
+
+static int snd_ca0106_i2c_capture_source_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.enumerated.item[0] = emu->i2c_capture_source;
+ return 0;
+}
+
+static int snd_ca0106_i2c_capture_source_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
+ unsigned int source_id;
+ unsigned int ngain, ogain;
+ int change = 0;
+ u32 source;
+ /* If the capture source has changed,
+ * update the capture volume from the cached value
+ * for the particular source.
+ */
+ source_id = ucontrol->value.enumerated.item[0] ;
+ change = (emu->i2c_capture_source != source_id);
+ if (change) {
+ snd_ca0106_i2c_write(emu, ADC_MUX, 0); /* Mute input */
+ ngain = emu->i2c_capture_volume[source_id][0]; /* Left */
+ ogain = emu->i2c_capture_volume[emu->i2c_capture_source][0]; /* Left */
+ if (ngain != ogain)
+ snd_ca0106_i2c_write(emu, ADC_ATTEN_ADCL, ((ngain) & 0xff));
+ ngain = emu->i2c_capture_volume[source_id][1]; /* Left */
+ ogain = emu->i2c_capture_volume[emu->i2c_capture_source][1]; /* Left */
+ if (ngain != ogain)
+ snd_ca0106_i2c_write(emu, ADC_ATTEN_ADCR, ((ngain) & 0xff));
+ source = 1 << source_id;
+ snd_ca0106_i2c_write(emu, ADC_MUX, source); /* Set source */
+ emu->i2c_capture_source = source_id;
+ }
+ return change;
+}
+
+static int snd_ca0106_capture_line_in_side_out_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ static char *texts[2] = { "Side out", "Line in" };
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = 2;
+ if (uinfo->value.enumerated.item > 1)
+ uinfo->value.enumerated.item = 1;
+ strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+ return 0;
+}
+
static int snd_ca0106_capture_mic_line_in_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
if (change) {
emu->capture_mic_line_in = val;
if (val) {
- snd_ca0106_i2c_write(emu, ADC_MUX, ADC_MUX_PHONE); /* Mute input */
+ //snd_ca0106_i2c_write(emu, ADC_MUX, 0); /* Mute input */
tmp = inl(emu->port+GPIO) & ~0x400;
tmp = tmp | 0x400;
outl(tmp, emu->port+GPIO);
- snd_ca0106_i2c_write(emu, ADC_MUX, ADC_MUX_MIC);
+ //snd_ca0106_i2c_write(emu, ADC_MUX, ADC_MUX_MIC);
} else {
- snd_ca0106_i2c_write(emu, ADC_MUX, ADC_MUX_PHONE); /* Mute input */
+ //snd_ca0106_i2c_write(emu, ADC_MUX, 0); /* Mute input */
tmp = inl(emu->port+GPIO) & ~0x400;
outl(tmp, emu->port+GPIO);
- snd_ca0106_i2c_write(emu, ADC_MUX, ADC_MUX_LINEIN);
+ //snd_ca0106_i2c_write(emu, ADC_MUX, ADC_MUX_LINEIN);
}
}
return change;
static struct snd_kcontrol_new snd_ca0106_capture_mic_line_in __devinitdata =
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Mic/Line in Capture",
+ .name = "Shared Mic/Line in Capture Switch",
.info = snd_ca0106_capture_mic_line_in_info,
.get = snd_ca0106_capture_mic_line_in_get,
.put = snd_ca0106_capture_mic_line_in_put
};
+static struct snd_kcontrol_new snd_ca0106_capture_line_in_side_out __devinitdata =
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Shared Line in/Side out Capture Switch",
+ .info = snd_ca0106_capture_line_in_side_out_info,
+ .get = snd_ca0106_capture_mic_line_in_get,
+ .put = snd_ca0106_capture_mic_line_in_put
+};
+
+
static int snd_ca0106_spdif_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
return 1;
}
+static int snd_ca0106_i2c_volume_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 2;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 255;
+ return 0;
+}
+
+static int snd_ca0106_i2c_volume_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
+ int source_id;
+
+ source_id = kcontrol->private_value;
+
+ ucontrol->value.integer.value[0] = emu->i2c_capture_volume[source_id][0];
+ ucontrol->value.integer.value[1] = emu->i2c_capture_volume[source_id][1];
+ return 0;
+}
+
+static int snd_ca0106_i2c_volume_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
+ unsigned int ogain;
+ unsigned int ngain;
+ int source_id;
+ int change = 0;
+
+ source_id = kcontrol->private_value;
+ ogain = emu->i2c_capture_volume[source_id][0]; /* Left */
+ ngain = ucontrol->value.integer.value[0];
+ if (ngain > 0xff)
+ return 0;
+ if (ogain != ngain) {
+ if (emu->i2c_capture_source == source_id)
+ snd_ca0106_i2c_write(emu, ADC_ATTEN_ADCL, ((ngain) & 0xff) );
+ emu->i2c_capture_volume[source_id][0] = ucontrol->value.integer.value[0];
+ change = 1;
+ }
+ ogain = emu->i2c_capture_volume[source_id][1]; /* Right */
+ ngain = ucontrol->value.integer.value[1];
+ if (ngain > 0xff)
+ return 0;
+ if (ogain != ngain) {
+ if (emu->i2c_capture_source == source_id)
+ snd_ca0106_i2c_write(emu, ADC_ATTEN_ADCR, ((ngain) & 0xff));
+ emu->i2c_capture_volume[source_id][1] = ucontrol->value.integer.value[1];
+ change = 1;
+ }
+
+ return change;
+}
+
#define CA_VOLUME(xname,chid,reg) \
{ \
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
- .info = snd_ca0106_volume_info, \
- .get = snd_ca0106_volume_get, \
- .put = snd_ca0106_volume_put, \
+ .info = snd_ca0106_volume_info, \
+ .get = snd_ca0106_volume_get, \
+ .put = snd_ca0106_volume_put, \
.private_value = ((chid) << 8) | (reg) \
}
+#define I2C_VOLUME(xname,chid) \
+{ \
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .info = snd_ca0106_i2c_volume_info, \
+ .get = snd_ca0106_i2c_volume_get, \
+ .put = snd_ca0106_i2c_volume_put, \
+ .private_value = chid \
+}
+
static struct snd_kcontrol_new snd_ca0106_volume_ctls[] __devinitdata = {
CA_VOLUME("Analog Front Playback Volume",
CA_VOLUME("CAPTURE feedback Playback Volume",
1, CAPTURE_CONTROL),
+ I2C_VOLUME("Phone Capture Volume", 0),
+ I2C_VOLUME("Mic Capture Volume", 1),
+ I2C_VOLUME("Line in Capture Volume", 2),
+ I2C_VOLUME("Aux Capture Volume", 3),
+
{
.access = SNDRV_CTL_ELEM_ACCESS_READ,
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Capture Source",
+ .name = "Digital Capture Source",
.info = snd_ca0106_capture_source_info,
.get = snd_ca0106_capture_source_get,
.put = snd_ca0106_capture_source_put
},
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Capture Source",
+ .info = snd_ca0106_i2c_capture_source_info,
+ .get = snd_ca0106_i2c_capture_source_get,
+ .put = snd_ca0106_i2c_capture_source_put
+ },
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
return err;
}
if (emu->details->i2c_adc == 1) {
- err = snd_ctl_add(card, snd_ctl_new1(&snd_ca0106_capture_mic_line_in, emu));
+ if (emu->details->gpio_type == 1)
+ err = snd_ctl_add(card, snd_ctl_new1(&snd_ca0106_capture_mic_line_in, emu));
+ else /* gpio_type == 2 */
+ err = snd_ctl_add(card, snd_ctl_new1(&snd_ca0106_capture_line_in_side_out, emu));
if (err < 0)
return err;
}
struct snd_info_entry *entry;
if(! snd_card_proc_new(emu->card, "iec958", &entry))
- snd_info_set_text_ops(entry, emu, 1024, snd_ca0106_proc_iec958);
+ snd_info_set_text_ops(entry, emu, snd_ca0106_proc_iec958);
if(! snd_card_proc_new(emu->card, "ca0106_reg32", &entry)) {
- snd_info_set_text_ops(entry, emu, 1024, snd_ca0106_proc_reg_read32);
- entry->c.text.write_size = 64;
+ snd_info_set_text_ops(entry, emu, snd_ca0106_proc_reg_read32);
entry->c.text.write = snd_ca0106_proc_reg_write32;
entry->mode |= S_IWUSR;
}
if(! snd_card_proc_new(emu->card, "ca0106_reg16", &entry))
- snd_info_set_text_ops(entry, emu, 1024, snd_ca0106_proc_reg_read16);
+ snd_info_set_text_ops(entry, emu, snd_ca0106_proc_reg_read16);
if(! snd_card_proc_new(emu->card, "ca0106_reg8", &entry))
- snd_info_set_text_ops(entry, emu, 1024, snd_ca0106_proc_reg_read8);
+ snd_info_set_text_ops(entry, emu, snd_ca0106_proc_reg_read8);
if(! snd_card_proc_new(emu->card, "ca0106_regs1", &entry)) {
- snd_info_set_text_ops(entry, emu, 1024, snd_ca0106_proc_reg_read1);
- entry->c.text.write_size = 64;
+ snd_info_set_text_ops(entry, emu, snd_ca0106_proc_reg_read1);
entry->c.text.write = snd_ca0106_proc_reg_write;
entry->mode |= S_IWUSR;
// entry->private_data = emu;
}
if(! snd_card_proc_new(emu->card, "ca0106_i2c", &entry)) {
- snd_info_set_text_ops(entry, emu, 1024, snd_ca0106_proc_i2c_write);
- entry->c.text.write_size = 64;
+ snd_info_set_text_ops(entry, emu, snd_ca0106_proc_i2c_write);
entry->c.text.write = snd_ca0106_proc_i2c_write;
entry->mode |= S_IWUSR;
// entry->private_data = emu;
}
if(! snd_card_proc_new(emu->card, "ca0106_regs2", &entry))
- snd_info_set_text_ops(entry, emu, 1024, snd_ca0106_proc_reg_read2);
+ snd_info_set_text_ops(entry, emu, snd_ca0106_proc_reg_read2);
return 0;
}
CMIPCI_MIXER_VOL_MONO("Mic Capture Volume", CM_REG_MIXER2, CM_VADMIC_SHIFT, 7),
CMIPCI_SB_VOL_MONO("Phone Playback Volume", CM_REG_EXTENT_IND, 5, 7),
CMIPCI_DOUBLE("Phone Playback Switch", CM_REG_EXTENT_IND, CM_REG_EXTENT_IND, 4, 4, 1, 0, 0),
- CMIPCI_DOUBLE("PC Speaker Playnack Switch", CM_REG_EXTENT_IND, CM_REG_EXTENT_IND, 3, 3, 1, 0, 0),
+ CMIPCI_DOUBLE("PC Speaker Playback Switch", CM_REG_EXTENT_IND, CM_REG_EXTENT_IND, 3, 3, 1, 0, 0),
CMIPCI_DOUBLE("Mic Boost Capture Switch", CM_REG_EXTENT_IND, CM_REG_EXTENT_IND, 0, 0, 1, 0, 0),
};
struct snd_info_entry *entry;
if (! snd_card_proc_new(cm->card, "cmipci", &entry))
- snd_info_set_text_ops(entry, cm, 1024, snd_cmipci_proc_read);
+ snd_info_set_text_ops(entry, cm, snd_cmipci_proc_read);
}
#else /* !CONFIG_PROC_FS */
static inline void snd_cmipci_proc_init(struct cmipci *cm) {}
}
integrated_midi = snd_cmipci_read_b(cm, CM_REG_MPU_PCI) != 0xff;
- if (integrated_midi)
+ if (integrated_midi && mpu_port[dev] == 1)
iomidi = cm->iobase + CM_REG_MPU_PCI;
else {
iomidi = mpu_port[dev];
if (iomidi > 0) {
if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_CMIPCI,
- iomidi, integrated_midi,
+ iomidi,
+ (integrated_midi ?
+ MPU401_INFO_INTEGRATED : 0),
cm->irq, 0, &cm->rmidi)) < 0) {
printk(KERN_ERR "cmipci: no UART401 device at 0x%lx\n", iomidi);
}
struct snd_info_entry *entry;
if (! snd_card_proc_new(chip->card, "cs4281", &entry))
- snd_info_set_text_ops(entry, chip, 1024, snd_cs4281_proc_read);
+ snd_info_set_text_ops(entry, chip, snd_cs4281_proc_read);
if (! snd_card_proc_new(chip->card, "cs4281_BA0", &entry)) {
entry->content = SNDRV_INFO_CONTENT_DATA;
entry->private_data = chip;
chip->ba0_addr = pci_resource_start(pci, 0);
chip->ba1_addr = pci_resource_start(pci, 1);
+ chip->ba0 = ioremap_nocache(chip->ba0_addr, pci_resource_len(pci, 0));
+ chip->ba1 = ioremap_nocache(chip->ba1_addr, pci_resource_len(pci, 1));
+ if (!chip->ba0 || !chip->ba1) {
+ snd_cs4281_free(chip);
+ return -ENOMEM;
+ }
+
if (request_irq(pci->irq, snd_cs4281_interrupt, SA_INTERRUPT|SA_SHIRQ,
"CS4281", chip)) {
snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
}
chip->irq = pci->irq;
- chip->ba0 = ioremap_nocache(chip->ba0_addr, pci_resource_len(pci, 0));
- chip->ba1 = ioremap_nocache(chip->ba1_addr, pci_resource_len(pci, 1));
- if (!chip->ba0 || !chip->ba1) {
- snd_cs4281_free(chip);
- return -ENOMEM;
- }
-
tmp = snd_cs4281_chip_init(chip);
if (tmp) {
snd_cs4281_free(chip);
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
-static int external_amp[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0};
-static int thinkpad[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0};
+static int external_amp[SNDRV_CARDS];
+static int thinkpad[SNDRV_CARDS];
static int mmap_valid[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1};
module_param_array(index, int, NULL, 0444);
if (chip->region.idx[0].resource)
snd_cs46xx_hw_stop(chip);
+ if (chip->irq >= 0)
+ free_irq(chip->irq, chip);
+
for (idx = 0; idx < 5; idx++) {
struct snd_cs46xx_region *region = &chip->region.idx[idx];
if (region->remap_addr)
iounmap(region->remap_addr);
release_and_free_resource(region->resource);
}
- if (chip->irq >= 0)
- free_irq(chip->irq, chip);
if (chip->active_ctrl)
chip->active_ctrl(chip, -chip->amplifier);
if ((entry = snd_info_create_card_entry(card, "dsp", card->proc_root)) != NULL) {
entry->content = SNDRV_INFO_CONTENT_TEXT;
entry->mode = S_IFDIR | S_IRUGO | S_IXUGO;
- entry->c.text.read_size = 512;
if (snd_info_register(entry) < 0) {
snd_info_free_entry(entry);
entry->content = SNDRV_INFO_CONTENT_TEXT;
entry->private_data = chip;
entry->mode = S_IFREG | S_IRUGO | S_IWUSR;
- entry->c.text.read_size = 512;
entry->c.text.read = cs46xx_dsp_proc_symbol_table_read;
if (snd_info_register(entry) < 0) {
snd_info_free_entry(entry);
entry->content = SNDRV_INFO_CONTENT_TEXT;
entry->private_data = chip;
entry->mode = S_IFREG | S_IRUGO | S_IWUSR;
- entry->c.text.read_size = 512;
entry->c.text.read = cs46xx_dsp_proc_modules_read;
if (snd_info_register(entry) < 0) {
snd_info_free_entry(entry);
entry->content = SNDRV_INFO_CONTENT_TEXT;
entry->private_data = chip;
entry->mode = S_IFREG | S_IRUGO | S_IWUSR;
- entry->c.text.read_size = 512;
entry->c.text.read = cs46xx_dsp_proc_parameter_dump_read;
if (snd_info_register(entry) < 0) {
snd_info_free_entry(entry);
entry->content = SNDRV_INFO_CONTENT_TEXT;
entry->private_data = chip;
entry->mode = S_IFREG | S_IRUGO | S_IWUSR;
- entry->c.text.read_size = 512;
entry->c.text.read = cs46xx_dsp_proc_sample_dump_read;
if (snd_info_register(entry) < 0) {
snd_info_free_entry(entry);
entry->content = SNDRV_INFO_CONTENT_TEXT;
entry->private_data = chip;
entry->mode = S_IFREG | S_IRUGO | S_IWUSR;
- entry->c.text.read_size = 512;
entry->c.text.read = cs46xx_dsp_proc_task_tree_read;
if (snd_info_register(entry) < 0) {
snd_info_free_entry(entry);
entry->content = SNDRV_INFO_CONTENT_TEXT;
entry->private_data = chip;
entry->mode = S_IFREG | S_IRUGO | S_IWUSR;
- entry->c.text.read_size = 1024;
entry->c.text.read = cs46xx_dsp_proc_scb_read;
if (snd_info_register(entry) < 0) {
snd_info_free_entry(entry);
entry->private_data = scb_info;
entry->mode = S_IFREG | S_IRUGO | S_IWUSR;
- entry->c.text.read_size = 512;
entry->c.text.read = cs46xx_dsp_proc_scb_info_read;
if (snd_info_register(entry) < 0) {
snd-cs5535audio-objs := cs5535audio.o cs5535audio_pcm.o
+ifdef CONFIG_PM
+snd-cs5535audio-objs += cs5535audio_pm.o
+endif
+
# Toplevel Module Dependency
obj-$(CONFIG_SND_CS5535AUDIO) += snd-cs5535audio.o
/*
- * Driver for audio on multifunction CS5535 companion device
+ * Driver for audio on multifunction CS5535/6 companion device
* Copyright (C) Jaya Kumar
*
* Based on Jaroslav Kysela and Takashi Iwai's examples.
#define DRIVER_NAME "cs5535audio"
+static char *ac97_quirk;
+module_param(ac97_quirk, charp, 0444);
+MODULE_PARM_DESC(ac97_quirk, "AC'97 board specific workarounds.");
+
+static struct ac97_quirk ac97_quirks[] __devinitdata = {
+#if 0 /* Not yet confirmed if all 5536 boards are HP only */
+ {
+ .subvendor = PCI_VENDOR_ID_AMD,
+ .subdevice = PCI_DEVICE_ID_AMD_CS5536_AUDIO,
+ .name = "AMD RDK",
+ .type = AC97_TUNE_HP_ONLY
+ },
+#endif
+ {}
+};
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for " DRIVER_NAME);
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string for " DRIVER_NAME);
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable " DRIVER_NAME);
+
static struct pci_device_id snd_cs5535audio_ids[] __devinitdata = {
- { PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_CS5535_AUDIO,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, },
- { PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_CS5536_AUDIO,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, },
+ { PCI_DEVICE(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_CS5535_AUDIO) },
+ { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_CS5536_AUDIO) },
{}
};
udelay(1);
} while (--timeout);
if (!timeout)
- snd_printk(KERN_ERR "Failure reading cs5535 codec\n");
+ snd_printk(KERN_ERR "Failure reading codec reg 0x%x,"
+ "Last value=0x%x\n", reg, val);
return (unsigned short) val;
}
return err;
}
+ snd_ac97_tune_hardware(cs5535au->ac97, ac97_quirks, ac97_quirk);
+
return 0;
}
if ((err = snd_cs5535audio_create(card, pci, &cs5535au)) < 0)
goto probefail_out;
+ card->private_data = cs5535au;
+
if ((err = snd_cs5535audio_mixer(cs5535au)) < 0)
goto probefail_out;
.id_table = snd_cs5535audio_ids,
.probe = snd_cs5535audio_probe,
.remove = __devexit_p(snd_cs5535audio_remove),
+#ifdef CONFIG_PM
+ .suspend = snd_cs5535audio_suspend,
+ .resume = snd_cs5535audio_resume,
+#endif
};
static int __init alsa_card_cs5535audio_init(void)
#define PRM_RDY_STS 0x00800000
#define ACC_CODEC_CNTL_WR_CMD (~0x80000000)
#define ACC_CODEC_CNTL_RD_CMD 0x80000000
+#define ACC_CODEC_CNTL_LNK_SHUTDOWN 0x00040000
+#define ACC_CODEC_CNTL_LNK_WRM_RST 0x00020000
#define PRD_JMP 0x2000
#define PRD_EOP 0x4000
#define PRD_EOT 0x8000
void (*disable_dma)(struct cs5535audio *cs5535au);
void (*pause_dma)(struct cs5535audio *cs5535au);
void (*setup_prd)(struct cs5535audio *cs5535au, u32 prd_addr);
+ u32 (*read_prd)(struct cs5535audio *cs5535au);
u32 (*read_dma_pntr)(struct cs5535audio *cs5535au);
};
struct snd_pcm_substream *substream;
unsigned int buf_addr, buf_bytes;
unsigned int period_bytes, periods;
+ int suspended;
+ u32 saved_prd;
};
struct cs5535audio {
struct snd_card *card;
struct snd_ac97 *ac97;
+ struct snd_pcm *pcm;
int irq;
struct pci_dev *pci;
unsigned long port;
struct cs5535audio_dma dmas[NUM_CS5535AUDIO_DMAS];
};
+int snd_cs5535audio_suspend(struct pci_dev *pci, pm_message_t state);
+int snd_cs5535audio_resume(struct pci_dev *pci);
int __devinit snd_cs5535audio_pcm(struct cs5535audio *cs5535audio);
#endif /* __SOUND_CS5535AUDIO_H */
SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_PAUSE |
- SNDRV_PCM_INFO_SYNC_START
+ SNDRV_PCM_INFO_SYNC_START |
+ SNDRV_PCM_INFO_RESUME
),
.formats = (
SNDRV_PCM_FMTBIT_S16_LE
cs_writel(cs5535au, ACC_BM0_PRD, prd_addr);
}
+static u32 cs5535audio_playback_read_prd(struct cs5535audio *cs5535au)
+{
+ return cs_readl(cs5535au, ACC_BM0_PRD);
+}
+
static u32 cs5535audio_playback_read_dma_pntr(struct cs5535audio *cs5535au)
{
return cs_readl(cs5535au, ACC_BM0_PNTR);
cs_writel(cs5535au, ACC_BM1_PRD, prd_addr);
}
+static u32 cs5535audio_capture_read_prd(struct cs5535audio *cs5535au)
+{
+ return cs_readl(cs5535au, ACC_BM1_PRD);
+}
+
static u32 cs5535audio_capture_read_dma_pntr(struct cs5535audio *cs5535au)
{
return cs_readl(cs5535au, ACC_BM1_PNTR);
case SNDRV_PCM_TRIGGER_START:
dma->ops->enable_dma(cs5535au);
break;
+ case SNDRV_PCM_TRIGGER_RESUME:
+ dma->ops->enable_dma(cs5535au);
+ dma->suspended = 0;
+ break;
case SNDRV_PCM_TRIGGER_STOP:
dma->ops->disable_dma(cs5535au);
break;
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ dma->ops->disable_dma(cs5535au);
+ dma->suspended = 1;
+ break;
default:
snd_printk(KERN_ERR "unhandled trigger\n");
err = -EINVAL;
.enable_dma = cs5535audio_playback_enable_dma,
.disable_dma = cs5535audio_playback_disable_dma,
.setup_prd = cs5535audio_playback_setup_prd,
+ .read_prd = cs5535audio_playback_read_prd,
.pause_dma = cs5535audio_playback_pause_dma,
.read_dma_pntr = cs5535audio_playback_read_dma_pntr,
};
.enable_dma = cs5535audio_capture_enable_dma,
.disable_dma = cs5535audio_capture_disable_dma,
.setup_prd = cs5535audio_capture_setup_prd,
+ .read_prd = cs5535audio_capture_read_prd,
.pause_dma = cs5535audio_capture_pause_dma,
.read_dma_pntr = cs5535audio_capture_read_dma_pntr,
};
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
snd_dma_pci_data(cs5535au->pci),
64*1024, 128*1024);
+ cs5535au->pcm = pcm;
return 0;
}
--- /dev/null
+/*
+ * Power management for audio on multifunction CS5535 companion device
+ * Copyright (C) Jaya Kumar
+ *
+ * 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/init.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <sound/driver.h>
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/initval.h>
+#include <sound/asoundef.h>
+#include <sound/pcm.h>
+#include <sound/ac97_codec.h>
+#include "cs5535audio.h"
+
+static void snd_cs5535audio_stop_hardware(struct cs5535audio *cs5535au)
+{
+ /*
+ we depend on snd_ac97_suspend to tell the
+ AC97 codec to shutdown. the amd spec suggests
+ that the LNK_SHUTDOWN be done at the same time
+ that the codec power-down is issued. instead,
+ we do it just after rather than at the same
+ time. excluding codec specific build_ops->suspend
+ ac97 powerdown hits:
+ 0x8000 EAPD
+ 0x4000 Headphone amplifier
+ 0x0300 ADC & DAC
+ 0x0400 Analog Mixer powerdown (Vref on)
+ I am not sure if this is the best that we can do.
+ The remainder to be investigated are:
+ - analog mixer (vref off) 0x0800
+ - AC-link powerdown 0x1000
+ - codec internal clock 0x2000
+ */
+
+ /* set LNK_SHUTDOWN to shutdown AC link */
+ cs_writel(cs5535au, ACC_CODEC_CNTL, ACC_CODEC_CNTL_LNK_SHUTDOWN);
+
+}
+
+int snd_cs5535audio_suspend(struct pci_dev *pci, pm_message_t state)
+{
+ struct snd_card *card = pci_get_drvdata(pci);
+ struct cs5535audio *cs5535au = card->private_data;
+ int i;
+
+ snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
+ for (i = 0; i < NUM_CS5535AUDIO_DMAS; i++) {
+ struct cs5535audio_dma *dma = &cs5535au->dmas[i];
+ if (dma && dma->substream && !dma->suspended)
+ dma->saved_prd = dma->ops->read_prd(cs5535au);
+ }
+ snd_pcm_suspend_all(cs5535au->pcm);
+ snd_ac97_suspend(cs5535au->ac97);
+ /* save important regs, then disable aclink in hw */
+ snd_cs5535audio_stop_hardware(cs5535au);
+ pci_disable_device(pci);
+ pci_save_state(pci);
+
+ return 0;
+}
+
+int snd_cs5535audio_resume(struct pci_dev *pci)
+{
+ struct snd_card *card = pci_get_drvdata(pci);
+ struct cs5535audio *cs5535au = card->private_data;
+ u32 tmp;
+ int timeout;
+ int i;
+
+ pci_restore_state(pci);
+ pci_enable_device(pci);
+ pci_set_master(pci);
+
+ /* set LNK_WRM_RST to reset AC link */
+ cs_writel(cs5535au, ACC_CODEC_CNTL, ACC_CODEC_CNTL_LNK_WRM_RST);
+
+ timeout = 50;
+ do {
+ tmp = cs_readl(cs5535au, ACC_CODEC_STATUS);
+ if (tmp & PRM_RDY_STS)
+ break;
+ udelay(1);
+ } while (--timeout);
+
+ if (!timeout)
+ snd_printk(KERN_ERR "Failure getting AC Link ready\n");
+
+ /* we depend on ac97 to perform the codec power up */
+ snd_ac97_resume(cs5535au->ac97);
+ /* set up rate regs, dma. actual initiation is done in trig */
+ for (i = 0; i < NUM_CS5535AUDIO_DMAS; i++) {
+ struct cs5535audio_dma *dma = &cs5535au->dmas[i];
+ if (dma && dma->substream && dma->suspended) {
+ dma->substream->ops->prepare(dma->substream);
+ dma->ops->setup_prd(cs5535au, dma->saved_prd);
+ }
+ }
+
+ snd_power_change_state(card, SNDRV_CTL_POWER_D0);
+
+ return 0;
+}
+
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
-static int extin[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0};
-static int extout[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0};
+static int extin[SNDRV_CARDS];
+static int extout[SNDRV_CARDS];
static int seq_ports[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 4};
static int max_synth_voices[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 64};
static int max_buffer_size[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 128};
-static int enable_ir[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0};
-static uint subsystem[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0}; /* Force card subsystem model */
+static int enable_ir[SNDRV_CARDS];
+static uint subsystem[SNDRV_CARDS]; /* Force card subsystem model */
module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Index value for the EMU10K1 soundcard.");
static struct snd_emu_chip_details emu_chip_details[] = {
/* Audigy 2 Value AC3 out does not work yet. Need to find out how to turn off interpolators.*/
- /* Audigy4 SB0400 */
- {.vendor = 0x1102, .device = 0x0008, .subsystem = 0x10211102,
- .driver = "Audigy2", .name = "Audigy 4 [SB0400]",
- .id = "Audigy2",
- .emu10k2_chip = 1,
- .ca0108_chip = 1,
- .spk71 = 1,
- .ac97_chip = 1} ,
/* Tested by James@superbug.co.uk 3rd July 2005 */
/* DSP: CA0108-IAT
* DAC: CS4382-KQ
.ca0108_chip = 1,
.spk71 = 1,
.ac97_chip = 1} ,
+ /* Audigy4 (Not PRO) SB0610 */
+ /* Tested by James@superbug.co.uk 4th April 2006 */
+ /* A_IOCFG bits
+ * Output
+ * 0: ?
+ * 1: ?
+ * 2: ?
+ * 3: 0 - Digital Out, 1 - Line in
+ * 4: ?
+ * 5: ?
+ * 6: ?
+ * 7: ?
+ * Input
+ * 8: ?
+ * 9: ?
+ * A: Green jack sense (Front)
+ * B: ?
+ * C: Black jack sense (Rear/Side Right)
+ * D: Yellow jack sense (Center/LFE/Side Left)
+ * E: ?
+ * F: ?
+ *
+ * Digital Out/Line in switch using A_IOCFG bit 3 (0x08)
+ * 0 - Digital Out
+ * 1 - Line in
+ */
+ /* Mic input not tested.
+ * Analog CD input not tested
+ * Digital Out not tested.
+ * Line in working.
+ * Audio output 5.1 working. Side outputs not working.
+ */
+ /* DSP: CA10300-IAT LF
+ * DAC: Cirrus Logic CS4382-KQZ
+ * ADC: Philips 1361T
+ * AC97: Sigmatel STAC9750
+ * CA0151: None
+ */
+ {.vendor = 0x1102, .device = 0x0008, .subsystem = 0x10211102,
+ .driver = "Audigy2", .name = "Audigy 4 [SB0610]",
+ .id = "Audigy2",
+ .emu10k2_chip = 1,
+ .ca0108_chip = 1,
+ .spk71 = 1,
+ .adc_1361t = 1, /* 24 bit capture instead of 16bit */
+ .ac97_chip = 1} ,
/* Audigy 2 ZS Notebook Cardbus card.*/
/* Tested by James@superbug.co.uk 22th December 2005 */
/* Audio output 7.1/Headphones working.
* Digital output working. (AC3 not checked, only PCM)
* Audio inputs not tested.
*/
- /* DSP: Tiny2
+ /* DSP: Tina2
* DAC: Wolfson WM8768/WM8568
* ADC: Wolfson WM8775
* AC97: None
}
}
#endif
-
-/* memory.c */
-EXPORT_SYMBOL(snd_emu10k1_synth_alloc);
-EXPORT_SYMBOL(snd_emu10k1_synth_free);
-EXPORT_SYMBOL(snd_emu10k1_synth_bzero);
-EXPORT_SYMBOL(snd_emu10k1_synth_copy_from_user);
-EXPORT_SYMBOL(snd_emu10k1_memblk_map);
-/* voice.c */
-EXPORT_SYMBOL(snd_emu10k1_voice_alloc);
-EXPORT_SYMBOL(snd_emu10k1_voice_free);
-/* io.c */
-EXPORT_SYMBOL(snd_emu10k1_ptr_read);
-EXPORT_SYMBOL(snd_emu10k1_ptr_write);
struct snd_info_entry *entry;
if(! snd_card_proc_new(emu->card, "emu10k1x_regs", &entry)) {
- snd_info_set_text_ops(entry, emu, 1024, snd_emu10k1x_proc_reg_read);
- entry->c.text.write_size = 64;
+ snd_info_set_text_ops(entry, emu, snd_emu10k1x_proc_reg_read);
entry->c.text.write = snd_emu10k1x_proc_reg_write;
entry->mode |= S_IWUSR;
entry->private_data = emu;
};
static char *audigy_remove_ctls[] = {
/* Master/PCM controls on ac97 of Audigy has no effect */
+ /* On the Audigy2 the AC97 playback is piped into
+ * the Philips ADC for 24bit capture */
"PCM Playback Switch",
"PCM Playback Volume",
"Master Mono Playback Switch",
"AMic Playback Volume", "Mic Playback Volume",
NULL
};
+ static char *audigy_remove_ctls_1361t_adc[] = {
+ /* On the Audigy2 the AC97 playback is piped into
+ * the Philips ADC for 24bit capture */
+ "PCM Playback Switch",
+ "PCM Playback Volume",
+ "Master Mono Playback Switch",
+ "Master Mono Playback Volume",
+ "Capture Source",
+ "Capture Switch",
+ "Capture Volume",
+ "Mic Capture Volume",
+ "Headphone Playback Switch",
+ "Headphone Playback Volume",
+ "3D Control - Center",
+ "3D Control - Depth",
+ "3D Control - Switch",
+ "Line2 Playback Volume",
+ "Line2 Capture Volume",
+ NULL
+ };
+ static char *audigy_rename_ctls_1361t_adc[] = {
+ "Master Playback Switch", "Master Capture Switch",
+ "Master Playback Volume", "Master Capture Volume",
+ "Wave Master Playback Volume", "Master Playback Volume",
+ "PC Speaker Playback Switch", "PC Speaker Capture Switch",
+ "PC Speaker Playback Volume", "PC Speaker Capture Volume",
+ "Phone Playback Switch", "Phone Capture Switch",
+ "Phone Playback Volume", "Phone Capture Volume",
+ "Mic Playback Switch", "Mic Capture Switch",
+ "Mic Playback Volume", "Mic Capture Volume",
+ "Line Playback Switch", "Line Capture Switch",
+ "Line Playback Volume", "Line Capture Volume",
+ "CD Playback Switch", "CD Capture Switch",
+ "CD Playback Volume", "CD Capture Volume",
+ "Aux Playback Switch", "Aux Capture Switch",
+ "Aux Playback Volume", "Aux Capture Volume",
+ "Video Playback Switch", "Video Capture Switch",
+ "Video Playback Volume", "Video Capture Volume",
+
+ NULL
+ };
if (emu->card_capabilities->ac97_chip) {
struct snd_ac97_bus *pbus;
snd_ac97_write_cache(emu->ac97, AC97_MASTER, 0x0000);
/* set capture source to mic */
snd_ac97_write_cache(emu->ac97, AC97_REC_SEL, 0x0000);
- c = audigy_remove_ctls;
+ if (emu->card_capabilities->adc_1361t)
+ c = audigy_remove_ctls_1361t_adc;
+ else
+ c = audigy_remove_ctls;
} else {
/*
* Credits for cards based on STAC9758:
}
if (emu->audigy)
- c = audigy_rename_ctls;
+ if (emu->card_capabilities->adc_1361t)
+ c = audigy_rename_ctls_1361t_adc;
+ else
+ c = audigy_rename_ctls;
else
c = emu10k1_rename_ctls;
for (; *c; c += 2)
rename_ctl(card, c[0], c[1]);
+
if (emu->card_capabilities->subsystem == 0x20071102) { /* Audigy 4 Pro */
rename_ctl(card, "Line2 Capture Volume", "Line1/Mic Capture Volume");
rename_ctl(card, "Analog Mix Capture Volume", "Line2 Capture Volume");
struct snd_info_entry *entry;
#ifdef CONFIG_SND_DEBUG
if (! snd_card_proc_new(emu->card, "io_regs", &entry)) {
- snd_info_set_text_ops(entry, emu, 1024, snd_emu_proc_io_reg_read);
- entry->c.text.write_size = 64;
+ snd_info_set_text_ops(entry, emu, snd_emu_proc_io_reg_read);
entry->c.text.write = snd_emu_proc_io_reg_write;
entry->mode |= S_IWUSR;
}
if (! snd_card_proc_new(emu->card, "ptr_regs00a", &entry)) {
- snd_info_set_text_ops(entry, emu, 65536, snd_emu_proc_ptr_reg_read00a);
- entry->c.text.write_size = 64;
+ snd_info_set_text_ops(entry, emu, snd_emu_proc_ptr_reg_read00a);
entry->c.text.write = snd_emu_proc_ptr_reg_write00;
entry->mode |= S_IWUSR;
}
if (! snd_card_proc_new(emu->card, "ptr_regs00b", &entry)) {
- snd_info_set_text_ops(entry, emu, 65536, snd_emu_proc_ptr_reg_read00b);
- entry->c.text.write_size = 64;
+ snd_info_set_text_ops(entry, emu, snd_emu_proc_ptr_reg_read00b);
entry->c.text.write = snd_emu_proc_ptr_reg_write00;
entry->mode |= S_IWUSR;
}
if (! snd_card_proc_new(emu->card, "ptr_regs20a", &entry)) {
- snd_info_set_text_ops(entry, emu, 65536, snd_emu_proc_ptr_reg_read20a);
- entry->c.text.write_size = 64;
+ snd_info_set_text_ops(entry, emu, snd_emu_proc_ptr_reg_read20a);
entry->c.text.write = snd_emu_proc_ptr_reg_write20;
entry->mode |= S_IWUSR;
}
if (! snd_card_proc_new(emu->card, "ptr_regs20b", &entry)) {
- snd_info_set_text_ops(entry, emu, 65536, snd_emu_proc_ptr_reg_read20b);
- entry->c.text.write_size = 64;
+ snd_info_set_text_ops(entry, emu, snd_emu_proc_ptr_reg_read20b);
entry->c.text.write = snd_emu_proc_ptr_reg_write20;
entry->mode |= S_IWUSR;
}
if (! snd_card_proc_new(emu->card, "ptr_regs20c", &entry)) {
- snd_info_set_text_ops(entry, emu, 65536, snd_emu_proc_ptr_reg_read20c);
- entry->c.text.write_size = 64;
+ snd_info_set_text_ops(entry, emu, snd_emu_proc_ptr_reg_read20c);
entry->c.text.write = snd_emu_proc_ptr_reg_write20;
entry->mode |= S_IWUSR;
}
#endif
if (! snd_card_proc_new(emu->card, "emu10k1", &entry))
- snd_info_set_text_ops(entry, emu, 2048, snd_emu10k1_proc_read);
+ snd_info_set_text_ops(entry, emu, snd_emu10k1_proc_read);
if (emu->card_capabilities->emu10k2_chip) {
if (! snd_card_proc_new(emu->card, "spdif-in", &entry))
- snd_info_set_text_ops(entry, emu, 2048, snd_emu10k1_proc_spdif_read);
+ snd_info_set_text_ops(entry, emu, snd_emu10k1_proc_spdif_read);
}
if (emu->card_capabilities->ca0151_chip) {
if (! snd_card_proc_new(emu->card, "capture-rates", &entry))
- snd_info_set_text_ops(entry, emu, 2048, snd_emu10k1_proc_rates_read);
+ snd_info_set_text_ops(entry, emu, snd_emu10k1_proc_rates_read);
}
if (! snd_card_proc_new(emu->card, "voices", &entry))
- snd_info_set_text_ops(entry, emu, 2048, snd_emu10k1_proc_voices_read);
+ snd_info_set_text_ops(entry, emu, snd_emu10k1_proc_voices_read);
if (! snd_card_proc_new(emu->card, "fx8010_gpr", &entry)) {
entry->content = SNDRV_INFO_CONTENT_DATA;
entry->content = SNDRV_INFO_CONTENT_TEXT;
entry->private_data = emu;
entry->mode = S_IFREG | S_IRUGO /*| S_IWUSR*/;
- entry->c.text.read_size = 128*1024;
entry->c.text.read = snd_emu10k1_proc_acode_read;
}
return 0;
}
}
+EXPORT_SYMBOL(snd_emu10k1_ptr_read);
+
void snd_emu10k1_ptr_write(struct snd_emu10k1 *emu, unsigned int reg, unsigned int chn, unsigned int data)
{
unsigned int regptr;
}
}
+EXPORT_SYMBOL(snd_emu10k1_ptr_write);
+
unsigned int snd_emu10k1_ptr20_read(struct snd_emu10k1 * emu,
unsigned int reg,
unsigned int chn)
return err;
}
+EXPORT_SYMBOL(snd_emu10k1_memblk_map);
+
/*
* page allocation for DMA
*/
return (struct snd_util_memblk *)blk;
}
+EXPORT_SYMBOL(snd_emu10k1_synth_alloc);
/*
* free a synth sample area
return 0;
}
+EXPORT_SYMBOL(snd_emu10k1_synth_free);
/* check new allocation range */
static void get_single_page_range(struct snd_util_memhdr *hdr,
return 0;
}
+EXPORT_SYMBOL(snd_emu10k1_synth_bzero);
+
/*
* copy_from_user(blk + offset, data, size)
*/
} while (offset < end_offset);
return 0;
}
+
+EXPORT_SYMBOL(snd_emu10k1_synth_copy_from_user);
--- /dev/null
+/*
+ * Copyright (c) by James Courtier-Dutton <James@superbug.demon.co.uk>
+ * Driver p17v chips
+ * Version: 0.01
+ *
+ * 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
+ *
+ */
+
+/******************************************************************************/
+/* Audigy2Value Tina (P17V) pointer-offset register set,
+ * accessed through the PTR20 and DATA24 registers */
+/******************************************************************************/
+
+/* 00 - 07: Not used */
+#define P17V_PLAYBACK_FIFO_PTR 0x08 /* Current playback fifo pointer
+ * and number of sound samples in cache.
+ */
+/* 09 - 12: Not used */
+#define P17V_CAPTURE_FIFO_PTR 0x13 /* Current capture fifo pointer
+ * and number of sound samples in cache.
+ */
+/* 14 - 17: Not used */
+#define P17V_PB_CHN_SEL 0x18 /* P17v playback channel select */
+#define P17V_SE_SLOT_SEL_L 0x19 /* Sound Engine slot select low */
+#define P17V_SE_SLOT_SEL_H 0x1a /* Sound Engine slot select high */
+/* 1b - 1f: Not used */
+/* 20 - 2f: Not used */
+/* 30 - 3b: Not used */
+#define P17V_SPI 0x3c /* SPI interface register */
+#define P17V_I2C_ADDR 0x3d /* I2C Address */
+#define P17V_I2C_0 0x3e /* I2C Data */
+#define P17V_I2C_1 0x3f /* I2C Data */
+
+#define P17V_START_AUDIO 0x40 /* Start Audio bit */
+/* 41 - 47: Reserved */
+#define P17V_START_CAPTURE 0x48 /* Start Capture bit */
+#define P17V_CAPTURE_FIFO_BASE 0x49 /* Record FIFO base address */
+#define P17V_CAPTURE_FIFO_SIZE 0x4a /* Record FIFO buffer size */
+#define P17V_CAPTURE_FIFO_INDEX 0x4b /* Record FIFO capture index */
+#define P17V_CAPTURE_VOL_H 0x4c /* P17v capture volume control */
+#define P17V_CAPTURE_VOL_L 0x4d /* P17v capture volume control */
+/* 4e - 4f: Not used */
+/* 50 - 5f: Not used */
+#define P17V_SRCSel 0x60 /* SRC48 and SRCMulti sample rate select
+ * and output select
+ */
+#define P17V_MIXER_AC97_10K1_VOL_L 0x61 /* 10K to Mixer_AC97 input volume control */
+#define P17V_MIXER_AC97_10K1_VOL_H 0x62 /* 10K to Mixer_AC97 input volume control */
+#define P17V_MIXER_AC97_P17V_VOL_L 0x63 /* P17V to Mixer_AC97 input volume control */
+#define P17V_MIXER_AC97_P17V_VOL_H 0x64 /* P17V to Mixer_AC97 input volume control */
+#define P17V_MIXER_AC97_SRP_REC_VOL_L 0x65 /* SRP Record to Mixer_AC97 input volume control */
+#define P17V_MIXER_AC97_SRP_REC_VOL_H 0x66 /* SRP Record to Mixer_AC97 input volume control */
+/* 67 - 68: Reserved */
+#define P17V_MIXER_Spdif_10K1_VOL_L 0x69 /* 10K to Mixer_Spdif input volume control */
+#define P17V_MIXER_Spdif_10K1_VOL_H 0x6A /* 10K to Mixer_Spdif input volume control */
+#define P17V_MIXER_Spdif_P17V_VOL_L 0x6B /* P17V to Mixer_Spdif input volume control */
+#define P17V_MIXER_Spdif_P17V_VOL_H 0x6C /* P17V to Mixer_Spdif input volume control */
+#define P17V_MIXER_Spdif_SRP_REC_VOL_L 0x6D /* SRP Record to Mixer_Spdif input volume control */
+#define P17V_MIXER_Spdif_SRP_REC_VOL_H 0x6E /* SRP Record to Mixer_Spdif input volume control */
+/* 6f - 70: Reserved */
+#define P17V_MIXER_I2S_10K1_VOL_L 0x71 /* 10K to Mixer_I2S input volume control */
+#define P17V_MIXER_I2S_10K1_VOL_H 0x72 /* 10K to Mixer_I2S input volume control */
+#define P17V_MIXER_I2S_P17V_VOL_L 0x73 /* P17V to Mixer_I2S input volume control */
+#define P17V_MIXER_I2S_P17V_VOL_H 0x74 /* P17V to Mixer_I2S input volume control */
+#define P17V_MIXER_I2S_SRP_REC_VOL_L 0x75 /* SRP Record to Mixer_I2S input volume control */
+#define P17V_MIXER_I2S_SRP_REC_VOL_H 0x76 /* SRP Record to Mixer_I2S input volume control */
+/* 77 - 78: Reserved */
+#define P17V_MIXER_AC97_ENABLE 0x79 /* Mixer AC97 input audio enable */
+#define P17V_MIXER_SPDIF_ENABLE 0x7A /* Mixer SPDIF input audio enable */
+#define P17V_MIXER_I2S_ENABLE 0x7B /* Mixer I2S input audio enable */
+#define P17V_AUDIO_OUT_ENABLE 0x7C /* Audio out enable */
+#define P17V_MIXER_ATT 0x7D /* SRP Mixer Attenuation Select */
+#define P17V_SRP_RECORD_SRR 0x7E /* SRP Record channel source Select */
+#define P17V_SOFT_RESET_SRP_MIXER 0x7F /* SRP and mixer soft reset */
+
+#define P17V_AC97_OUT_MASTER_VOL_L 0x80 /* AC97 Output master volume control */
+#define P17V_AC97_OUT_MASTER_VOL_H 0x81 /* AC97 Output master volume control */
+#define P17V_SPDIF_OUT_MASTER_VOL_L 0x82 /* SPDIF Output master volume control */
+#define P17V_SPDIF_OUT_MASTER_VOL_H 0x83 /* SPDIF Output master volume control */
+#define P17V_I2S_OUT_MASTER_VOL_L 0x84 /* I2S Output master volume control */
+#define P17V_I2S_OUT_MASTER_VOL_H 0x85 /* I2S Output master volume control */
+/* 86 - 87: Not used */
+#define P17V_I2S_CHANNEL_SWAP_PHASE_INVERSE 0x88 /* I2S out mono channel swap
+ * and phase inverse */
+#define P17V_SPDIF_CHANNEL_SWAP_PHASE_INVERSE 0x89 /* SPDIF out mono channel swap
+ * and phase inverse */
+/* 8A: Not used */
+#define P17V_SRP_P17V_ESR 0x8B /* SRP_P17V estimated sample rate and rate lock */
+#define P17V_SRP_REC_ESR 0x8C /* SRP_REC estimated sample rate and rate lock */
+#define P17V_SRP_BYPASS 0x8D /* srps channel bypass and srps bypass */
+/* 8E - 92: Not used */
+#define P17V_I2S_SRC_SEL 0x93 /* I2SIN mode sel */
+
+
+
+
+
+
/*
* Copyright (c) by James Courtier-Dutton <James@superbug.demon.co.uk>
- * Driver p16v chips
- * Version: 0.21
- *
- *
- * This code was initally based on code from ALSA's emu10k1x.c which is:
- * Copyright (c) by Francisco Moraes <fmoraes@nc.rr.com>
+ * Driver tina2 chips
+ * Version: 0.1
*
* 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
return result;
}
+EXPORT_SYMBOL(snd_emu10k1_voice_alloc);
+
int snd_emu10k1_voice_free(struct snd_emu10k1 *emu,
struct snd_emu10k1_voice *pvoice)
{
spin_unlock_irqrestore(&emu->voice_lock, flags);
return 0;
}
+
+EXPORT_SYMBOL(snd_emu10k1_voice_free);
struct snd_info_entry *entry;
if (! snd_card_proc_new(ensoniq->card, "audiopci", &entry))
- snd_info_set_text_ops(entry, ensoniq, 1024, snd_ensoniq_proc_read);
+ snd_info_set_text_ops(entry, ensoniq, snd_ensoniq_proc_read);
}
/*
}
}
if (snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401,
- chip->mpu_port, 1, chip->irq, 0, &chip->rmidi) < 0) {
+ chip->mpu_port, MPU401_INFO_INTEGRATED,
+ chip->irq, 0, &chip->rmidi) < 0) {
printk(KERN_ERR "es1938: unable to initialize MPU-401\n");
} else {
// this line is vital for MIDI interrupt handling on ess-solo1
static int total_bufsize[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1024 };
static int pcm_substreams_p[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 4 };
static int pcm_substreams_c[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1 };
-static int clock[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0};
+static int clock[SNDRV_CARDS];
static int use_pm[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 2};
static int enable_mpu[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 2};
#ifdef SUPPORT_JOYSTICK
}
if (enable_mpu[dev]) {
if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401,
- chip->io_port + ESM_MPU401_PORT, 1,
+ chip->io_port + ESM_MPU401_PORT,
+ MPU401_INFO_INTEGRATED,
chip->irq, 0, &chip->rmidi)) < 0) {
printk(KERN_WARNING "es1968: skipping MPU-401 MIDI support..\n");
}
* 3 = MediaForte 64-PCR
* High 16-bits are video (radio) device number + 1
*/
-static int tea575x_tuner[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS-1)] = 0 };
+static int tea575x_tuner[SNDRV_CARDS];
module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Index value for the FM801 soundcard.");
return err;
}
if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_FM801,
- FM801_REG(chip, MPU401_DATA), 1,
+ FM801_REG(chip, MPU401_DATA),
+ MPU401_INFO_INTEGRATED,
chip->irq, 0, &chip->rmidi)) < 0) {
snd_card_free(card);
return err;
snd-hda-intel-objs := hda_intel.o
-snd-hda-codec-objs := hda_codec.o hda_generic.o patch_realtek.o patch_cmedia.o patch_analog.o patch_sigmatel.o patch_si3054.o
+snd-hda-codec-objs := hda_codec.o hda_generic.o patch_realtek.o patch_cmedia.o patch_analog.o patch_sigmatel.o patch_si3054.o patch_atihdmi.o
ifdef CONFIG_PROC_FS
snd-hda-codec-objs += hda_proc.o
endif
return res;
}
+EXPORT_SYMBOL(snd_hda_codec_read);
+
/**
* snd_hda_codec_write - send a single command without waiting for response
* @codec: the HDA codec
return err;
}
+EXPORT_SYMBOL(snd_hda_codec_write);
+
/**
* snd_hda_sequence_write - sequence writes
* @codec: the HDA codec
snd_hda_codec_write(codec, seq->nid, 0, seq->verb, seq->param);
}
+EXPORT_SYMBOL(snd_hda_sequence_write);
+
/**
* snd_hda_get_sub_nodes - get the range of sub nodes
* @codec: the HDA codec
return (int)(parm & 0x7fff);
}
+EXPORT_SYMBOL(snd_hda_get_sub_nodes);
+
/**
* snd_hda_get_connections - get connection list
* @codec: the HDA codec
return 0;
}
+EXPORT_SYMBOL(snd_hda_queue_unsol_event);
+
/*
* process queueud unsolicited events
*/
return 0;
}
+EXPORT_SYMBOL(snd_hda_bus_new);
/*
* find a matching codec preset
return 0;
}
+EXPORT_SYMBOL(snd_hda_codec_new);
+
/**
* snd_hda_codec_setup_stream - set up the codec for streaming
* @codec: the CODEC to set up
snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_STREAM_FORMAT, format);
}
+EXPORT_SYMBOL(snd_hda_codec_setup_stream);
/*
* amp access functions
return 0;
}
+EXPORT_SYMBOL(snd_hda_build_controls);
/*
* stream formats
return val;
}
+EXPORT_SYMBOL(snd_hda_calc_stream_format);
+
/**
* snd_hda_query_supported_pcm - query the supported PCM rates and formats
* @codec: the HDA codec
return 0;
}
+EXPORT_SYMBOL(snd_hda_build_pcms);
/**
* snd_hda_check_board_config - compare the current codec with the config table
return 0;
}
+EXPORT_SYMBOL(snd_hda_suspend);
+
/**
* snd_hda_resume - resume the codecs
* @bus: the HDA bus
return 0;
}
+EXPORT_SYMBOL(snd_hda_resume);
+
/**
* snd_hda_resume_ctls - resume controls in the new control list
* @codec: the HDA codec
}
#endif
-/*
- * symbols exported for controller modules
- */
-EXPORT_SYMBOL(snd_hda_codec_read);
-EXPORT_SYMBOL(snd_hda_codec_write);
-EXPORT_SYMBOL(snd_hda_sequence_write);
-EXPORT_SYMBOL(snd_hda_get_sub_nodes);
-EXPORT_SYMBOL(snd_hda_queue_unsol_event);
-EXPORT_SYMBOL(snd_hda_bus_new);
-EXPORT_SYMBOL(snd_hda_codec_new);
-EXPORT_SYMBOL(snd_hda_codec_setup_stream);
-EXPORT_SYMBOL(snd_hda_calc_stream_format);
-EXPORT_SYMBOL(snd_hda_build_pcms);
-EXPORT_SYMBOL(snd_hda_build_controls);
-#ifdef CONFIG_PM
-EXPORT_SYMBOL(snd_hda_suspend);
-EXPORT_SYMBOL(snd_hda_resume);
-#endif
-
/*
* INIT part
*/
"{Intel, ICH8},"
"{ATI, SB450},"
"{ATI, SB600},"
+ "{ATI, RS600},"
"{VIA, VT8251},"
"{VIA, VT8237A},"
"{SiS, SIS966},"
#define ULI_PLAYBACK_INDEX 5
#define ULI_NUM_PLAYBACK 6
+/* ATI HDMI has 1 playback and 0 capture */
+#define ATIHDMI_CAPTURE_INDEX 0
+#define ATIHDMI_NUM_CAPTURE 0
+#define ATIHDMI_PLAYBACK_INDEX 0
+#define ATIHDMI_NUM_PLAYBACK 1
+
/* this number is statically defined for simplicity */
#define MAX_AZX_DEV 16
enum {
AZX_DRIVER_ICH,
AZX_DRIVER_ATI,
+ AZX_DRIVER_ATIHDMI,
AZX_DRIVER_VIA,
AZX_DRIVER_SIS,
AZX_DRIVER_ULI,
static char *driver_short_names[] __devinitdata = {
[AZX_DRIVER_ICH] = "HDA Intel",
[AZX_DRIVER_ATI] = "HDA ATI SB",
+ [AZX_DRIVER_ATIHDMI] = "HDA ATI HDMI",
[AZX_DRIVER_VIA] = "HDA VIA VT82xx",
[AZX_DRIVER_SIS] = "HDA SIS966",
[AZX_DRIVER_ULI] = "HDA ULI M5461",
msleep(1);
}
- if (chip->remap_addr)
- iounmap(chip->remap_addr);
if (chip->irq >= 0)
free_irq(chip->irq, (void*)chip);
+ if (chip->remap_addr)
+ iounmap(chip->remap_addr);
if (chip->bdl.area)
snd_dma_free_pages(&chip->bdl);
chip->playback_index_offset = ULI_PLAYBACK_INDEX;
chip->capture_index_offset = ULI_CAPTURE_INDEX;
break;
+ case AZX_DRIVER_ATIHDMI:
+ chip->playback_streams = ATIHDMI_NUM_PLAYBACK;
+ chip->capture_streams = ATIHDMI_NUM_CAPTURE;
+ chip->playback_index_offset = ATIHDMI_PLAYBACK_INDEX;
+ chip->capture_index_offset = ATIHDMI_CAPTURE_INDEX;
+ break;
default:
chip->playback_streams = ICH6_NUM_PLAYBACK;
chip->capture_streams = ICH6_NUM_CAPTURE;
{ 0x8086, 0x284b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ICH }, /* ICH8 */
{ 0x1002, 0x437b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATI }, /* ATI SB450 */
{ 0x1002, 0x4383, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATI }, /* ATI SB600 */
+ { 0x1002, 0x793b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI RS600 HDMI */
{ 0x1106, 0x3288, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_VIA }, /* VIA VT8251/VT8237A */
{ 0x1039, 0x7502, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_SIS }, /* SIS966 */
{ 0x10b9, 0x5461, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ULI }, /* ULI M5461 */
extern struct hda_codec_preset snd_hda_preset_sigmatel[];
/* SiLabs 3054/3055 modem codecs */
extern struct hda_codec_preset snd_hda_preset_si3054[];
+/* ATI HDMI codecs */
+extern struct hda_codec_preset snd_hda_preset_atihdmi[];
static const struct hda_codec_preset *hda_preset_tables[] = {
snd_hda_preset_realtek,
snd_hda_preset_analog,
snd_hda_preset_sigmatel,
snd_hda_preset_si3054,
+ snd_hda_preset_atihdmi,
NULL
};
snd_iprintf(buffer, " OUT");
if (caps & AC_PINCAP_HP_DRV)
snd_iprintf(buffer, " HP");
+ if (caps & AC_PINCAP_EAPD)
+ snd_iprintf(buffer, " EAPD");
+ if (caps & AC_PINCAP_PRES_DETECT)
+ snd_iprintf(buffer, " Detect");
snd_iprintf(buffer, "\n");
caps = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONFIG_DEFAULT, 0);
snd_iprintf(buffer, " Pin Default 0x%08x: [%s] %s at %s %s\n", caps,
if (err < 0)
return err;
- snd_info_set_text_ops(entry, codec, 32 * 1024, print_codec_info);
+ snd_info_set_text_ops(entry, codec, print_codec_info);
return 0;
}
{ .modelname = "3stack", .config = AD1986A_3STACK },
{ .pci_subvendor = 0x10de, .pci_subdevice = 0xcb84,
.config = AD1986A_3STACK }, /* ASUS A8N-VM CSM */
+ { .pci_subvendor = 0x1043, .pci_subdevice = 0x81b3,
+ .config = AD1986A_3STACK }, /* ASUS P5RD2-VM / P5GPL-X SE */
{ .modelname = "laptop", .config = AD1986A_LAPTOP },
{ .pci_subvendor = 0x144d, .pci_subdevice = 0xc01e,
.config = AD1986A_LAPTOP }, /* FSC V2060 */
.config = AD1986A_LAPTOP_EAPD }, /* ASUS Z62F */
{ .pci_subvendor = 0x103c, .pci_subdevice = 0x30af,
.config = AD1986A_LAPTOP_EAPD }, /* HP Compaq Presario B2800 */
+ { .pci_subvendor = 0x17aa, .pci_subdevice = 0x2066,
+ .config = AD1986A_LAPTOP_EAPD }, /* Lenovo 3000 N100-07684JU */
{}
};
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Route",
+ .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
.info = ad1983_spdif_route_info,
.get = ad1983_spdif_route_get,
.put = ad1983_spdif_route_put,
/* identical with AD1983 */
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Route",
+ .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
.info = ad1983_spdif_route_info,
.get = ad1983_spdif_route_get,
.put = ad1983_spdif_route_put,
return 0;
}
+/* configuration for Lenovo Thinkpad T60 */
+static struct snd_kcontrol_new ad1981_thinkpad_mixers[] = {
+ HDA_CODEC_VOLUME("Master Playback Volume", 0x05, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Master Playback Switch", 0x05, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("CD Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("CD Playback Switch", 0x1d, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("Mic Boost", 0x08, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Capture Source",
+ .info = ad198x_mux_enum_info,
+ .get = ad198x_mux_enum_get,
+ .put = ad198x_mux_enum_put,
+ },
+ /* identical with AD1983 */
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
+ .info = ad1983_spdif_route_info,
+ .get = ad1983_spdif_route_get,
+ .put = ad1983_spdif_route_put,
+ },
+ { } /* end */
+};
+
+static struct hda_input_mux ad1981_thinkpad_capture_source = {
+ .num_items = 3,
+ .items = {
+ { "Mic", 0x0 },
+ { "Mix", 0x2 },
+ { "CD", 0x4 },
+ },
+};
+
/* models */
-enum { AD1981_BASIC, AD1981_HP };
+enum { AD1981_BASIC, AD1981_HP, AD1981_THINKPAD };
static struct hda_board_config ad1981_cfg_tbl[] = {
{ .modelname = "hp", .config = AD1981_HP },
/* All HP models */
{ .pci_subvendor = 0x103c, .config = AD1981_HP },
+ { .pci_subvendor = 0x30b0, .pci_subdevice = 0x103c,
+ .config = AD1981_HP }, /* HP nx6320 (reversed SSID, H/W bug) */
+ { .modelname = "thinkpad", .config = AD1981_THINKPAD },
+ /* Lenovo Thinkpad T60/X60/Z6xx */
+ { .pci_subvendor = 0x17aa, .config = AD1981_THINKPAD },
+ { .pci_subvendor = 0x1014, .pci_subdevice = 0x0597,
+ .config = AD1981_THINKPAD }, /* Z60m/t */
{ .modelname = "basic", .config = AD1981_BASIC },
{}
};
codec->patch_ops.init = ad1981_hp_init;
codec->patch_ops.unsol_event = ad1981_hp_unsol_event;
break;
+ case AD1981_THINKPAD:
+ spec->mixers[0] = ad1981_thinkpad_mixers;
+ spec->input_mux = &ad1981_thinkpad_capture_source;
+ break;
}
return 0;
--- /dev/null
+/*
+ * Universal Interface for Intel High Definition Audio Codec
+ *
+ * HD audio interface patch for ATI HDMI codecs
+ *
+ * Copyright (c) 2006 ATI Technologies Inc.
+ *
+ *
+ * This driver 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 driver 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 <sound/driver.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include <sound/core.h>
+#include "hda_codec.h"
+#include "hda_local.h"
+
+struct atihdmi_spec {
+ struct hda_multi_out multiout;
+
+ struct hda_pcm pcm_rec;
+};
+
+static struct hda_verb atihdmi_basic_init[] = {
+ /* enable digital output on pin widget */
+ { 0x03, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+ {} /* terminator */
+};
+
+/*
+ * Controls
+ */
+static int atihdmi_build_controls(struct hda_codec *codec)
+{
+ struct atihdmi_spec *spec = codec->spec;
+ int err;
+
+ err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+static int atihdmi_init(struct hda_codec *codec)
+{
+ snd_hda_sequence_write(codec, atihdmi_basic_init);
+ return 0;
+}
+
+#ifdef CONFIG_PM
+/*
+ * resume
+ */
+static int atihdmi_resume(struct hda_codec *codec)
+{
+ atihdmi_init(codec);
+ snd_hda_resume_spdif_out(codec);
+
+ return 0;
+}
+#endif
+
+/*
+ * Digital out
+ */
+static int atihdmi_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct atihdmi_spec *spec = codec->spec;
+ return snd_hda_multi_out_dig_open(codec, &spec->multiout);
+}
+
+static int atihdmi_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct atihdmi_spec *spec = codec->spec;
+ return snd_hda_multi_out_dig_close(codec, &spec->multiout);
+}
+
+static struct hda_pcm_stream atihdmi_pcm_digital_playback = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 2,
+ .nid = 0x2, /* NID to query formats and rates and setup streams */
+ .ops = {
+ .open = atihdmi_dig_playback_pcm_open,
+ .close = atihdmi_dig_playback_pcm_close
+ },
+};
+
+static int atihdmi_build_pcms(struct hda_codec *codec)
+{
+ struct atihdmi_spec *spec = codec->spec;
+ struct hda_pcm *info = &spec->pcm_rec;
+
+ codec->num_pcms = 1;
+ codec->pcm_info = info;
+
+ info->name = "ATI HDMI";
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK] = atihdmi_pcm_digital_playback;
+
+ return 0;
+}
+
+static void atihdmi_free(struct hda_codec *codec)
+{
+ kfree(codec->spec);
+}
+
+static struct hda_codec_ops atihdmi_patch_ops = {
+ .build_controls = atihdmi_build_controls,
+ .build_pcms = atihdmi_build_pcms,
+ .init = atihdmi_init,
+ .free = atihdmi_free,
+#ifdef CONFIG_PM
+ .resume = atihdmi_resume,
+#endif
+};
+
+static int patch_atihdmi(struct hda_codec *codec)
+{
+ struct atihdmi_spec *spec;
+
+ spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+ if (spec == NULL)
+ return -ENOMEM;
+
+ codec->spec = spec;
+
+ spec->multiout.num_dacs = 0; /* no analog */
+ spec->multiout.max_channels = 2;
+ spec->multiout.dig_out_nid = 0x2; /* NID for copying analog to digital,
+ * seems to be unused in pure-digital
+ * case. */
+
+ codec->patch_ops = atihdmi_patch_ops;
+
+ return 0;
+}
+
+/*
+ * patch entries
+ */
+struct hda_codec_preset snd_hda_preset_atihdmi[] = {
+ { .id = 0x1002793c, .name = "ATI RS600 HDMI", .patch = patch_atihdmi },
+ {} /* terminator */
+};
{ .modelname = "lg", .config = ALC880_LG },
{ .pci_subvendor = 0x1854, .pci_subdevice = 0x003b, .config = ALC880_LG },
+ { .pci_subvendor = 0x1854, .pci_subdevice = 0x0068, .config = ALC880_LG },
{ .modelname = "lg-lw", .config = ALC880_LG_LW },
{ .pci_subvendor = 0x1854, .pci_subdevice = 0x0018, .config = ALC880_LG_LW },
{ }
};
+#if 0 /* should be identical with alc260_init_verbs? */
static struct hda_verb alc260_hp_init_verbs[] = {
/* Headphone and output */
{0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0},
{0x0a, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
{ }
};
+#endif
static struct hda_verb alc260_hp_3013_init_verbs[] = {
/* Line out and output */
{ .modelname = "basic", .config = ALC260_BASIC },
{ .pci_subvendor = 0x104d, .pci_subdevice = 0x81bb,
.config = ALC260_BASIC }, /* Sony VAIO */
+ { .pci_subvendor = 0x104d, .pci_subdevice = 0x81cc,
+ .config = ALC260_BASIC }, /* Sony VAIO VGN-S3HP */
+ { .pci_subvendor = 0x104d, .pci_subdevice = 0x81cd,
+ .config = ALC260_BASIC }, /* Sony VAIO */
{ .pci_subvendor = 0x152d, .pci_subdevice = 0x0729,
.config = ALC260_BASIC }, /* CTL Travel Master U553W */
{ .modelname = "hp", .config = ALC260_HP },
{ .pci_subvendor = 0x103c, .pci_subdevice = 0x3010, .config = ALC260_HP },
{ .pci_subvendor = 0x103c, .pci_subdevice = 0x3011, .config = ALC260_HP },
- { .pci_subvendor = 0x103c, .pci_subdevice = 0x3012, .config = ALC260_HP },
+ { .pci_subvendor = 0x103c, .pci_subdevice = 0x3012, .config = ALC260_HP_3013 },
{ .pci_subvendor = 0x103c, .pci_subdevice = 0x3013, .config = ALC260_HP_3013 },
{ .pci_subvendor = 0x103c, .pci_subdevice = 0x3014, .config = ALC260_HP },
{ .pci_subvendor = 0x103c, .pci_subdevice = 0x3015, .config = ALC260_HP },
.mixers = { alc260_base_output_mixer,
alc260_input_mixer,
alc260_capture_alt_mixer },
- .init_verbs = { alc260_hp_init_verbs },
+ .init_verbs = { alc260_init_verbs },
.num_dacs = ARRAY_SIZE(alc260_dac_nids),
.dac_nids = alc260_dac_nids,
.num_adc_nids = ARRAY_SIZE(alc260_hp_adc_nids),
HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT),
HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT),
- HDA_CODEC_VOLUME("Capture Volume", 0x07, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x07, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME_IDX("Capture Volume", 2, 0x09, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 2, 0x09, 0x0, HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* .name = "Capture Source", */
- .name = "Input Source",
- .count = 3,
- .info = alc882_mux_enum_info,
- .get = alc882_mux_enum_get,
- .put = alc882_mux_enum_put,
- },
{ } /* end */
};
.num_dacs = ARRAY_SIZE(alc882_dac_nids),
.dac_nids = alc882_dac_nids,
.dig_out_nid = ALC882_DIGOUT_NID,
- .num_adc_nids = ARRAY_SIZE(alc882_adc_nids),
- .adc_nids = alc882_adc_nids,
.dig_in_nid = ALC882_DIGIN_NID,
.num_channel_mode = ARRAY_SIZE(alc882_ch_modes),
.channel_mode = alc882_ch_modes,
.num_dacs = ARRAY_SIZE(alc882_dac_nids),
.dac_nids = alc882_dac_nids,
.dig_out_nid = ALC882_DIGOUT_NID,
- .num_adc_nids = ARRAY_SIZE(alc882_adc_nids),
- .adc_nids = alc882_adc_nids,
.dig_in_nid = ALC882_DIGIN_NID,
.num_channel_mode = ARRAY_SIZE(alc882_sixstack_modes),
.channel_mode = alc882_sixstack_modes,
#define STAC_REF 0
#define STAC_D945GTP3 1
#define STAC_D945GTP5 2
+#define STAC_MACMINI 3
struct sigmatel_spec {
struct snd_kcontrol_new *mixers[4];
unsigned int mic_switch: 1;
unsigned int alt_switch: 1;
unsigned int hp_detect: 1;
+ unsigned int gpio_mute: 1;
/* playback */
struct hda_multi_out multiout;
ref922x_pin_configs,
d945gtp3_pin_configs,
d945gtp5_pin_configs,
+ NULL, /* STAC_MACMINI */
};
static struct hda_board_config stac922x_cfg_tbl[] = {
{ .pci_subvendor = PCI_VENDOR_ID_INTEL,
.pci_subdevice = 0x0417,
.config = STAC_D945GTP5 }, /* Intel D975XBK - 5 Stack */
+ { .pci_subvendor = 0x8384,
+ .pci_subdevice = 0x7680,
+ .config = STAC_MACMINI }, /* Apple Mac Mini (early 2006) */
{} /* terminator */
};
}
}
+ if (imux->num_items == 1) {
+ /*
+ * Set the current input for the muxes.
+ * The STAC9221 has two input muxes with identical source
+ * NID lists. Hopefully this won't get confused.
+ */
+ for (i = 0; i < spec->num_muxes; i++) {
+ snd_hda_codec_write(codec, spec->mux_nids[i], 0,
+ AC_VERB_SET_CONNECT_SEL,
+ imux->items[0].index);
+ }
+ }
+
return 0;
}
return 1;
}
+/*
+ * Early 2006 Intel Macintoshes with STAC9220X5 codecs seem to have a
+ * funky external mute control using GPIO pins.
+ */
+
+static void stac922x_gpio_mute(struct hda_codec *codec, int pin, int muted)
+{
+ unsigned int gpiostate, gpiomask, gpiodir;
+
+ gpiostate = snd_hda_codec_read(codec, codec->afg, 0,
+ AC_VERB_GET_GPIO_DATA, 0);
+
+ if (!muted)
+ gpiostate |= (1 << pin);
+ else
+ gpiostate &= ~(1 << pin);
+
+ gpiomask = snd_hda_codec_read(codec, codec->afg, 0,
+ AC_VERB_GET_GPIO_MASK, 0);
+ gpiomask |= (1 << pin);
+
+ gpiodir = snd_hda_codec_read(codec, codec->afg, 0,
+ AC_VERB_GET_GPIO_DIRECTION, 0);
+ gpiodir |= (1 << pin);
+
+ /* AppleHDA seems to do this -- WTF is this verb?? */
+ snd_hda_codec_write(codec, codec->afg, 0, 0x7e7, 0);
+
+ snd_hda_codec_write(codec, codec->afg, 0,
+ AC_VERB_SET_GPIO_MASK, gpiomask);
+ snd_hda_codec_write(codec, codec->afg, 0,
+ AC_VERB_SET_GPIO_DIRECTION, gpiodir);
+
+ msleep(1);
+
+ snd_hda_codec_write(codec, codec->afg, 0,
+ AC_VERB_SET_GPIO_DATA, gpiostate);
+}
+
static int stac92xx_init(struct hda_codec *codec)
{
struct sigmatel_spec *spec = codec->spec;
stac92xx_auto_set_pinctl(codec, cfg->dig_in_pin,
AC_PINCTL_IN_EN);
+ if (spec->gpio_mute) {
+ stac922x_gpio_mute(codec, 0, 0);
+ stac922x_gpio_mute(codec, 1, 0);
+ }
+
return 0;
}
spec->board_config = snd_hda_check_board_config(codec, stac922x_cfg_tbl);
if (spec->board_config < 0)
snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC922x, using BIOS defaults\n");
- else {
+ else if (stac922x_brd_tbl[spec->board_config] != NULL) {
spec->num_pins = 10;
spec->pin_nids = stac922x_pin_nids;
spec->pin_configs = stac922x_brd_tbl[spec->board_config];
return err;
}
+ if (spec->board_config == STAC_MACMINI)
+ spec->gpio_mute = 1;
+
codec->patch_ops = stac92xx_patch_ops;
return 0;
int change;
change = snd_hda_codec_amp_update(codec, 0x02, 0, HDA_OUTPUT, 0,
- 0x80, valp[0] & 0x80);
+ 0x80, (valp[0] ? 0 : 0x80));
change |= snd_hda_codec_amp_update(codec, 0x02, 1, HDA_OUTPUT, 0,
- 0x80, valp[1] & 0x80);
+ 0x80, (valp[1] ? 0 : 0x80));
snd_hda_codec_amp_update(codec, 0x05, 0, HDA_OUTPUT, 0,
- 0x80, valp[0] & 0x80);
+ 0x80, (valp[0] ? 0 : 0x80));
snd_hda_codec_amp_update(codec, 0x05, 1, HDA_OUTPUT, 0,
- 0x80, valp[1] & 0x80);
+ 0x80, (valp[1] ? 0 : 0x80));
return change;
}
{ .id = 0x83847681, .name = "STAC9220D/9223D A2", .patch = patch_stac922x },
{ .id = 0x83847682, .name = "STAC9221 A2", .patch = patch_stac922x },
{ .id = 0x83847683, .name = "STAC9221D A2", .patch = patch_stac922x },
+ { .id = 0x83847618, .name = "STAC9227", .patch = patch_stac922x },
+ { .id = 0x83847619, .name = "STAC9227", .patch = patch_stac922x },
+ { .id = 0x83847616, .name = "STAC9228", .patch = patch_stac922x },
+ { .id = 0x83847617, .name = "STAC9228", .patch = patch_stac922x },
+ { .id = 0x83847614, .name = "STAC9229", .patch = patch_stac922x },
+ { .id = 0x83847615, .name = "STAC9229", .patch = patch_stac922x },
{ .id = 0x83847620, .name = "STAC9274", .patch = patch_stac927x },
{ .id = 0x83847621, .name = "STAC9274D", .patch = patch_stac927x },
{ .id = 0x83847622, .name = "STAC9273X", .patch = patch_stac927x },
tmp2 = tmp = snd_ice1712_gpio_read(ice);
if (enable)
- tmp |= AUREON_HP_SEL;
+ if (ice->eeprom.subvendor != VT1724_SUBDEVICE_PRODIGY71LT)
+ tmp |= AUREON_HP_SEL;
+ else
+ tmp |= PRODIGY_HP_SEL;
else
- tmp &= ~ AUREON_HP_SEL;
+ if (ice->eeprom.subvendor != VT1724_SUBDEVICE_PRODIGY71LT)
+ tmp &= ~ AUREON_HP_SEL;
+ else
+ tmp &= ~ PRODIGY_HP_SEL;
if (tmp != tmp2) {
snd_ice1712_gpio_write(ice, tmp);
return 1;
};
static unsigned char prodigy71lt_eeprom[] __devinitdata = {
- 0x0b, /* SYSCINF: clock 512, spdif-in/ADC, 4DACs */
+ 0x4b, /* SYSCINF: clock 512, spdif-in/ADC, 4DACs */
0x80, /* ACLINK: I2S */
0xfc, /* I2S: vol, 96k, 24bit, 192k */
- 0xc3, /* SPDUF: out-en, out-int */
- 0x00, /* GPIO_DIR */
- 0x07, /* GPIO_DIR1 */
- 0x00, /* GPIO_DIR2 */
- 0xff, /* GPIO_MASK */
- 0xf8, /* GPIO_MASK1 */
- 0xff, /* GPIO_MASK2 */
+ 0xc3, /* SPDIF: out-en, out-int, spdif-in */
+ 0xff, /* GPIO_DIR */
+ 0xff, /* GPIO_DIR1 */
+ 0x5f, /* GPIO_DIR2 */
+ 0x00, /* GPIO_MASK */
+ 0x00, /* GPIO_MASK1 */
+ 0x00, /* GPIO_MASK2 */
0x00, /* GPIO_STATE */
0x00, /* GPIO_STATE1 */
0x00, /* GPIO_STATE2 */
#define PRODIGY_WM_CS (1 << 8)
#define PRODIGY_SPI_MOSI (1 << 10)
#define PRODIGY_SPI_CLK (1 << 9)
+#define PRODIGY_HP_SEL (1 << 5)
#endif /* __SOUND_AUREON_H */
.model = "dmx6fire",
.chip_init = snd_ice1712_ews_init,
.build_controls = snd_ice1712_ews_add_controls,
+ .mpu401_1_name = "MIDI-Front DMX6fire",
+ .mpu401_2_name = "Wavetable DMX6fire",
+ .mpu401_2_info_flags = MPU401_INFO_OUTPUT,
},
{ } /* terminator */
};
#include <sound/core.h>
#include <sound/cs8427.h>
#include <sound/info.h>
-#include <sound/mpu401.h>
#include <sound/initval.h>
#include <sound/asoundef.h>
struct snd_info_entry *entry;
if (! snd_card_proc_new(ice->card, "ice1712", &entry))
- snd_info_set_text_ops(entry, ice, 1024, snd_ice1712_proc_read);
+ snd_info_set_text_ops(entry, ice, snd_ice1712_proc_read);
}
/*
udelay(200);
outb(ICE1712_NATIVE, ICEREG(ice, CONTROL));
udelay(200);
- if (ice->eeprom.subvendor == ICE1712_SUBDEVICE_DMX6FIRE && !ice->dxr_enable) {
- /* Limit active ADCs and DACs to 6; */
- /* Note: DXR extension not supported */
- pci_write_config_byte(ice->pci, 0x60, 0x2a);
- } else {
- pci_write_config_byte(ice->pci, 0x60, ice->eeprom.data[ICE_EEP1_CODEC]);
- }
+ if (ice->eeprom.subvendor == ICE1712_SUBDEVICE_DMX6FIRE &&
+ !ice->dxr_enable)
+ /* Set eeprom value to limit active ADCs and DACs to 6;
+ * Also disable AC97 as no hardware in standard 6fire card/box
+ * Note: DXR extensions are not currently supported
+ */
+ ice->eeprom.data[ICE_EEP1_CODEC] = 0x3a;
+ pci_write_config_byte(ice->pci, 0x60, ice->eeprom.data[ICE_EEP1_CODEC]);
pci_write_config_byte(ice->pci, 0x61, ice->eeprom.data[ICE_EEP1_ACLINK]);
pci_write_config_byte(ice->pci, 0x62, ice->eeprom.data[ICE_EEP1_I2SID]);
pci_write_config_byte(ice->pci, 0x63, ice->eeprom.data[ICE_EEP1_SPDIF]);
if (! c->no_mpu401) {
if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_ICE1712,
- ICEREG(ice, MPU1_CTRL), 1,
+ ICEREG(ice, MPU1_CTRL),
+ (c->mpu401_1_info_flags |
+ MPU401_INFO_INTEGRATED),
ice->irq, 0,
&ice->rmidi[0])) < 0) {
snd_card_free(card);
return err;
}
-
- if (ice->eeprom.data[ICE_EEP1_CODEC] & ICE1712_CFG_2xMPU401)
+ if (c->mpu401_1_name)
+ /* Prefered name available in card_info */
+ snprintf(ice->rmidi[0]->name,
+ sizeof(ice->rmidi[0]->name),
+ "%s %d", c->mpu401_1_name, card->number);
+
+ if (ice->eeprom.data[ICE_EEP1_CODEC] & ICE1712_CFG_2xMPU401) {
+ /* 2nd port used */
if ((err = snd_mpu401_uart_new(card, 1, MPU401_HW_ICE1712,
- ICEREG(ice, MPU2_CTRL), 1,
+ ICEREG(ice, MPU2_CTRL),
+ (c->mpu401_2_info_flags |
+ MPU401_INFO_INTEGRATED),
ice->irq, 0,
&ice->rmidi[1])) < 0) {
snd_card_free(card);
return err;
}
+ if (c->mpu401_2_name)
+ /* Prefered name available in card_info */
+ snprintf(ice->rmidi[1]->name,
+ sizeof(ice->rmidi[1]->name),
+ "%s %d", c->mpu401_2_name,
+ card->number);
+ }
}
snd_ice1712_set_input_clock_source(ice, 0);
#include <sound/ak4xxx-adda.h>
#include <sound/ak4114.h>
#include <sound/pcm.h>
+#include <sound/mpu401.h>
/*
int (*chip_init)(struct snd_ice1712 *);
int (*build_controls)(struct snd_ice1712 *);
unsigned int no_mpu401: 1;
+ unsigned int mpu401_1_info_flags;
+ unsigned int mpu401_2_info_flags;
+ const char *mpu401_1_name;
+ const char *mpu401_2_name;
unsigned int eeprom_size;
unsigned char *eeprom_data;
};
struct snd_info_entry *entry;
if (! snd_card_proc_new(ice->card, "ice1724", &entry))
- snd_info_set_text_ops(entry, ice, 1024, snd_vt1724_proc_read);
+ snd_info_set_text_ops(entry, ice, snd_vt1724_proc_read);
}
/*
if (! c->no_mpu401) {
if (ice->eeprom.data[ICE_EEP2_SYSCONF] & VT1724_CFG_MPU401) {
if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_ICE1712,
- ICEREG1724(ice, MPU_CTRL), 1,
+ ICEREG1724(ice, MPU_CTRL),
+ MPU401_INFO_INTEGRATED,
ice->irq, 0,
&ice->rmidi[0])) < 0) {
snd_card_free(card);
{
struct snd_info_entry *entry;
if (! snd_card_proc_new(ice->card, "wm_codec", &entry)) {
- snd_info_set_text_ops(entry, ice, 1024, wm_proc_regs_read);
+ snd_info_set_text_ops(entry, ice, wm_proc_regs_read);
entry->mode |= S_IWUSR;
- entry->c.text.write_size = 1024;
entry->c.text.write = wm_proc_regs_write;
}
}
static void cs_proc_init(struct snd_ice1712 *ice)
{
struct snd_info_entry *entry;
- if (! snd_card_proc_new(ice->card, "cs_codec", &entry)) {
- snd_info_set_text_ops(entry, ice, 1024, cs_proc_regs_read);
- }
+ if (! snd_card_proc_new(ice->card, "cs_codec", &entry))
+ snd_info_set_text_ops(entry, ice, cs_proc_regs_read);
}
static int index = SNDRV_DEFAULT_IDX1; /* Index 0-MAX */
static char *id = SNDRV_DEFAULT_STR1; /* ID for this card */
-static int ac97_clock = 0;
+static int ac97_clock;
static char *ac97_quirk;
static int buggy_semaphore;
static int buggy_irq = -1; /* auto-check */
.name = "Dell Optiplex GX270", /* AD1981B */
.type = AC97_TUNE_HP_ONLY
},
+ {
+ .subvendor = 0x1028,
+ .subdevice = 0x014e,
+ .name = "Dell D800", /* STAC9750/51 */
+ .type = AC97_TUNE_HP_ONLY
+ },
{
.subvendor = 0x1028,
.subdevice = 0x0163,
struct snd_info_entry *entry;
if (! snd_card_proc_new(chip->card, "intel8x0", &entry))
- snd_info_set_text_ops(entry, chip, 1024, snd_intel8x0_proc_read);
+ snd_info_set_text_ops(entry, chip, snd_intel8x0_proc_read);
}
#else
#define snd_intel8x0_proc_init(x)
static int index = -2; /* Exclude the first card */
static char *id = SNDRV_DEFAULT_STR1; /* ID for this card */
-static int ac97_clock = 0;
+static int ac97_clock;
module_param(index, int, 0444);
MODULE_PARM_DESC(index, "Index value for Intel i8x0 modemcard.");
struct snd_info_entry *entry;
if (! snd_card_proc_new(chip->card, "intel8x0m", &entry))
- snd_info_set_text_ops(entry, chip, 1024, snd_intel8x0m_proc_read);
+ snd_info_set_text_ops(entry, chip, snd_intel8x0m_proc_read);
}
#else /* !CONFIG_PROC_FS */
#define snd_intel8x0m_proc_init(chip)
struct snd_info_entry *entry;
if (! snd_card_proc_new(korg1212->card, "korg1212", &entry))
- snd_info_set_text_ops(entry, korg1212, 1024, snd_korg1212_proc_read);
+ snd_info_set_text_ops(entry, korg1212, snd_korg1212_proc_read);
}
static int
#if 0 /* TODO: not supported yet */
/* TODO enable MIDI IRQ and I/O */
err = snd_mpu401_uart_new(chip->card, 0, MPU401_HW_MPU401,
- chip->iobase + MPU401_DATA_PORT, 1,
+ chip->iobase + MPU401_DATA_PORT,
+ MPU401_INFO_INTEGRATED,
chip->irq, 0, &chip->rmidi);
if (err < 0)
printk(KERN_WARNING "maestro3: no MIDI support.\n");
/* text interface to read perf and temp meters */
if (! snd_card_proc_new(chip->card, "board_info", &entry)) {
entry->private_data = chip;
- entry->c.text.read_size = 1024;
entry->c.text.read = snd_mixart_proc_read;
}
struct snd_info_entry *entry;
if (! snd_card_proc_new(chip->card, "info", &entry))
- snd_info_set_text_ops(entry, chip, 1024, pcxhr_proc_info);
+ snd_info_set_text_ops(entry, chip, pcxhr_proc_info);
if (! snd_card_proc_new(chip->card, "sync", &entry))
- snd_info_set_text_ops(entry, chip, 1024, pcxhr_proc_sync);
+ snd_info_set_text_ops(entry, chip, pcxhr_proc_sync);
}
/* end of proc interface */
UNSET_GRESET(cif->hwport);
kfree(chip->cif);
}
+ if (chip->irq >= 0)
+ free_irq(chip->irq, chip);
if (chip->fw_entry)
release_firmware(chip->fw_entry);
release_and_free_resource(chip->res_port);
- if (chip->irq >= 0)
- free_irq(chip->irq, chip);
kfree(chip);
return 0;
}
struct snd_info_entry *entry;
if (!snd_card_proc_new(chip->card, "riptide", &entry))
- snd_info_set_text_ops(entry, chip, 4096, snd_riptide_proc_read);
+ snd_info_set_text_ops(entry, chip, snd_riptide_proc_read);
}
static int __devinit snd_riptide_mixer(struct snd_riptide *chip)
return err;
rme32->port = pci_resource_start(rme32->pci, 0);
- if (request_irq(pci->irq, snd_rme32_interrupt, SA_INTERRUPT | SA_SHIRQ, "RME32", (void *) rme32)) {
- snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
- return -EBUSY;
- }
- rme32->irq = pci->irq;
-
if ((rme32->iobase = ioremap_nocache(rme32->port, RME32_IO_SIZE)) == 0) {
snd_printk(KERN_ERR "unable to remap memory region 0x%lx-0x%lx\n",
rme32->port, rme32->port + RME32_IO_SIZE - 1);
return -ENOMEM;
}
+ if (request_irq(pci->irq, snd_rme32_interrupt, SA_INTERRUPT | SA_SHIRQ, "RME32", (void *) rme32)) {
+ snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
+ return -EBUSY;
+ }
+ rme32->irq = pci->irq;
+
/* read the card's revision number */
pci_read_config_byte(pci, 8, &rme32->rev);
struct snd_info_entry *entry;
if (! snd_card_proc_new(rme32->card, "rme32", &entry))
- snd_info_set_text_ops(entry, rme32, 1024, snd_rme32_proc_read);
+ snd_info_set_text_ops(entry, rme32, snd_rme32_proc_read);
}
/*
.mask = 0
};
+static void
+rme96_set_buffer_size_constraint(struct rme96 *rme96,
+ struct snd_pcm_runtime *runtime)
+{
+ unsigned int size;
+
+ snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
+ RME96_BUFFER_SIZE, RME96_BUFFER_SIZE);
+ if ((size = rme96->playback_periodsize) != 0 ||
+ (size = rme96->capture_periodsize) != 0)
+ snd_pcm_hw_constraint_minmax(runtime,
+ SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
+ size, size);
+ else
+ snd_pcm_hw_constraint_list(runtime, 0,
+ SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
+ &hw_constraints_period_bytes);
+}
+
static int
snd_rme96_playback_spdif_open(struct snd_pcm_substream *substream)
{
runtime->hw.rate_min = rate;
runtime->hw.rate_max = rate;
}
- snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, RME96_BUFFER_SIZE, RME96_BUFFER_SIZE);
- snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, &hw_constraints_period_bytes);
+ rme96_set_buffer_size_constraint(rme96, runtime);
rme96->wcreg_spdif_stream = rme96->wcreg_spdif;
rme96->spdif_ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
rme96->capture_substream = substream;
spin_unlock_irq(&rme96->lock);
- snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, RME96_BUFFER_SIZE, RME96_BUFFER_SIZE);
- snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, &hw_constraints_period_bytes);
-
+ rme96_set_buffer_size_constraint(rme96, runtime);
return 0;
}
runtime->hw.rate_min = rate;
runtime->hw.rate_max = rate;
}
- snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, RME96_BUFFER_SIZE, RME96_BUFFER_SIZE);
- snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, &hw_constraints_period_bytes);
+ rme96_set_buffer_size_constraint(rme96, runtime);
return 0;
}
rme96->capture_substream = substream;
spin_unlock_irq(&rme96->lock);
- snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, RME96_BUFFER_SIZE, RME96_BUFFER_SIZE);
- snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, &hw_constraints_period_bytes);
+ rme96_set_buffer_size_constraint(rme96, runtime);
return 0;
}
return err;
rme96->port = pci_resource_start(rme96->pci, 0);
+ if ((rme96->iobase = ioremap_nocache(rme96->port, RME96_IO_SIZE)) == 0) {
+ snd_printk(KERN_ERR "unable to remap memory region 0x%lx-0x%lx\n", rme96->port, rme96->port + RME96_IO_SIZE - 1);
+ return -ENOMEM;
+ }
+
if (request_irq(pci->irq, snd_rme96_interrupt, SA_INTERRUPT|SA_SHIRQ, "RME96", (void *)rme96)) {
snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
return -EBUSY;
}
rme96->irq = pci->irq;
- if ((rme96->iobase = ioremap_nocache(rme96->port, RME96_IO_SIZE)) == 0) {
- snd_printk(KERN_ERR "unable to remap memory region 0x%lx-0x%lx\n", rme96->port, rme96->port + RME96_IO_SIZE - 1);
- return -ENOMEM;
- }
-
/* read the card's revision number */
pci_read_config_byte(pci, 8, &rme96->rev);
struct snd_info_entry *entry;
if (! snd_card_proc_new(rme96->card, "rme96", &entry))
- snd_info_set_text_ops(entry, rme96, 1024, snd_rme96_proc_read);
+ snd_info_set_text_ops(entry, rme96, snd_rme96_proc_read);
}
/*
/* use hotplug firmeare loader? */
#if defined(CONFIG_FW_LOADER) || defined(CONFIG_FW_LOADER_MODULE)
-#ifndef HDSP_USE_HWDEP_LOADER
+#if !defined(HDSP_USE_HWDEP_LOADER) && !defined(CONFIG_SND_HDSP)
#define HDSP_FW_LOADER
#endif
#endif
char *clock_source;
int x;
- if (hdsp_check_for_iobox (hdsp))
+ if (hdsp_check_for_iobox (hdsp)) {
snd_iprintf(buffer, "No I/O box connected.\nPlease connect one and upload firmware.\n");
return;
+ }
if (hdsp_check_for_firmware(hdsp, 0)) {
if (hdsp->state & HDSP_FirmwareCached) {
struct snd_info_entry *entry;
if (! snd_card_proc_new(hdsp->card, "hdsp", &entry))
- snd_info_set_text_ops(entry, hdsp, 1024, snd_hdsp_proc_read);
+ snd_info_set_text_ops(entry, hdsp, snd_hdsp_proc_read);
}
static void snd_hdsp_free_buffers(struct hdsp *hdsp)
struct snd_info_entry *entry;
if (!snd_card_proc_new(hdspm->card, "hdspm", &entry))
- snd_info_set_text_ops(entry, hdspm, 1024,
+ snd_info_set_text_ops(entry, hdspm,
snd_hdspm_proc_read);
}
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
-static int precise_ptr[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS-1)] = 0 }; /* Enable precise pointer */
+static int precise_ptr[SNDRV_CARDS]; /* Enable precise pointer */
module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Index value for RME Digi9652 (Hammerfall) soundcard.");
struct snd_info_entry *entry;
if (! snd_card_proc_new(rme9652->card, "rme9652", &entry))
- snd_info_set_text_ops(entry, rme9652, 1024, snd_rme9652_proc_read);
+ snd_info_set_text_ops(entry, rme9652, snd_rme9652_proc_read);
}
static void snd_rme9652_free_buffers(struct snd_rme9652 *rme9652)
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
-static int reverb[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0};
-static int mge[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0};
+static int reverb[SNDRV_CARDS];
+static int mge[SNDRV_CARDS];
static unsigned int dmaio = 0x7a00; /* DDMA i/o address */
module_param_array(index, int, NULL, 0444);
struct snd_info_entry *entry;
if (! snd_card_proc_new(sonic->card, "sonicvibes", &entry))
- snd_info_set_text_ops(entry, sonic, 1024, snd_sonicvibes_proc_read);
+ snd_info_set_text_ops(entry, sonic, snd_sonicvibes_proc_read);
}
/*
return err;
}
if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_SONICVIBES,
- sonic->midi_port, 1,
+ sonic->midi_port, MPU401_INFO_INTEGRATED,
sonic->irq, 0,
&midi_uart)) < 0) {
snd_card_free(card);
}
if (trident->device != TRIDENT_DEVICE_ID_SI7018 &&
(err = snd_mpu401_uart_new(card, 0, MPU401_HW_TRID4DWAVE,
- trident->midi_port, 1,
+ trident->midi_port,
+ MPU401_INFO_INTEGRATED,
trident->irq, 0, &trident->rmidi)) < 0) {
snd_card_free(card);
return err;
outl(mask, TRID_REG(trident, reg));
}
+EXPORT_SYMBOL(snd_trident_start_voice);
+
/*---------------------------------------------------------------------------
void snd_trident_stop_voice(struct snd_trident * trident, unsigned int voice)
outl(mask, TRID_REG(trident, reg));
}
+EXPORT_SYMBOL(snd_trident_stop_voice);
+
/*---------------------------------------------------------------------------
int snd_trident_allocate_pcm_channel(struct snd_trident *trident)
#endif
}
+EXPORT_SYMBOL(snd_trident_write_voice_regs);
+
/*---------------------------------------------------------------------------
snd_trident_write_cso_reg
if (trident->device == TRIDENT_DEVICE_ID_SI7018)
s = "sis7018";
if (! snd_card_proc_new(trident->card, s, &entry))
- snd_info_set_text_ops(entry, trident, 1024, snd_trident_proc_read);
+ snd_info_set_text_ops(entry, trident, snd_trident_proc_read);
}
static int snd_trident_dev_free(struct snd_device *device)
return NULL;
}
+EXPORT_SYMBOL(snd_trident_alloc_voice);
+
void snd_trident_free_voice(struct snd_trident * trident, struct snd_trident_voice *voice)
{
unsigned long flags;
private_free(voice);
}
+EXPORT_SYMBOL(snd_trident_free_voice);
+
static void snd_trident_clear_voices(struct snd_trident * trident, unsigned short v_min, unsigned short v_max)
{
unsigned int i, val, mask[2] = { 0, 0 };
return 0;
}
#endif /* CONFIG_PM */
-
-EXPORT_SYMBOL(snd_trident_alloc_voice);
-EXPORT_SYMBOL(snd_trident_free_voice);
-EXPORT_SYMBOL(snd_trident_start_voice);
-EXPORT_SYMBOL(snd_trident_stop_voice);
-EXPORT_SYMBOL(snd_trident_write_voice_regs);
-/* trident_memory.c symbols */
-EXPORT_SYMBOL(snd_trident_synth_alloc);
-EXPORT_SYMBOL(snd_trident_synth_free);
-EXPORT_SYMBOL(snd_trident_synth_copy_from_user);
return blk;
}
+EXPORT_SYMBOL(snd_trident_synth_alloc);
/*
* free a synth sample area
return 0;
}
+EXPORT_SYMBOL(snd_trident_synth_free);
/*
* reset TLB entry and free kernel page
return 0;
}
+EXPORT_SYMBOL(snd_trident_synth_copy_from_user);
&callbacks,
SNDRV_SEQ_PORT_CAP_WRITE | SNDRV_SEQ_PORT_CAP_SUBS_WRITE,
SNDRV_SEQ_PORT_TYPE_DIRECT_SAMPLE |
- SNDRV_SEQ_PORT_TYPE_SYNTH,
+ SNDRV_SEQ_PORT_TYPE_SYNTH |
+ SNDRV_SEQ_PORT_TYPE_HARDWARE |
+ SNDRV_SEQ_PORT_TYPE_SYNTHESIZER,
16, 0,
name);
if (p->chset->port < 0) {
.name = "Targa Traveller 811",
.type = AC97_TUNE_HP_ONLY,
},
+ {
+ .subvendor = 0x161f,
+ .subdevice = 0x2032,
+ .name = "m680x",
+ .type = AC97_TUNE_HP_ONLY, /* http://launchpad.net/bugs/38546 */
+ },
{ } /* terminator */
};
pci_write_config_byte(chip->pci, VIA_PNP_CONTROL, legacy_cfg);
if (chip->mpu_res) {
if (snd_mpu401_uart_new(chip->card, 0, MPU401_HW_VIA686A,
- mpu_port, 1,
+ mpu_port, MPU401_INFO_INTEGRATED,
chip->irq, 0, &chip->rmidi) < 0) {
printk(KERN_WARNING "unable to initialize MPU-401"
" at 0x%lx, skipping\n", mpu_port);
struct snd_info_entry *entry;
if (! snd_card_proc_new(chip->card, "via82xx", &entry))
- snd_info_set_text_ops(entry, chip, 1024, snd_via82xx_proc_read);
+ snd_info_set_text_ops(entry, chip, snd_via82xx_proc_read);
}
/*
{ .subvendor = 0x1462, .subdevice = 0x0470, .action = VIA_DXS_SRC }, /* MSI KT880 Delta-FSR */
{ .subvendor = 0x1462, .subdevice = 0x3800, .action = VIA_DXS_ENABLE }, /* MSI KT266 */
{ .subvendor = 0x1462, .subdevice = 0x5901, .action = VIA_DXS_NO_VRA }, /* MSI KT6 Delta-SR */
- { .subvendor = 0x1462, .subdevice = 0x7023, .action = VIA_DXS_NO_VRA }, /* MSI K8T Neo2-FI */
+ { .subvendor = 0x1462, .subdevice = 0x7023, .action = VIA_DXS_SRC }, /* MSI K8T Neo2-FI */
{ .subvendor = 0x1462, .subdevice = 0x7120, .action = VIA_DXS_ENABLE }, /* MSI KT4V */
{ .subvendor = 0x1462, .subdevice = 0x7142, .action = VIA_DXS_ENABLE }, /* MSI K8MM-V */
{ .subvendor = 0x1462, .subdevice = 0xb012, .action = VIA_DXS_SRC }, /* P4M800/VIA8237R */
struct snd_info_entry *entry;
if (! snd_card_proc_new(chip->card, "via82xx", &entry))
- snd_info_set_text_ops(entry, chip, 1024, snd_via82xx_proc_read);
+ snd_info_set_text_ops(entry, chip, snd_via82xx_proc_read);
}
/*
}
if (chip->mpu_res) {
if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_YMFPCI,
- mpu_port[dev], 1,
+ mpu_port[dev],
+ MPU401_INFO_INTEGRATED,
pci->irq, 0, &chip->rawmidi)) < 0) {
printk(KERN_WARNING "ymfpci: cannot initialize MPU401 at 0x%lx, skipping...\n", mpu_port[dev]);
legacy_ctrl &= ~YMFPCI_LEGACY_MIEN; /* disable MPU401 irq */
struct snd_info_entry *entry;
if (! snd_card_proc_new(card, "ymfpci", &entry))
- snd_info_set_text_ops(entry, chip, 1024, snd_ymfpci_proc_read);
+ snd_info_set_text_ops(entry, chip, snd_ymfpci_proc_read);
return 0;
}
struct snd_info_entry *entry;
if (! snd_card_proc_new(chip->card, "pdaudiocf", &entry))
- snd_info_set_text_ops(entry, chip, 1024, pdacf_proc_read);
+ snd_info_set_text_ops(entry, chip, pdacf_proc_read);
}
struct snd_pdacf *snd_pdacf_create(struct snd_card *card)
c |= (int)vx_inb(chip, RXM) << 8;
c |= vx_inb(chip, RXL);
- snd_printdd(KERN_DEBUG "xilinx: dsp size received 0x%x, orig 0x%x\n", c, fw->size);
+ snd_printdd(KERN_DEBUG "xilinx: dsp size received 0x%x, orig 0x%Zx\n", c, fw->size);
vx_outb(chip, ICR, ICR_HF0);
link->dev_node = &vxp->node;
kfree(parse);
- return 9;
+ return 0;
cs_failed:
cs_error(link, last_fn, last_ret);
# Copyright (c) 2001 by Jaroslav Kysela <perex@suse.cz>
#
-snd-powermac-objs := powermac.o pmac.o awacs.o burgundy.o daca.o tumbler.o toonie.o keywest.o beep.o
+snd-powermac-objs := powermac.o pmac.o awacs.o burgundy.o daca.o tumbler.o keywest.o beep.o
# Toplevel Module Dependency
obj-$(CONFIG_SND_POWERMAC) += snd-powermac.o
unsigned int *prop, l;
struct macio_chip* macio;
- u32 layout_id = 0;
-
if (!machine_is(powermac))
return -ENODEV;
if (prop && *prop < 16)
chip->subframe = *prop;
prop = (unsigned int *) get_property(sound, "layout-id", NULL);
- if (prop)
- layout_id = *prop;
+ if (prop) {
+ /* partly deprecate snd-powermac, for those machines
+ * that have a layout-id property for now */
+ printk(KERN_INFO "snd-powermac no longer handles any "
+ "machines with a layout-id property "
+ "in the device-tree, use snd-aoa.\n");
+ return -ENODEV;
+ }
/* This should be verified on older screamers */
if (device_is_compatible(sound, "screamer")) {
chip->model = PMAC_SCREAMER;
chip->freq_table = tumbler_freqs;
chip->control_mask = MASK_IEPC | 0x11; /* disable IEE */
}
- if (device_is_compatible(sound, "AOAKeylargo") ||
- device_is_compatible(sound, "AOAbase") ||
- device_is_compatible(sound, "AOAK2")) {
- /* For now, only support very basic TAS3004 based machines with
- * single frequency until proper i2s control is implemented
- */
- switch(layout_id) {
- case 0x24:
- case 0x29:
- case 0x33:
- case 0x46:
- case 0x48:
- case 0x50:
- case 0x5c:
- chip->num_freqs = ARRAY_SIZE(tumbler_freqs);
- chip->model = PMAC_SNAPPER;
- chip->can_byte_swap = 0; /* FIXME: check this */
- chip->control_mask = MASK_IEPC | 0x11;/* disable IEE */
- break;
- case 0x3a:
- chip->num_freqs = ARRAY_SIZE(tumbler_freqs);
- chip->model = PMAC_TOONIE;
- chip->can_byte_swap = 0; /* FIXME: check this */
- chip->control_mask = MASK_IEPC | 0x11;/* disable IEE */
- break;
- default:
- printk(KERN_ERR "snd: Unknown layout ID 0x%x\n",
- layout_id);
- return -ENODEV;
-
- }
- }
prop = (unsigned int *)get_property(sound, "device-id", NULL);
if (prop)
chip->device_id = *prop;
enum snd_pmac_model {
PMAC_AWACS, PMAC_SCREAMER, PMAC_BURGUNDY, PMAC_DACA, PMAC_TUMBLER,
- PMAC_SNAPPER, PMAC_TOONIE
+ PMAC_SNAPPER
};
struct snd_pmac {
int snd_pmac_daca_init(struct snd_pmac *chip);
int snd_pmac_tumbler_init(struct snd_pmac *chip);
int snd_pmac_tumbler_post_init(void);
-int snd_pmac_toonie_init(struct snd_pmac *chip);
/* i2c functions */
struct pmac_keywest {
if ( snd_pmac_tumbler_init(chip) < 0 || snd_pmac_tumbler_post_init() < 0)
goto __error;
break;
- case PMAC_TOONIE:
- strcpy(card->driver, "PMac Toonie");
- strcpy(card->shortname, "PowerMac Toonie");
- strcpy(card->longname, card->shortname);
- if ((err = snd_pmac_toonie_init(chip)) < 0)
- goto __error;
- break;
case PMAC_AWACS:
case PMAC_SCREAMER:
name_ext = chip->model == PMAC_SCREAMER ? "Screamer" : "AWACS";
if ((err = platform_driver_register(&snd_pmac_driver)) < 0)
return err;
device = platform_device_register_simple(SND_PMAC_DRIVER, -1, NULL, 0);
- if (IS_ERR(device)) {
- platform_driver_unregister(&snd_pmac_driver);
- return PTR_ERR(device);
- }
- return 0;
+ if (!IS_ERR(device)) {
+ if (platform_get_drvdata(device))
+ return 0;
+ platform_device_unregister(device);
+ err = -ENODEV;
+ } else
+ err = PTR_ERR(device);
+ platform_driver_unregister(&snd_pmac_driver);
+ return err;
}
-/*
- * Mac Mini "toonie" mixer control
- *
- * Copyright (c) 2005 by Benjamin Herrenschmidt <benh@kernel.crashing.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
-#include <sound/driver.h>
-#include <linux/init.h>
-#include <linux/delay.h>
-#include <linux/i2c.h>
-#include <linux/kmod.h>
-#include <linux/slab.h>
-#include <linux/interrupt.h>
-#include <sound/core.h>
-#include <asm/io.h>
-#include <asm/irq.h>
-#include <asm/machdep.h>
-#include <asm/pmac_feature.h>
-#include "pmac.h"
-
-#undef DEBUG
-
-#ifdef DEBUG
-#define DBG(fmt...) printk(fmt)
-#else
-#define DBG(fmt...)
-#endif
-
-struct pmac_gpio {
- unsigned int addr;
- u8 active_val;
- u8 inactive_val;
- u8 active_state;
-};
-
-struct pmac_toonie
-{
- struct pmac_gpio hp_detect_gpio;
- struct pmac_gpio hp_mute_gpio;
- struct pmac_gpio amp_mute_gpio;
- int hp_detect_irq;
- int auto_mute_notify;
- struct work_struct detect_work;
-};
-
-
-/*
- * gpio access
- */
-#define do_gpio_write(gp, val) \
- pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, (gp)->addr, val)
-#define do_gpio_read(gp) \
- pmac_call_feature(PMAC_FTR_READ_GPIO, NULL, (gp)->addr, 0)
-#define tumbler_gpio_free(gp) /* NOP */
-
-static void write_audio_gpio(struct pmac_gpio *gp, int active)
-{
- if (! gp->addr)
- return;
- active = active ? gp->active_val : gp->inactive_val;
- do_gpio_write(gp, active);
- DBG("(I) gpio %x write %d\n", gp->addr, active);
-}
-
-static int check_audio_gpio(struct pmac_gpio *gp)
-{
- int ret;
-
- if (! gp->addr)
- return 0;
-
- ret = do_gpio_read(gp);
-
- return (ret & 0xd) == (gp->active_val & 0xd);
-}
-
-static int read_audio_gpio(struct pmac_gpio *gp)
-{
- int ret;
- if (! gp->addr)
- return 0;
- ret = ((do_gpio_read(gp) & 0x02) !=0);
- return ret == gp->active_state;
-}
-
-
-enum { TOONIE_MUTE_HP, TOONIE_MUTE_AMP };
-
-static int toonie_get_mute_switch(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
- struct pmac_toonie *mix = chip->mixer_data;
- struct pmac_gpio *gp;
-
- if (mix == NULL)
- return -ENODEV;
- switch(kcontrol->private_value) {
- case TOONIE_MUTE_HP:
- gp = &mix->hp_mute_gpio;
- break;
- case TOONIE_MUTE_AMP:
- gp = &mix->amp_mute_gpio;
- break;
- default:
- return -EINVAL;
- }
- ucontrol->value.integer.value[0] = !check_audio_gpio(gp);
- return 0;
-}
-
-static int toonie_put_mute_switch(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
- struct pmac_toonie *mix = chip->mixer_data;
- struct pmac_gpio *gp;
- int val;
-
- if (chip->update_automute && chip->auto_mute)
- return 0; /* don't touch in the auto-mute mode */
-
- if (mix == NULL)
- return -ENODEV;
-
- switch(kcontrol->private_value) {
- case TOONIE_MUTE_HP:
- gp = &mix->hp_mute_gpio;
- break;
- case TOONIE_MUTE_AMP:
- gp = &mix->amp_mute_gpio;
- break;
- default:
- return -EINVAL;
- }
- val = ! check_audio_gpio(gp);
- if (val != ucontrol->value.integer.value[0]) {
- write_audio_gpio(gp, ! ucontrol->value.integer.value[0]);
- return 1;
- }
- return 0;
-}
-
-static struct snd_kcontrol_new toonie_hp_sw __initdata = {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Headphone Playback Switch",
- .info = snd_pmac_boolean_mono_info,
- .get = toonie_get_mute_switch,
- .put = toonie_put_mute_switch,
- .private_value = TOONIE_MUTE_HP,
-};
-static struct snd_kcontrol_new toonie_speaker_sw __initdata = {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "PC Speaker Playback Switch",
- .info = snd_pmac_boolean_mono_info,
- .get = toonie_get_mute_switch,
- .put = toonie_put_mute_switch,
- .private_value = TOONIE_MUTE_AMP,
-};
-
-/*
- * auto-mute stuffs
- */
-static int toonie_detect_headphone(struct snd_pmac *chip)
-{
- struct pmac_toonie *mix = chip->mixer_data;
- int detect = 0;
-
- if (mix->hp_detect_gpio.addr)
- detect |= read_audio_gpio(&mix->hp_detect_gpio);
- return detect;
-}
-
-static void toonie_check_mute(struct snd_pmac *chip, struct pmac_gpio *gp, int val,
- int do_notify, struct snd_kcontrol *sw)
-{
- if (check_audio_gpio(gp) != val) {
- write_audio_gpio(gp, val);
- if (do_notify)
- snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
- &sw->id);
- }
-}
-
-static void toonie_detect_handler(void *self)
-{
- struct snd_pmac *chip = (struct snd_pmac *) self;
- struct pmac_toonie *mix;
- int headphone;
-
- if (!chip)
- return;
-
- mix = chip->mixer_data;
- snd_assert(mix, return);
-
- headphone = toonie_detect_headphone(chip);
-
- DBG("headphone: %d, lineout: %d\n", headphone, lineout);
-
- if (headphone) {
- /* unmute headphone/lineout & mute speaker */
- toonie_check_mute(chip, &mix->hp_mute_gpio, 0,
- mix->auto_mute_notify, chip->master_sw_ctl);
- toonie_check_mute(chip, &mix->amp_mute_gpio, 1,
- mix->auto_mute_notify, chip->speaker_sw_ctl);
- } else {
- /* unmute speaker, mute others */
- toonie_check_mute(chip, &mix->amp_mute_gpio, 0,
- mix->auto_mute_notify, chip->speaker_sw_ctl);
- toonie_check_mute(chip, &mix->hp_mute_gpio, 1,
- mix->auto_mute_notify, chip->master_sw_ctl);
- }
- if (mix->auto_mute_notify) {
- snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
- &chip->hp_detect_ctl->id);
- }
-}
-
-static void toonie_update_automute(struct snd_pmac *chip, int do_notify)
-{
- if (chip->auto_mute) {
- struct pmac_toonie *mix;
- mix = chip->mixer_data;
- snd_assert(mix, return);
- mix->auto_mute_notify = do_notify;
- schedule_work(&mix->detect_work);
- }
-}
-
-/* interrupt - headphone plug changed */
-static irqreturn_t toonie_hp_intr(int irq, void *devid, struct pt_regs *regs)
-{
- struct snd_pmac *chip = devid;
-
- if (chip->update_automute && chip->initialized) {
- chip->update_automute(chip, 1);
- return IRQ_HANDLED;
- }
- return IRQ_NONE;
-}
-
-/* look for audio gpio device */
-static int find_audio_gpio(const char *name, const char *platform,
- struct pmac_gpio *gp)
-{
- struct device_node *np;
- u32 *base, addr;
-
- if (! (np = find_devices("gpio")))
- return -ENODEV;
-
- for (np = np->child; np; np = np->sibling) {
- char *property = get_property(np, "audio-gpio", NULL);
- if (property && strcmp(property, name) == 0)
- break;
- if (device_is_compatible(np, name))
- break;
- }
- if (np == NULL)
- return -ENODEV;
-
- base = (u32 *)get_property(np, "AAPL,address", NULL);
- if (! base) {
- base = (u32 *)get_property(np, "reg", NULL);
- if (!base) {
- DBG("(E) cannot find address for device %s !\n", name);
- return -ENODEV;
- }
- addr = *base;
- if (addr < 0x50)
- addr += 0x50;
- } else
- addr = *base;
-
- gp->addr = addr & 0x0000ffff;
-
- /* Try to find the active state, default to 0 ! */
- base = (u32 *)get_property(np, "audio-gpio-active-state", NULL);
- if (base) {
- gp->active_state = *base;
- gp->active_val = (*base) ? 0x5 : 0x4;
- gp->inactive_val = (*base) ? 0x4 : 0x5;
- } else {
- u32 *prop = NULL;
- gp->active_state = 0;
- gp->active_val = 0x4;
- gp->inactive_val = 0x5;
- /* Here are some crude hacks to extract the GPIO polarity and
- * open collector informations out of the do-platform script
- * as we don't yet have an interpreter for these things
- */
- if (platform)
- prop = (u32 *)get_property(np, platform, NULL);
- if (prop) {
- if (prop[3] == 0x9 && prop[4] == 0x9) {
- gp->active_val = 0xd;
- gp->inactive_val = 0xc;
- }
- if (prop[3] == 0x1 && prop[4] == 0x1) {
- gp->active_val = 0x5;
- gp->inactive_val = 0x4;
- }
- }
- }
-
- DBG("(I) GPIO device %s found, offset: %x, active state: %d !\n",
- name, gp->addr, gp->active_state);
-
- return (np->n_intrs > 0) ? np->intrs[0].line : 0;
-}
-
-static void toonie_cleanup(struct snd_pmac *chip)
-{
- struct pmac_toonie *mix = chip->mixer_data;
- if (! mix)
- return;
- if (mix->hp_detect_irq >= 0)
- free_irq(mix->hp_detect_irq, chip);
- kfree(mix);
- chip->mixer_data = NULL;
-}
-
-int __init snd_pmac_toonie_init(struct snd_pmac *chip)
-{
- struct pmac_toonie *mix;
-
- mix = kmalloc(sizeof(*mix), GFP_KERNEL);
- if (! mix)
- return -ENOMEM;
-
- chip->mixer_data = mix;
- chip->mixer_free = toonie_cleanup;
-
- find_audio_gpio("headphone-mute", NULL, &mix->hp_mute_gpio);
- find_audio_gpio("amp-mute", NULL, &mix->amp_mute_gpio);
- mix->hp_detect_irq = find_audio_gpio("headphone-detect",
- NULL, &mix->hp_detect_gpio);
-
- strcpy(chip->card->mixername, "PowerMac Toonie");
-
- chip->master_sw_ctl = snd_ctl_new1(&toonie_hp_sw, chip);
- snd_ctl_add(chip->card, chip->master_sw_ctl);
-
- chip->speaker_sw_ctl = snd_ctl_new1(&toonie_speaker_sw, chip);
- snd_ctl_add(chip->card, chip->speaker_sw_ctl);
-
- INIT_WORK(&mix->detect_work, toonie_detect_handler, (void *)chip);
-
- if (mix->hp_detect_irq >= 0) {
- snd_pmac_add_automute(chip);
-
- chip->detect_headphone = toonie_detect_headphone;
- chip->update_automute = toonie_update_automute;
- toonie_update_automute(chip, 0);
-
- if (request_irq(mix->hp_detect_irq, toonie_hp_intr, 0,
- "Sound Headphone Detection", chip) < 0)
- mix->hp_detect_irq = -1;
- }
-
- return 0;
-}
-
#define D_USR (1<<4)
#define D_DESC (1<<5)
-static int dbri_debug = 0;
+static int dbri_debug;
module_param(dbri_debug, int, 0644);
MODULE_PARM_DESC(dbri_debug, "Debug value for Sun DBRI soundcard.");
/* Return a pointer to dbri_streaminfo */
#define DBRI_STREAM(dbri, substream) &dbri->stream_info[DBRI_STREAMNO(substream)]
-static struct snd_dbri *dbri_list = NULL; /* All DBRI devices */
+static struct snd_dbri *dbri_list; /* All DBRI devices */
/*
* Short data pipes transmit LSB first. The CS4215 receives MSB first. Grrr.
struct snd_info_entry *entry;
if (! snd_card_proc_new(dbri->card, "regs", &entry))
- snd_info_set_text_ops(entry, dbri, 1024, dbri_regs_read);
+ snd_info_set_text_ops(entry, dbri, dbri_regs_read);
#ifdef DBRI_DEBUG
if (! snd_card_proc_new(dbri->card, "debug", &entry)) {
- snd_info_set_text_ops(entry, dbri, 4096, dbri_debug_read);
+ snd_info_set_text_ops(entry, dbri, dbri_debug_read);
entry->mode = S_IFREG | S_IRUGO; /* Readable only. */
}
#endif
return 0;
}
+EXPORT_SYMBOL(snd_emux_new);
/*
*/
return 0;
}
+EXPORT_SYMBOL(snd_emux_register);
/*
*/
return 0;
}
-
-EXPORT_SYMBOL(snd_emux_new);
-EXPORT_SYMBOL(snd_emux_register);
EXPORT_SYMBOL(snd_emux_free);
-EXPORT_SYMBOL(snd_emux_terminate_all);
-EXPORT_SYMBOL(snd_emux_lock_voice);
-EXPORT_SYMBOL(snd_emux_unlock_voice);
-
-/* soundfont.c */
-EXPORT_SYMBOL(snd_sf_linear_to_log);
-
/*
* INIT part
entry->content = SNDRV_INFO_CONTENT_TEXT;
entry->private_data = emu;
- entry->c.text.read_size = 1024;
entry->c.text.read = snd_emux_proc_info_read;
if (snd_info_register(entry) < 0)
snd_info_free_entry(entry);
SNDRV_SEQ_PORT_TYPE_MIDI_GM |\
SNDRV_SEQ_PORT_TYPE_MIDI_GS |\
SNDRV_SEQ_PORT_TYPE_MIDI_XG |\
- SNDRV_SEQ_PORT_TYPE_DIRECT_SAMPLE)
+ SNDRV_SEQ_PORT_TYPE_HARDWARE |\
+ SNDRV_SEQ_PORT_TYPE_SYNTHESIZER)
/*
* Initialise the EMUX Synth by creating a client and registering
spin_unlock_irqrestore(&emu->voice_lock, flags);
}
+EXPORT_SYMBOL(snd_emux_terminate_all);
/*
* Terminate all voices associated with the given port
spin_unlock_irqrestore(&emu->voice_lock, flags);
}
+EXPORT_SYMBOL(snd_emux_lock_voice);
+
/*
*/
void snd_emux_unlock_voice(struct snd_emux *emu, int voice)
voice, emu->voices[voice].state);
spin_unlock_irqrestore(&emu->voice_lock, flags);
}
+
+EXPORT_SYMBOL(snd_emux_unlock_voice);
break;
case SNDRV_SFNT_REMOVE_INFO:
/* patch must be opened */
- if (sflist->currsf) {
+ if (!sflist->currsf) {
snd_printk("soundfont: remove_info: patch not opened\n");
rc = -EINVAL;
} else {
return v;
}
+EXPORT_SYMBOL(snd_sf_linear_to_log);
+
+
#define OFFSET_MSEC 653117 /* base = 1000 */
#define OFFSET_ABSCENT 851781 /* base = 8176 */
#define OFFSET_SAMPLERATE 1011119 /* base = 44100 */
unlock_preset(sflist);
return 0;
}
-
sprintf(name, "stream%d", stream->pcm_index);
if (! snd_card_proc_new(card, name, &entry))
- snd_info_set_text_ops(entry, stream, 1024, proc_pcm_format_read);
+ snd_info_set_text_ops(entry, stream, proc_pcm_format_read);
}
#else
if (!csep && altsd->bNumEndpoints >= 2)
csep = snd_usb_find_desc(alts->endpoint[1].extra, alts->endpoint[1].extralen, NULL, USB_DT_CS_ENDPOINT);
if (!csep || csep[0] < 7 || csep[2] != EP_GENERAL) {
- snd_printk(KERN_ERR "%d:%u:%d : no or invalid class specific endpoint descriptor\n",
+ snd_printk(KERN_WARNING "%d:%u:%d : no or invalid"
+ " class specific endpoint descriptor\n",
dev->devnum, iface_no, altno);
- continue;
+ csep = NULL;
}
fp = kmalloc(sizeof(*fp), GFP_KERNEL);
if (snd_usb_get_speed(dev) == USB_SPEED_HIGH)
fp->maxpacksize = (((fp->maxpacksize >> 11) & 3) + 1)
* (fp->maxpacksize & 0x7ff);
- fp->attributes = csep[3];
+ fp->attributes = csep ? csep[3] : 0;
/* some quirks for attributes here */
return -ENXIO;
alts = &iface->altsetting[1];
altsd = get_iface_desc(alts);
- if (alts->extralen != 11 || alts->extra[1] != CS_AUDIO_INTERFACE ||
+ if (alts->extralen != 11 || alts->extra[1] != USB_DT_CS_INTERFACE ||
altsd->bNumEndpoints != 1)
return -ENXIO;
{
struct snd_info_entry *entry;
if (! snd_card_proc_new(chip->card, "usbbus", &entry))
- snd_info_set_text_ops(entry, chip, 1024, proc_audio_usbbus_read);
+ snd_info_set_text_ops(entry, chip, proc_audio_usbbus_read);
if (! snd_card_proc_new(chip->card, "usbid", &entry))
- snd_info_set_text_ops(entry, chip, 1024, proc_audio_usbid_read);
+ snd_info_set_text_ops(entry, chip, proc_audio_usbid_read);
}
/*
#define USB_SUBCLASS_MIDI_STREAMING 0x03
#define USB_SUBCLASS_VENDOR_SPEC 0xff
-#define CS_AUDIO_UNDEFINED 0x20
-#define CS_AUDIO_DEVICE 0x21
-#define CS_AUDIO_CONFIGURATION 0x22
-#define CS_AUDIO_STRING 0x23
-#define CS_AUDIO_INTERFACE 0x24
-#define CS_AUDIO_ENDPOINT 0x25
-
#define HEADER 0x01
#define INPUT_TERMINAL 0x02
#define OUTPUT_TERMINAL 0x03
#include <linux/usb.h>
#include <sound/core.h>
#include <sound/rawmidi.h>
+#include <sound/asequencer.h>
#include "usbaudio.h"
* "(product) MIDI (n)" schema because they aren't external MIDI ports,
* such as internal control or synthesizer ports.
*/
-static struct {
+static struct port_info {
u32 id;
- int port;
- const char *name_format;
-} snd_usbmidi_port_names[] = {
+ short int port;
+ short int voices;
+ const char *name;
+ unsigned int seq_flags;
+} snd_usbmidi_port_info[] = {
+#define PORT_INFO(vendor, product, num, name_, voices_, flags) \
+ { .id = USB_ID(vendor, product), \
+ .port = num, .voices = voices_, \
+ .name = name_, .seq_flags = flags }
+#define EXTERNAL_PORT(vendor, product, num, name) \
+ PORT_INFO(vendor, product, num, name, 0, \
+ SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC | \
+ SNDRV_SEQ_PORT_TYPE_HARDWARE | \
+ SNDRV_SEQ_PORT_TYPE_PORT)
+#define CONTROL_PORT(vendor, product, num, name) \
+ PORT_INFO(vendor, product, num, name, 0, \
+ SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC | \
+ SNDRV_SEQ_PORT_TYPE_HARDWARE)
+#define ROLAND_SYNTH_PORT(vendor, product, num, name, voices) \
+ PORT_INFO(vendor, product, num, name, voices, \
+ SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC | \
+ SNDRV_SEQ_PORT_TYPE_MIDI_GM | \
+ SNDRV_SEQ_PORT_TYPE_MIDI_GM2 | \
+ SNDRV_SEQ_PORT_TYPE_MIDI_GS | \
+ SNDRV_SEQ_PORT_TYPE_MIDI_XG | \
+ SNDRV_SEQ_PORT_TYPE_HARDWARE | \
+ SNDRV_SEQ_PORT_TYPE_SYNTHESIZER)
+#define SOUNDCANVAS_PORT(vendor, product, num, name, voices) \
+ PORT_INFO(vendor, product, num, name, voices, \
+ SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC | \
+ SNDRV_SEQ_PORT_TYPE_MIDI_GM | \
+ SNDRV_SEQ_PORT_TYPE_MIDI_GM2 | \
+ SNDRV_SEQ_PORT_TYPE_MIDI_GS | \
+ SNDRV_SEQ_PORT_TYPE_MIDI_XG | \
+ SNDRV_SEQ_PORT_TYPE_MIDI_MT32 | \
+ SNDRV_SEQ_PORT_TYPE_HARDWARE | \
+ SNDRV_SEQ_PORT_TYPE_SYNTHESIZER)
/* Roland UA-100 */
- { USB_ID(0x0582, 0x0000), 2, "%s Control" },
+ CONTROL_PORT(0x0582, 0x0000, 2, "%s Control"),
/* Roland SC-8850 */
- { USB_ID(0x0582, 0x0003), 0, "%s Part A" },
- { USB_ID(0x0582, 0x0003), 1, "%s Part B" },
- { USB_ID(0x0582, 0x0003), 2, "%s Part C" },
- { USB_ID(0x0582, 0x0003), 3, "%s Part D" },
- { USB_ID(0x0582, 0x0003), 4, "%s MIDI 1" },
- { USB_ID(0x0582, 0x0003), 5, "%s MIDI 2" },
+ SOUNDCANVAS_PORT(0x0582, 0x0003, 0, "%s Part A", 128),
+ SOUNDCANVAS_PORT(0x0582, 0x0003, 1, "%s Part B", 128),
+ SOUNDCANVAS_PORT(0x0582, 0x0003, 2, "%s Part C", 128),
+ SOUNDCANVAS_PORT(0x0582, 0x0003, 3, "%s Part D", 128),
+ EXTERNAL_PORT(0x0582, 0x0003, 4, "%s MIDI 1"),
+ EXTERNAL_PORT(0x0582, 0x0003, 5, "%s MIDI 2"),
/* Roland U-8 */
- { USB_ID(0x0582, 0x0004), 0, "%s MIDI" },
- { USB_ID(0x0582, 0x0004), 1, "%s Control" },
+ EXTERNAL_PORT(0x0582, 0x0004, 0, "%s MIDI"),
+ CONTROL_PORT(0x0582, 0x0004, 1, "%s Control"),
/* Roland SC-8820 */
- { USB_ID(0x0582, 0x0007), 0, "%s Part A" },
- { USB_ID(0x0582, 0x0007), 1, "%s Part B" },
- { USB_ID(0x0582, 0x0007), 2, "%s MIDI" },
+ SOUNDCANVAS_PORT(0x0582, 0x0007, 0, "%s Part A", 64),
+ SOUNDCANVAS_PORT(0x0582, 0x0007, 1, "%s Part B", 64),
+ EXTERNAL_PORT(0x0582, 0x0007, 2, "%s MIDI"),
/* Roland SK-500 */
- { USB_ID(0x0582, 0x000b), 0, "%s Part A" },
- { USB_ID(0x0582, 0x000b), 1, "%s Part B" },
- { USB_ID(0x0582, 0x000b), 2, "%s MIDI" },
+ SOUNDCANVAS_PORT(0x0582, 0x000b, 0, "%s Part A", 64),
+ SOUNDCANVAS_PORT(0x0582, 0x000b, 1, "%s Part B", 64),
+ EXTERNAL_PORT(0x0582, 0x000b, 2, "%s MIDI"),
/* Roland SC-D70 */
- { USB_ID(0x0582, 0x000c), 0, "%s Part A" },
- { USB_ID(0x0582, 0x000c), 1, "%s Part B" },
- { USB_ID(0x0582, 0x000c), 2, "%s MIDI" },
+ SOUNDCANVAS_PORT(0x0582, 0x000c, 0, "%s Part A", 64),
+ SOUNDCANVAS_PORT(0x0582, 0x000c, 1, "%s Part B", 64),
+ EXTERNAL_PORT(0x0582, 0x000c, 2, "%s MIDI"),
/* Edirol UM-880 */
- { USB_ID(0x0582, 0x0014), 8, "%s Control" },
+ CONTROL_PORT(0x0582, 0x0014, 8, "%s Control"),
/* Edirol SD-90 */
- { USB_ID(0x0582, 0x0016), 0, "%s Part A" },
- { USB_ID(0x0582, 0x0016), 1, "%s Part B" },
- { USB_ID(0x0582, 0x0016), 2, "%s MIDI 1" },
- { USB_ID(0x0582, 0x0016), 3, "%s MIDI 2" },
+ ROLAND_SYNTH_PORT(0x0582, 0x0016, 0, "%s Part A", 128),
+ ROLAND_SYNTH_PORT(0x0582, 0x0016, 1, "%s Part B", 128),
+ EXTERNAL_PORT(0x0582, 0x0016, 2, "%s MIDI 1"),
+ EXTERNAL_PORT(0x0582, 0x0016, 3, "%s MIDI 2"),
/* Edirol UM-550 */
- { USB_ID(0x0582, 0x0023), 5, "%s Control" },
+ CONTROL_PORT(0x0582, 0x0023, 5, "%s Control"),
/* Edirol SD-20 */
- { USB_ID(0x0582, 0x0027), 0, "%s Part A" },
- { USB_ID(0x0582, 0x0027), 1, "%s Part B" },
- { USB_ID(0x0582, 0x0027), 2, "%s MIDI" },
+ ROLAND_SYNTH_PORT(0x0582, 0x0027, 0, "%s Part A", 64),
+ ROLAND_SYNTH_PORT(0x0582, 0x0027, 1, "%s Part B", 64),
+ EXTERNAL_PORT(0x0582, 0x0027, 2, "%s MIDI"),
/* Edirol SD-80 */
- { USB_ID(0x0582, 0x0029), 0, "%s Part A" },
- { USB_ID(0x0582, 0x0029), 1, "%s Part B" },
- { USB_ID(0x0582, 0x0029), 2, "%s MIDI 1" },
- { USB_ID(0x0582, 0x0029), 3, "%s MIDI 2" },
+ ROLAND_SYNTH_PORT(0x0582, 0x0029, 0, "%s Part A", 128),
+ ROLAND_SYNTH_PORT(0x0582, 0x0029, 1, "%s Part B", 128),
+ EXTERNAL_PORT(0x0582, 0x0029, 2, "%s MIDI 1"),
+ EXTERNAL_PORT(0x0582, 0x0029, 3, "%s MIDI 2"),
/* Edirol UA-700 */
- { USB_ID(0x0582, 0x002b), 0, "%s MIDI" },
- { USB_ID(0x0582, 0x002b), 1, "%s Control" },
+ EXTERNAL_PORT(0x0582, 0x002b, 0, "%s MIDI"),
+ CONTROL_PORT(0x0582, 0x002b, 1, "%s Control"),
/* Roland VariOS */
- { USB_ID(0x0582, 0x002f), 0, "%s MIDI" },
- { USB_ID(0x0582, 0x002f), 1, "%s External MIDI" },
- { USB_ID(0x0582, 0x002f), 2, "%s Sync" },
+ EXTERNAL_PORT(0x0582, 0x002f, 0, "%s MIDI"),
+ EXTERNAL_PORT(0x0582, 0x002f, 1, "%s External MIDI"),
+ EXTERNAL_PORT(0x0582, 0x002f, 2, "%s Sync"),
/* Edirol PCR */
- { USB_ID(0x0582, 0x0033), 0, "%s MIDI" },
- { USB_ID(0x0582, 0x0033), 1, "%s 1" },
- { USB_ID(0x0582, 0x0033), 2, "%s 2" },
+ EXTERNAL_PORT(0x0582, 0x0033, 0, "%s MIDI"),
+ EXTERNAL_PORT(0x0582, 0x0033, 1, "%s 1"),
+ EXTERNAL_PORT(0x0582, 0x0033, 2, "%s 2"),
/* BOSS GS-10 */
- { USB_ID(0x0582, 0x003b), 0, "%s MIDI" },
- { USB_ID(0x0582, 0x003b), 1, "%s Control" },
+ EXTERNAL_PORT(0x0582, 0x003b, 0, "%s MIDI"),
+ CONTROL_PORT(0x0582, 0x003b, 1, "%s Control"),
/* Edirol UA-1000 */
- { USB_ID(0x0582, 0x0044), 0, "%s MIDI" },
- { USB_ID(0x0582, 0x0044), 1, "%s Control" },
+ EXTERNAL_PORT(0x0582, 0x0044, 0, "%s MIDI"),
+ CONTROL_PORT(0x0582, 0x0044, 1, "%s Control"),
/* Edirol UR-80 */
- { USB_ID(0x0582, 0x0048), 0, "%s MIDI" },
- { USB_ID(0x0582, 0x0048), 1, "%s 1" },
- { USB_ID(0x0582, 0x0048), 2, "%s 2" },
+ EXTERNAL_PORT(0x0582, 0x0048, 0, "%s MIDI"),
+ EXTERNAL_PORT(0x0582, 0x0048, 1, "%s 1"),
+ EXTERNAL_PORT(0x0582, 0x0048, 2, "%s 2"),
/* Edirol PCR-A */
- { USB_ID(0x0582, 0x004d), 0, "%s MIDI" },
- { USB_ID(0x0582, 0x004d), 1, "%s 1" },
- { USB_ID(0x0582, 0x004d), 2, "%s 2" },
+ EXTERNAL_PORT(0x0582, 0x004d, 0, "%s MIDI"),
+ EXTERNAL_PORT(0x0582, 0x004d, 1, "%s 1"),
+ EXTERNAL_PORT(0x0582, 0x004d, 2, "%s 2"),
/* Edirol UM-3EX */
- { USB_ID(0x0582, 0x009a), 3, "%s Control" },
+ CONTROL_PORT(0x0582, 0x009a, 3, "%s Control"),
/* M-Audio MidiSport 8x8 */
- { USB_ID(0x0763, 0x1031), 8, "%s Control" },
- { USB_ID(0x0763, 0x1033), 8, "%s Control" },
+ CONTROL_PORT(0x0763, 0x1031, 8, "%s Control"),
+ CONTROL_PORT(0x0763, 0x1033, 8, "%s Control"),
/* MOTU Fastlane */
- { USB_ID(0x07fd, 0x0001), 0, "%s MIDI A" },
- { USB_ID(0x07fd, 0x0001), 1, "%s MIDI B" },
+ EXTERNAL_PORT(0x07fd, 0x0001, 0, "%s MIDI A"),
+ EXTERNAL_PORT(0x07fd, 0x0001, 1, "%s MIDI B"),
/* Emagic Unitor8/AMT8/MT4 */
- { USB_ID(0x086a, 0x0001), 8, "%s Broadcast" },
- { USB_ID(0x086a, 0x0002), 8, "%s Broadcast" },
- { USB_ID(0x086a, 0x0003), 4, "%s Broadcast" },
+ EXTERNAL_PORT(0x086a, 0x0001, 8, "%s Broadcast"),
+ EXTERNAL_PORT(0x086a, 0x0002, 8, "%s Broadcast"),
+ EXTERNAL_PORT(0x086a, 0x0003, 4, "%s Broadcast"),
};
+static struct port_info *find_port_info(struct snd_usb_midi* umidi, int number)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(snd_usbmidi_port_info); ++i) {
+ if (snd_usbmidi_port_info[i].id == umidi->chip->usb_id &&
+ snd_usbmidi_port_info[i].port == number)
+ return &snd_usbmidi_port_info[i];
+ }
+ return NULL;
+}
+
+static void snd_usbmidi_get_port_info(struct snd_rawmidi *rmidi, int number,
+ struct snd_seq_port_info *seq_port_info)
+{
+ struct snd_usb_midi *umidi = rmidi->private_data;
+ struct port_info *port_info;
+
+ /* TODO: read port flags from descriptors */
+ port_info = find_port_info(umidi, number);
+ if (port_info) {
+ seq_port_info->type = port_info->seq_flags;
+ seq_port_info->midi_voices = port_info->voices;
+ }
+}
+
static void snd_usbmidi_init_substream(struct snd_usb_midi* umidi,
int stream, int number,
struct snd_rawmidi_substream ** rsubstream)
{
- int i;
+ struct port_info *port_info;
const char *name_format;
struct snd_rawmidi_substream *substream = snd_usbmidi_find_substream(umidi, stream, number);
}
/* TODO: read port name from jack descriptor */
- name_format = "%s MIDI %d";
- for (i = 0; i < ARRAY_SIZE(snd_usbmidi_port_names); ++i) {
- if (snd_usbmidi_port_names[i].id == umidi->chip->usb_id &&
- snd_usbmidi_port_names[i].port == number) {
- name_format = snd_usbmidi_port_names[i].name_format;
- break;
- }
- }
+ port_info = find_port_info(umidi, number);
+ name_format = port_info ? port_info->name : "%s MIDI %d";
snprintf(substream->name, sizeof(substream->name),
name_format, umidi->chip->card->shortname, number + 1);
for (cs_desc = hostif->extra;
cs_desc < hostif->extra + hostif->extralen && cs_desc[0] >= 2;
cs_desc += cs_desc[0]) {
- if (cs_desc[1] == CS_AUDIO_INTERFACE) {
+ if (cs_desc[1] == USB_DT_CS_INTERFACE) {
if (cs_desc[2] == MIDI_IN_JACK)
endpoint->in_cables = (endpoint->in_cables << 1) | 1;
else if (cs_desc[2] == MIDI_OUT_JACK)
return 0;
}
+static struct snd_rawmidi_global_ops snd_usbmidi_ops = {
+ .get_port_info = snd_usbmidi_get_port_info,
+};
+
static int snd_usbmidi_create_rawmidi(struct snd_usb_midi* umidi,
int out_ports, int in_ports)
{
rmidi->info_flags = SNDRV_RAWMIDI_INFO_OUTPUT |
SNDRV_RAWMIDI_INFO_INPUT |
SNDRV_RAWMIDI_INFO_DUPLEX;
+ rmidi->ops = &snd_usbmidi_ops;
rmidi->private_data = umidi;
rmidi->private_free = snd_usbmidi_rawmidi_free;
snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_usbmidi_output_ops);
/* ignore error from controls - for debugging */
/* #define IGNORE_CTL_ERROR */
+/*
+ * Sound Blaster remote control configuration
+ *
+ * format of remote control data:
+ * Extigy: xx 00
+ * Audigy 2 NX: 06 80 xx 00 00 00
+ * Live! 24-bit: 06 80 xx yy 22 83
+ */
+static const struct rc_config {
+ u32 usb_id;
+ u8 offset;
+ u8 length;
+ u8 packet_length;
+ u8 mute_mixer_id;
+ u32 mute_code;
+} rc_configs[] = {
+ { USB_ID(0x041e, 0x3000), 0, 1, 2, 18, 0x0013 }, /* Extigy */
+ { USB_ID(0x041e, 0x3020), 2, 1, 6, 18, 0x0013 }, /* Audigy 2 NX */
+ { USB_ID(0x041e, 0x3040), 2, 2, 6, 2, 0x6e91 }, /* Live! 24-bit */
+};
+
struct usb_mixer_interface {
struct snd_usb_audio *chip;
unsigned int ctrlif;
struct usb_mixer_elem_info **id_elems; /* array[256], indexed by unit id */
/* Sound Blaster remote control stuff */
- enum {
- RC_NONE,
- RC_EXTIGY,
- RC_AUDIGY2NX,
- } rc_type;
+ const struct rc_config *rc_cfg;
unsigned long rc_hwdep_open;
u32 rc_code;
wait_queue_head_t rc_waitq;
static void snd_usb_mixer_memory_change(struct usb_mixer_interface *mixer,
int unitid)
{
- if (mixer->rc_type == RC_NONE)
+ if (!mixer->rc_cfg)
return;
/* unit ids specific to Extigy/Audigy 2 NX: */
switch (unitid) {
struct pt_regs *regs)
{
struct usb_mixer_interface *mixer = urb->context;
- /*
- * format of remote control data:
- * Extigy: xx 00
- * Audigy 2 NX: 06 80 xx 00 00 00
- */
- int offset = mixer->rc_type == RC_EXTIGY ? 0 : 2;
+ const struct rc_config *rc = mixer->rc_cfg;
u32 code;
- if (urb->status < 0 || urb->actual_length <= offset)
+ if (urb->status < 0 || urb->actual_length < rc->packet_length)
return;
- code = mixer->rc_buffer[offset];
+
+ code = mixer->rc_buffer[rc->offset];
+ if (rc->length == 2)
+ code |= mixer->rc_buffer[rc->offset + 1] << 8;
+
/* the Mute button actually changes the mixer control */
- if (code == 13)
- snd_usb_mixer_notify_id(mixer, 18);
+ if (code == rc->mute_code)
+ snd_usb_mixer_notify_id(mixer, rc->mute_mixer_id);
mixer->rc_code = code;
wmb();
wake_up(&mixer->rc_waitq);
static int snd_usb_soundblaster_remote_init(struct usb_mixer_interface *mixer)
{
struct snd_hwdep *hwdep;
- int err, len;
+ int err, len, i;
- switch (mixer->chip->usb_id) {
- case USB_ID(0x041e, 0x3000):
- mixer->rc_type = RC_EXTIGY;
- len = 2;
- break;
- case USB_ID(0x041e, 0x3020):
- mixer->rc_type = RC_AUDIGY2NX;
- len = 6;
- break;
- default:
+ for (i = 0; i < ARRAY_SIZE(rc_configs); ++i)
+ if (rc_configs[i].usb_id == mixer->chip->usb_id)
+ break;
+ if (i >= ARRAY_SIZE(rc_configs))
return 0;
- }
+ mixer->rc_cfg = &rc_configs[i];
+ len = mixer->rc_cfg->packet_length;
+
init_waitqueue_head(&mixer->rc_waitq);
err = snd_hwdep_new(mixer->chip->card, "SB remote control", 0, &hwdep);
if (err < 0)
if ((err = snd_audigy2nx_controls_create(mixer)) < 0)
goto _error;
if (!snd_card_proc_new(chip->card, "audigy2nx", &entry))
- snd_info_set_text_ops(entry, mixer, 1024,
+ snd_info_set_text_ops(entry, mixer,
snd_audigy2nx_proc_read);
}
for (s = 0; s < 2; ++s) {
struct snd_pcm_substream *substream;
substream = pcm->streams[s].substream;
- if (substream && substream->ffile != NULL)
+ if (SUBSTREAM_BUSY(substream))
err = -EBUSY;
}
}