diff -Nru a/Documentation/DocBook/procfs-guide.tmpl b/Documentation/DocBook/procfs-guide.tmpl --- a/Documentation/DocBook/procfs-guide.tmpl Mon Oct 28 21:14:41 2002 +++ b/Documentation/DocBook/procfs-guide.tmpl Thu Jul 10 22:22:53 2003 @@ -253,41 +253,6 @@ - - - - - Creating a device - - - - struct proc_dir_entry* proc_mknod - const char* name - mode_t mode - struct proc_dir_entry* parent - kdev_t rdev - - - - - Creates a device file name with mode - mode in the procfs directory - parent. The device file will work on - the device rdev, which can be generated - by using the MKDEV macro from - linux/kdev_t.h. The - mode parameter - must contain S_IFBLK - or S_IFCHR to create a device - node. Compare with userland mknod - --mode=mode - name rdev. - - - - - - Creating a directory diff -Nru a/Documentation/DocBook/procfs_example.c b/Documentation/DocBook/procfs_example.c --- a/Documentation/DocBook/procfs_example.c Sat Nov 16 15:02:15 2002 +++ b/Documentation/DocBook/procfs_example.c Thu Jul 10 22:22:53 2003 @@ -63,7 +63,7 @@ static struct proc_dir_entry *example_dir, *foo_file, - *bar_file, *jiffies_file, *tty_device, *symlink; + *bar_file, *jiffies_file, *symlink; struct fb_data_t foo_data, bar_data; @@ -173,16 +173,6 @@ bar_file->write_proc = proc_write_foobar; bar_file->owner = THIS_MODULE; - /* create tty device */ - tty_device = proc_mknod("tty", S_IFCHR | 0666, - example_dir, MKDEV(5, 0)); - if(tty_device == NULL) { - rv = -ENOMEM; - goto no_tty; - } - - tty_device->owner = THIS_MODULE; - /* create symlink */ symlink = proc_symlink("jiffies_too", example_dir, "jiffies"); diff -Nru a/Documentation/filesystems/cifs.txt b/Documentation/filesystems/cifs.txt --- a/Documentation/filesystems/cifs.txt Mon Mar 31 15:55:42 2003 +++ b/Documentation/filesystems/cifs.txt Thu Jul 10 19:26:46 2003 @@ -1,33 +1,51 @@ -This module, cifs, is a filesystem that implements the SMB/CIFS -protocol, which is the protocol used by Windows based operating systems -(including Windows 2000 and its successors) as well as Samba, OS/2 and -many others operating systems and network file server appliances. The -Cifs VFS filesystem module is designed to work well with servers that -implement the newer versions (dialects) of the SMB/CIFS protocol such as -Samba, the program written by Andrew Tridgell that turns any Unix host -into a file server for DOS or Windows clients, as well as Windows NT, -Windows 2000 and its successors. It is not designed to handle older smb -servers well, those that implement older versions of the dialect (such -as OS/2 or Windows 95), for this purpose use smbfs. - -This module can support mounting without a mount helper program. The -mount syntax is: - mount //server_ip/share_name /mnt -o user=username,password=your_password - -where "username", "your_password" and "server_ip" and "share_name" -should be replaced with specific values (supplied by the user) e.g. - mount //9.53.216.16/public /mnt -o user=jsmith,password=openup - -This cifs implementation is designed to handle network caching (safely) -as well as to implement locking, large file (64 bit access), distributed -file system ("dfs") and other advanced protocol features. It also -implements the SNIA standard for Unix extensions to CIFS (when -communicating with servers such as Samba 2.2.3 or later which support it). - -For more information contact sfrench@us.ibm.com - -Cifs is an SMB client (or gateway). For more info on the SMB/CIFS -protocol and Samba, including documentation, please go to -http://www.samba.org/ and then on to your nearest mirror. For more -information about the cifs vfs, go to the project page at: - http://us1.samba.org/samba/Linux_CIFS_client.html + This is the client VFS module for the Common Internet File System + (CIFS) protocol which is the successor to the Server Message Block + (SMB) protocol, the native file sharing mechanism for most early + PC operating systems. CIFS is fully supported by current network + file servers such as Windows 2000, Windows 2003 (including + Windows XP) as well by Samba (which provides excellent CIFS + server support for Linux and many other operating systems), so + this network filesystem client can mount to a wide variety of + servers. The smbfs module should be used instead of this cifs module + for mounting to older SMB servers such as OS/2. The smbfs and cifs + modules can coexist and do not conflict. The CIFS VFS filesystem + module is designed to work well with servers that implement the + newer versions (dialects) of the SMB/CIFS protocol such as Samba, + the program written by Andrew Tridgell that turns any Unix host + into a SMB/CIFS file server. + + The intent of this module is to provide the most advanced network + file system function for CIFS compliant servers, including better + POSIX compliance, secure per-user session establishment, high + performance safe distributed caching (oplock), optional packet + signing, large files, Unicode support and other internationalization + improvements. Since both Samba server and this filesystem client support + the CIFS Unix extensions, the combination can provide a reasonable + alternative to NFSv4 for fileserving in some Linux to Linux environments, + not just in Linux to Windows environments. + + This filesystem has an optional mount utility (mount.cifs) that can + be obtained from the project page and installed in the path in the same + directory with the other mount helpers (such as mount.smbfs). + Mounting using the cifs filesystem without installing the mount helper + requires specifying the server's ip address. + + For Linux 2.4: + mount //anything/here /mnt_target -o + user=username,pass=password,unc=//ip_address_of_server/sharename + + For Linux 2.5: + mount //ip_address_of_server/sharename /mnt_target -o user=username, pass=password + + + For more information on the module see the project page at + + http://us1.samba.org/samba/Linux_CIFS_client.html + + For more information on CIFS see: + + http://www.snia.org/tech_activities/CIFS + + or the Samba site: + + http://www.samba.org diff -Nru a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt --- a/Documentation/kernel-parameters.txt Thu Jun 5 23:37:53 2003 +++ b/Documentation/kernel-parameters.txt Thu Jul 10 22:23:36 2003 @@ -772,7 +772,7 @@ Ranges are in pairs (memory base and size). profile= [KNL] Enable kernel profiling via /proc/profile - Format: + (param: profile step/bucket size as a power of 2) prompt_ramdisk= [RAM] List of RAM disks to prompt for floppy disk before loading. diff -Nru a/Documentation/nbd.txt b/Documentation/nbd.txt --- a/Documentation/nbd.txt Thu May 2 20:49:06 2002 +++ b/Documentation/nbd.txt Thu Jul 10 22:23:52 2003 @@ -1,57 +1,47 @@ Network Block Device (TCP version) - Note: Network Block Device is now experimental, which approximately - means, that it works on my computer, and it worked on one of school - computers. + What is it: With this compiled in the kernel (or as a module), Linux + can use a remote server as one of its block devices. So every time + the client computer wants to read, e.g., /dev/nb0, it sends a + request over TCP to the server, which will reply with the data read. + This can be used for stations with low disk space (or even diskless - + if you boot from floppy) to borrow disk space from another computer. + Unlike NFS, it is possible to put any filesystem on it, etc. It should + even be possible to use NBD as a root filesystem (I've never tried), + but it requires a user-level program to be in the initrd to start. + It also allows you to run block-device in user land (making server + and client physically the same computer, communicating using loopback). - What is it: With this compiled in the kernel, Linux can use a remote - server as one of its block devices. So every time the client computer - wants to read /dev/nd0, it sends a request over TCP to the server, which - will reply with the data read. This can be used for stations with - low disk space (or even diskless - if you boot from floppy) to - borrow disk space from another computer. Unlike NFS, it is possible to - put any filesystem on it etc. It is impossible to use NBD as a root - filesystem, since it requires a user-level program to start. It also - allows you to run block-device in user land (making server and client - physically the same computer, communicating using loopback). + Current state: It currently works. Network block device is stable. + I originally thought that it was impossible to swap over TCP. It + turned out not to be true - swapping over TCP now works and seems + to be deadlock-free, but it requires heavy patches into Linux's + network layer. - Current state: It currently works. Network block device looks like - being pretty stable. I originally thought that it is impossible to swap - over TCP. It turned out not to be true - swapping over TCP now works - and seems to be deadlock-free, but it requires heavy patches into - Linux's network layer. - - Devices: Network block device uses major 43, minors 0..n (where n is - configurable in nbd.h). Create these files by mknod when needed. After - that, your ls -l /dev/ should look like: - -brw-rw-rw- 1 root root 43, 0 Apr 11 00:28 nd0 -brw-rw-rw- 1 root root 43, 1 Apr 11 00:28 nd1 -... - - Protocol: Userland program passes file handle with connected TCP - socket to actual kernel driver. This way, the kernel does not have to - care about connecting etc. Protocol is rather simple: If the driver is - asked to read from block device, it sends packet of following form - "request" (all data are in network byte order): - - __u32 magic; must be equal to 0x12560953 - __u32 from; position in bytes to read from / write at - __u32 len; number of bytes to be read / written - __u64 handle; handle of operation - __u32 type; 0 = read - 1 = write - ... in case of write operation, this is - immediately followed len bytes of data + For more information, or to download the nbd-client and nbd-server + tools, go to http://nbd.sf.net/. - When operation is completed, server responds with packet of following - structure "reply": - - __u32 magic; must be equal to - __u64 handle; handle copied from request - __u32 error; 0 = operation completed successfully, - else error code - ... in case of read operation with no error, - this is immediately followed len bytes of data + Howto: To setup nbd, you can simply do the following: + + First, serve a device or file from a remote server: + + nbd-server + + e.g., + root@server1 # nbd-server 1234 /dev/sdb1 + + (serves sdb1 partition on TCP port 1234) + + Then, on the local (client) system: + + nbd-client /dev/nb[0-n] + + e.g., + root@client1 # nbd-client server1 1234 /dev/nb0 + + (creates the nb0 device on client1) - For more information, look at http://nbd.sf.net/. + The nbd kernel module need only be installed on the client + system, as the nbd-server is completely in userspace. In fact, + the nbd-server has been successfully ported to other operating + systems, including Windows. diff -Nru a/MAINTAINERS b/MAINTAINERS --- a/MAINTAINERS Tue Jul 8 05:00:18 2003 +++ b/MAINTAINERS Sat Jul 12 19:56:53 2003 @@ -879,6 +879,8 @@ S: Supported IDE DRIVER [GENERAL] +P: Bartlomiej Zolnierkiewicz +M: B.Zolnierkiewicz@elka.pw.edu.pl L: linux-kernel@vger.kernel.org L: linux-ide@vger.kernel.org S: Maintained @@ -1910,7 +1912,7 @@ L: linux-usb-devel@lists.sourceforge.net S: Maintained -USB BLUETOOTH DRIVER +USB BLUETOOTH TTY CONVERTER DRIVER P: Greg Kroah-Hartman M: greg@kroah.com L: linux-usb-users@lists.sourceforge.net diff -Nru a/Makefile b/Makefile --- a/Makefile Thu Jul 10 12:13:30 2003 +++ b/Makefile Sun Jul 13 04:32:56 2003 @@ -1,7 +1,7 @@ VERSION = 2 PATCHLEVEL = 5 SUBLEVEL = 75 -EXTRAVERSION = +EXTRAVERSION = -bk3 # *DOCUMENTATION* # To see a list of typical targets execute "make help" @@ -35,7 +35,7 @@ SUBARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ \ -e s/arm.*/arm/ -e s/sa110/arm/ \ - -e s/s390x/s390/ ) + -e s/s390x/s390/ -e s/parisc64/parisc/ ) # Remove hyphens since they have special meaning in RPM filenames KERNELPATH=kernel-$(subst -,,$(KERNELRELEASE)) diff -Nru a/arch/alpha/kernel/time.c b/arch/alpha/kernel/time.c --- a/arch/alpha/kernel/time.c Sat Jun 28 08:50:41 2003 +++ b/arch/alpha/kernel/time.c Thu Jul 10 22:22:57 2003 @@ -451,8 +451,9 @@ int do_settimeofday(struct timespec *tv) { + time_t wtm_sec, sec = tv->tv_sec; + long wtm_nsec, nsec = tv->tv_nsec; unsigned long delta_nsec; - long sec, nsec; if ((unsigned long)tv->tv_nsec >= NSEC_PER_SEC) return -EINVAL; @@ -474,30 +475,18 @@ delta_nsec *= 1000; #endif - sec = tv->tv_sec; - nsec = tv->tv_nsec; nsec -= delta_nsec; - if (nsec < 0) { - nsec += NSEC_PER_SEC; - sec -= 1; - } - xtime.tv_sec = sec; - xtime.tv_nsec = nsec; + wtm_sec = wall_to_monotonic.tv_sec + (xtime.tv_sec - sec); + wtm_nsec = wall_to_monotonic.tv_nsec + (xtime.tv_nsec - nsec); + + set_normalized_timespec(&xtime, sec, nsec); + set_normalized_timespec(&wall_to_monotonic, wtm_sec, wtm_nsec); + time_adjust = 0; /* stop active adjtime() */ time_status |= STA_UNSYNC; time_maxerror = NTP_PHASE_LIMIT; time_esterror = NTP_PHASE_LIMIT; - - wall_to_monotonic.tv_sec += xtime.tv_sec - tv->tv_sec; - wall_to_monotonic.tv_nsec += xtime.tv_nsec - tv->tv_nsec; - if (wall_to_monotonic.tv_nsec > NSEC_PER_SEC) { - wall_to_monotonic.tv_nsec -= NSEC_PER_SEC; - wall_to_monotonic.tv_sec++; - } else if (wall_to_monotonic.tv_nsec < 0) { - wall_to_monotonic.tv_nsec += NSEC_PER_SEC; - wall_to_monotonic.tv_sec--; - } write_sequnlock_irq(&xtime_lock); return 0; diff -Nru a/arch/arm/Kconfig b/arch/arm/Kconfig --- a/arch/arm/Kconfig Tue Jun 24 22:21:45 2003 +++ b/arch/arm/Kconfig Thu Jul 10 22:23:10 2003 @@ -1061,14 +1061,6 @@ you are concerned with the code size or don't want to see these messages. -config KALLSYMS - bool "Load all symbols for debugging/kksymoops" - depends on DEBUG_KERNEL - help - Say Y here to let the kernel print out symbolic crash information and - symbolic stack backtraces. This increases the size of the kernel - somewhat, as all symbols have to be loaded into the kernel image. - # These options are only for real kernel hackers who want to get their hands dirty. config DEBUG_LL bool "Kernel low-level debugging functions" diff -Nru a/arch/arm26/Kconfig b/arch/arm26/Kconfig --- a/arch/arm26/Kconfig Sun Jul 6 16:40:01 2003 +++ b/arch/arm26/Kconfig Thu Jul 10 22:23:10 2003 @@ -391,14 +391,6 @@ you are concerned with the code size or don't want to see these messages. -config KALLSYMS - bool "Load all symbols for debugging/kksymoops" - depends on DEBUG_KERNEL - help - Say Y here to let the kernel print out symbolic crash information and - symbolic stack backtraces. This increases the size of the kernel - somewhat, as all symbols have to be loaded into the kernel image. - # These options are only for real kernel hackers who want to get their hands dirty. config DEBUG_LL bool "Kernel low-level debugging functions" diff -Nru a/arch/i386/Kconfig b/arch/i386/Kconfig --- a/arch/i386/Kconfig Wed Jul 2 21:19:42 2003 +++ b/arch/i386/Kconfig Thu Jul 10 22:23:10 2003 @@ -1354,13 +1354,6 @@ This options enables addition error checking for high memory systems. Disable for production systems. -config KALLSYMS - bool "Load all symbols for debugging/kksymoops" - help - Say Y here to let the kernel print out symbolic crash information and - symbolic stack backtraces. This increases the size of the kernel - somewhat, as all symbols have to be loaded into the kernel image. - config DEBUG_SPINLOCK_SLEEP bool "Sleep-inside-spinlock checking" help diff -Nru a/arch/i386/kernel/entry.S b/arch/i386/kernel/entry.S --- a/arch/i386/kernel/entry.S Mon Jul 7 12:36:55 2003 +++ b/arch/i386/kernel/entry.S Fri Jul 11 22:46:02 2003 @@ -876,6 +876,7 @@ .long sys_clock_nanosleep .long sys_statfs64 .long sys_fstatfs64 - .long sys_tgkill + .long sys_tgkill /* 270 */ + .long sys_utimes nr_syscalls=(.-sys_call_table)/4 diff -Nru a/arch/i386/kernel/microcode.c b/arch/i386/kernel/microcode.c --- a/arch/i386/kernel/microcode.c Wed Apr 30 09:19:41 2003 +++ b/arch/i386/kernel/microcode.c Fri Jul 11 06:29:05 2003 @@ -380,7 +380,6 @@ return -ENODATA; default: - printk(KERN_ERR "microcode: unknown ioctl cmd=%d\n", cmd); return -EINVAL; } return -EINVAL; diff -Nru a/arch/i386/kernel/time.c b/arch/i386/kernel/time.c --- a/arch/i386/kernel/time.c Wed Jul 2 21:21:32 2003 +++ b/arch/i386/kernel/time.c Thu Jul 10 22:22:57 2003 @@ -115,6 +115,9 @@ int do_settimeofday(struct timespec *tv) { + time_t wtm_sec, sec = tv->tv_sec; + long wtm_nsec, nsec = tv->tv_nsec; + if ((unsigned long)tv->tv_nsec >= NSEC_PER_SEC) return -EINVAL; @@ -125,28 +128,15 @@ * wall time. Discover what correction gettimeofday() would have * made, and then undo it! */ - tv->tv_nsec -= cur_timer->get_offset() * NSEC_PER_USEC; - tv->tv_nsec -= (jiffies - wall_jiffies) * TICK_NSEC; - - while (tv->tv_nsec < 0) { - tv->tv_nsec += NSEC_PER_SEC; - tv->tv_sec--; - } + nsec -= cur_timer->get_offset() * NSEC_PER_USEC; + nsec -= (jiffies - wall_jiffies) * TICK_NSEC; - wall_to_monotonic.tv_sec += xtime.tv_sec - tv->tv_sec; - wall_to_monotonic.tv_nsec += xtime.tv_nsec - tv->tv_nsec; + wtm_sec = wall_to_monotonic.tv_sec + (xtime.tv_sec - sec); + wtm_nsec = wall_to_monotonic.tv_nsec + (xtime.tv_nsec - nsec); - if (wall_to_monotonic.tv_nsec > NSEC_PER_SEC) { - wall_to_monotonic.tv_nsec -= NSEC_PER_SEC; - wall_to_monotonic.tv_sec++; - } - if (wall_to_monotonic.tv_nsec < 0) { - wall_to_monotonic.tv_nsec += NSEC_PER_SEC; - wall_to_monotonic.tv_sec--; - } + set_normalized_timespec(&xtime, sec, nsec); + set_normalized_timespec(&wall_to_monotonic, wtm_sec, wtm_nsec); - xtime.tv_sec = tv->tv_sec; - xtime.tv_nsec = tv->tv_nsec; time_adjust = 0; /* stop active adjtime() */ time_status |= STA_UNSYNC; time_maxerror = NTP_PHASE_LIMIT; diff -Nru a/arch/ia64/Kconfig b/arch/ia64/Kconfig --- a/arch/ia64/Kconfig Wed Jul 9 14:23:16 2003 +++ b/arch/ia64/Kconfig Thu Jul 10 22:23:10 2003 @@ -667,14 +667,6 @@ Say Y here if you are developing drivers or trying to debug and identify kernel problems. -config KALLSYMS - bool "Load all symbols for debugging/kksymoops" - depends on DEBUG_KERNEL - help - Say Y here to let the kernel print out symbolic crash information and - symbolic stack backtraces. This increases the size of the kernel - somewhat, as all symbols have to be loaded into the kernel image. - config IA64_PRINT_HAZARDS bool "Print possible IA-64 dependency violations to console" depends on DEBUG_KERNEL diff -Nru a/arch/ia64/kernel/time.c b/arch/ia64/kernel/time.c --- a/arch/ia64/kernel/time.c Wed Jun 18 00:37:28 2003 +++ b/arch/ia64/kernel/time.c Thu Jul 10 22:22:57 2003 @@ -104,21 +104,6 @@ .reset = itc_reset }; -static inline void -set_normalized_timespec (struct timespec *ts, time_t sec, long nsec) -{ - while (nsec > NSEC_PER_SEC) { - nsec -= NSEC_PER_SEC; - ++sec; - } - while (nsec < 0) { - nsec += NSEC_PER_SEC; - --sec; - } - ts->tv_sec = sec; - ts->tv_nsec = nsec; -} - int do_settimeofday (struct timespec *tv) { diff -Nru a/arch/m68k/kernel/time.c b/arch/m68k/kernel/time.c --- a/arch/m68k/kernel/time.c Sat Jun 14 16:16:05 2003 +++ b/arch/m68k/kernel/time.c Thu Jul 10 22:22:57 2003 @@ -103,6 +103,7 @@ time.tm_hour, time.tm_min, time.tm_sec); xtime.tv_nsec = 0; } + wall_to_monotonic.tv_sec = -xtime.tv_sec; mach_sched_init(timer_interrupt); } @@ -140,6 +141,8 @@ int do_settimeofday(struct timespec *tv) { + time_t wtm_sec, sec = tv->tv_sec; + long wtm_nsec, nsec = tv->tv_nsec; extern unsigned long wall_jiffies; if ((unsigned long)tv->tv_nsec >= NSEC_PER_SEC) @@ -152,16 +155,15 @@ * Discover what correction gettimeofday * would have done, and then undo it! */ - tv->tv_nsec -= 1000 * (mach_gettimeoffset() + + nsec -= 1000 * (mach_gettimeoffset() + (jiffies - wall_jiffies) * (1000000 / HZ)); - while (tv->tv_nsec < 0) { - tv->tv_nsec += NSEC_PER_SEC; - tv->tv_sec--; - } + wtm_sec = wall_to_monotonic.tv_sec + (xtime.tv_sec - sec); + wtm_nsec = wall_to_monotonic.tv_nsec + (xtime.tv_nsec - nsec); + + set_normalized_timespec(&xtime, sec, nsec); + set_normalized_timespec(&wall_to_monotonic, wtm_sec, wtm_nsec); - xtime.tv_sec = tv->tv_sec; - xtime.tv_nsec = tv->tv_nsec; time_adjust = 0; /* stop active adjtime() */ time_status |= STA_UNSYNC; time_maxerror = NTP_PHASE_LIMIT; diff -Nru a/arch/m68knommu/kernel/time.c b/arch/m68knommu/kernel/time.c --- a/arch/m68knommu/kernel/time.c Wed Jul 9 16:13:07 2003 +++ b/arch/m68knommu/kernel/time.c Thu Jul 10 22:22:57 2003 @@ -131,6 +131,7 @@ year += 100; xtime.tv_sec = mktime(year, mon, day, hour, min, sec); xtime.tv_nsec = 0; + wall_to_monotonic.tv_sec = -xtime.tv_sec; mach_sched_init(timer_interrupt); } @@ -165,6 +166,9 @@ int do_settimeofday(struct timespec *tv) { + time_t wtm_sec, sec = tv->tv_sec; + long wtm_nsec, nsec = tv->tv_nsec; + if ((unsigned long)tv->tv_nsec >= NSEC_PER_SEC) return -EINVAL; @@ -177,15 +181,14 @@ * would have done, and then undo it! */ if (mach_gettimeoffset) - tv->tv_nsec -= (mach_gettimeoffset() * 1000); + nsec -= (mach_gettimeoffset() * 1000); - while (tv->tv_nsec < 0) { - tv->tv_nsec += NSEC_PER_SEC; - tv->tv_sec--; - } + wtm_sec = wall_to_monotonic.tv_sec + (xtime.tv_sec - sec); + wtm_nsec = wall_to_monotonic.tv_nsec + (xtime.tv_nsec - nsec); + + set_normalized_timespec(&xtime, sec, nsec); + set_normalized_timespec(&wall_to_monotonic, wtm_sec, wtm_nsec); - xtime.tv_sec = tv->tv_sec; - xtime.tv_nsec = tv->tv_nsec; time_adjust = 0; /* stop active adjtime() */ time_status |= STA_UNSYNC; time_maxerror = NTP_PHASE_LIMIT; diff -Nru a/arch/mips/kernel/time.c b/arch/mips/kernel/time.c --- a/arch/mips/kernel/time.c Sun Jun 22 16:31:20 2003 +++ b/arch/mips/kernel/time.c Thu Jul 10 22:22:57 2003 @@ -98,6 +98,9 @@ int do_settimeofday(struct timespec *tv) { + time_t wtm_sec, sec = tv->tv_sec; + long wtm_nsec, nsec = tv->tv_nsec; + if ((unsigned long)tv->tv_nsec >= NSEC_PER_SEC) return -EINVAL; @@ -108,16 +111,15 @@ * wall time. Discover what correction gettimeofday() would have * made, and then undo it! */ - tv->tv_nsec -= do_gettimeoffset() * NSEC_PER_USEC; - tv->tv_nsec -= (jiffies - wall_jiffies) * TICK_NSEC; + nsec -= do_gettimeoffset() * NSEC_PER_USEC; + nsec -= (jiffies - wall_jiffies) * TICK_NSEC; - while (tv->tv_nsec < 0) { - tv->tv_nsec += NSEC_PER_SEC; - tv->tv_sec--; - } + wtm_sec = wall_to_monotonic.tv_sec + (xtime.tv_sec - sec); + wtm_nsec = wall_to_monotonic.tv_nsec + (xtime.tv_nsec - nsec); + + set_normalized_timespec(&xtime, sec, nsec); + set_normalized_timespec(&wall_to_monotonic, wtm_sec, wtm_nsec); - xtime.tv_sec = tv->tv_sec; - xtime.tv_nsec = tv->tv_nsec; time_adjust = 0; /* stop active adjtime() */ time_status |= STA_UNSYNC; time_maxerror = NTP_PHASE_LIMIT; @@ -499,6 +501,9 @@ xtime.tv_sec = rtc_get_time(); xtime.tv_nsec = 0; + + set_normalized_timespec(&wall_to_monotonic, + -xtime.tv_sec, -xtime.tv_nsec); /* choose appropriate gettimeoffset routine */ if (!cpu_has_counter) { diff -Nru a/arch/mips64/kernel/time.c b/arch/mips64/kernel/time.c --- a/arch/mips64/kernel/time.c Mon Jun 23 12:08:34 2003 +++ b/arch/mips64/kernel/time.c Thu Jul 10 22:22:57 2003 @@ -98,6 +98,9 @@ int do_settimeofday(struct timespec *tv) { + time_t wtm_sec, sec = tv->tv_sec; + long wtm_nsec, nsec = tv->tv_nsec; + if ((unsigned long)tv->tv_nsec >= NSEC_PER_SEC) return -EINVAL; @@ -108,16 +111,15 @@ * wall time. Discover what correction gettimeofday() would have * made, and then undo it! */ - tv->tv_nsec -= do_gettimeoffset() * NSEC_PER_USEC; - tv->tv_nsec -= (jiffies - wall_jiffies) * TICK_NSEC; + nsec -= do_gettimeoffset() * NSEC_PER_USEC; + nsec -= (jiffies - wall_jiffies) * TICK_NSEC; - while (tv->tv_nsec < 0) { - tv->tv_nsec += NSEC_PER_SEC; - tv->tv_sec--; - } + wtm_sec = wall_to_monotonic.tv_sec + (xtime.tv_sec - tsec); + wtm_nsec = wall_to_monotonic.tv_nsec + (xtime.tv_nsec - nsec); + + set_normalized_timespec(&xtime, sec, nsec); + set_normalized_timespec(&wall_to_monotonic, wtm_sec, wtm_nsec); - xtime.tv_sec = tv->tv_sec; - xtime.tv_nsec = tv->tv_nsec; time_adjust = 0; /* stop active adjtime() */ time_status |= STA_UNSYNC; time_maxerror = NTP_PHASE_LIMIT; @@ -499,6 +501,8 @@ xtime.tv_sec = rtc_get_time(); xtime.tv_nsec = 0; + set_normalized_timespec(&wall_to_monotonic, + -xtime.tv_sec, -xtime.tv_nsec); /* choose appropriate gettimeoffset routine */ if (!cpu_has_counter) { diff -Nru a/arch/parisc/Kconfig b/arch/parisc/Kconfig --- a/arch/parisc/Kconfig Tue Jun 24 22:21:45 2003 +++ b/arch/parisc/Kconfig Thu Jul 10 22:23:32 2003 @@ -1,6 +1,6 @@ # # For a description of the syntax of this configuration file, -# see the Configure script. +# see Documentation/kbuild/kconfig-language.txt. # mainmenu "Linux/PA-RISC Kernel Configuration" @@ -287,13 +287,13 @@ keys are documented in . Don't say Y unless you really know what this hack does. -config KALLSYMS - bool "Load all symbols for debugging/kksymoops" - depends on DEBUG_KERNEL +config FRAME_POINTER + bool "Compile the kernel with frame pointers" help - Say Y here to let the kernel print out symbolic crash information and - symbolic stack backtraces. This increases the size of the kernel - somewhat, as all symbols have to be loaded into the kernel image. + If you say Y here the resulting kernel image will be slightly larger + and slower, but it will give very useful debugging information. + If you don't debug the kernel, you can say N, but we may not be able + to solve problems without frame pointers. endmenu diff -Nru a/arch/parisc/hpux/sys_hpux.c b/arch/parisc/hpux/sys_hpux.c --- a/arch/parisc/hpux/sys_hpux.c Tue Dec 31 17:34:28 2002 +++ b/arch/parisc/hpux/sys_hpux.c Thu Jul 10 07:21:18 2003 @@ -121,7 +121,7 @@ { struct super_block *s; struct hpux_ustat tmp; /* Changed to hpux_ustat */ - struct statfs sbuf; + struct kstatfs sbuf; int err = -EINVAL; s = user_get_super(dev); diff -Nru a/arch/parisc/kernel/drivers.c b/arch/parisc/kernel/drivers.c --- a/arch/parisc/kernel/drivers.c Sun May 25 17:00:00 2003 +++ b/arch/parisc/kernel/drivers.c Wed Jul 2 08:57:46 2003 @@ -112,7 +112,9 @@ static int parisc_driver_remove(struct device *dev) { struct parisc_device *pa_dev = to_parisc_device(dev); - + struct parisc_driver *pa_drv = to_parisc_driver(dev->driver); + if (pa_drv->remove) + pa_drv->remove(pa_dev); release_mem_region(pa_dev->hpa, 0x1000); return 0; diff -Nru a/arch/parisc/kernel/ioctl32.c b/arch/parisc/kernel/ioctl32.c --- a/arch/parisc/kernel/ioctl32.c Tue May 27 04:21:38 2003 +++ b/arch/parisc/kernel/ioctl32.c Sun Jun 22 14:25:47 2003 @@ -36,8 +36,9 @@ #include #include #include -#include +#include #include +#include #include #include #include diff -Nru a/arch/parisc/kernel/module.c b/arch/parisc/kernel/module.c --- a/arch/parisc/kernel/module.c Thu Jun 12 08:18:06 2003 +++ b/arch/parisc/kernel/module.c Wed Jul 2 08:57:46 2003 @@ -671,6 +671,11 @@ const Elf_Shdr *sechdrs, struct module *me) { + int i; + unsigned long nsyms; + const char *strtab = NULL; + Elf_Sym *newptr, *oldptr; + Elf_Shdr *symhdr = NULL; #ifdef DEBUG struct fdesc_entry *entry; u32 *addr; @@ -690,7 +695,49 @@ me->arch.got_count, me->arch.got_max, me->arch.fdesc_count, me->arch.fdesc_max); #endif - + + /* haven't filled in me->symtab yet, so have to find it + * ourselves */ + for (i = 1; i < hdr->e_shnum; i++) { + if(sechdrs[i].sh_type == SHT_SYMTAB + && (sechdrs[i].sh_type & SHF_ALLOC)) { + int strindex = sechdrs[i].sh_link; + /* FIXME: AWFUL HACK + * The cast is to drop the const from + * the sechdrs pointer */ + symhdr = (Elf_Shdr *)&sechdrs[i]; + strtab = (char *)sechdrs[strindex].sh_addr; + break; + } + } + + printk("module %s: strtab %p, symhdr %p\n", + me->name, strtab, symhdr); + + /* no symbol table */ + if(symhdr == NULL) + return 0; + + oldptr = (void *)symhdr->sh_addr; + newptr = oldptr + 1; /* we start counting at 1 */ + nsyms = symhdr->sh_size / sizeof(Elf_Sym); + DEBUGP("OLD num_symtab %lu\n", nsyms); + + for (i = 1; i < nsyms; i++) { + oldptr++; /* note, count starts at 1 so preincrement */ + if(strncmp(strtab + oldptr->st_name, + ".L", 2) == 0) + continue; + + if(newptr != oldptr) + *newptr++ = *oldptr; + else + newptr++; + + } + nsyms = newptr - (Elf_Sym *)symhdr->sh_addr; + DEBUGP("NEW num_symtab %lu\n", nsyms); + symhdr->sh_size = nsyms * sizeof(Elf_Sym); return 0; } diff -Nru a/arch/parisc/kernel/pci.c b/arch/parisc/kernel/pci.c --- a/arch/parisc/kernel/pci.c Mon Mar 17 09:56:45 2003 +++ b/arch/parisc/kernel/pci.c Sun Jun 22 14:25:47 2003 @@ -10,11 +10,12 @@ * Copyright (C) 1999-2001 Grant Grundler */ #include -#include +#include +#include #include -#include /* for __init and __devinit */ #include #include +#include #include #include diff -Nru a/arch/parisc/kernel/sys_parisc32.c b/arch/parisc/kernel/sys_parisc32.c --- a/arch/parisc/kernel/sys_parisc32.c Mon May 5 04:05:48 2003 +++ b/arch/parisc/kernel/sys_parisc32.c Sun Jun 22 14:25:47 2003 @@ -418,11 +418,11 @@ } asmlinkage int -sys32_settimeofday(struct compat_timeval *tv, struct timezone *tz) +sys32_settimeofday(struct compat_timespec *tv, struct timezone *tz) { struct timeval ktv; struct timezone ktz; - extern int do_sys_settimeofday(struct timeval *tv, struct timezone *tz); + extern int do_sys_settimeofday(struct timespec *tv, struct timezone *tz); if (tv) { if (get_compat_timeval(tv, &ktv)) diff -Nru a/arch/parisc/kernel/time.c b/arch/parisc/kernel/time.c --- a/arch/parisc/kernel/time.c Mon May 5 08:34:24 2003 +++ b/arch/parisc/kernel/time.c Thu Jul 10 22:22:57 2003 @@ -188,8 +188,11 @@ } int -do_settimeofday (struct timeval *tv) +do_settimeofday (struct timespec *tv) { + time_t wtm_sec, sec = tv->tv_sec; + long wtm_nsec, nsec = tv->tv_nsec; + if ((unsigned long)tv->tv_nsec >= NSEC_PER_SEC) return -EINVAL; @@ -202,16 +205,15 @@ * Discover what correction gettimeofday would have * done, and then undo it! */ - tv->tv_nsec -= gettimeoffset() * 1000; - tv->tv_nsec -= (jiffies - wall_jiffies) * (NSEC_PER_SEC / HZ); + nsec -= gettimeoffset() * 1000; + nsec -= (jiffies - wall_jiffies) * (NSEC_PER_SEC / HZ); + + wtm_sec = wall_to_monotonic.tv_sec + (xtime.tv_sec - sec); + wtm_nsec = wall_to_monotonic.tv_nsec + (xtime.tv_nsec - nsec); - while (tv->tv_nsec < 0) { - tv->tv_nsec += NSEC_PER_SEC; - tv->tv_sec--; - } + set_normalized_timespec(&xtime, sec, nsec); + set_normalized_timespec(&wall_to_monotonic, wtm_sec, wtm_nsec); - xtime.tv_sec = tv->tv_sec; - xtime.tv_nsec = tv->tv_nsec; time_adjust = 0; /* stop active adjtime() */ time_status |= STA_UNSYNC; time_maxerror = NTP_PHASE_LIMIT; @@ -243,6 +245,8 @@ write_seqlock_irq(&xtime_lock); xtime.tv_sec = tod_data.tod_sec; xtime.tv_nsec = tod_data.tod_usec * 1000; + set_normalized_timespec(&wall_to_monotonic, + -xtime.tv_sec, -xtime.tv_nsec); write_sequnlock_irq(&xtime_lock); } else { printk(KERN_ERR "Error reading tod clock\n"); diff -Nru a/arch/parisc/kernel/traps.c b/arch/parisc/kernel/traps.c --- a/arch/parisc/kernel/traps.c Sun Jun 8 13:25:47 2003 +++ b/arch/parisc/kernel/traps.c Wed Jul 2 08:57:46 2003 @@ -126,7 +126,7 @@ void dump_stack(void) { unsigned long stack; - show_trace(&stack); + show_trace(current, &stack); } @@ -136,7 +136,7 @@ static int kstack_depth_to_print = 128 * 4; #endif -void show_stack(unsigned long *sp) +void show_stack(struct task_struct *task, unsigned long *sp) { unsigned long *stack; int i; @@ -145,8 +145,10 @@ * debugging aid: "show_stack(NULL);" prints the * back trace for this cpu. */ - if (sp==NULL) + if (task==NULL) sp = (unsigned long*)&sp; + else if(sp == NULL) + sp = (unsigned long*)task->thread.regs.ksp; stack = sp; printk("\n" KERN_CRIT "Stack Dump:\n"); @@ -160,11 +162,11 @@ printk(RFMT " ", *stack--); } printk("\n" KERN_CRIT "\n"); - show_trace(sp); + show_trace(task, sp); } -void show_trace(unsigned long *stack) +void show_trace(struct task_struct *task, unsigned long *stack) { unsigned long *startstack; unsigned long addr; @@ -201,7 +203,7 @@ void show_trace_task(struct task_struct *tsk) { - show_trace((unsigned long *)tsk->thread.regs.ksp); + show_trace(tsk, (unsigned long *)tsk->thread.regs.ksp); } void die_if_kernel(char *str, struct pt_regs *regs, long err) @@ -426,7 +428,7 @@ if (code == 1) transfer_pim_to_trap_frame(regs); - show_stack((unsigned long *)regs->gr[30]); + show_stack(NULL, (unsigned long *)regs->gr[30]); printk("\n"); printk(KERN_CRIT "%s: Code=%d regs=%p (Addr=" RFMT ")\n", diff -Nru a/arch/parisc/kernel/unaligned.c b/arch/parisc/kernel/unaligned.c --- a/arch/parisc/kernel/unaligned.c Sat Jan 11 21:50:25 2003 +++ b/arch/parisc/kernel/unaligned.c Thu Jul 10 07:21:18 2003 @@ -92,6 +92,15 @@ #define OPCODE_STWA OPCODE1(0x03,1,0xe) #define OPCODE_STDA OPCODE1(0x03,1,0xf) +#define OPCODE_FLDWX OPCODE1(0x09,0,0x0) +#define OPCODE_FSTWX OPCODE1(0x09,0,0x4) +#define OPCODE_FLDWS OPCODE1(0x09,1,0x0) +#define OPCODE_FSTWS OPCODE1(0x09,1,0x4) +#define OPCODE_FLDDX OPCODE1(0x0b,0,0x0) +#define OPCODE_FSTDX OPCODE1(0x0b,0,0x4) +#define OPCODE_FLDDS OPCODE1(0x0b,1,0x0) +#define OPCODE_FSTDS OPCODE1(0x0b,1,0x4) + #define OPCODE_LDD_L OPCODE2(0x14,0) #define OPCODE_FLDD_L OPCODE2(0x14,1) #define OPCODE_STD_L OPCODE2(0x1c,0) @@ -113,6 +122,7 @@ #define R1(i) (((i)>>21)&0x1f) #define R2(i) (((i)>>16)&0x1f) #define R3(i) ((i)&0x1f) +#define FR3(i) ((((i)<<1)&0x1f)|(((i)>>6)&1)) #define IM(i,n) (((i)>>1&((1<<(n-1))-1))|((i)&1?((0-1L)<<(n-1)):0)) #define IM5_2(i) IM((i)>>16,5) #define IM5_3(i) IM((i),5) @@ -146,7 +156,7 @@ return 0; } -static int emulate_ldw(struct pt_regs *regs, int toreg) +static int emulate_ldw(struct pt_regs *regs, int toreg, int flop) { unsigned long saddr = regs->ior; unsigned long val = 0; @@ -169,20 +179,26 @@ DPRINTF("val = 0x" RFMT "\n", val); - if (toreg) + if (flop) + ((__u32*)(regs->fr))[toreg] = val; + else if (toreg) regs->gr[toreg] = val; return 0; } -#ifdef __LP64__ -static int emulate_ldd(struct pt_regs *regs, int toreg) +static int emulate_ldd(struct pt_regs *regs, int toreg, int flop) { unsigned long saddr = regs->ior; - unsigned long val = 0; + __u64 val = 0; DPRINTF("load " RFMT ":" RFMT " to r%d for 8 bytes\n", regs->isr, regs->ior, toreg); +#ifdef CONFIG_PA20 +#ifndef __LP64__ + if (!flop) + return -1; +#endif __asm__ __volatile__ ( " depd,z %2,60,3,%%r19\n" /* r19=(ofs&7)*8 */ " mtsp %3, %%sr1\n" @@ -195,15 +211,36 @@ : "=r" (val) : "0" (val), "r" (saddr), "r" (regs->isr) : "r19", "r20" ); +#else + { + unsigned long valh=0,vall=0; + __asm__ __volatile__ ( +" zdep %4,29,2,%%r19\n" /* r19=(ofs&3)*8 */ +" mtsp %5, %%sr1\n" +" dep %%r0,31,2,%4\n" +" ldw 0(%%sr1,%5),%0\n" +" ldw 4(%%sr1,%5),%1\n" +" ldw 8(%%sr1,%5),%%r20\n" +" subi 32,%%r19,%%r19\n" +" mtsar %%r19\n" +" vshd %0,%1,%0\n" +" vshd %1,%%r20,%1\n" + : "=r" (valh), "=r" (vall) + : "0" (valh), "1" (vall), "r" (saddr), "r" (regs->isr) + : "r19", "r20" ); + val=((__u64)valh<<32)|(__u64)vall; + } +#endif DPRINTF("val = 0x" RFMT "\n", val); - if (toreg) + if (flop) + regs->fr[toreg] = val; + else if (toreg) regs->gr[toreg] = val; return 0; } -#endif static int emulate_sth(struct pt_regs *regs, int frreg) { @@ -212,7 +249,7 @@ val = 0; DPRINTF("store r%d (0x" RFMT ") to " RFMT ":" RFMT " for 2 bytes\n", frreg, - regs->gr[frreg], regs->isr, regs->ior); + val, regs->isr, regs->ior); __asm__ __volatile__ ( " mtsp %2, %%sr1\n" @@ -225,14 +262,19 @@ return 0; } -static int emulate_stw(struct pt_regs *regs, int frreg) +static int emulate_stw(struct pt_regs *regs, int frreg, int flop) { - unsigned long val = regs->gr[frreg]; - if (!frreg) + unsigned long val; + + if (flop) + val = ((__u32*)(regs->fr))[frreg]; + else if (frreg) + val = regs->gr[frreg]; + else val = 0; DPRINTF("store r%d (0x" RFMT ") to " RFMT ":" RFMT " for 4 bytes\n", frreg, - regs->gr[frreg], regs->isr, regs->ior); + val, regs->isr, regs->ior); __asm__ __volatile__ ( @@ -257,17 +299,25 @@ return 0; } -#ifdef __LP64__ -static int emulate_std(struct pt_regs *regs, int frreg) +static int emulate_std(struct pt_regs *regs, int frreg, int flop) { - unsigned long val = regs->gr[frreg]; - if (!frreg) - val = 0; + __u64 val; - DPRINTF("store r%d (0x" RFMT ") to " RFMT ":" RFMT " for 8 bytes\n", frreg, - regs->gr[frreg], regs->isr, regs->ior); + if (flop) + val = regs->fr[frreg]; + else if (frreg) + val = regs->gr[frreg]; + else + val = 0; + DPRINTF("store r%d (0x" %016llx ") to " RFMT ":" RFMT " for 8 bytes\n", frreg, + val, regs->isr, regs->ior); +#ifdef CONFIG_PA20 +#ifndef __LP64__ + if (!flop) + return -1; +#endif __asm__ __volatile__ ( " mtsp %2, %%sr1\n" " depd,z %1, 60, 3, %%r19\n" @@ -287,19 +337,45 @@ : : "r" (val), "r" (regs->ior), "r" (regs->isr) : "r19", "r20", "r21", "r22", "r1" ); +#else + { + unsigned long valh=(val>>32),vall=(val&0xffffffffl); + __asm__ __volatile__ ( +" mtsp %3, %%sr1\n" +" zdep %1, 29, 2, %%r19\n" +" dep %%r0, 31, 2, %1\n" +" mtsar %%r19\n" +" zvdepi -2, 32, %%r19\n" +" ldw 0(%%sr1,%2),%%r20\n" +" ldw 8(%%sr1,%2),%%r21\n" +" vshd %0, %1, %%r1\n" +" vshd %%r0, %0, %0\n" +" vshd %1, %%r0, %1\n" +" and %%r20, %%r19, %%r20\n" +" andcm %%r21, %%r19, %%r21\n" +" or %0, %%r20, %0\n" +" or %1, %%r21, %1\n" +" stw %0,0(%%sr1,%2)\n" +" stw %%r1,4(%%sr1,%2)\n" +" stw %1,8(%%sr1,%2)\n" + : + : "r" (valh), "r" (vall), "r" (regs->ior), "r" (regs->isr) + : "r19", "r20", "r21", "r1" ); + } +#endif return 0; } -#endif void handle_unaligned(struct pt_regs *regs) { unsigned long unaligned_count = 0; unsigned long last_time = 0; - unsigned long newbase = regs->gr[R1(regs->iir)]; + unsigned long newbase = R1(regs->iir)?regs->gr[R1(regs->iir)]:0; int modify = 0; int ret = -1; struct siginfo si; + register int flop=0; /* true if this is a flop */ /* if the unaligned access is inside the kernel: * if the access is caused by a syscall, then we fault the calling @@ -383,9 +459,9 @@ case OPCODE_LDDA_I: shift= 3; break; } - newbase += regs->gr[R2(regs->iir)]<iir)?regs->gr[R2(regs->iir)]:0)<gr[R2(regs->iir)]; + newbase += (R2(regs->iir)?regs->gr[R2(regs->iir)]:0); } break; case 0x13: @@ -438,7 +514,7 @@ case OPCODE_LDWA_I: case OPCODE_LDW_S: case OPCODE_LDWA_S: - ret = emulate_ldw(regs, R3(regs->iir)); + ret = emulate_ldw(regs, R3(regs->iir),0); break; case OPCODE_STH: @@ -447,23 +523,47 @@ case OPCODE_STW: case OPCODE_STWA: - ret = emulate_stw(regs, R2(regs->iir)); + ret = emulate_stw(regs, R2(regs->iir),0); break; -#ifdef __LP64__ +#ifdef CONFIG_PA20 case OPCODE_LDD_I: case OPCODE_LDDA_I: case OPCODE_LDD_S: case OPCODE_LDDA_S: - ret = emulate_ldd(regs, R3(regs->iir)); + ret = emulate_ldd(regs, R3(regs->iir),0); break; case OPCODE_STD: case OPCODE_STDA: - ret = emulate_std(regs, R2(regs->iir)); + ret = emulate_std(regs, R2(regs->iir),0); break; #endif + case OPCODE_FLDWX: + case OPCODE_FLDWS: + flop=1; + ret = emulate_ldw(regs,FR3(regs->iir),1); + break; + + case OPCODE_FLDDX: + case OPCODE_FLDDS: + flop=1; + ret = emulate_ldd(regs,R3(regs->iir),1); + break; + + case OPCODE_FSTWX: + case OPCODE_FSTWS: + flop=1; + ret = emulate_stw(regs,FR3(regs->iir),1); + break; + + case OPCODE_FSTDX: + case OPCODE_FSTDS: + flop=1; + ret = emulate_std(regs,R3(regs->iir),1); + break; + case OPCODE_LDCD_I: case OPCODE_LDCW_I: case OPCODE_LDCD_S: @@ -471,30 +571,44 @@ ret = -1; /* "undefined", but lets kill them. */ break; } -#ifdef __LP64__ +#ifdef CONFIG_PA20 switch (regs->iir & OPCODE2_MASK) { - case OPCODE_LDD_L: case OPCODE_FLDD_L: - ret = emulate_ldd(regs, R2(regs->iir)); + flop=1; + ret = emulate_ldd(regs,R2(regs->iir),1); + break; + case OPCODE_FSTD_L: + flop=1; + ret = emulate_std(regs, R2(regs->iir),1); break; +#ifdef CONFIG_PA20 + case OPCODE_LDD_L: + ret = emulate_ldd(regs, R2(regs->iir),0); + break; case OPCODE_STD_L: - case OPCODE_FSTD_L: - ret = emulate_std(regs, R2(regs->iir)); + ret = emulate_std(regs, R2(regs->iir),0); break; +#endif } #endif switch (regs->iir & OPCODE3_MASK) { - case OPCODE_LDW_M: case OPCODE_FLDW_L: - ret = emulate_ldw(regs, R2(regs->iir)); + flop=1; + ret = emulate_ldw(regs, R2(regs->iir),0); + break; + case OPCODE_LDW_M: + ret = emulate_ldw(regs, R2(regs->iir),1); break; case OPCODE_FSTW_L: + flop=1; + ret = emulate_stw(regs, R2(regs->iir),1); + break; case OPCODE_STW_M: - ret = emulate_stw(regs, R2(regs->iir)); + ret = emulate_stw(regs, R2(regs->iir),0); break; } switch (regs->iir & OPCODE4_MASK) @@ -504,19 +618,18 @@ break; case OPCODE_LDW_L: case OPCODE_LDWM: - ret = emulate_ldw(regs, R2(regs->iir)); + ret = emulate_ldw(regs, R2(regs->iir),0); break; case OPCODE_STH_L: ret = emulate_sth(regs, R2(regs->iir)); break; case OPCODE_STW_L: case OPCODE_STWM: - ret = emulate_stw(regs, R2(regs->iir)); + ret = emulate_stw(regs, R2(regs->iir),0); break; } - /* XXX LJ - need to handle float load/store */ - if (modify) + if (modify && R1(regs->iir)) regs->gr[R1(regs->iir)] = newbase; @@ -540,9 +653,8 @@ return; } - /* else we handled it, advance the PC.... */ - regs->iaoq[0] = regs->iaoq[1]; - regs->iaoq[1] = regs->iaoq[0] + 4; + /* else we handled it, let life go on. */ + regs->gr[0]|=PSW_N; } /* diff -Nru a/arch/ppc/Kconfig b/arch/ppc/Kconfig --- a/arch/ppc/Kconfig Wed Jul 2 17:44:13 2003 +++ b/arch/ppc/Kconfig Thu Jul 10 22:23:14 2003 @@ -1449,13 +1449,6 @@ This options enables additional error checking for high memory systems. Disable for production systems. -config KALLSYMS - bool "Load all symbols for debugging/kksymoops" - help - Say Y here to let the kernel print out symbolic crash information and - symbolic stack backtraces. This increases the size of the kernel - somewhat, as all symbols have to be loaded into the kernel image. - config DEBUG_SPINLOCK_SLEEP bool "Sleep-inside-spinlock checking" depends on DEBUG_KERNEL diff -Nru a/arch/ppc/kernel/time.c b/arch/ppc/kernel/time.c --- a/arch/ppc/kernel/time.c Sat Jun 14 16:16:05 2003 +++ b/arch/ppc/kernel/time.c Thu Jul 10 22:22:57 2003 @@ -241,6 +241,8 @@ int do_settimeofday(struct timespec *tv) { + time_t wtm_sec, new_sec = tv->tv_sec; + long wtm_nsec, new_nsec = tv->tv_nsec; unsigned long flags; int tb_delta, new_nsec, new_sec; @@ -268,14 +270,14 @@ */ tb_delta = tb_ticks_since(last_jiffy_stamp(smp_processor_id())); tb_delta += (jiffies - wall_jiffies) * tb_ticks_per_jiffy; - new_sec = tv->tv_sec; - new_nsec = tv->tv_nsec - 1000 * mulhwu(tb_to_us, tb_delta); - while (new_nsec < 0) { - new_sec--; - new_nsec += NSEC_PER_SEC; - } - xtime.tv_nsec = new_nsec; - xtime.tv_sec = new_sec; + + new_nsec -= 1000 * mulhwu(tb_to_us, tb_delta); + + wtm_sec = wall_to_monotonic.tv_sec + (xtime.tv_sec - new_sec); + wtm_nsec = wall_to_monotonic.tv_nsec + (xtime.tv_nsec - new_nsec); + + set_normalized_timespec(&xtime, new_sec, new_nsec); + set_normalized_timespec(&wall_to_monotonic, wtm_sec, wtm_nsec); /* In case of a large backwards jump in time with NTP, we want the * clock to be updated as soon as the PLL is again in lock. @@ -347,6 +349,8 @@ sys_tz.tz_dsttime = 0; xtime.tv_sec -= time_offset; } + set_normalized_timespec(&wall_to_monotonic, + -xtime.tv_sec, -xtime.tv_nsec); } #define FEBRUARY 2 diff -Nru a/arch/ppc64/kernel/time.c b/arch/ppc64/kernel/time.c --- a/arch/ppc64/kernel/time.c Sat Jun 14 16:15:59 2003 +++ b/arch/ppc64/kernel/time.c Thu Jul 10 22:22:57 2003 @@ -339,9 +339,11 @@ int do_settimeofday(struct timespec *tv) { + time_t wtm_sec, new_sec = tv->tv_sec; + long wtm_nsec, new_nsec = tv->tv_nsec; unsigned long flags; unsigned long delta_xsec; - long int tb_delta, new_nsec, new_sec; + long int tb_delta; unsigned long new_xsec; if ((unsigned long)tv->tv_nsec >= NSEC_PER_SEC) @@ -364,14 +366,13 @@ tb_delta = tb_ticks_since(tb_last_stamp); tb_delta += (jiffies - wall_jiffies) * tb_ticks_per_jiffy; - new_sec = tv->tv_sec; - new_nsec = tv->tv_nsec - (tb_delta / tb_ticks_per_usec / 1000); - while (new_nsec < 0) { - new_sec--; - new_nsec += NSEC_PER_SEC; - } - xtime.tv_nsec = new_nsec; - xtime.tv_sec = new_sec; + new_nsec -= tb_delta / tb_ticks_per_usec / 1000; + + wtm_sec = wall_to_monotonic.tv_sec + (xtime.tv_sec - new_sec); + wtm_nsec = wall_to_monotonic.tv_nsec + (xtime.tv_nsec - new_nsec); + + set_normalized_timespec(&xtime, new_sec, new_nsec); + set_normalized_timespec(&wall_to_monotonic, wtm_sec, wtm_nsec); /* In case of a large backwards jump in time with NTP, we want the * clock to be updated as soon as the PLL is again in lock. @@ -484,6 +485,8 @@ xtime.tv_nsec = 0; last_rtc_update = xtime.tv_sec; + set_normalized_timespec(&wall_to_monotonic, + -xtime.tv_sec, -xtime.tv_nsec); write_sequnlock_irqrestore(&xtime_lock, flags); /* Not exact, but the timer interrupt takes care of this */ diff -Nru a/arch/s390/kernel/time.c b/arch/s390/kernel/time.c --- a/arch/s390/kernel/time.c Sat Jun 14 16:16:02 2003 +++ b/arch/s390/kernel/time.c Thu Jul 10 22:22:57 2003 @@ -104,6 +104,9 @@ int do_settimeofday(struct timespec *tv) { + time_t wtm_sec, sec = tv->tv_sec; + long wtm_nsec, nsec = tv->tv_nsec; + if ((unsigned long)tv->tv_nsec >= NSEC_PER_SEC) return -EINVAL; @@ -114,15 +117,14 @@ * Discover what correction gettimeofday * would have done, and then undo it! */ - tv->tv_nsec -= do_gettimeoffset() * 1000; + nsec -= do_gettimeoffset() * 1000; - while (tv->tv_nsec < 0) { - tv->tv_nsec += NSEC_PER_SEC; - tv->tv_sec--; - } + wtm_sec = wall_to_monotonic.tv_sec + (xtime.tv_sec - sec); + wtm_nsec = wall_to_monotonic.tv_nsec + (xtime.tv_nsec - nsec); + + set_normalized_timespec(&xtime, sec, nsec); + set_normalized_timespec(&wall_to_monotonic, wtm_sec, wtm_nsec); - xtime.tv_sec = tv->tv_sec; - xtime.tv_nsec = tv->tv_nsec; time_adjust = 0; /* stop active adjtime() */ time_status |= STA_UNSYNC; time_maxerror = NTP_PHASE_LIMIT; @@ -263,6 +265,8 @@ set_time_cc = init_timer_cc - 0x8126d60e46000000LL + (0x3c26700LL*1000000*4096); tod_to_timeval(set_time_cc, &xtime); + set_normalized_timespec(&wall_to_monotonic, + -xtime.tv_sec, -xtime.tv_nsec); /* request the 0x1004 external interrupt */ if (register_early_external_interrupt(0x1004, do_comparator_interrupt, diff -Nru a/arch/sh/kernel/time.c b/arch/sh/kernel/time.c --- a/arch/sh/kernel/time.c Sat Jun 28 05:01:28 2003 +++ b/arch/sh/kernel/time.c Thu Jul 10 22:22:57 2003 @@ -179,6 +179,9 @@ int do_settimeofday(struct timespec *tv) { + time_t wtm_sec, sec = tv->tv_sec; + long wtm_nsec, nsec = tv->tv_nsec; + if ((unsigned long)tv->tv_nsec >= NSEC_PER_SEC) return -EINVAL; @@ -189,16 +192,15 @@ * wall time. Discover what correction gettimeofday() would have * made, and then undo it! */ - tv->tv_nsec -= 1000 * (do_gettimeoffset() + - (jiffies - wall_jiffies) * (1000000 / HZ)); + nsec -= 1000 * (do_gettimeoffset() + + (jiffies - wall_jiffies) * (1000000 / HZ)); - while (tv->tv_nsec < 0) { - tv->tv_nsec += NSEC_PER_SEC; - tv->tv_sec--; - } + wtm_sec = wall_to_monotonic.tv_sec + (xtime.tv_sec - sec); + wtm_nsec = wall_to_monotonic.tv_nsec + (xtime.tv_nsec - nsec); + + set_normalized_timespec(&xtime, sec, nsec); + set_normalized_timespec(&wall_to_monotonic, wtm_sec, wtm_nsec); - xtime.tv_sec = tv->tv_sec; - xtime.tv_nsec = tv->tv_nsec; time_adjust = 0; /* stop active adjtime() */ time_status |= STA_UNSYNC; time_maxerror = NTP_PHASE_LIMIT; @@ -417,6 +419,9 @@ current_cpu_data.module_clock = timer_freq * 4; rtc_get_time(&xtime); + + set_normalized_timespec(&wall_to_monotonic, + -xtime.tv_sec, -xtime.tv_nsec); if (board_timer_setup) { board_timer_setup(&irq0); diff -Nru a/arch/sparc/kernel/time.c b/arch/sparc/kernel/time.c --- a/arch/sparc/kernel/time.c Tue Jun 17 15:46:44 2003 +++ b/arch/sparc/kernel/time.c Thu Jul 10 22:22:57 2003 @@ -408,9 +408,9 @@ mon = MSTK_REG_MONTH(mregs); year = MSTK_CVT_YEAR( MSTK_REG_YEAR(mregs) ); xtime.tv_sec = mktime(year, mon, day, hour, min, sec); - wall_to_monotonic.tv_sec = -xtime.tv_sec; xtime.tv_nsec = (INITIAL_JIFFIES % HZ) * (NSEC_PER_SEC / HZ); - wall_to_monotonic.tv_nsec = -xtime.tv_nsec; + set_normalized_timespec(&wall_to_monotonic, + -xtime.tv_sec, -xtime.tv_nsec); mregs->creg &= ~MSTK_CREG_READ; spin_unlock_irq(&mostek_lock); #ifdef CONFIG_SUN4 @@ -441,9 +441,9 @@ intersil_start(iregs); xtime.tv_sec = mktime(year, mon, day, hour, min, sec); - wall_to_monotonic.tv_sec = -xtime.tv_sec; xtime.tv_nsec = (INITIAL_JIFFIES % HZ) * (NSEC_PER_SEC / HZ); - wall_to_monotonic.tv_nsec = -xtime.tv_nsec; + set_normalized_timespec(&wall_to_monotonic, + -xtime.tv_sec, -xtime.tv_nsec); printk("%u/%u/%u %u:%u:%u\n",day,mon,year,hour,min,sec); } #endif @@ -512,6 +512,9 @@ static int sbus_do_settimeofday(struct timespec *tv) { + time_t wtm_sec, sec = tv->tv_sec; + long wtm_nsec, nsec = tv->tv_nsec; + if ((unsigned long)tv->tv_nsec >= NSEC_PER_SEC) return -EINVAL; @@ -521,28 +524,15 @@ * wall time. Discover what correction gettimeofday() would have * made, and then undo it! */ - tv->tv_nsec -= 1000 * (do_gettimeoffset() + + nsec -= 1000 * (do_gettimeoffset() + (jiffies - wall_jiffies) * (USEC_PER_SEC / HZ)); - while (tv->tv_nsec < 0) { - tv->tv_nsec += NSEC_PER_SEC; - tv->tv_sec--; - } - - wall_to_monotonic.tv_sec += xtime.tv_sec - tv->tv_sec; - wall_to_monotonic.tv_nsec += xtime.tv_nsec - tv->tv_nsec; + wtm_sec = wall_to_monotonic.tv_sec + (xtime.tv_sec - sec); + wtm_nsec = wall_to_monotonic.tv_nsec + (xtime.tv_nsec - nsec); - if (wall_to_monotonic.tv_nsec > NSEC_PER_SEC) { - wall_to_monotonic.tv_nsec -= NSEC_PER_SEC; - wall_to_monotonic.tv_sec++; - } - if (wall_to_monotonic.tv_nsec < 0) { - wall_to_monotonic.tv_nsec += NSEC_PER_SEC; - wall_to_monotonic.tv_sec--; - } + set_normalized_timespec(&xtime, sec, nsec); + set_normalized_timespec(&wall_to_monotonic, wtm_sec, wtm_nsec); - xtime.tv_sec = tv->tv_sec; - xtime.tv_nsec = tv->tv_nsec; time_adjust = 0; /* stop active adjtime() */ time_status |= STA_UNSYNC; time_maxerror = NTP_PHASE_LIMIT; diff -Nru a/arch/sparc64/kernel/time.c b/arch/sparc64/kernel/time.c --- a/arch/sparc64/kernel/time.c Tue Jun 17 14:51:27 2003 +++ b/arch/sparc64/kernel/time.c Thu Jul 10 22:22:57 2003 @@ -703,9 +703,9 @@ } xtime.tv_sec = mktime(year, mon, day, hour, min, sec); - wall_to_monotonic.tv_sec = -xtime.tv_sec; xtime.tv_nsec = (INITIAL_JIFFIES % HZ) * (NSEC_PER_SEC / HZ); - wall_to_monotonic.tv_nsec = -xtime.tv_nsec; + set_normalized_timespec(&wall_to_monotonic, + -xtime.tv_sec, -xtime.tv_nsec); if (mregs) { tmp = mostek_read(mregs + MOSTEK_CREG); @@ -743,9 +743,9 @@ (unsigned int) (long) &unix_tod); prom_feval(obp_gettod); xtime.tv_sec = unix_tod; - wall_to_monotonic.tv_sec = -xtime.tv_sec; xtime.tv_nsec = (INITIAL_JIFFIES % HZ) * (NSEC_PER_SEC / HZ); - wall_to_monotonic.tv_nsec = -xtime.tv_nsec; + set_normalized_timespec(&wall_to_monotonic, + -xtime.tv_sec, -xtime.tv_nsec); return; } @@ -1090,6 +1090,9 @@ int do_settimeofday(struct timespec *tv) { + time_t wtm_sec, sec = tv->tv_sec; + long wtm_nsec, nsec = tv->tv_nsec; + if ((unsigned long)tv->tv_nsec >= NSEC_PER_SEC) return -EINVAL; @@ -1103,28 +1106,15 @@ * wall time. Discover what correction gettimeofday() would have * made, and then undo it! */ - tv->tv_nsec -= do_gettimeoffset() * 1000; - tv->tv_nsec -= (jiffies - wall_jiffies) * (NSEC_PER_SEC / HZ); - - while (tv->tv_nsec < 0) { - tv->tv_nsec += NSEC_PER_SEC; - tv->tv_sec--; - } + nsec -= do_gettimeoffset() * 1000; + nsec -= (jiffies - wall_jiffies) * (NSEC_PER_SEC / HZ); - wall_to_monotonic.tv_sec += xtime.tv_sec - tv->tv_sec; - wall_to_monotonic.tv_nsec += xtime.tv_nsec - tv->tv_nsec; + wtm_sec = wall_to_monotonic.tv_sec + (xtime.tv_sec - sec); + wtm_nsec = wall_to_monotonic.tv_nsec + (xtime.tv_nsec - nsec); - if (wall_to_monotonic.tv_nsec > NSEC_PER_SEC) { - wall_to_monotonic.tv_nsec -= NSEC_PER_SEC; - wall_to_monotonic.tv_sec++; - } - if (wall_to_monotonic.tv_nsec < 0) { - wall_to_monotonic.tv_nsec += NSEC_PER_SEC; - wall_to_monotonic.tv_sec--; - } + set_normalized_timespec(&xtime, sec, nsec); + set_normalized_timespec(&wall_to_monotonic, wtm_sec, wtm_nsec); - xtime.tv_sec = tv->tv_sec; - xtime.tv_nsec = tv->tv_nsec; time_adjust = 0; /* stop active adjtime() */ time_status |= STA_UNSYNC; time_maxerror = NTP_PHASE_LIMIT; diff -Nru a/arch/v850/kernel/bug.c b/arch/v850/kernel/bug.c --- a/arch/v850/kernel/bug.c Mon Feb 17 18:41:09 2003 +++ b/arch/v850/kernel/bug.c Thu Jul 10 18:06:18 2003 @@ -17,6 +17,7 @@ #include #include +#include #include /* We should use __builtin_return_address, but it doesn't work in gcc-2.90 @@ -100,12 +101,21 @@ } } -void show_stack (unsigned long *sp) -{ - unsigned long end; - unsigned long addr = (unsigned long)sp; - - if (! addr) +/* + * TASK is a pointer to the task whose backtrace we want to see (or NULL + * for current task), SP is the stack pointer of the first frame that + * should be shown in the back trace (or NULL if the entire call-chain of + * the task should be shown). + */ +void show_stack (struct task_struct *task, unsigned long *sp) +{ + unsigned long addr, end; + + if (sp) + addr = (unsigned long)sp; + else if (task) + addr = task_sp (task); + else addr = stack_addr (); addr = addr & ~3; @@ -125,5 +135,5 @@ void dump_stack () { - show_stack (0); + show_stack (0, 0); } diff -Nru a/arch/v850/kernel/gbus_int.c b/arch/v850/kernel/gbus_int.c --- a/arch/v850/kernel/gbus_int.c Thu Apr 3 15:01:04 2003 +++ b/arch/v850/kernel/gbus_int.c Thu Jul 10 18:06:18 2003 @@ -1,8 +1,8 @@ /* * arch/v850/kernel/gbus_int.c -- Midas labs GBUS interrupt support * - * Copyright (C) 2001,02 NEC Corporation - * Copyright (C) 2001,02 Miles Bader + * Copyright (C) 2001,02,03 NEC Electronics Corporation + * Copyright (C) 2001,02,03 Miles Bader * * This file is subject to the terms and conditions of the GNU General * Public License. See the file COPYING in the main directory of this @@ -99,9 +99,11 @@ /* Handle a shared GINT interrupt by passing to the appropriate GBUS interrupt handler. */ -static void gbus_int_handle_irq (int irq, void *dev_id, struct pt_regs *regs) +static irqreturn_t gbus_int_handle_irq (int irq, void *dev_id, + struct pt_regs *regs) { unsigned w; + irqreturn_t rval = IRQ_NONE; unsigned gint = irq - IRQ_GINT (0); for (w = 0; w < GBUS_INT_NUM_WORDS; w++) { @@ -127,6 +129,7 @@ /* Recursively call handle_irq to handle it. */ handle_irq (irq, regs); + rval = IRQ_HANDLED; } while (status); } } @@ -136,6 +139,8 @@ still pending, and so result in another CPU interrupt. */ GBUS_INT_ENABLE (0, gint) &= ~0x1; GBUS_INT_ENABLE (0, gint) |= 0x1; + + return rval; } diff -Nru a/arch/v850/kernel/simcons.c b/arch/v850/kernel/simcons.c --- a/arch/v850/kernel/simcons.c Wed Jun 11 12:33:09 2003 +++ b/arch/v850/kernel/simcons.c Thu Jul 10 18:06:18 2003 @@ -30,7 +30,7 @@ V850_SIM_SYSCALL (write, 1, buf, len); } -static int simcons_read (struct console *co, const char *buf, unsigned len) +static int simcons_read (struct console *co, char *buf, unsigned len) { return V850_SIM_SYSCALL (read, 0, buf, len); } diff -Nru a/arch/v850/kernel/time.c b/arch/v850/kernel/time.c --- a/arch/v850/kernel/time.c Sat Jun 14 16:16:08 2003 +++ b/arch/v850/kernel/time.c Thu Jul 10 18:06:18 2003 @@ -51,7 +51,7 @@ * timer_interrupt() needs to keep up the real-time clock, * as well as call the "do_timer()" routine every clocktick */ -static void timer_interrupt (int irq, void *dummy, struct pt_regs *regs) +static irqreturn_t timer_interrupt (int irq, void *dummy, struct pt_regs *regs) { #if 0 /* last time the cmos clock got updated */ @@ -106,6 +106,8 @@ } #endif /* CONFIG_HEARTBEAT */ #endif /* 0 */ + + return IRQ_HANDLED; } /* diff -Nru a/arch/x86_64/Kconfig b/arch/x86_64/Kconfig --- a/arch/x86_64/Kconfig Tue Jun 24 22:21:45 2003 +++ b/arch/x86_64/Kconfig Thu Jul 10 22:23:14 2003 @@ -540,13 +540,6 @@ Fill __init and __initdata at the end of boot. This helps debugging illegal uses of __init and __initdata after initialization. -config KALLSYMS - bool "Load all symbols for debugging/kksymoops" - help - Say Y here to let the kernel print out symbolic crash information and - symbolic stack backtraces. This increases the size of the kernel - somewhat, as all symbols have to be loaded into the kernel image. - config FRAME_POINTER bool "Compile the kernel with frame pointers" help diff -Nru a/arch/x86_64/boot/setup.S b/arch/x86_64/boot/setup.S --- a/arch/x86_64/boot/setup.S Sat Mar 22 10:08:09 2003 +++ b/arch/x86_64/boot/setup.S Thu Jul 3 01:17:02 2003 @@ -350,6 +350,11 @@ sse_ok: popw %ds +# tell BIOS we want to go to long mode + movl $0xec00,%eax # declare target operating mode + movl $2,%ebx # long mode + int $0x15 + # Get memory size (extended mem, kB) xorl %eax, %eax diff -Nru a/arch/x86_64/ia32/ipc32.c b/arch/x86_64/ia32/ipc32.c --- a/arch/x86_64/ia32/ipc32.c Sat Mar 22 14:30:49 2003 +++ b/arch/x86_64/ia32/ipc32.c Sat Jul 5 07:01:09 2003 @@ -271,7 +271,7 @@ fourth.__pad = &s; old_fs = get_fs(); set_fs(KERNEL_DS); - err = sys_semctl(first, second, third|IPC_64, fourth); + err = sys_semctl(first, second, third, fourth); set_fs(old_fs); if (!err) err = put_semid((void *)A(pad), &s, version); @@ -396,7 +396,7 @@ case MSG_STAT: old_fs = get_fs(); set_fs(KERNEL_DS); - err = sys_msgctl(first, second|IPC_64, (void *) &m64); + err = sys_msgctl(first, second, (void *) &m64); set_fs(old_fs); if (version == IPC_64) { if (!access_ok(VERIFY_WRITE, up64, sizeof(*up64))) { @@ -529,7 +529,7 @@ case IPC_INFO: old_fs = get_fs(); set_fs(KERNEL_DS); - err = sys_shmctl(first, second|IPC_64, (struct shmid_ds *)&smi); + err = sys_shmctl(first, second, (struct shmid_ds *)&smi); set_fs(old_fs); if (version == IPC_64) { @@ -587,7 +587,7 @@ case SHM_STAT: old_fs = get_fs(); set_fs(KERNEL_DS); - err = sys_shmctl(first, second|IPC_64, (void *) &s64); + err = sys_shmctl(first, second, (void *) &s64); set_fs(old_fs); if (err < 0) diff -Nru a/arch/x86_64/kernel/time.c b/arch/x86_64/kernel/time.c --- a/arch/x86_64/kernel/time.c Tue Jun 24 06:31:58 2003 +++ b/arch/x86_64/kernel/time.c Thu Jul 10 22:22:57 2003 @@ -125,33 +125,22 @@ int do_settimeofday(struct timespec *tv) { + time_t wtm_sec, sec = tv->tv_sec; + long wtm_nsec, nsec = tv->tv_nsec; + if ((unsigned long)tv->tv_nsec >= NSEC_PER_SEC) return -EINVAL; write_seqlock_irq(&xtime_lock); - tv->tv_nsec -= do_gettimeoffset() * 1000 + + nsec -= do_gettimeoffset() * 1000 + (jiffies - wall_jiffies) * (NSEC_PER_SEC/HZ); - while (tv->tv_nsec < 0) { - tv->tv_nsec += NSEC_PER_SEC; - tv->tv_sec--; - } - - wall_to_monotonic.tv_sec += xtime.tv_sec - tv->tv_sec; - wall_to_monotonic.tv_nsec += xtime.tv_nsec - tv->tv_nsec; - - if (wall_to_monotonic.tv_nsec > NSEC_PER_SEC) { - wall_to_monotonic.tv_nsec -= NSEC_PER_SEC; - wall_to_monotonic.tv_sec++; - } - if (wall_to_monotonic.tv_nsec < 0) { - wall_to_monotonic.tv_nsec += NSEC_PER_SEC; - wall_to_monotonic.tv_sec--; - } + wtm_sec = wall_to_monotonic.tv_sec + (xtime.tv_sec - sec); + wtm_nsec = wall_to_monotonic.tv_nsec + (xtime.tv_nsec - nsec); - xtime.tv_sec = tv->tv_sec; - xtime.tv_nsec = tv->tv_nsec; + set_normalized_timespec(&xtime, sec, nsec); + set_normalized_timespec(&wall_to_monotonic, wtm_sec, wtm_nsec); time_adjust = 0; /* stop active adjtime() */ time_status |= STA_UNSYNC; @@ -603,8 +592,8 @@ xtime.tv_sec = get_cmos_time(); xtime.tv_nsec = 0; - wall_to_monotonic.tv_sec = -xtime.tv_sec; - wall_to_monotonic.tv_nsec = -xtime.tv_nsec; + set_normalized_timespec(&wall_to_monotonic, + -xtime.tv_sec, -xtime.tv_nsec); if (!hpet_init()) { vxtime_hz = (1000000000000000L + hpet_period / 2) / diff -Nru a/arch/x86_64/kernel/x8664_ksyms.c b/arch/x86_64/kernel/x8664_ksyms.c --- a/arch/x86_64/kernel/x8664_ksyms.c Tue Jul 1 06:40:21 2003 +++ b/arch/x86_64/kernel/x8664_ksyms.c Thu Jul 3 10:14:29 2003 @@ -214,5 +214,7 @@ EXPORT_SYMBOL(clear_page); +#ifdef CONFIG_SMP EXPORT_SYMBOL(flush_tlb_page); EXPORT_SYMBOL_GPL(flush_tlb_all); +#endif diff -Nru a/drivers/block/Kconfig b/drivers/block/Kconfig --- a/drivers/block/Kconfig Sun Apr 20 16:21:17 2003 +++ b/drivers/block/Kconfig Thu Jul 10 17:46:23 2003 @@ -237,29 +237,20 @@ root file system inside a DOS FAT file using this loop device driver. - The loop device driver can also be used to "hide" a file system in a - disk partition, floppy, or regular file, either using encryption + To use the loop device, you need the losetup utility, found in the + util-linux package, see + . + + The loop device driver can also be used to "hide" a file system in + a disk partition, floppy, or regular file, either using encryption (scrambling the data) or steganography (hiding the data in the low bits of, say, a sound file). This is also safe if the file resides - on a remote file server. If you want to do this, you will first have - to acquire and install a kernel patch from - , and then you need to - say Y to this option. - - Note that alternative ways to use encrypted file systems are - provided by the cfs package, which can be gotten from - , and the newer tcfs - package, available at . You do not need - to say Y here if you want to use one of these. However, using cfs - requires saying Y to "NFS file system support" below while using - tcfs requires applying a kernel patch. An alternative steganography - solution is provided by StegFS, also available from - . - - To use the loop device, you need the losetup utility and a recent - version of the mount program, both contained in the util-linux - package. The location and current version number of util-linux is - contained in the file . + on a remote file server. + + There are several ways of doing this. Some of these require kernel + patches. The vanilla kernel offers the cryptoloop option. If you + want to use that, say Y to both LOOP and CRYPTOLOOP, and make sure + you have a recent (version 2.12 or later) version of util-linux. Note that this loop device has nothing to do with the loopback device used for network connections from the machine to itself. @@ -270,6 +261,14 @@ will be called loop. Most users will answer N here. + +config BLK_DEV_CRYPTOLOOP + tristate "Cryptoloop Support" + depends on BLK_DEV_LOOP + ---help--- + Say Y here if you want to be able to use the ciphers that are + provided by the CryptoAPI as loop transformation. This might be + used as hard disk encryption. config BLK_DEV_NBD tristate "Network block device support" diff -Nru a/drivers/block/Makefile b/drivers/block/Makefile --- a/drivers/block/Makefile Fri Jul 4 23:52:37 2003 +++ b/drivers/block/Makefile Thu Jul 10 17:46:23 2003 @@ -36,3 +36,4 @@ obj-$(CONFIG_BLK_DEV_UMEM) += umem.o obj-$(CONFIG_BLK_DEV_NBD) += nbd.o +obj-$(CONFIG_BLK_DEV_CRYPTOLOOP) += cryptoloop.o diff -Nru a/drivers/block/as-iosched.c b/drivers/block/as-iosched.c --- a/drivers/block/as-iosched.c Fri Jul 4 23:52:51 2003 +++ b/drivers/block/as-iosched.c Sat Jul 12 04:28:01 2003 @@ -837,7 +837,7 @@ aic->seek_total += 256*seek_dist; if (aic->seek_samples) { aic->seek_mean = aic->seek_total + 128; - do_div(aic->seek_mean, aic->seek_samples); + sector_div(aic->seek_mean, aic->seek_samples); } aic->seek_samples = (aic->seek_samples>>1) + (aic->seek_samples>>2); diff -Nru a/drivers/block/cryptoloop.c b/drivers/block/cryptoloop.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/block/cryptoloop.c Thu Jul 10 17:46:23 2003 @@ -0,0 +1,182 @@ +/* + Linux loop encryption enabling module + + Copyright (C) 2002 Herbert Valerio Riedel + Copyright (C) 2003 Fruhwirth Clemens + + This module 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 module 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 module; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("loop blockdevice transferfunction adaptor / CryptoAPI"); +MODULE_AUTHOR("Herbert Valerio Riedel "); + +#define LOOP_IV_SECTOR_BITS 9 +#define LOOP_IV_SECTOR_SIZE (1 << LOOP_IV_SECTOR_BITS) + +static int +cryptoloop_init(struct loop_device *lo, const struct loop_info64 *info) +{ + int err = -EINVAL; + char cms[LO_NAME_SIZE]; /* cipher-mode string */ + char *cipher; + char *mode; + char *cmsp = cms; /* c-m string pointer */ + struct crypto_tfm *tfm = NULL; + + /* encryption breaks for non sector aligned offsets */ + + if (info->lo_offset % LOOP_IV_SECTOR_SIZE) + goto out; + + strncpy(cms, info->lo_crypt_name, LO_NAME_SIZE); + cms[LO_NAME_SIZE - 1] = 0; + cipher = strsep(&cmsp, "-"); + mode = strsep(&cmsp, "-"); + + if (mode == NULL || strcmp(mode, "cbc") == 0) + tfm = crypto_alloc_tfm(cipher, CRYPTO_TFM_MODE_CBC); + else if (strcmp(mode, "ecb") == 0) + tfm = crypto_alloc_tfm(cipher, CRYPTO_TFM_MODE_ECB); + if (tfm == NULL) + return -EINVAL; + + err = tfm->crt_u.cipher.cit_setkey(tfm, info->lo_encrypt_key, + info->lo_encrypt_key_size); + + if (err != 0) + goto out_free_tfm; + + lo->key_data = tfm; + return 0; + + out_free_tfm: + crypto_free_tfm(tfm); + + out: + return err; +} + +typedef int (*encdec_t)(struct crypto_tfm *tfm, + struct scatterlist *sg_out, + struct scatterlist *sg_in, + unsigned int nsg, u8 *iv); + +static int +cryptoloop_transfer(struct loop_device *lo, int cmd, char *raw_buf, + char *loop_buf, int size, sector_t IV) +{ + struct crypto_tfm *tfm = (struct crypto_tfm *) lo->key_data; + struct scatterlist sg_out = { 0, }; + struct scatterlist sg_in = { 0, }; + + encdec_t encdecfunc; + char const *in; + char *out; + + if (cmd == READ) { + in = raw_buf; + out = loop_buf; + encdecfunc = tfm->crt_u.cipher.cit_decrypt_iv; + } else { + in = loop_buf; + out = raw_buf; + encdecfunc = tfm->crt_u.cipher.cit_encrypt_iv; + } + + while (size > 0) { + const int sz = min(size, LOOP_IV_SECTOR_SIZE); + u32 iv[4] = { 0, }; + iv[0] = cpu_to_le32(IV & 0xffffffff); + + sg_in.page = virt_to_page(in); + sg_in.offset = (unsigned long)in & ~PAGE_MASK; + sg_in.length = sz; + + sg_out.page = virt_to_page(out); + sg_out.offset = (unsigned long)out & ~PAGE_MASK; + sg_out.length = sz; + + encdecfunc(tfm, &sg_out, &sg_in, sz, (u8 *)iv); + + IV++; + size -= sz; + in += sz; + out += sz; + } + + return 0; +} + + +static int +cryptoloop_ioctl(struct loop_device *lo, int cmd, unsigned long arg) +{ + return -EINVAL; +} + +static int +cryptoloop_release(struct loop_device *lo) +{ + struct crypto_tfm *tfm = (struct crypto_tfm *) lo->key_data; + if (tfm != NULL) { + crypto_free_tfm(tfm); + lo->key_data = NULL; + return 0; + } + printk(KERN_ERR "cryptoloop_release(): tfm == NULL?\n"); + return -EINVAL; +} + +static struct loop_func_table cryptoloop_funcs = { + .number = LO_CRYPT_CRYPTOAPI, + .init = cryptoloop_init, + .ioctl = cryptoloop_ioctl, + .transfer = cryptoloop_transfer, + .release = cryptoloop_release, + .owner = THIS_MODULE +}; + +static int __init +init_cryptoloop(void) +{ + int rc = loop_register_transfer(&cryptoloop_funcs); + + if (rc) + printk(KERN_ERR "cryptoloop: loop_register_transfer failed\n"); + return rc; +} + +static void __exit +cleanup_cryptoloop(void) +{ + if (loop_unregister_transfer(LO_CRYPT_CRYPTOAPI)) + printk(KERN_ERR + "cryptoloop: loop_unregister_transfer failed\n"); +} + +module_init(init_cryptoloop); +module_exit(cleanup_cryptoloop); diff -Nru a/drivers/block/floppy98.c b/drivers/block/floppy98.c --- a/drivers/block/floppy98.c Sun May 25 02:45:06 2003 +++ b/drivers/block/floppy98.c Fri Jul 11 06:33:22 2003 @@ -4064,21 +4064,32 @@ static char *table[] = {"", #if 0 -"d360", + "d360", #else -"h1232", + "h1232", #endif -"h1200", "u360", "u720", "h360", "h720", -"u1440", "u2880", "CompaQ", "h1440", "u1680", "h410", -"u820", "h1476", "u1722", "h420", "u830", "h1494", "u1743", -"h880", "u1040", "u1120", "h1600", "u1760", "u1920", -"u3200", "u3520", "u3840", "u1840", "u800", "u1600", + "h1200", "u360", "u720", "h360", "h720", + "u1440", "u2880", "CompaQ", "h1440", "u1680", "h410", + "u820", "h1476", "u1722", "h420", "u830", "h1494", "u1743", + "h880", "u1040", "u1120", "h1600", "u1760", "u1920", + "u3200", "u3520", "u3840", "u1840", "u800", "u1600", NULL }; -static int t360[] = {1,0}, t1200[] = {2,5,6,10,12,14,16,18,20,23,0}, -t3in[] = {8,9,26,27,28, 7,11,15,19,24,25,29,31, 3,4,13,17,21,22,30,0}; -static int *table_sup[] = -{NULL, t360, t1200, t3in+5+8, t3in+5, t3in, t3in}; + +static int t360[] = { + 1,0 +}; +static int t1200[] = { + 2,5,6,10,12,14,16,18,20,23,0 +}; +static int t3in[] = { + 8, 9,26,27,28, 7,11,15,19,24,25, + 29,31, 3, 4,13,17,21,22,30, 0 +}; + +static int *table_sup[] = { + NULL, t360, t1200, t3in+5+8, t3in+5, t3in, t3in +}; static void __init register_devfs_entries (int drive) { @@ -4402,7 +4413,7 @@ return 0; out1: - del_timer(&fd_timeout); + del_timer_sync(&fd_timeout); out2: blk_unregister_region(MKDEV(FLOPPY_MAJOR, 0), 256); unregister_blkdev(FLOPPY_MAJOR,"fd"); @@ -4430,11 +4441,9 @@ return 0; } spin_unlock_irqrestore(&floppy_usage_lock, flags); - MOD_INC_USE_COUNT; if (fd_request_irq()) { DPRINT("Unable to grab IRQ%d for the floppy driver\n", FLOPPY_IRQ); - MOD_DEC_USE_COUNT; spin_lock_irqsave(&floppy_usage_lock, flags); usage_count--; spin_unlock_irqrestore(&floppy_usage_lock, flags); @@ -4444,7 +4453,6 @@ DPRINT("Unable to grab DMA%d for the floppy driver\n", FLOPPY_DMA); fd_free_irq(); - MOD_DEC_USE_COUNT; spin_lock_irqsave(&floppy_usage_lock, flags); usage_count--; spin_unlock_irqrestore(&floppy_usage_lock, flags); @@ -4521,7 +4529,6 @@ release_region(0x04be, 1); } } - MOD_DEC_USE_COUNT; spin_lock_irqsave(&floppy_usage_lock, flags); usage_count--; spin_unlock_irqrestore(&floppy_usage_lock, flags); @@ -4587,7 +4594,6 @@ } } fdc = old_fdc; - MOD_DEC_USE_COUNT; } diff -Nru a/drivers/block/ll_rw_blk.c b/drivers/block/ll_rw_blk.c --- a/drivers/block/ll_rw_blk.c Fri Jul 4 23:52:53 2003 +++ b/drivers/block/ll_rw_blk.c Fri Jul 11 01:27:41 2003 @@ -2354,7 +2354,7 @@ struct gendisk *disk = req->rq_disk; struct completion *waiting = req->waiting; - if (disk) { + if (disk && blk_fs_request(req)) { unsigned long duration = jiffies - req->start_time; switch (rq_data_dir(req)) { case WRITE: diff -Nru a/drivers/block/loop.c b/drivers/block/loop.c --- a/drivers/block/loop.c Tue Jun 24 13:10:43 2003 +++ b/drivers/block/loop.c Thu Jul 10 22:22:59 2003 @@ -140,7 +140,8 @@ sector_t x; /* Compute loopsize in bytes */ - size = lo->lo_backing_file->f_dentry->d_inode->i_mapping->host->i_size; + size = i_size_read(lo->lo_backing_file->f_dentry-> + d_inode->i_mapping->host); offset = lo->lo_offset; loopsize = size - offset; if (lo->lo_sizelimit > 0 && lo->lo_sizelimit < loopsize) @@ -840,7 +841,8 @@ lo->lo_flags = 0; lo->lo_queue.queuedata = NULL; memset(lo->lo_encrypt_key, 0, LO_KEY_SIZE); - memset(lo->lo_name, 0, LO_NAME_SIZE); + memset(lo->lo_crypt_name, 0, LO_NAME_SIZE); + memset(lo->lo_file_name, 0, LO_NAME_SIZE); invalidate_bdev(bdev, 0); set_capacity(disks[lo->lo_number], 0); filp->f_dentry->d_inode->i_mapping->gfp_mask = gfp; @@ -892,7 +894,10 @@ return -EFBIG; } - strlcpy(lo->lo_name, info->lo_name, LO_NAME_SIZE); + memcpy(lo->lo_file_name, info->lo_file_name, LO_NAME_SIZE); + memcpy(lo->lo_crypt_name, info->lo_crypt_name, LO_NAME_SIZE); + lo->lo_file_name[LO_NAME_SIZE-1] = 0; + lo->lo_crypt_name[LO_NAME_SIZE-1] = 0; if (!xfer) xfer = &none_funcs; @@ -931,7 +936,8 @@ info->lo_offset = lo->lo_offset; info->lo_sizelimit = lo->lo_sizelimit; info->lo_flags = lo->lo_flags; - strlcpy(info->lo_name, lo->lo_name, LO_NAME_SIZE); + memcpy(info->lo_file_name, lo->lo_file_name, LO_NAME_SIZE); + memcpy(info->lo_crypt_name, lo->lo_crypt_name, LO_NAME_SIZE); info->lo_encrypt_type = lo->lo_encryption ? lo->lo_encryption->number : 0; if (lo->lo_encrypt_key_size && capable(CAP_SYS_ADMIN)) { @@ -957,7 +963,10 @@ info64->lo_flags = info->lo_flags; info64->lo_init[0] = info->lo_init[0]; info64->lo_init[1] = info->lo_init[1]; - memcpy(info64->lo_name, info->lo_name, LO_NAME_SIZE); + if (info->lo_encrypt_type == LO_CRYPT_CRYPTOAPI) + memcpy(info64->lo_crypt_name, info->lo_name, LO_NAME_SIZE); + else + memcpy(info64->lo_file_name, info->lo_name, LO_NAME_SIZE); memcpy(info64->lo_encrypt_key, info->lo_encrypt_key, LO_KEY_SIZE); } @@ -975,8 +984,11 @@ info->lo_flags = info64->lo_flags; info->lo_init[0] = info64->lo_init[0]; info->lo_init[1] = info64->lo_init[1]; - memcpy(info->lo_name, info64->lo_name, LO_NAME_SIZE); - memcpy(info->lo_encrypt_key,info64->lo_encrypt_key,LO_KEY_SIZE); + if (info->lo_encrypt_type == LO_CRYPT_CRYPTOAPI) + memcpy(info->lo_name, info64->lo_crypt_name, LO_NAME_SIZE); + else + memcpy(info->lo_name, info64->lo_file_name, LO_NAME_SIZE); + memcpy(info->lo_encrypt_key, info64->lo_encrypt_key, LO_KEY_SIZE); /* error in case values were truncated */ if (info->lo_device != info64->lo_device || diff -Nru a/drivers/block/nbd.c b/drivers/block/nbd.c --- a/drivers/block/nbd.c Sun Jun 22 18:13:08 2003 +++ b/drivers/block/nbd.c Thu Jul 10 22:23:53 2003 @@ -31,13 +31,17 @@ * 03-06-22 Make nbd work with new linux 2.5 block layer design. This fixes * memory corruption from module removal and possible memory corruption * from sending/receiving disk data. + * 03-06-23 Cosmetic changes. + * 03-06-23 Enhance diagnostics support. + * 03-06-24 Remove unneeded blksize_bits field from nbd_device struct. + * + * 03-06-24 Cleanup PARANOIA usage & code. * * possible FIXME: make set_sock / set_blksize / set_size / do_it one syscall * why not: would need verify_area and friends, would share yet another * structure with userland */ -#define PARANOIA #include #include @@ -60,9 +64,27 @@ #include #include +/* Define PARANOIA in linux/nbd.h to turn on extra sanity checking */ #include +#ifdef PARANOIA #define LO_MAGIC 0x68797548 +#endif + +#ifdef NDEBUG +#define dprintk(flags, fmt...) +#else /* NDEBUG */ +#define dprintk(flags, fmt...) do { \ + if (debugflags & (flags)) printk(KERN_DEBUG fmt); \ +} while (0) +#define DBG_IOCTL 0x0004 +#define DBG_INIT 0x0010 +#define DBG_EXIT 0x0020 +#define DBG_BLKDEV 0x0100 +#define DBG_RX 0x0200 +#define DBG_TX 0x0400 +static unsigned int debugflags; +#endif /* NDEBUG */ static struct nbd_device nbd_dev[MAX_NBD]; @@ -78,12 +100,40 @@ */ static spinlock_t nbd_lock = SPIN_LOCK_UNLOCKED; -#define DEBUG( s ) -/* #define DEBUG( s ) printk( s ) - */ - +#ifdef PARANOIA static int requests_in; static int requests_out; +#endif + +#ifndef NDEBUG +static const char *ioctl_cmd_to_ascii(int cmd) +{ + switch (cmd) { + case NBD_SET_SOCK: return "set-sock"; + case NBD_SET_BLKSIZE: return "set-blksize"; + case NBD_SET_SIZE: return "set-size"; + case NBD_DO_IT: return "do-it"; + case NBD_CLEAR_SOCK: return "clear-sock"; + case NBD_CLEAR_QUE: return "clear-que"; + case NBD_PRINT_DEBUG: return "print-debug"; + case NBD_SET_SIZE_BLOCKS: return "set-size-blocks"; + case NBD_DISCONNECT: return "disconnect"; + case BLKROSET: return "set-read-only"; + case BLKFLSBUF: return "flush-buffer-cache"; + } + return "unknown"; +} + +static const char *nbdcmd_to_ascii(int cmd) +{ + switch (cmd) { + case NBD_CMD_READ: return "read"; + case NBD_CMD_WRITE: return "write"; + case NBD_CMD_DISC: return "disconnect"; + } + return "invalid"; +} +#endif /* NDEBUG */ static void nbd_end_request(struct request *req) { @@ -91,6 +141,8 @@ request_queue_t *q = req->q; unsigned long flags; + dprintk(DBG_BLKDEV, "%s: request %p: %s\n", req->rq_disk->disk_name, + req, uptodate? "done": "failed"); #ifdef PARANOIA requests_out++; #endif @@ -101,17 +153,11 @@ spin_unlock_irqrestore(q->queue_lock, flags); } -static int nbd_open(struct inode *inode, struct file *file) -{ - struct nbd_device *lo = inode->i_bdev->bd_disk->private_data; - lo->refcnt++; - return 0; -} - /* * Send or receive packet. */ -static int nbd_xmit(int send, struct socket *sock, char *buf, int size, int msg_flags) +static int sock_xmit(struct socket *sock, int send, void *buf, int size, + int msg_flags) { mm_segment_t oldfs; int result; @@ -131,7 +177,6 @@ recalc_sigpending(); spin_unlock_irqrestore(¤t->sighand->siglock, flags); - do { sock->sk->sk_allocation = GFP_NOIO; iov.iov_base = buf; @@ -153,7 +198,7 @@ if (signal_pending(current)) { siginfo_t info; spin_lock_irqsave(¤t->sighand->siglock, flags); - printk(KERN_WARNING "NBD (pid %d: %s) got signal %d\n", + printk(KERN_WARNING "nbd (pid %d: %s) got signal %d\n", current->pid, current->comm, dequeue_signal(current, ¤t->blocked, &info)); spin_unlock_irqrestore(¤t->sighand->siglock, flags); @@ -163,8 +208,8 @@ if (result <= 0) { #ifdef PARANOIA - printk(KERN_ERR "NBD: %s - sock=%ld at buf=%ld, size=%d returned %d.\n", - send ? "send" : "receive", (long) sock, (long) buf, size, result); + printk(KERN_ERR "nbd: %s - sock=%p at buf=%p, size=%d returned %d.\n", + send? "send": "receive", sock, buf, size, result); #endif break; } @@ -186,14 +231,12 @@ { int result; void *kaddr = kmap(bvec->bv_page); - result = nbd_xmit(1, sock, kaddr + bvec->bv_offset, bvec->bv_len, + result = sock_xmit(sock, 1, kaddr + bvec->bv_offset, bvec->bv_len, flags); kunmap(bvec->bv_page); return result; } -#define FAIL( s ) { printk( KERN_ERR "NBD: " s "(result %d)\n", result ); goto error_out; } - void nbd_send_req(struct nbd_device *lo, struct request *req) { int result, i, flags; @@ -201,24 +244,31 @@ unsigned long size = req->nr_sectors << 9; struct socket *sock = lo->sock; - DEBUG("NBD: sending control, "); - request.magic = htonl(NBD_REQUEST_MAGIC); request.type = htonl(nbd_cmd(req)); - request.from = cpu_to_be64( (u64) req->sector << 9); + request.from = cpu_to_be64((u64) req->sector << 9); request.len = htonl(size); memcpy(request.handle, &req, sizeof(req)); down(&lo->tx_lock); if (!sock || !lo->sock) { - printk(KERN_ERR "NBD: Attempted sendmsg to closed socket\n"); + printk(KERN_ERR "%s: Attempted send on closed socket\n", + lo->disk->disk_name); goto error_out; } - result = nbd_xmit(1, sock, (char *) &request, sizeof(request), nbd_cmd(req) == NBD_CMD_WRITE ? MSG_MORE : 0); - if (result <= 0) - FAIL("Sendmsg failed for control."); + dprintk(DBG_TX, "%s: request %p: sending control (%s@%llu,%luB)\n", + lo->disk->disk_name, req, + nbdcmd_to_ascii(nbd_cmd(req)), + req->sector << 9, req->nr_sectors << 9); + result = sock_xmit(sock, 1, &request, sizeof(request), + (nbd_cmd(req) == NBD_CMD_WRITE)? MSG_MORE: 0); + if (result <= 0) { + printk(KERN_ERR "%s: Send control failed (result %d)\n", + lo->disk->disk_name, result); + goto error_out; + } if (nbd_cmd(req) == NBD_CMD_WRITE) { struct bio *bio; @@ -232,10 +282,16 @@ flags = 0; if ((i < (bio->bi_vcnt - 1)) || bio->bi_next) flags = MSG_MORE; - DEBUG("data, "); + dprintk(DBG_TX, "%s: request %p: sending %d bytes data\n", + lo->disk->disk_name, req, + bvec->bv_len); result = sock_send_bvec(sock, bvec, flags); - if (result <= 0) - FAIL("Send data failed."); + if (result <= 0) { + printk(KERN_ERR "%s: Send data failed (result %d)\n", + lo->disk->disk_name, + result); + goto error_out; + } } } } @@ -272,55 +328,71 @@ { int result; void *kaddr = kmap(bvec->bv_page); - result = nbd_xmit(0, sock, kaddr + bvec->bv_offset, bvec->bv_len, + result = sock_xmit(sock, 0, kaddr + bvec->bv_offset, bvec->bv_len, MSG_WAITALL); kunmap(bvec->bv_page); return result; } -#define HARDFAIL( s ) { printk( KERN_ERR "NBD: " s "(result %d)\n", result ); lo->harderror = result; return NULL; } +/* NULL returned = something went wrong, inform userspace */ struct request *nbd_read_stat(struct nbd_device *lo) - /* NULL returned = something went wrong, inform userspace */ { int result; struct nbd_reply reply; struct request *req; struct socket *sock = lo->sock; - DEBUG("reading control, "); reply.magic = 0; - result = nbd_xmit(0, sock, (char *) &reply, sizeof(reply), MSG_WAITALL); - if (result <= 0) - HARDFAIL("Recv control failed."); + result = sock_xmit(sock, 0, &reply, sizeof(reply), MSG_WAITALL); + if (result <= 0) { + printk(KERN_ERR "%s: Receive control failed (result %d)\n", + lo->disk->disk_name, result); + lo->harderror = result; + return NULL; + } req = nbd_find_request(lo, reply.handle); - if (req == NULL) - HARDFAIL("Unexpected reply"); - - DEBUG("ok, "); - if (ntohl(reply.magic) != NBD_REPLY_MAGIC) - HARDFAIL("Not enough magic."); - if (ntohl(reply.error)) - FAIL("Other side returned error."); + if (req == NULL) { + printk(KERN_ERR "%s: Unexpected reply (%p)\n", + lo->disk->disk_name, reply.handle); + lo->harderror = result; + return NULL; + } + + if (ntohl(reply.magic) != NBD_REPLY_MAGIC) { + printk(KERN_ERR "%s: Wrong magic (0x%lx)\n", + lo->disk->disk_name, + (unsigned long)ntohl(reply.magic)); + lo->harderror = result; + return NULL; + } + if (ntohl(reply.error)) { + printk(KERN_ERR "%s: Other side returned error (%d)\n", + lo->disk->disk_name, ntohl(reply.error)); + req->errors++; + return req; + } + dprintk(DBG_RX, "%s: request %p: got reply\n", + lo->disk->disk_name, req); if (nbd_cmd(req) == NBD_CMD_READ) { int i; struct bio *bio; - DEBUG("data, "); rq_for_each_bio(bio, req) { struct bio_vec *bvec; bio_for_each_segment(bvec, bio, i) { result = sock_recv_bvec(sock, bvec); - if (result <= 0) - HARDFAIL("Recv data failed."); + if (result <= 0) { + printk(KERN_ERR "%s: Receive data failed (result %d)\n", + lo->disk->disk_name, + result); + lo->harderror = result; + return NULL; + } + dprintk(DBG_RX, "%s: request %p: got %d bytes data\n", + lo->disk->disk_name, req, bvec->bv_len); } } } - DEBUG("done.\n"); - return req; - -/* Can we get here? Yes, if other side returns error */ - error_out: - req->errors++; return req; } @@ -328,17 +400,13 @@ { struct request *req; - while (1) { - req = nbd_read_stat(lo); - - if (!req) { - printk(KERN_ALERT "req should never be null\n" ); - goto out; - } - BUG_ON(lo->magic != LO_MAGIC); +#ifdef PARANOIA + BUG_ON(lo->magic != LO_MAGIC); +#endif + while ((req = nbd_read_stat(lo)) != NULL) nbd_end_request(req); - } - out: + printk(KERN_NOTICE "%s: req should never be null\n", + lo->disk->disk_name); return; } @@ -346,7 +414,9 @@ { struct request *req; +#ifdef PARANOIA BUG_ON(lo->magic != LO_MAGIC); +#endif do { req = NULL; @@ -360,7 +430,7 @@ req->errors++; nbd_end_request(req); } - } while(req); + } while (req); } /* @@ -370,9 +440,6 @@ * { printk( "Warning: Ignoring result!\n"); nbd_end_request( req ); } */ -#undef FAIL -#define FAIL( s ) { printk( KERN_ERR "%s: " s "\n", req->rq_disk->disk_name ); goto error_out; } - static void do_nbd_request(request_queue_t * q) { struct request *req; @@ -380,30 +447,44 @@ while ((req = elv_next_request(q)) != NULL) { struct nbd_device *lo; + blkdev_dequeue_request(req); + dprintk(DBG_BLKDEV, "%s: request %p: dequeued (flags=%lx)\n", + req->rq_disk->disk_name, req, req->flags); + if (!(req->flags & REQ_CMD)) goto error_out; lo = req->rq_disk->private_data; - if (!lo->file) - FAIL("Request when not-ready."); +#ifdef PARANOIA + BUG_ON(lo->magic != LO_MAGIC); +#endif + if (!lo->file) { + printk(KERN_ERR "%s: Request when not-ready\n", + lo->disk->disk_name); + goto error_out; + } nbd_cmd(req) = NBD_CMD_READ; if (rq_data_dir(req) == WRITE) { nbd_cmd(req) = NBD_CMD_WRITE; - if (lo->flags & NBD_READ_ONLY) - FAIL("Write on read-only"); + if (lo->flags & NBD_READ_ONLY) { + printk(KERN_ERR "%s: Write on read-only\n", + lo->disk->disk_name); + goto error_out; + } } - BUG_ON(lo->magic != LO_MAGIC); +#ifdef PARANOIA requests_in++; +#endif req->errors = 0; - blkdev_dequeue_request(req); spin_unlock_irq(q->queue_lock); spin_lock(&lo->queue_lock); if (!lo->file) { spin_unlock(&lo->queue_lock); - printk(KERN_ERR "nbd: failed between accept and semaphore, file lost\n"); + printk(KERN_ERR "%s: failed between accept and semaphore, file lost\n", + lo->disk->disk_name); req->errors++; nbd_end_request(req); spin_lock_irq(q->queue_lock); @@ -416,7 +497,8 @@ nbd_send_req(lo, req); if (req->errors) { - printk(KERN_ERR "nbd: nbd_send_req failed\n"); + printk(KERN_ERR "%s: Request send failed\n", + lo->disk->disk_name); spin_lock(&lo->queue_lock); list_del_init(&req->queuelist); spin_unlock(&lo->queue_lock); @@ -430,7 +512,6 @@ error_out: req->errors++; - blkdev_dequeue_request(req); spin_unlock(q->queue_lock); nbd_end_request(req); spin_lock(q->queue_lock); @@ -442,18 +523,30 @@ unsigned int cmd, unsigned long arg) { struct nbd_device *lo = inode->i_bdev->bd_disk->private_data; - int error, temp; + int error; struct request sreq ; - /* Anyone capable of this syscall can do *real bad* things */ - if (!capable(CAP_SYS_ADMIN)) return -EPERM; +#ifdef PARANOIA + BUG_ON(lo->magic != LO_MAGIC); +#endif + /* Anyone capable of this syscall can do *real bad* things */ + dprintk(DBG_IOCTL, "%s: nbd_ioctl cmd=%s(0x%x) arg=%lu\n", + lo->disk->disk_name, ioctl_cmd_to_ascii(cmd), cmd, arg); + switch (cmd) { case NBD_DISCONNECT: - printk(KERN_INFO "NBD_DISCONNECT\n"); + printk(KERN_INFO "%s: NBD_DISCONNECT\n", lo->disk->disk_name); sreq.flags = REQ_SPECIAL; nbd_cmd(&sreq) = NBD_CMD_DISC; + /* + * Set these to sane values in case server implementation + * fails to check the request type first and also to keep + * debugging output cleaner. + */ + sreq.sector = 0; + sreq.nr_sectors = 0; if (!lo->sock) return -EINVAL; nbd_send_req(lo, &sreq); @@ -464,7 +557,8 @@ spin_lock(&lo->queue_lock); if (!list_empty(&lo->queue_head)) { spin_unlock(&lo->queue_lock); - printk(KERN_ERR "nbd: Some requests are in progress -> can not turn off.\n"); + printk(KERN_ERR "%s: Some requests are in progress -> can not turn off.\n", + lo->disk->disk_name); return -EBUSY; } file = lo->file; @@ -494,24 +588,22 @@ } return error; case NBD_SET_BLKSIZE: - if ((arg & (arg-1)) || (arg < 512) || (arg > PAGE_SIZE)) - return -EINVAL; lo->blksize = arg; - temp = arg >> 9; - lo->blksize_bits = 9; - while (temp > 1) { - lo->blksize_bits++; - temp >>= 1; - } - lo->bytesize &= ~(lo->blksize-1); + lo->bytesize &= ~(lo->blksize-1); + inode->i_bdev->bd_inode->i_size = lo->bytesize; + set_blocksize(inode->i_bdev, lo->blksize); set_capacity(lo->disk, lo->bytesize >> 9); return 0; case NBD_SET_SIZE: - lo->bytesize = arg & ~(lo->blksize-1); + lo->bytesize = arg & ~(lo->blksize-1); + inode->i_bdev->bd_inode->i_size = lo->bytesize; + set_blocksize(inode->i_bdev, lo->blksize); set_capacity(lo->disk, lo->bytesize >> 9); return 0; case NBD_SET_SIZE_BLOCKS: - lo->bytesize = ((u64) arg) << lo->blksize_bits; + lo->bytesize = ((u64) arg) * lo->blksize; + inode->i_bdev->bd_inode->i_size = lo->bytesize; + set_blocksize(inode->i_bdev, lo->blksize); set_capacity(lo->disk, lo->bytesize >> 9); return 0; case NBD_DO_IT: @@ -526,7 +618,8 @@ * there should be a more generic interface rather than * calling socket ops directly here */ down(&lo->tx_lock); - printk(KERN_WARNING "nbd: shutting down socket\n"); + printk(KERN_WARNING "%s: shutting down socket\n", + lo->disk->disk_name); lo->sock->ops->shutdown(lo->sock, SEND_SHUTDOWN|RCV_SHUTDOWN); lo->sock = NULL; up(&lo->tx_lock); @@ -535,39 +628,31 @@ lo->file = NULL; spin_unlock(&lo->queue_lock); nbd_clear_que(lo); - printk(KERN_WARNING "nbd: queue cleared\n"); + printk(KERN_WARNING "%s: queue cleared\n", lo->disk->disk_name); if (file) fput(file); return lo->harderror; case NBD_CLEAR_QUE: nbd_clear_que(lo); return 0; -#ifdef PARANOIA case NBD_PRINT_DEBUG: +#ifdef PARANOIA printk(KERN_INFO "%s: next = %p, prev = %p. Global: in %d, out %d\n", - inode->i_bdev->bd_disk->disk_name, lo->queue_head.next, - lo->queue_head.prev, requests_in, requests_out); - return 0; + inode->i_bdev->bd_disk->disk_name, lo->queue_head.next, + lo->queue_head.prev, requests_in, requests_out); +#else + printk(KERN_INFO "%s: next = %p, prev = %p\n", + inode->i_bdev->bd_disk->disk_name, + lo->queue_head.next, lo->queue_head.prev); #endif + return 0; } return -EINVAL; } -static int nbd_release(struct inode *inode, struct file *file) -{ - struct nbd_device *lo = inode->i_bdev->bd_disk->private_data; - if (lo->refcnt <= 0) - printk(KERN_ALERT "nbd_release: refcount(%d) <= 0\n", lo->refcnt); - lo->refcnt--; - /* N.B. Doesn't lo->file need an fput?? */ - return 0; -} - static struct block_device_operations nbd_fops = { .owner = THIS_MODULE, - .open = nbd_open, - .release = nbd_release, .ioctl = nbd_ioctl, }; @@ -581,10 +666,12 @@ int err = -ENOMEM; int i; +#ifdef PARANOIA if (sizeof(struct nbd_request) != 28) { - printk(KERN_CRIT "Sizeof nbd_request needs to be 28 in order to work!\n" ); + printk(KERN_CRIT "nbd: Sizeof nbd_request needs to be 28 in order to work!\n" ); return -EIO; } +#endif for (i = 0; i < MAX_NBD; i++) { struct gendisk *disk = alloc_disk(1); @@ -601,6 +688,7 @@ put_disk(disk); goto out; } + memset(disk->queue, 0, sizeof(struct request_queue)); blk_init_queue(disk->queue, do_nbd_request, &nbd_lock); } @@ -608,21 +696,22 @@ err = -EIO; goto out; } -#ifdef MODULE - printk("nbd: registered device at major %d\n", NBD_MAJOR); -#endif + + printk(KERN_INFO "nbd: registered device at major %d\n", NBD_MAJOR); + dprintk(DBG_INIT, "nbd: debugflags=0x%x\n", debugflags); + devfs_mk_dir("nbd"); for (i = 0; i < MAX_NBD; i++) { struct gendisk *disk = nbd_dev[i].disk; - nbd_dev[i].refcnt = 0; nbd_dev[i].file = NULL; +#ifdef PARANOIA nbd_dev[i].magic = LO_MAGIC; +#endif nbd_dev[i].flags = 0; spin_lock_init(&nbd_dev[i].queue_lock); INIT_LIST_HEAD(&nbd_dev[i].queue_head); init_MUTEX(&nbd_dev[i].tx_lock); nbd_dev[i].blksize = 1024; - nbd_dev[i].blksize_bits = 10; nbd_dev[i].bytesize = ((u64)0x7ffffc00) << 10; /* 2TB */ disk->major = NBD_MAJOR; disk->first_minor = i; @@ -660,9 +749,7 @@ } devfs_remove("nbd"); unregister_blkdev(NBD_MAJOR, "nbd"); -#ifdef MODULE - printk("nbd: unregistered device at major %d\n", NBD_MAJOR); -#endif + printk(KERN_INFO "nbd: unregistered device at major %d\n", NBD_MAJOR); } module_init(nbd_init); @@ -671,4 +758,7 @@ MODULE_DESCRIPTION("Network Block Device"); MODULE_LICENSE("GPL"); - +#ifndef NDEBUG +MODULE_PARM(debugflags, "i"); +MODULE_PARM_DESC(debugflags, "flags for controlling debug output"); +#endif diff -Nru a/drivers/char/agp/via-agp.c b/drivers/char/agp/via-agp.c --- a/drivers/char/agp/via-agp.c Thu Jul 3 09:01:42 2003 +++ b/drivers/char/agp/via-agp.c Thu Jul 10 15:17:58 2003 @@ -382,7 +382,7 @@ } } - if (agp_try_unsupported) { + if (!agp_try_unsupported) { printk(KERN_ERR PFX "Unsupported VIA chipset (device id: %04x)," " you might want to try agp_try_unsupported=1.\n", diff -Nru a/drivers/char/drm/ati_pcigart.h b/drivers/char/drm/ati_pcigart.h --- a/drivers/char/drm/ati_pcigart.h Thu Mar 13 16:52:15 2003 +++ b/drivers/char/drm/ati_pcigart.h Thu Jul 10 23:18:01 2003 @@ -1,4 +1,11 @@ -/* ati_pcigart.h -- ATI PCI GART support -*- linux-c -*- +/** + * \file ati_pcigart.h + * ATI PCI GART support + * + * \author Gareth Hughes + */ + +/* * Created: Wed Dec 13 21:52:19 2000 by gareth@valinux.com * * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. @@ -22,9 +29,6 @@ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. - * - * Authors: - * Gareth Hughes */ #include "drmP.h" @@ -45,8 +49,8 @@ # error - PAGE_SIZE not 64K, 16K, 8K or 4K #endif -# define ATI_MAX_PCIGART_PAGES 8192 /* 32 MB aperture, 4K pages */ -# define ATI_PCIGART_PAGE_SIZE 4096 /* PCI GART page size */ +# define ATI_MAX_PCIGART_PAGES 8192 /**< 32 MB aperture, 4K pages */ +# define ATI_PCIGART_PAGE_SIZE 4096 /**< PCI GART page size */ static unsigned long DRM(ati_alloc_pcigart_table)( void ) { diff -Nru a/drivers/char/drm/drm.h b/drivers/char/drm/drm.h --- a/drivers/char/drm/drm.h Wed Dec 18 09:56:28 2002 +++ b/drivers/char/drm/drm.h Thu Jul 10 23:18:01 2003 @@ -1,6 +1,14 @@ -/* drm.h -- Header for Direct Rendering Manager -*- linux-c -*- - * Created: Mon Jan 4 10:05:05 1999 by faith@precisioninsight.com +/** + * \file drm.h + * Header for the Direct Rendering Manager + * + * \author Rickard E. (Rik) Faith * + * \par Acknowledgments: + * Dec 1999, Richard Henderson , move to generic \c cmpxchg. + */ + +/* * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. * All rights reserved. @@ -23,15 +31,9 @@ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. - * - * Authors: - * Rickard E. (Rik) Faith - * - * Acknowledgements: - * Dec 1999, Richard Henderson , move to generic cmpxchg. - * */ + #ifndef _DRM_H_ #define _DRM_H_ @@ -82,29 +84,33 @@ #define DRM_MAJOR 226 #define DRM_MAX_MINOR 15 #endif -#define DRM_NAME "drm" /* Name in kernel, /dev, and /proc */ -#define DRM_MIN_ORDER 5 /* At least 2^5 bytes = 32 bytes */ -#define DRM_MAX_ORDER 22 /* Up to 2^22 bytes = 4MB */ -#define DRM_RAM_PERCENT 10 /* How much system ram can we lock? */ +#define DRM_NAME "drm" /**< Name in kernel, /dev, and /proc */ +#define DRM_MIN_ORDER 5 /**< At least 2^5 bytes = 32 bytes */ +#define DRM_MAX_ORDER 22 /**< Up to 2^22 bytes = 4MB */ +#define DRM_RAM_PERCENT 10 /**< How much system ram can we lock? */ -#define _DRM_LOCK_HELD 0x80000000 /* Hardware lock is held */ -#define _DRM_LOCK_CONT 0x40000000 /* Hardware lock is contended */ +#define _DRM_LOCK_HELD 0x80000000 /**< Hardware lock is held */ +#define _DRM_LOCK_CONT 0x40000000 /**< Hardware lock is contended */ #define _DRM_LOCK_IS_HELD(lock) ((lock) & _DRM_LOCK_HELD) #define _DRM_LOCK_IS_CONT(lock) ((lock) & _DRM_LOCK_CONT) #define _DRM_LOCKING_CONTEXT(lock) ((lock) & ~(_DRM_LOCK_HELD|_DRM_LOCK_CONT)) + typedef unsigned long drm_handle_t; typedef unsigned int drm_context_t; typedef unsigned int drm_drawable_t; typedef unsigned int drm_magic_t; -/* Warning: If you change this structure, make sure you change - * XF86DRIClipRectRec in the server as well */ -/* KW: Actually it's illegal to change either for +/** + * Cliprect. + * + * \warning: If you change this structure, make sure you change + * XF86DRIClipRectRec in the server as well + * + * \note KW: Actually it's illegal to change either for * backwards-compatibility reasons. */ - typedef struct drm_clip_rect { unsigned short x1; unsigned short y1; @@ -112,6 +118,10 @@ unsigned short y2; } drm_clip_rect_t; + +/** + * Texture region, + */ typedef struct drm_tex_region { unsigned char next; unsigned char prev; @@ -120,32 +130,52 @@ unsigned int age; } drm_tex_region_t; + +/** + * DRM_IOCTL_VERSION ioctl argument type. + * + * \sa drmGetVersion(). + */ typedef struct drm_version { - int version_major; /* Major version */ - int version_minor; /* Minor version */ - int version_patchlevel;/* Patch level */ - size_t name_len; /* Length of name buffer */ - char *name; /* Name of driver */ - size_t date_len; /* Length of date buffer */ - char *date; /* User-space buffer to hold date */ - size_t desc_len; /* Length of desc buffer */ - char *desc; /* User-space buffer to hold desc */ + int version_major; /**< Major version */ + int version_minor; /**< Minor version */ + int version_patchlevel;/**< Patch level */ + size_t name_len; /**< Length of name buffer */ + char *name; /**< Name of driver */ + size_t date_len; /**< Length of date buffer */ + char *date; /**< User-space buffer to hold date */ + size_t desc_len; /**< Length of desc buffer */ + char *desc; /**< User-space buffer to hold desc */ } drm_version_t; + +/** + * DRM_IOCTL_GET_UNIQUE ioctl argument type. + * + * \sa drmGetBusid() and drmSetBusId(). + */ typedef struct drm_unique { - size_t unique_len; /* Length of unique */ - char *unique; /* Unique name for driver instantiation */ + size_t unique_len; /**< Length of unique */ + char *unique; /**< Unique name for driver instantiation */ } drm_unique_t; + typedef struct drm_list { - int count; /* Length of user-space structures */ + int count; /**< Length of user-space structures */ drm_version_t *version; } drm_list_t; + typedef struct drm_block { int unused; } drm_block_t; + +/** + * DRM_IOCTL_CONTROL ioctl argument type. + * + * \sa drmCtlInstHandler() and drmCtlUninstHandler(). + */ typedef struct drm_control { enum { DRM_ADD_COMMAND, @@ -156,49 +186,70 @@ int irq; } drm_control_t; + +/** + * Type of memory to map. + */ typedef enum drm_map_type { - _DRM_FRAME_BUFFER = 0, /* WC (no caching), no core dump */ - _DRM_REGISTERS = 1, /* no caching, no core dump */ - _DRM_SHM = 2, /* shared, cached */ - _DRM_AGP = 3, /* AGP/GART */ - _DRM_SCATTER_GATHER = 4 /* Scatter/gather memory for PCI DMA */ + _DRM_FRAME_BUFFER = 0, /**< WC (no caching), no core dump */ + _DRM_REGISTERS = 1, /**< no caching, no core dump */ + _DRM_SHM = 2, /**< shared, cached */ + _DRM_AGP = 3, /**< AGP/GART */ + _DRM_SCATTER_GATHER = 4 /**< Scatter/gather memory for PCI DMA */ } drm_map_type_t; + +/** + * Memory mapping flags. + */ typedef enum drm_map_flags { - _DRM_RESTRICTED = 0x01, /* Cannot be mapped to user-virtual */ + _DRM_RESTRICTED = 0x01, /**< Cannot be mapped to user-virtual */ _DRM_READ_ONLY = 0x02, - _DRM_LOCKED = 0x04, /* shared, cached, locked */ - _DRM_KERNEL = 0x08, /* kernel requires access */ - _DRM_WRITE_COMBINING = 0x10, /* use write-combining if available */ - _DRM_CONTAINS_LOCK = 0x20, /* SHM page that contains lock */ - _DRM_REMOVABLE = 0x40 /* Removable mapping */ + _DRM_LOCKED = 0x04, /**< shared, cached, locked */ + _DRM_KERNEL = 0x08, /**< kernel requires access */ + _DRM_WRITE_COMBINING = 0x10, /**< use write-combining if available */ + _DRM_CONTAINS_LOCK = 0x20, /**< SHM page that contains lock */ + _DRM_REMOVABLE = 0x40 /**< Removable mapping */ } drm_map_flags_t; + typedef struct drm_ctx_priv_map { - unsigned int ctx_id; /* Context requesting private mapping */ - void *handle; /* Handle of map */ + unsigned int ctx_id; /**< Context requesting private mapping */ + void *handle; /**< Handle of map */ } drm_ctx_priv_map_t; + +/** + * DRM_IOCTL_GET_MAP, DRM_IOCTL_ADD_MAP and DRM_IOCTL_RM_MAP ioctls + * argument type. + * + * \sa drmAddMap(). + */ typedef struct drm_map { - unsigned long offset; /* Requested physical address (0 for SAREA)*/ - unsigned long size; /* Requested physical size (bytes) */ - drm_map_type_t type; /* Type of memory to map */ - drm_map_flags_t flags; /* Flags */ - void *handle; /* User-space: "Handle" to pass to mmap */ - /* Kernel-space: kernel-virtual address */ - int mtrr; /* MTRR slot used */ - /* Private data */ + unsigned long offset; /**< Requested physical address (0 for SAREA)*/ + unsigned long size; /**< Requested physical size (bytes) */ + drm_map_type_t type; /**< Type of memory to map */ + drm_map_flags_t flags; /**< Flags */ + void *handle; /**< User-space: "Handle" to pass to mmap() */ + /**< Kernel-space: kernel-virtual address */ + int mtrr; /**< MTRR slot used */ + /* Private data */ } drm_map_t; + +/** + * DRM_IOCTL_GET_CLIENT ioctl argument type. + */ typedef struct drm_client { - int idx; /* Which client desired? */ - int auth; /* Is client authenticated? */ - unsigned long pid; /* Process id */ - unsigned long uid; /* User id */ - unsigned long magic; /* Magic */ - unsigned long iocs; /* Ioctl count */ + int idx; /**< Which client desired? */ + int auth; /**< Is client authenticated? */ + unsigned long pid; /**< Process ID */ + unsigned long uid; /**< User ID */ + unsigned long magic; /**< Magic */ + unsigned long iocs; /**< Ioctl count */ } drm_client_t; + typedef enum { _DRM_STAT_LOCK, _DRM_STAT_OPENS, @@ -206,20 +257,24 @@ _DRM_STAT_IOCTLS, _DRM_STAT_LOCKS, _DRM_STAT_UNLOCKS, - _DRM_STAT_VALUE, /* Generic value */ - _DRM_STAT_BYTE, /* Generic byte counter (1024bytes/K) */ - _DRM_STAT_COUNT, /* Generic non-byte counter (1000/k) */ - - _DRM_STAT_IRQ, /* IRQ */ - _DRM_STAT_PRIMARY, /* Primary DMA bytes */ - _DRM_STAT_SECONDARY, /* Secondary DMA bytes */ - _DRM_STAT_DMA, /* DMA */ - _DRM_STAT_SPECIAL, /* Special DMA (e.g., priority or polled) */ - _DRM_STAT_MISSED /* Missed DMA opportunity */ + _DRM_STAT_VALUE, /**< Generic value */ + _DRM_STAT_BYTE, /**< Generic byte counter (1024bytes/K) */ + _DRM_STAT_COUNT, /**< Generic non-byte counter (1000/k) */ + + _DRM_STAT_IRQ, /**< IRQ */ + _DRM_STAT_PRIMARY, /**< Primary DMA bytes */ + _DRM_STAT_SECONDARY, /**< Secondary DMA bytes */ + _DRM_STAT_DMA, /**< DMA */ + _DRM_STAT_SPECIAL, /**< Special DMA (e.g., priority or polled) */ + _DRM_STAT_MISSED /**< Missed DMA opportunity */ /* Add to the *END* of the list */ } drm_stat_type_t; + +/** + * DRM_IOCTL_GET_STATS ioctl argument type. + */ typedef struct drm_stats { unsigned long count; struct { @@ -228,137 +283,220 @@ } data[15]; } drm_stats_t; + +/** + * Hardware locking flags. + */ typedef enum drm_lock_flags { - _DRM_LOCK_READY = 0x01, /* Wait until hardware is ready for DMA */ - _DRM_LOCK_QUIESCENT = 0x02, /* Wait until hardware quiescent */ - _DRM_LOCK_FLUSH = 0x04, /* Flush this context's DMA queue first */ - _DRM_LOCK_FLUSH_ALL = 0x08, /* Flush all DMA queues first */ + _DRM_LOCK_READY = 0x01, /**< Wait until hardware is ready for DMA */ + _DRM_LOCK_QUIESCENT = 0x02, /**< Wait until hardware quiescent */ + _DRM_LOCK_FLUSH = 0x04, /**< Flush this context's DMA queue first */ + _DRM_LOCK_FLUSH_ALL = 0x08, /**< Flush all DMA queues first */ /* These *HALT* flags aren't supported yet -- they will be used to support the full-screen DGA-like mode. */ - _DRM_HALT_ALL_QUEUES = 0x10, /* Halt all current and future queues */ - _DRM_HALT_CUR_QUEUES = 0x20 /* Halt all current queues */ + _DRM_HALT_ALL_QUEUES = 0x10, /**< Halt all current and future queues */ + _DRM_HALT_CUR_QUEUES = 0x20 /**< Halt all current queues */ } drm_lock_flags_t; + +/** + * DRM_IOCTL_LOCK, DRM_IOCTL_UNLOCK and DRM_IOCTL_FINISH ioctl argument type. + * + * \sa drmGetLock() and drmUnlock(). + */ typedef struct drm_lock { int context; drm_lock_flags_t flags; } drm_lock_t; -typedef enum drm_dma_flags { /* These values *MUST* match xf86drm.h */ - /* Flags for DMA buffer dispatch */ - _DRM_DMA_BLOCK = 0x01, /* Block until buffer dispatched. - Note, the buffer may not yet have - been processed by the hardware -- - getting a hardware lock with the - hardware quiescent will ensure - that the buffer has been - processed. */ - _DRM_DMA_WHILE_LOCKED = 0x02, /* Dispatch while lock held */ - _DRM_DMA_PRIORITY = 0x04, /* High priority dispatch */ - - /* Flags for DMA buffer request */ - _DRM_DMA_WAIT = 0x10, /* Wait for free buffers */ - _DRM_DMA_SMALLER_OK = 0x20, /* Smaller-than-requested buffers ok */ - _DRM_DMA_LARGER_OK = 0x40 /* Larger-than-requested buffers ok */ + +/** + * DMA flags + * + * \warning + * These values \e must match xf86drm.h. + * + * \sa drm_dma. + */ +typedef enum drm_dma_flags { + /* Flags for DMA buffer dispatch */ + _DRM_DMA_BLOCK = 0x01, /**< + * Block until buffer dispatched. + * + * \note The buffer may not yet have + * been processed by the hardware -- + * getting a hardware lock with the + * hardware quiescent will ensure + * that the buffer has been + * processed. + */ + _DRM_DMA_WHILE_LOCKED = 0x02, /**< Dispatch while lock held */ + _DRM_DMA_PRIORITY = 0x04, /**< High priority dispatch */ + + /* Flags for DMA buffer request */ + _DRM_DMA_WAIT = 0x10, /**< Wait for free buffers */ + _DRM_DMA_SMALLER_OK = 0x20, /**< Smaller-than-requested buffers OK */ + _DRM_DMA_LARGER_OK = 0x40 /**< Larger-than-requested buffers OK */ } drm_dma_flags_t; + +/** + * DRM_IOCTL_ADD_BUFS and DRM_IOCTL_MARK_BUFS ioctl argument type. + * + * \sa drmAddBufs(). + */ typedef struct drm_buf_desc { - int count; /* Number of buffers of this size */ - int size; /* Size in bytes */ - int low_mark; /* Low water mark */ - int high_mark; /* High water mark */ + int count; /**< Number of buffers of this size */ + int size; /**< Size in bytes */ + int low_mark; /**< Low water mark */ + int high_mark; /**< High water mark */ enum { - _DRM_PAGE_ALIGN = 0x01, /* Align on page boundaries for DMA */ - _DRM_AGP_BUFFER = 0x02, /* Buffer is in agp space */ - _DRM_SG_BUFFER = 0x04 /* Scatter/gather memory buffer */ + _DRM_PAGE_ALIGN = 0x01, /**< Align on page boundaries for DMA */ + _DRM_AGP_BUFFER = 0x02, /**< Buffer is in AGP space */ + _DRM_SG_BUFFER = 0x04 /**< Scatter/gather memory buffer */ } flags; - unsigned long agp_start; /* Start address of where the agp buffers - * are in the agp aperture */ + unsigned long agp_start; /**< + * Start address of where the AGP buffers are + * in the AGP aperture + */ } drm_buf_desc_t; + +/** + * DRM_IOCTL_INFO_BUFS ioctl argument type. + */ typedef struct drm_buf_info { - int count; /* Entries in list */ + int count; /**< Entries in list */ drm_buf_desc_t *list; } drm_buf_info_t; + +/** + * DRM_IOCTL_FREE_BUFS ioctl argument type. + */ typedef struct drm_buf_free { int count; int *list; } drm_buf_free_t; + +/** + * Buffer information + * + * \sa drm_buf_map. + */ typedef struct drm_buf_pub { - int idx; /* Index into master buflist */ - int total; /* Buffer size */ - int used; /* Amount of buffer in use (for DMA) */ - void *address; /* Address of buffer */ + int idx; /**< Index into the master buffer list */ + int total; /**< Buffer size */ + int used; /**< Amount of buffer in use (for DMA) */ + void *address; /**< Address of buffer */ } drm_buf_pub_t; + +/** + * DRM_IOCTL_MAP_BUFS ioctl argument type. + */ typedef struct drm_buf_map { - int count; /* Length of buflist */ - void *virtual; /* Mmaped area in user-virtual */ - drm_buf_pub_t *list; /* Buffer information */ + int count; /**< Length of the buffer list */ + void *virtual; /**< Mmap'd area in user-virtual */ + drm_buf_pub_t *list; /**< Buffer information */ } drm_buf_map_t; + +/** + * DRM_IOCTL_DMA ioctl argument type. + * + * Indices here refer to the offset into the buffer list in drm_buf_get. + * + * \sa drmDMA(). + */ typedef struct drm_dma { - /* Indices here refer to the offset into - buflist in drm_buf_get_t. */ - int context; /* Context handle */ - int send_count; /* Number of buffers to send */ - int *send_indices; /* List of handles to buffers */ - int *send_sizes; /* Lengths of data to send */ - drm_dma_flags_t flags; /* Flags */ - int request_count; /* Number of buffers requested */ - int request_size; /* Desired size for buffers */ - int *request_indices; /* Buffer information */ + int context; /**< Context handle */ + int send_count; /**< Number of buffers to send */ + int *send_indices; /**< List of handles to buffers */ + int *send_sizes; /**< Lengths of data to send */ + drm_dma_flags_t flags; /**< Flags */ + int request_count; /**< Number of buffers requested */ + int request_size; /**< Desired size for buffers */ + int *request_indices; /**< Buffer information */ int *request_sizes; - int granted_count; /* Number of buffers granted */ + int granted_count; /**< Number of buffers granted */ } drm_dma_t; + typedef enum { _DRM_CONTEXT_PRESERVED = 0x01, _DRM_CONTEXT_2DONLY = 0x02 } drm_ctx_flags_t; + +/** + * DRM_IOCTL_ADD_CTX ioctl argument type. + * + * \sa drmCreateContext() and drmDestroyContext(). + */ typedef struct drm_ctx { drm_context_t handle; drm_ctx_flags_t flags; } drm_ctx_t; + +/** + * DRM_IOCTL_RES_CTX ioctl argument type. + */ typedef struct drm_ctx_res { int count; drm_ctx_t *contexts; } drm_ctx_res_t; + +/** + * DRM_IOCTL_ADD_DRAW and DRM_IOCTL_RM_DRAW ioctl argument type. + */ typedef struct drm_draw { drm_drawable_t handle; } drm_draw_t; + +/** + * DRM_IOCTL_GET_MAGIC and DRM_IOCTL_AUTH_MAGIC ioctl argument type. + */ typedef struct drm_auth { drm_magic_t magic; } drm_auth_t; + +/** + * DRM_IOCTL_IRQ_BUSID ioctl argument type. + * + * \sa drmGetInterruptFromBusID(). + */ typedef struct drm_irq_busid { - int irq; - int busnum; - int devnum; - int funcnum; + int irq; /**< IRQ number */ + int busnum; /**< bus number */ + int devnum; /**< device number */ + int funcnum; /**< function number */ } drm_irq_busid_t; + typedef enum { - _DRM_VBLANK_ABSOLUTE = 0x0, /* Wait for specific vblank sequence number */ - _DRM_VBLANK_RELATIVE = 0x1, /* Wait for given number of vblanks */ - _DRM_VBLANK_SIGNAL = 0x40000000 /* Send signal instead of blocking */ + _DRM_VBLANK_ABSOLUTE = 0x0, /**< Wait for specific vblank sequence number */ + _DRM_VBLANK_RELATIVE = 0x1, /**< Wait for given number of vblanks */ + _DRM_VBLANK_SIGNAL = 0x40000000 /**< Send signal instead of blocking */ } drm_vblank_seq_type_t; + #define _DRM_VBLANK_FLAGS_MASK _DRM_VBLANK_SIGNAL + struct drm_wait_vblank_request { drm_vblank_seq_type_t type; unsigned int sequence; unsigned long signal; }; + struct drm_wait_vblank_reply { drm_vblank_seq_type_t type; unsigned int sequence; @@ -366,29 +504,59 @@ long tval_usec; }; + +/** + * DRM_IOCTL_WAIT_VBLANK ioctl argument type. + * + * \sa drmWaitVBlank(). + */ typedef union drm_wait_vblank { struct drm_wait_vblank_request request; struct drm_wait_vblank_reply reply; } drm_wait_vblank_t; + +/** + * DRM_IOCTL_AGP_ENABLE ioctl argument type. + * + * \sa drmAgpEnable(). + */ typedef struct drm_agp_mode { - unsigned long mode; + unsigned long mode; /**< AGP mode */ } drm_agp_mode_t; - /* For drm_agp_alloc -- allocated a buffer */ + +/** + * DRM_IOCTL_AGP_ALLOC and DRM_IOCTL_AGP_FREE ioctls argument type. + * + * \sa drmAgpAlloc() and drmAgpFree(). + */ typedef struct drm_agp_buffer { - unsigned long size; /* In bytes -- will round to page boundary */ - unsigned long handle; /* Used for BIND/UNBIND ioctls */ - unsigned long type; /* Type of memory to allocate */ - unsigned long physical; /* Physical used by i810 */ + unsigned long size; /**< In bytes -- will round to page boundary */ + unsigned long handle; /**< Used for binding / unbinding */ + unsigned long type; /**< Type of memory to allocate */ + unsigned long physical; /**< Physical used by i810 */ } drm_agp_buffer_t; - /* For drm_agp_bind */ + +/** + * DRM_IOCTL_AGP_BIND and DRM_IOCTL_AGP_UNBIND ioctls argument type. + * + * \sa drmAgpBind() and drmAgpUnbind(). + */ typedef struct drm_agp_binding { - unsigned long handle; /* From drm_agp_buffer */ - unsigned long offset; /* In bytes -- will round to page boundary */ + unsigned long handle; /**< From drm_agp_buffer */ + unsigned long offset; /**< In bytes -- will round to page boundary */ } drm_agp_binding_t; + +/** + * DRM_IOCTL_AGP_INFO ioctl argument type. + * + * \sa drmAgpVersionMajor(), drmAgpVersionMinor(), drmAgpGetMode(), + * drmAgpBase(), drmAgpSize(), drmAgpMemoryUsed(), drmAgpMemoryAvail(), + * drmAgpVendorId() and drmAgpDeviceId(). + */ typedef struct drm_agp_info { int agp_version_major; int agp_version_minor; @@ -403,11 +571,16 @@ unsigned short id_device; } drm_agp_info_t; + +/** + * DRM_IOCTL_SG_ALLOC ioctl argument type. + */ typedef struct drm_scatter_gather { - unsigned long size; /* In bytes -- will round to page boundary */ - unsigned long handle; /* Used for mapping / unmapping */ + unsigned long size; /**< In bytes -- will round to page boundary */ + unsigned long handle; /**< Used for mapping / unmapping */ } drm_scatter_gather_t; + #define DRM_IOCTL_BASE 'd' #define DRM_IO(nr) _IO(DRM_IOCTL_BASE,nr) #define DRM_IOR(nr,type) _IOR(DRM_IOCTL_BASE,nr,type) @@ -467,8 +640,13 @@ #define DRM_IOCTL_WAIT_VBLANK DRM_IOWR(0x3a, drm_wait_vblank_t) -/* Device specfic ioctls should only be in their respective headers - * The device specific ioctl range is 0x40 to 0x79. */ +/** + * Device specific ioctls should only be in their respective headers + * The device specific ioctl range is from 0x40 to 0x79. + * + * \sa drmCommandNone(), drmCommandRead(), drmCommandWrite(), and + * drmCommandReadWrite(). + */ #define DRM_COMMAND_BASE 0x40 #endif diff -Nru a/drivers/char/drm/drmP.h b/drivers/char/drm/drmP.h --- a/drivers/char/drm/drmP.h Sat May 24 12:57:42 2003 +++ b/drivers/char/drm/drmP.h Thu Jul 10 23:18:01 2003 @@ -1,6 +1,12 @@ -/* drmP.h -- Private header for Direct Rendering Manager -*- linux-c -*- - * Created: Mon Jan 4 10:05:05 1999 by faith@precisioninsight.com - * +/** + * \file drmP.h + * Private header for Direct Rendering Manager + * + * \author Rickard E. (Rik) Faith + * \author Gareth Hughes + */ + +/* * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. * All rights reserved. @@ -23,15 +29,12 @@ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. - * - * Authors: - * Rickard E. (Rik) Faith - * Gareth Hughes */ #ifndef _DRM_P_H_ #define _DRM_P_H_ + #ifdef __KERNEL__ #ifdef __alpha__ /* add include of current.h so that "current" is defined @@ -72,8 +75,11 @@ #include "drm_os_linux.h" -/* DRM template customization defaults - */ + +/***********************************************************************/ +/** \name DRM template customization defaults */ +/*@{*/ + #ifndef __HAVE_AGP #define __HAVE_AGP 0 #endif @@ -101,19 +107,23 @@ #define __REALLY_HAVE_MTRR (__HAVE_MTRR && defined(CONFIG_MTRR)) #define __REALLY_HAVE_SG (__HAVE_SG) -/* Begin the DRM... - */ +/*@}*/ + -#define DRM_DEBUG_CODE 2 /* Include debugging code (if > 1, then +/***********************************************************************/ +/** \name Begin the DRM... */ +/*@{*/ + +#define DRM_DEBUG_CODE 2 /**< Include debugging code if > 1, then also include looping detection. */ -#define DRM_HASH_SIZE 16 /* Size of key hash table */ -#define DRM_KERNEL_CONTEXT 0 /* Change drm_resctx if changed */ -#define DRM_RESERVED_CONTEXTS 1 /* Change drm_resctx if changed */ +#define DRM_HASH_SIZE 16 /**< Size of key hash table. Must be power of 2. */ +#define DRM_KERNEL_CONTEXT 0 /**< Change drm_resctx if changed */ +#define DRM_RESERVED_CONTEXTS 1 /**< Change drm_resctx if changed */ #define DRM_LOOPING_LIMIT 5000000 -#define DRM_BSZ 1024 /* Buffer size for /dev/drm? output */ -#define DRM_TIME_SLICE (HZ/20) /* Time slice for GLXContexts */ -#define DRM_LOCK_SLICE 1 /* Time slice for lock, in jiffies */ +#define DRM_BSZ 1024 /**< Buffer size for /dev/drm? output */ +#define DRM_TIME_SLICE (HZ/20) /**< Time slice for GLXContexts */ +#define DRM_LOCK_SLICE 1 /**< Time slice for lock, in jiffies */ #define DRM_FLAG_DEBUG 0x01 @@ -140,8 +150,14 @@ #define DRM_MEM_SGLISTS 20 #define DRM_MAX_CTXBITMAP (PAGE_SIZE * 8) + +/*@}*/ + + +/***********************************************************************/ +/** \name Backward compatibility section */ +/*@{*/ - /* Backward compatibility section */ #ifndef minor #define minor(x) MINOR((x)) #endif @@ -191,17 +207,42 @@ #define DRM_RPR_ARG(vma) vma, #endif - #define VM_OFFSET(vma) ((vma)->vm_pgoff << PAGE_SHIFT) - /* Macros to make printk easier */ +/*@}*/ + + +/***********************************************************************/ +/** \name Macros to make printk easier */ +/*@{*/ + +/** + * Error output. + * + * \param fmt printf() like format string. + * \param arg arguments + */ #define DRM_ERROR(fmt, arg...) \ printk(KERN_ERR "[" DRM_NAME ":%s] *ERROR* " fmt , __FUNCTION__ , ##arg) + +/** + * Memory error output. + * + * \param area memory area where the error occurred. + * \param fmt printf() like format string. + * \param arg arguments + */ #define DRM_MEM_ERROR(area, fmt, arg...) \ printk(KERN_ERR "[" DRM_NAME ":%s:%s] *ERROR* " fmt , __FUNCTION__, \ DRM(mem_stats)[area].name , ##arg) #define DRM_INFO(fmt, arg...) printk(KERN_INFO "[" DRM_NAME "] " fmt , ##arg) +/** + * Debug output. + * + * \param fmt printf() like format string. + * \param arg arguments + */ #if DRM_DEBUG_CODE #define DRM_DEBUG(fmt, arg...) \ do { \ @@ -224,7 +265,13 @@ len += sprintf(&buf[len], fmt , ##arg); \ if (len > DRM_PROC_LIMIT) { ret; *eof = 1; return len - offset; } - /* Mapping helper macros */ +/*@}*/ + + +/***********************************************************************/ +/** \name Mapping helper macros */ +/*@{*/ + #define DRM_IOREMAP(map, dev) \ (map)->handle = DRM(ioremap)( (map)->offset, (map)->size, (dev) ) @@ -237,6 +284,15 @@ DRM(ioremapfree)( (map)->handle, (map)->size, (dev) ); \ } while (0) +/** + * Find mapping. + * + * \param _map matching mapping if found, untouched otherwise. + * \param _o offset. + * + * Expects the existence of a local variable named \p dev pointing to the + * drm_device structure. + */ #define DRM_FIND_MAP(_map, _o) \ do { \ struct list_head *_list; \ @@ -249,9 +305,21 @@ } \ } \ } while(0) + +/** + * Drop mapping. + * + * \sa #DRM_FIND_MAP. + */ #define DRM_DROP_MAP(_map) - /* Internal types and structures */ +/*@}*/ + + +/***********************************************************************/ +/** \name Internal types and structures */ +/*@{*/ + #define DRM_ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0])) #define DRM_MIN(a,b) ((a)<(b)?(a):(b)) #define DRM_MAX(a,b) ((a)>(b)?(a):(b)) @@ -260,10 +328,23 @@ #define DRM_BUFCOUNT(x) ((x)->count - DRM_LEFTCOUNT(x)) #define DRM_WAITCOUNT(dev,idx) DRM_BUFCOUNT(&dev->queuelist[idx]->waitlist) +/** + * Get the private SAREA mapping. + * + * \param _dev DRM device. + * \param _ctx context number. + * \param _map output mapping. + */ #define DRM_GET_PRIV_SAREA(_dev, _ctx, _map) do { \ (_map) = (_dev)->context_sareas[_ctx]; \ } while(0) +/** + * Test that the hardware lock is held by the caller, returning otherwise. + * + * \param dev DRM device. + * \param filp file pointer of the caller. + */ #define LOCK_TEST_WITH_RETURN( dev, filp ) \ do { \ if ( !_DRM_LOCK_IS_HELD( dev->lock.hw_lock->lock ) || \ @@ -274,7 +355,14 @@ } \ } while (0) - +/** + * Ioctl function type. + * + * \param inode device inode. + * \param filp file pointer. + * \param cmd command. + * \param arg argument. + */ typedef int drm_ioctl_t( struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg ); @@ -290,8 +378,7 @@ } drm_ioctl_desc_t; typedef struct drm_devstate { - pid_t owner; /* X server pid holding x_lock */ - + pid_t owner; /**< X server pid holding x_lock */ } drm_devstate_t; typedef struct drm_magic_entry { @@ -311,21 +398,24 @@ pid_t pid; } drm_vma_entry_t; +/** + * DMA buffer. + */ typedef struct drm_buf { - int idx; /* Index into master buflist */ - int total; /* Buffer size */ - int order; /* log-base-2(total) */ - int used; /* Amount of buffer in use (for DMA) */ - unsigned long offset; /* Byte offset (used internally) */ - void *address; /* Address of buffer */ - unsigned long bus_address; /* Bus address of buffer */ - struct drm_buf *next; /* Kernel-only: used for free list */ - __volatile__ int waiting; /* On kernel DMA queue */ - __volatile__ int pending; /* On hardware DMA queue */ - wait_queue_head_t dma_wait; /* Processes waiting */ - struct file *filp; /* Pointer to holding file descr */ - int context; /* Kernel queue for this buffer */ - int while_locked;/* Dispatch this buffer while locked */ + int idx; /**< Index into master buflist */ + int total; /**< Buffer size */ + int order; /**< log-base-2(total) */ + int used; /**< Amount of buffer in use (for DMA) */ + unsigned long offset; /**< Byte offset (used internally) */ + void *address; /**< Address of buffer */ + unsigned long bus_address; /**< Bus address of buffer */ + struct drm_buf *next; /**< Kernel-only: used for free list */ + __volatile__ int waiting; /**< On kernel DMA queue */ + __volatile__ int pending; /**< On hardware DMA queue */ + wait_queue_head_t dma_wait; /**< Processes waiting */ + struct file *filp; /**< Pointer to holding file descr */ + int context; /**< Kernel queue for this buffer */ + int while_locked;/**< Dispatch this buffer while locked */ enum { DRM_LIST_NONE = 0, DRM_LIST_FREE = 1, @@ -333,41 +423,43 @@ DRM_LIST_PEND = 3, DRM_LIST_PRIO = 4, DRM_LIST_RECLAIM = 5 - } list; /* Which list we're on */ - + } list; /**< Which list we're on */ - int dev_priv_size; /* Size of buffer private stoarge */ - void *dev_private; /* Per-buffer private storage */ + int dev_priv_size; /**< Size of buffer private storage */ + void *dev_private; /**< Per-buffer private storage */ } drm_buf_t; - /* bufs is one longer than it has to be */ +/** bufs is one longer than it has to be */ typedef struct drm_waitlist { - int count; /* Number of possible buffers */ - drm_buf_t **bufs; /* List of pointers to buffers */ - drm_buf_t **rp; /* Read pointer */ - drm_buf_t **wp; /* Write pointer */ - drm_buf_t **end; /* End pointer */ + int count; /**< Number of possible buffers */ + drm_buf_t **bufs; /**< List of pointers to buffers */ + drm_buf_t **rp; /**< Read pointer */ + drm_buf_t **wp; /**< Write pointer */ + drm_buf_t **end; /**< End pointer */ spinlock_t read_lock; spinlock_t write_lock; } drm_waitlist_t; typedef struct drm_freelist { - int initialized; /* Freelist in use */ - atomic_t count; /* Number of free buffers */ - drm_buf_t *next; /* End pointer */ - - wait_queue_head_t waiting; /* Processes waiting on free bufs */ - int low_mark; /* Low water mark */ - int high_mark; /* High water mark */ - atomic_t wfh; /* If waiting for high mark */ + int initialized; /**< Freelist in use */ + atomic_t count; /**< Number of free buffers */ + drm_buf_t *next; /**< End pointer */ + + wait_queue_head_t waiting; /**< Processes waiting on free bufs */ + int low_mark; /**< Low water mark */ + int high_mark; /**< High water mark */ + atomic_t wfh; /**< If waiting for high mark */ spinlock_t lock; } drm_freelist_t; +/** + * Buffer entry. There is one of this for each buffer size order. + */ typedef struct drm_buf_entry { - int buf_size; - int buf_count; - drm_buf_t *buflist; + int buf_size; /**< size */ + int buf_count; /**< number of buffers */ + drm_buf_t *buflist; /**< buffer list */ int seg_count; int page_order; unsigned long *seglist; @@ -375,11 +467,19 @@ drm_freelist_t freelist; } drm_buf_entry_t; +/** + * Hardware lock. + * + * The lock structure is a simple cache-line aligned integer. To avoid + * processor bus contention on a multiprocessor system, there should not be any + * other data stored in the same cache line. + */ typedef struct drm_hw_lock { - __volatile__ unsigned int lock; - char padding[60]; /* Pad to cache line */ + __volatile__ unsigned int lock; /**< lock variable */ + char padding[60]; /**< Pad to cache line */ } drm_hw_lock_t; +/** File private data */ typedef struct drm_file { int authenticated; int minor; @@ -394,69 +494,85 @@ unsigned long lock_count; } drm_file_t; - +/** Wait queue */ typedef struct drm_queue { - atomic_t use_count; /* Outstanding uses (+1) */ - atomic_t finalization; /* Finalization in progress */ - atomic_t block_count; /* Count of processes waiting */ - atomic_t block_read; /* Queue blocked for reads */ - wait_queue_head_t read_queue; /* Processes waiting on block_read */ - atomic_t block_write; /* Queue blocked for writes */ - wait_queue_head_t write_queue; /* Processes waiting on block_write */ + atomic_t use_count; /**< Outstanding uses (+1) */ + atomic_t finalization; /**< Finalization in progress */ + atomic_t block_count; /**< Count of processes waiting */ + atomic_t block_read; /**< Queue blocked for reads */ + wait_queue_head_t read_queue; /**< Processes waiting on block_read */ + atomic_t block_write; /**< Queue blocked for writes */ + wait_queue_head_t write_queue; /**< Processes waiting on block_write */ #if 1 - atomic_t total_queued; /* Total queued statistic */ - atomic_t total_flushed;/* Total flushes statistic */ - atomic_t total_locks; /* Total locks statistics */ -#endif - drm_ctx_flags_t flags; /* Context preserving and 2D-only */ - drm_waitlist_t waitlist; /* Pending buffers */ - wait_queue_head_t flush_queue; /* Processes waiting until flush */ + atomic_t total_queued; /**< Total queued statistic */ + atomic_t total_flushed;/**< Total flushes statistic */ + atomic_t total_locks; /**< Total locks statistics */ +#endif + drm_ctx_flags_t flags; /**< Context preserving and 2D-only */ + drm_waitlist_t waitlist; /**< Pending buffers */ + wait_queue_head_t flush_queue; /**< Processes waiting until flush */ } drm_queue_t; +/** + * Lock data. + */ typedef struct drm_lock_data { - drm_hw_lock_t *hw_lock; /* Hardware lock */ - struct file *filp; /* File descr of lock holder (0=kernel) */ - wait_queue_head_t lock_queue; /* Queue of blocked processes */ - unsigned long lock_time; /* Time of last lock in jiffies */ + drm_hw_lock_t *hw_lock; /**< Hardware lock */ + struct file *filp; /**< File descr of lock holder (0=kernel) */ + wait_queue_head_t lock_queue; /**< Queue of blocked processes */ + unsigned long lock_time; /**< Time of last lock in jiffies */ } drm_lock_data_t; +/** + * DMA data. + */ typedef struct drm_device_dma { - drm_buf_entry_t bufs[DRM_MAX_ORDER+1]; - int buf_count; - drm_buf_t **buflist; /* Vector of pointers info bufs */ + drm_buf_entry_t bufs[DRM_MAX_ORDER+1]; /**< buffers, grouped by their size order */ + int buf_count; /**< total number of buffers */ + drm_buf_t **buflist; /**< Vector of pointers into drm_device_dma::bufs */ int seg_count; - int page_count; - unsigned long *pagelist; + int page_count; /**< number of pages */ + unsigned long *pagelist; /**< page list */ unsigned long byte_count; enum { _DRM_DMA_USE_AGP = 0x01, _DRM_DMA_USE_SG = 0x02 } flags; - /* DMA support */ - drm_buf_t *this_buffer; /* Buffer being sent */ - drm_buf_t *next_buffer; /* Selected buffer to send */ - drm_queue_t *next_queue; /* Queue from which buffer selected*/ - wait_queue_head_t waiting; /* Processes waiting on free bufs */ + /** \name DMA support */ + /*@{*/ + drm_buf_t *this_buffer; /**< Buffer being sent */ + drm_buf_t *next_buffer; /**< Selected buffer to send */ + drm_queue_t *next_queue; /**< Queue from which buffer selected*/ + wait_queue_head_t waiting; /**< Processes waiting on free bufs */ + /*@}*/ } drm_device_dma_t; #if __REALLY_HAVE_AGP +/** + * AGP memory entry. Stored as a doubly linked list. + */ typedef struct drm_agp_mem { - unsigned long handle; - struct agp_memory *memory; - unsigned long bound; /* address */ + unsigned long handle; /**< handle */ + struct agp_memory *memory; + unsigned long bound; /**< address */ int pages; - struct drm_agp_mem *prev; - struct drm_agp_mem *next; + struct drm_agp_mem *prev; /**< previous entry */ + struct drm_agp_mem *next; /**< next entry */ } drm_agp_mem_t; +/** + * AGP data. + * + * \sa DRM(agp_init)() and drm_device::agp. + */ typedef struct drm_agp_head { - struct agp_kern_info agp_info; - drm_agp_mem_t *memory; - unsigned long mode; - int enabled; - int acquired; + struct agp_kern_info agp_info; /**< AGP device information */ + drm_agp_mem_t *memory; /**< memory entries */ + unsigned long mode; /**< AGP mode */ + int enabled; /**< whether the AGP bus as been enabled */ + int acquired; /**< whether the AGP device has been acquired */ unsigned long base; int agp_mtrr; int cant_use_aperture; @@ -464,6 +580,9 @@ } drm_agp_head_t; #endif +/** + * Scatter-gather memory. + */ typedef struct drm_sg_mem { unsigned long handle; void *virtual; @@ -477,9 +596,12 @@ drm_hw_lock_t *lock; } drm_sigdata_t; +/** + * Mappings list + */ typedef struct drm_map_list { - struct list_head head; - drm_map_t *map; + struct list_head head; /**< list head */ + drm_map_t *map; /**< mapping */ } drm_map_list_t; typedef drm_map_t drm_local_map_t; @@ -495,88 +617,108 @@ #endif +/** + * DRM device structure. + */ typedef struct drm_device { - const char *name; /* Simple driver name */ - char *unique; /* Unique identifier: e.g., busid */ - int unique_len; /* Length of unique field */ - dev_t device; /* Device number for mknod */ - char *devname; /* For /proc/interrupts */ - - int blocked; /* Blocked due to VC switch? */ - struct proc_dir_entry *root; /* Root for this device's entries */ - - /* Locks */ - spinlock_t count_lock; /* For inuse, open_count, buf_use */ - struct semaphore struct_sem; /* For others */ - - /* Usage Counters */ - int open_count; /* Outstanding files open */ - atomic_t ioctl_count; /* Outstanding IOCTLs pending */ - atomic_t vma_count; /* Outstanding vma areas open */ - int buf_use; /* Buffers in use -- cannot alloc */ - atomic_t buf_alloc; /* Buffer allocation in progress */ + const char *name; /**< Simple driver name */ + char *unique; /**< Unique identifier: e.g., busid */ + int unique_len; /**< Length of unique field */ + dev_t device; /**< Device number for mknod */ + char *devname; /**< For /proc/interrupts */ + + int blocked; /**< Blocked due to VC switch? */ + struct proc_dir_entry *root; /**< Root for this device's entries */ + + /** \name Locks */ + /*@{*/ + spinlock_t count_lock; /**< For inuse, drm_device::open_count, drm_device::buf_use */ + struct semaphore struct_sem; /**< For others */ + /*@}*/ + + /** \name Usage Counters */ + /*@{*/ + int open_count; /**< Outstanding files open */ + atomic_t ioctl_count; /**< Outstanding IOCTLs pending */ + atomic_t vma_count; /**< Outstanding vma areas open */ + int buf_use; /**< Buffers in use -- cannot alloc */ + atomic_t buf_alloc; /**< Buffer allocation in progress */ + /*@}*/ - /* Performance counters */ + /** \name Performance counters */ + /*@{*/ unsigned long counters; drm_stat_type_t types[15]; atomic_t counts[15]; + /*@}*/ - /* Authentication */ - drm_file_t *file_first; - drm_file_t *file_last; - drm_magic_head_t magiclist[DRM_HASH_SIZE]; - - /* Memory management */ - drm_map_list_t *maplist; /* Linked list of regions */ + /** \name Authentication */ + /*@{*/ + drm_file_t *file_first; /**< file list head */ + drm_file_t *file_last; /**< file list tail */ + drm_magic_head_t magiclist[DRM_HASH_SIZE]; /**< magic hash table */ + /*@}*/ + + /** \name Memory management */ + /*@{*/ + drm_map_list_t *maplist; /**< Linked list of regions */ + int map_count; /**< Number of mappable regions */ - drm_map_t **context_sareas; + drm_map_t **context_sareas; /**< per-context SAREA's */ int max_context; - drm_vma_entry_t *vmalist; /* List of vmas (for debugging) */ - drm_lock_data_t lock; /* Information on hardware lock */ - - /* DMA queues (contexts) */ - int queue_count; /* Number of active DMA queues */ - int queue_reserved; /* Number of reserved DMA queues */ - int queue_slots; /* Actual length of queuelist */ - drm_queue_t **queuelist; /* Vector of pointers to DMA queues */ - drm_device_dma_t *dma; /* Optional pointer for DMA support */ - - /* Context support */ - int irq; /* Interrupt used by board */ - __volatile__ long context_flag; /* Context swapping flag */ - __volatile__ long interrupt_flag; /* Interruption handler flag */ - __volatile__ long dma_flag; /* DMA dispatch flag */ - struct timer_list timer; /* Timer for delaying ctx switch */ - wait_queue_head_t context_wait; /* Processes waiting on ctx switch */ - int last_checked; /* Last context checked for DMA */ - int last_context; /* Last current context */ - unsigned long last_switch; /* jiffies at last context switch */ - struct work_struct work; + drm_vma_entry_t *vmalist; /**< List of vmas (for debugging) */ + drm_lock_data_t lock; /**< Information on hardware lock */ + /*@}*/ + + /** \name DMA queues (contexts) */ + /*@{*/ + int queue_count; /**< Number of active DMA queues */ + int queue_reserved; /**< Number of reserved DMA queues */ + int queue_slots; /**< Actual length of queuelist */ + drm_queue_t **queuelist; /**< Vector of pointers to DMA queues */ + drm_device_dma_t *dma; /**< Optional pointer for DMA support */ + /*@}*/ + + /** \name Context support */ + /*@{*/ + int irq; /**< Interrupt used by board */ + __volatile__ long context_flag; /**< Context swapping flag */ + __volatile__ long interrupt_flag; /**< Interruption handler flag */ + __volatile__ long dma_flag; /**< DMA dispatch flag */ + struct timer_list timer; /**< Timer for delaying ctx switch */ + wait_queue_head_t context_wait; /**< Processes waiting on ctx switch */ + int last_checked; /**< Last context checked for DMA */ + int last_context; /**< Last current context */ + unsigned long last_switch; /**< jiffies at last context switch */ + /*@}*/ + + struct work_struct work; + /** \name VBLANK IRQ support */ + /*@{*/ #if __HAVE_VBL_IRQ - wait_queue_head_t vbl_queue; + wait_queue_head_t vbl_queue; /**< VBLANK wait queue */ atomic_t vbl_received; spinlock_t vbl_lock; - drm_vbl_sig_t vbl_sigs; + drm_vbl_sig_t vbl_sigs; /**< signal list to send on VBLANK */ unsigned int vbl_pending; #endif + /*@}*/ cycles_t ctx_start; cycles_t lck_start; - /* Callback to X server for context switch - and for heavy-handed reset. */ - char buf[DRM_BSZ]; /* Output buffer */ - char *buf_rp; /* Read pointer */ - char *buf_wp; /* Write pointer */ - char *buf_end; /* End pointer */ - struct fasync_struct *buf_async;/* Processes waiting for SIGIO */ - wait_queue_head_t buf_readers; /* Processes waiting to read */ - wait_queue_head_t buf_writers; /* Processes waiting to ctx switch */ + char buf[DRM_BSZ]; /**< Output buffer */ + char *buf_rp; /**< Read pointer */ + char *buf_wp; /**< Write pointer */ + char *buf_end; /**< End pointer */ + struct fasync_struct *buf_async;/**< Processes waiting for SIGIO */ + wait_queue_head_t buf_readers; /**< Processes waiting to read */ + wait_queue_head_t buf_writers; /**< Processes waiting to ctx switch */ #if __REALLY_HAVE_AGP - drm_agp_head_t *agp; + drm_agp_head_t *agp; /**< AGP data */ #endif - struct pci_dev *pdev; + struct pci_dev *pdev; /**< PCI device structure */ #ifdef __alpha__ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,3) struct pci_controler *hose; @@ -584,17 +726,17 @@ struct pci_controller *hose; #endif #endif - drm_sg_mem_t *sg; /* Scatter gather memory */ - unsigned long *ctx_bitmap; - void *dev_private; - drm_sigdata_t sigdata; /* For block_all_signals */ + drm_sg_mem_t *sg; /**< Scatter gather memory */ + unsigned long *ctx_bitmap; /**< context bitmap */ + void *dev_private; /**< device private data */ + drm_sigdata_t sigdata; /**< For block_all_signals */ sigset_t sigmask; } drm_device_t; -/* ================================================================ - * Internal function definitions - */ +/******************************************************************/ +/** \name Internal function definitions */ +/*@{*/ /* Misc. support (drm_init.h) */ extern int DRM(flags); @@ -653,7 +795,8 @@ extern void DRM(free_pages)(unsigned long address, int order, int area); extern void *DRM(ioremap)(unsigned long offset, unsigned long size, drm_device_t *dev); -extern void *DRM(ioremap_nocache)(unsigned long offset, unsigned long size, drm_device_t *dev); +extern void *DRM(ioremap_nocache)(unsigned long offset, unsigned long size, + drm_device_t *dev); extern void DRM(ioremapfree)(void *pt, unsigned long size, drm_device_t *dev); #if __REALLY_HAVE_AGP @@ -841,6 +984,8 @@ extern int DRM(ati_pcigart_cleanup)(drm_device_t *dev, unsigned long addr, dma_addr_t bus_addr); + +/*@}*/ #endif /* __KERNEL__ */ #endif diff -Nru a/drivers/char/drm/drm_agpsupport.h b/drivers/char/drm/drm_agpsupport.h --- a/drivers/char/drm/drm_agpsupport.h Wed May 21 02:15:24 2003 +++ b/drivers/char/drm/drm_agpsupport.h Thu Jul 10 23:40:39 2003 @@ -1,6 +1,12 @@ -/* drm_agpsupport.h -- DRM support for AGP/GART backend -*- linux-c -*- - * Created: Mon Dec 13 09:56:45 1999 by faith@precisioninsight.com - * +/** + * \file drm_agpsupport.h + * DRM support for AGP/GART backend + * + * \author Rickard E. (Rik) Faith + * \author Gareth Hughes + */ + +/* * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. * All Rights Reserved. @@ -23,10 +29,6 @@ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. - * - * Author: - * Rickard E. (Rik) Faith - * Gareth Hughes */ #include "drmP.h" @@ -34,17 +36,33 @@ #if __REALLY_HAVE_AGP + #define DRM_AGP_GET (drm_agp_t *)inter_module_get("drm_agp") #define DRM_AGP_PUT inter_module_put("drm_agp") +/** + * Pointer to the drm_agp_t structure made available by the agpgart module. + */ static const drm_agp_t *drm_agp = NULL; +/** + * AGP information ioctl. + * + * \param inode device inode. + * \param filp file pointer. + * \param cmd command. + * \param arg pointer to a (output) drm_agp_info structure. + * \return zero on success or a negative number on failure. + * + * Verifies the AGP device has been initialized and acquired and fills in the + * drm_agp_info structure with the information in drm_agp_head::agp_info. + */ int DRM(agp_info)(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) { drm_file_t *priv = filp->private_data; drm_device_t *dev = priv->dev; - struct agp_kern_info *kern; + struct agp_kern_info *kern; drm_agp_info_t info; if (!dev->agp || !dev->agp->acquired || !drm_agp->copy_info) @@ -66,6 +84,18 @@ return 0; } +/** + * Acquire the AGP device (ioctl). + * + * \param inode device inode. + * \param filp file pointer. + * \param cmd command. + * \param arg user argument. + * \return zero on success or a negative number on failure. + * + * Verifies the AGP device hasn't been acquired before and calls + * drm_agp->acquire(). + */ int DRM(agp_acquire)(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) { @@ -80,6 +110,17 @@ return 0; } +/** + * Release the AGP device (ioctl). + * + * \param inode device inode. + * \param filp file pointer. + * \param cmd command. + * \param arg user argument. + * \return zero on success or a negative number on failure. + * + * Verifies the AGP device has been acquired and calls drm_agp->release(). + */ int DRM(agp_release)(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) { @@ -94,11 +135,28 @@ } +/** + * Release the AGP device. + * + * Calls drm_agp->release(). + */ void DRM(agp_do_release)(void) { if (drm_agp->release) drm_agp->release(); } +/** + * Enable the AGP bus. + * + * \param inode device inode. + * \param filp file pointer. + * \param cmd command. + * \param arg pointer to a drm_agp_mode structure. + * \return zero on success or a negative number on failure. + * + * Verifies the AGP device has been acquired but not enabled, and calls + * drm_agp->enable(). + */ int DRM(agp_enable)(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) { @@ -119,6 +177,18 @@ return 0; } +/** + * Allocate AGP memory. + * + * \param inode device inode. + * \param filp file pointer. + * \param cmd command. + * \param arg pointer to a drm_agp_buffer structure. + * \return zero on success or a negative number on failure. + * + * Verifies the AGP device is present and has been acquired, allocates the + * memory via alloc_agp() and creates a drm_agp_mem entry for it. + */ int DRM(agp_alloc)(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) { @@ -146,7 +216,7 @@ return -ENOMEM; } - entry->handle = (unsigned long)memory->key; + entry->handle = (unsigned long)memory->key + 1; entry->memory = memory; entry->bound = 0; entry->pages = pages; @@ -168,6 +238,15 @@ return 0; } +/** + * Search for the AGP memory entry associated with a handle. + * + * \param dev DRM device structure. + * \param handle AGP memory handle. + * \return pointer to the drm_agp_mem structure associated with \p handle. + * + * Walks through drm_agp_head::memory until finding a matching handle. + */ static drm_agp_mem_t *DRM(agp_lookup_entry)(drm_device_t *dev, unsigned long handle) { @@ -179,6 +258,18 @@ return NULL; } +/** + * Unbind AGP memory from the GATT (ioctl). + * + * \param inode device inode. + * \param filp file pointer. + * \param cmd command. + * \param arg pointer to a drm_agp_binding structure. + * \return zero on success or a negative number on failure. + * + * Verifies the AGP device is present and acquired, looks-up the AGP memory + * entry and passes it to the unbind_agp() function. + */ int DRM(agp_unbind)(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) { @@ -200,6 +291,19 @@ return ret; } +/** + * Bind AGP memory into the GATT (ioctl) + * + * \param inode device inode. + * \param filp file pointer. + * \param cmd command. + * \param arg pointer to a drm_agp_binding structure. + * \return zero on success or a negative number on failure. + * + * Verifies the AGP device is present and has been acquired and that no memory + * is currently bound into the GATT. Looks-up the AGP memory entry and passes + * it to bind_agp() function. + */ int DRM(agp_bind)(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) { @@ -225,6 +329,20 @@ return 0; } +/** + * Free AGP memory (ioctl). + * + * \param inode device inode. + * \param filp file pointer. + * \param cmd command. + * \param arg pointer to a drm_agp_buffer structure. + * \return zero on success or a negative number on failure. + * + * Verifies the AGP device is present and has been acquired and looks up the + * AGP memory entry. If the memory it's currently bound, unbind it via + * unbind_agp(). Frees it via free_agp() as well as the entry itself + * and unlinks from the doubly linked list it's inserted in. + */ int DRM(agp_free)(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) { @@ -248,6 +366,15 @@ return 0; } +/** + * Initialize the AGP resources. + * + * \return pointer to a drm_agp_head structure. + * + * Gets the drm_agp_t structure which is made available by the agpgart module + * via the inter_module_* functions. Creates and initializes a drm_agp_head + * structure. + */ drm_agp_head_t *DRM(agp_init)(void) { drm_agp_head_t *head = NULL; @@ -274,18 +401,25 @@ return head; } +/** + * Free the AGP resources. + * + * Releases the pointer in ::drm_agp. + */ void DRM(agp_uninit)(void) { DRM_AGP_PUT; drm_agp = NULL; } +/** Calls drm_agp->allocate_memory() */ struct agp_memory *DRM(agp_allocate_memory)(size_t pages, u32 type) { if (!drm_agp->allocate_memory) return NULL; return drm_agp->allocate_memory(pages, type); } +/** Calls drm_agp->free_memory() */ int DRM(agp_free_memory)(struct agp_memory *handle) { if (!handle || !drm_agp->free_memory) return 0; @@ -293,12 +427,14 @@ return 1; } +/** Calls drm_agp->bind_memory() */ int DRM(agp_bind_memory)(struct agp_memory *handle, off_t start) { if (!handle || !drm_agp->bind_memory) return -EINVAL; return drm_agp->bind_memory(handle, start); } +/** Calls drm_agp->unbind_memory() */ int DRM(agp_unbind_memory)(struct agp_memory *handle) { if (!handle || !drm_agp->unbind_memory) return -EINVAL; diff -Nru a/drivers/char/drm/drm_auth.h b/drivers/char/drm/drm_auth.h --- a/drivers/char/drm/drm_auth.h Thu Mar 13 16:52:15 2003 +++ b/drivers/char/drm/drm_auth.h Thu Jul 10 23:18:01 2003 @@ -1,4 +1,12 @@ -/* drm_auth.h -- IOCTLs for authentication -*- linux-c -*- +/** + * \file drm_auth.h + * IOCTLs for authentication + * + * \author Rickard E. (Rik) Faith + * \author Gareth Hughes + */ + +/* * Created: Tue Feb 2 08:37:54 1999 by faith@valinux.com * * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. @@ -23,19 +31,34 @@ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. - * - * Authors: - * Rickard E. (Rik) Faith - * Gareth Hughes */ #include "drmP.h" +/** + * Generate a hash key from a magic. + * + * \param magic magic. + * \return hash key. + * + * The key is the modulus of the hash table size, #DRM_HASH_SIZE, which must be + * a power of 2. + */ static int DRM(hash_magic)(drm_magic_t magic) { return magic & (DRM_HASH_SIZE-1); } +/** + * Find the file with the given magic number. + * + * \param dev DRM device. + * \param magic magic number. + * + * Searches in drm_device::magiclist within all files with the same hash key + * the one with matching magic number, while holding the drm_device::struct_sem + * lock. + */ static drm_file_t *DRM(find_file)(drm_device_t *dev, drm_magic_t magic) { drm_file_t *retval = NULL; @@ -53,6 +76,17 @@ return retval; } +/** + * Adds a magic number. + * + * \param dev DRM device. + * \param priv file private data. + * \param magic magic number. + * + * Creates a drm_magic_entry structure and appends to the linked list + * associated the magic number hash key in drm_device::magiclist, while holding + * the drm_device::struct_sem lock. + */ int DRM(add_magic)(drm_device_t *dev, drm_file_t *priv, drm_magic_t magic) { int hash; @@ -81,12 +115,22 @@ return 0; } +/** + * Remove a magic number. + * + * \param dev DRM device. + * \param magic magic number. + * + * Searches and unlinks the entry in drm_device::magiclist with the magic + * number hash key, while holding the drm_device::struct_sem lock. + */ int DRM(remove_magic)(drm_device_t *dev, drm_magic_t magic) { drm_magic_entry_t *prev = NULL; drm_magic_entry_t *pt; int hash; + DRM_DEBUG("%d\n", magic); hash = DRM(hash_magic)(magic); @@ -113,6 +157,19 @@ return -EINVAL; } +/** + * Get a unique magic number (ioctl). + * + * \param inode device inode. + * \param filp file pointer. + * \param cmd command. + * \param arg pointer to a resulting drm_auth structure. + * \return zero on success, or a negative number on failure. + * + * If there is a magic number in drm_file::magic then use it, otherwise + * searches an unique non-zero magic number and add it associating it with \p + * filp. + */ int DRM(getmagic)(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) { @@ -142,6 +199,17 @@ return 0; } +/** + * Authenticate with a magic. + * + * \param inode device inode. + * \param filp file pointer. + * \param cmd command. + * \param arg pointer to a drm_auth structure. + * \return zero if authentication successed, or a negative number otherwise. + * + * Checks if \p filp is associated with the magic number passed in \arg. + */ int DRM(authmagic)(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) { diff -Nru a/drivers/char/drm/drm_bufs.h b/drivers/char/drm/drm_bufs.h --- a/drivers/char/drm/drm_bufs.h Sat May 24 12:57:42 2003 +++ b/drivers/char/drm/drm_bufs.h Thu Jul 10 23:18:01 2003 @@ -1,4 +1,12 @@ -/* drm_bufs.h -- Generic buffer template -*- linux-c -*- +/** + * \file drm_bufs.h + * Generic buffer template + * + * \author Rickard E. (Rik) Faith + * \author Gareth Hughes + */ + +/* * Created: Thu Nov 23 03:10:50 2000 by gareth@valinux.com * * Copyright 1999, 2000 Precision Insight, Inc., Cedar Park, Texas. @@ -23,10 +31,6 @@ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. - * - * Authors: - * Rickard E. (Rik) Faith - * Gareth Hughes */ #include @@ -51,8 +55,15 @@ #endif #endif -/* - * Compute order. Can be made faster. + +/** + * Compute size order. Returns the exponent of the smaller power of two which + * is greater or equal to given number. + * + * \param size size. + * \return order. + * + * \todo Can be made faster. */ int DRM(order)( unsigned long size ) { @@ -67,6 +78,19 @@ return order; } +/** + * Ioctl to specify a range of memory that is available for mapping by a non-root process. + * + * \param inode device inode. + * \param filp file pointer. + * \param cmd command. + * \param arg pointer to a drm_map structure. + * \return zero on success or a negative value on error. + * + * Adjusts the memory offset to its absolute value according to the mapping + * type. Adds the map to the map list drm_device::maplist. Adds MTRR's where + * applicable and if supported by the kernel. + */ int DRM(addmap)( struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg ) { @@ -186,10 +210,22 @@ } -/* Remove a map private from list and deallocate resources if the mapping +/** + * Remove a map private from list and deallocate resources if the mapping * isn't in use. + * + * \param inode device inode. + * \param filp file pointer. + * \param cmd command. + * \param arg pointer to a drm_map_t structure. + * \return zero on success or a negative value on error. + * + * Searches the map on drm_device::maplist, removes it from the list, see if + * its being used, and free any associate resource (such as MTRR's) if it's not + * being on use. + * + * \sa addmap(). */ - int DRM(rmmap)(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) { @@ -262,7 +298,13 @@ #if __HAVE_DMA - +/** + * Cleanup after an error on one of the addbufs() functions. + * + * \param entry buffer entry where the error occurred. + * + * Frees any pages and buffers associated with the given entry. + */ static void DRM(cleanup_buf_error)(drm_buf_entry_t *entry) { int i; @@ -305,6 +347,19 @@ } #if __REALLY_HAVE_AGP +/** + * Add AGP buffers for DMA transfers (ioctl). + * + * \param inode device inode. + * \param filp file pointer. + * \param cmd command. + * \param arg pointer to a drm_buf_desc_t request. + * \return zero on success or a negative number on failure. + * + * After some sanity checks creates a drm_buf structure for each buffer and + * reallocates the buffer list of the same size order to accommodate the new + * buffers. + */ int DRM(addbufs_agp)( struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg ) { @@ -884,6 +939,20 @@ } #endif /* __HAVE_SG */ +/** + * Add buffers for DMA transfers (ioctl). + * + * \param inode device inode. + * \param filp file pointer. + * \param cmd command. + * \param arg pointer to a drm_buf_desc_t request. + * \return zero on success or a negative number on failure. + * + * According with the memory type specified in drm_buf_desc::flags and the + * build options, it dispatches the call either to addbufs_agp(), + * addbufs_sg() or addbufs_pci() for AGP, scatter-gather or consistent + * PCI memory respectively. + */ int DRM(addbufs)( struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg ) { @@ -910,6 +979,24 @@ #endif } + +/** + * Get information about the buffer mappings. + * + * This was originally mean for debugging purposes, or by a sophisticated + * client library to determine how best to use the available buffers (e.g., + * large buffers can be used for image transfer). + * + * \param inode device inode. + * \param filp file pointer. + * \param cmd command. + * \param arg pointer to a drm_buf_info structure. + * \return zero on success or a negative number on failure. + * + * Increments drm_device::buf_use while holding the drm_device::count_lock + * lock, preventing of allocating more buffers after this call. Information + * about each requested buffer is then copied into user space. + */ int DRM(infobufs)( struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg ) { @@ -981,6 +1068,20 @@ return 0; } +/** + * Specifies a low and high water mark for buffer allocation + * + * \param inode device inode. + * \param filp file pointer. + * \param cmd command. + * \param arg a pointer to a drm_buf_desc structure. + * \return zero on success or a negative number on failure. + * + * Verifies that the size order is bounded between the admissible orders and + * updates the respective drm_device_dma::bufs entry low and high water mark. + * + * \note This ioctl is deprecated and mostly never used. + */ int DRM(markbufs)( struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg ) { @@ -1015,6 +1116,18 @@ return 0; } +/** + * Unreserve the buffers in list, previously reserved using drmDMA. + * + * \param inode device inode. + * \param filp file pointer. + * \param cmd command. + * \param arg pointer to a drm_buf_free structure. + * \return zero on success or a negative number on failure. + * + * Calls free_buffer() for each used buffer. + * This function is primarily used for debugging. + */ int DRM(freebufs)( struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg ) { @@ -1056,6 +1169,19 @@ return 0; } +/** + * Maps all of the DMA buffers into client-virtual space (ioctl). + * + * \param inode device inode. + * \param filp file pointer. + * \param cmd command. + * \param arg pointer to a drm_buf_map structure. + * \return zero on success or a negative number on failure. + * + * Maps the AGP or SG buffer region with do_mmap(), and copies information + * about each buffer into user space. The PCI buffers are already mapped on the + * addbufs_pci() call. + */ int DRM(mapbufs)( struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg ) { diff -Nru a/drivers/char/drm/drm_context.h b/drivers/char/drm/drm_context.h --- a/drivers/char/drm/drm_context.h Fri Apr 25 15:57:57 2003 +++ b/drivers/char/drm/drm_context.h Thu Jul 10 23:18:01 2003 @@ -1,4 +1,12 @@ -/* drm_context.h -- IOCTLs for generic contexts -*- linux-c -*- +/** + * \file drm_context.h + * IOCTLs for generic contexts + * + * \author Rickard E. (Rik) Faith + * \author Gareth Hughes + */ + +/* * Created: Fri Nov 24 18:31:37 2000 by gareth@valinux.com * * Copyright 1999, 2000 Precision Insight, Inc., Cedar Park, Texas. @@ -23,10 +31,9 @@ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. - * - * Authors: - * Rickard E. (Rik) Faith - * Gareth Hughes + */ + +/* * ChangeLog: * 2001-11-16 Torsten Duwe * added context constructor/destructor hooks, @@ -40,10 +47,20 @@ #endif -/* ================================================================ - * Context bitmap support - */ +/******************************************************************/ +/** \name Context bitmap support */ +/*@{*/ +/** + * Free a handle from the context bitmap. + * + * \param dev DRM device. + * \param ctx_handle context handle. + * + * Clears the bit specified by \p ctx_handle in drm_device::ctx_bitmap and the entry + * in drm_device::context_sareas, while holding the drm_device::struct_sem + * lock. + */ void DRM(ctxbitmap_free)( drm_device_t *dev, int ctx_handle ) { if ( ctx_handle < 0 ) goto failed; @@ -62,6 +79,16 @@ return; } +/** + * Context bitmap allocation. + * + * \param dev DRM device. + * \return (non-negative) context handle on success or a negative number on failure. + * + * Find the first zero bit in drm_device::ctx_bitmap and (re)allocates + * drm_device::context_sareas to accommodate the new entry while holding the + * drm_device::struct_sem lock. + */ int DRM(ctxbitmap_next)( drm_device_t *dev ) { int bit; @@ -112,6 +139,14 @@ return -1; } +/** + * Context bitmap initialization. + * + * \param dev DRM device. + * + * Allocates and initialize drm_device::ctx_bitmap and drm_device::context_sareas, while holding + * the drm_device::struct_sem lock. + */ int DRM(ctxbitmap_init)( drm_device_t *dev ) { int i; @@ -137,6 +172,14 @@ return 0; } +/** + * Context bitmap cleanup. + * + * \param dev DRM device. + * + * Frees drm_device::ctx_bitmap and drm_device::context_sareas, while holding + * the drm_device::struct_sem lock. + */ void DRM(ctxbitmap_cleanup)( drm_device_t *dev ) { down(&dev->struct_sem); @@ -148,10 +191,24 @@ up(&dev->struct_sem); } -/* ================================================================ - * Per Context SAREA Support - */ +/*@}*/ +/******************************************************************/ +/** \name Per Context SAREA Support */ +/*@{*/ + +/** + * Get per-context SAREA. + * + * \param inode device inode. + * \param filp file pointer. + * \param cmd command. + * \param arg user argument pointing to a drm_ctx_priv_map structure. + * \return zero on success or a negative number on failure. + * + * Gets the map from drm_device::context_sareas with the handle specified and + * returns its handle. + */ int DRM(getsareactx)(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) { @@ -180,6 +237,18 @@ return 0; } +/** + * Set per-context SAREA. + * + * \param inode device inode. + * \param filp file pointer. + * \param cmd command. + * \param arg user argument pointing to a drm_ctx_priv_map structure. + * \return zero on success or a negative number on failure. + * + * Searches the mapping specified in \p arg and update the entry in + * drm_device::context_sareas with it. + */ int DRM(setsareactx)(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) { @@ -218,10 +287,22 @@ return 0; } -/* ================================================================ - * The actual DRM context handling routines - */ +/*@}*/ +/******************************************************************/ +/** \name The actual DRM context handling routines */ +/*@{*/ + +/** + * Switch context. + * + * \param dev DRM device. + * \param old old context handle. + * \param new new context handle. + * \return zero on success or a negative number on failure. + * + * Attempt to set drm_device::context_flag. + */ int DRM(context_switch)( drm_device_t *dev, int old, int new ) { if ( test_and_set_bit( 0, &dev->context_flag ) ) { @@ -240,6 +321,17 @@ return 0; } +/** + * Complete context switch. + * + * \param dev DRM device. + * \param new new context handle. + * \return zero on success or a negative number on failure. + * + * Updates drm_device::last_context and drm_device::last_switch. Verifies the + * hardware lock is held, clears the drm_device::context_flag and wakes up + * drm_device::context_wait. + */ int DRM(context_switch_complete)( drm_device_t *dev, int new ) { dev->last_context = new; /* PRE/POST: This is the _only_ writer. */ @@ -258,6 +350,15 @@ return 0; } +/** + * Reserve contexts. + * + * \param inode device inode. + * \param filp file pointer. + * \param cmd command. + * \param arg user argument pointing to a drm_ctx_res structure. + * \return zero on success or a negative number on failure. + */ int DRM(resctx)( struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg ) { @@ -284,6 +385,17 @@ return 0; } +/** + * Add context. + * + * \param inode device inode. + * \param filp file pointer. + * \param cmd command. + * \param arg user argument pointing to a drm_ctx structure. + * \return zero on success or a negative number on failure. + * + * Get a new handle for the context and copy to userspace. + */ int DRM(addctx)( struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg ) { @@ -322,6 +434,15 @@ return 0; } +/** + * Get context. + * + * \param inode device inode. + * \param filp file pointer. + * \param cmd command. + * \param arg user argument pointing to a drm_ctx structure. + * \return zero on success or a negative number on failure. + */ int DRM(getctx)( struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg ) { @@ -338,6 +459,17 @@ return 0; } +/** + * Switch context. + * + * \param inode device inode. + * \param filp file pointer. + * \param cmd command. + * \param arg user argument pointing to a drm_ctx structure. + * \return zero on success or a negative number on failure. + * + * Calls context_switch(). + */ int DRM(switchctx)( struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg ) { @@ -352,6 +484,17 @@ return DRM(context_switch)( dev, dev->last_context, ctx.handle ); } +/** + * New context. + * + * \param inode device inode. + * \param filp file pointer. + * \param cmd command. + * \param arg user argument pointing to a drm_ctx structure. + * \return zero on success or a negative number on failure. + * + * Calls context_switch_complete(). + */ int DRM(newctx)( struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg ) { @@ -368,6 +511,17 @@ return 0; } +/** + * Remove context. + * + * \param inode device inode. + * \param filp file pointer. + * \param cmd command. + * \param arg user argument pointing to a drm_ctx structure. + * \return zero on success or a negative number on failure. + * + * If not the special kernel context, calls ctxbitmap_free() to free the specified context. + */ int DRM(rmctx)( struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg ) { @@ -392,3 +546,4 @@ return 0; } +/*@}*/ diff -Nru a/drivers/char/drm/drm_dma.h b/drivers/char/drm/drm_dma.h --- a/drivers/char/drm/drm_dma.h Mon Apr 28 20:00:59 2003 +++ b/drivers/char/drm/drm_dma.h Thu Jul 10 23:18:01 2003 @@ -1,4 +1,12 @@ -/* drm_dma.c -- DMA IOCTL and function support -*- linux-c -*- +/** + * \file drm_dma.h + * DMA IOCTL and function support + * + * \author Rickard E. (Rik) Faith + * \author Gareth Hughes + */ + +/* * Created: Fri Mar 19 14:30:16 1999 by faith@valinux.com * * Copyright 1999, 2000 Precision Insight, Inc., Cedar Park, Texas. @@ -23,10 +31,6 @@ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. - * - * Authors: - * Rickard E. (Rik) Faith - * Gareth Hughes */ #include "drmP.h" @@ -51,6 +55,14 @@ #if __HAVE_DMA +/** + * Initialize the DMA data. + * + * \param dev DRM device. + * \return zero on success or a negative value on failure. + * + * Allocate and initialize a drm_device_dma structure. + */ int DRM(dma_setup)( drm_device_t *dev ) { int i; @@ -67,6 +79,14 @@ return 0; } +/** + * Cleanup the DMA resources. + * + * \param dev DRM device. + * + * Free all pages associated with DMA buffers, the buffers and pages lists, and + * finally the the drm_device::dma structure itself. + */ void DRM(dma_takedown)(drm_device_t *dev) { drm_device_dma_t *dma = dev->dma; @@ -128,7 +148,14 @@ } - +/** + * Free a buffer. + * + * \param dev DRM device. + * \param buf buffer to free. + * + * Resets the fields of \p buf. + */ void DRM(free_buffer)(drm_device_t *dev, drm_buf_t *buf) { if (!buf) return; @@ -154,6 +181,13 @@ } #if !__HAVE_DMA_RECLAIM +/** + * Reclaim the buffers. + * + * \param filp file pointer. + * + * Frees each buffer associated with \p filp not already on the hardware. + */ void DRM(reclaim_buffers)( struct file *filp ) { drm_file_t *priv = filp->private_data; @@ -185,6 +219,16 @@ #if __HAVE_DMA_IRQ +/** + * Install IRQ handler. + * + * \param dev DRM device. + * \param irq IRQ number. + * + * Initializes the IRQ related data, and setups drm_device::vbl_queue. Installs the handler, calling the driver + * \c DRM(driver_irq_preinstall)() and \c DRM(driver_irq_postinstall)() functions + * before and after the installation. + */ int DRM(irq_install)( drm_device_t *dev, int irq ) { int ret; @@ -250,6 +294,13 @@ return 0; } +/** + * Uninstall the IRQ handler. + * + * \param dev DRM device. + * + * Calls the driver's \c DRM(driver_irq_uninstall)() function, and stops the irq. + */ int DRM(irq_uninstall)( drm_device_t *dev ) { int irq; @@ -271,6 +322,17 @@ return 0; } +/** + * IRQ control ioctl. + * + * \param inode device inode. + * \param filp file pointer. + * \param cmd command. + * \param arg user argument, pointing to a drm_control structure. + * \return zero on success or a negative number on failure. + * + * Calls irq_install() or irq_uninstall() according to \p arg. + */ int DRM(control)( struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg ) { @@ -293,6 +355,25 @@ #if __HAVE_VBL_IRQ +/** + * Wait for VBLANK. + * + * \param inode device inode. + * \param filp file pointer. + * \param cmd command. + * \param data user argument, pointing to a drm_wait_vblank structure. + * \return zero on success or a negative number on failure. + * + * Verifies the IRQ is installed. + * + * If a signal is requested checks if this task has already scheduled the same signal + * for the same vblank sequence number - nothing to be done in + * that case. If the number of tasks waiting for the interrupt exceeds 100 the + * function fails. Otherwise adds a new entry to drm_device::vbl_sigs for this + * task. + * + * If a signal is not requested, then calls vblank_wait(). + */ int DRM(wait_vblank)( DRM_IOCTL_ARGS ) { drm_file_t *priv = filp->private_data; @@ -381,6 +462,15 @@ return ret; } +/** + * Send the VBLANK signals. + * + * \param dev DRM device. + * + * Sends a signal for each task in drm_device::vbl_sigs and empties the list. + * + * If a signal is not requested, then calls vblank_wait(). + */ void DRM(vbl_send_signals)( drm_device_t *dev ) { struct list_head *list, *tmp; diff -Nru a/drivers/char/drm/drm_drawable.h b/drivers/char/drm/drm_drawable.h --- a/drivers/char/drm/drm_drawable.h Thu Mar 13 16:52:15 2003 +++ b/drivers/char/drm/drm_drawable.h Thu Jul 10 23:18:01 2003 @@ -1,4 +1,12 @@ -/* drm_drawable.h -- IOCTLs for drawables -*- linux-c -*- +/** + * \file drm_drawable.h + * IOCTLs for drawables + * + * \author Rickard E. (Rik) Faith + * \author Gareth Hughes + */ + +/* * Created: Tue Feb 2 08:37:54 1999 by faith@valinux.com * * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. @@ -23,14 +31,11 @@ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. - * - * Authors: - * Rickard E. (Rik) Faith - * Gareth Hughes */ #include "drmP.h" +/** No-op. */ int DRM(adddraw)(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) { @@ -43,6 +48,7 @@ return 0; } +/** No-op. */ int DRM(rmdraw)(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) { diff -Nru a/drivers/char/drm/drm_drv.h b/drivers/char/drm/drm_drv.h --- a/drivers/char/drm/drm_drv.h Sat May 24 12:57:42 2003 +++ b/drivers/char/drm/drm_drv.h Fri Jul 11 22:00:04 2003 @@ -1,4 +1,31 @@ -/* drm_drv.h -- Generic driver template -*- linux-c -*- +/** + * \file drm_drv.h + * Generic driver template + * + * \author Rickard E. (Rik) Faith + * \author Gareth Hughes + * + * To use this template, you must at least define the following (samples + * given for the MGA driver): + * + * \code + * #define DRIVER_AUTHOR "VA Linux Systems, Inc." + * + * #define DRIVER_NAME "mga" + * #define DRIVER_DESC "Matrox G200/G400" + * #define DRIVER_DATE "20001127" + * + * #define DRIVER_MAJOR 2 + * #define DRIVER_MINOR 0 + * #define DRIVER_PATCHLEVEL 2 + * + * #define DRIVER_IOCTL_COUNT DRM_ARRAY_SIZE( mga_ioctls ) + * + * #define DRM(x) mga_##x + * \endcode + */ + +/* * Created: Thu Nov 23 03:10:50 2000 by gareth@valinux.com * * Copyright 1999, 2000 Precision Insight, Inc., Cedar Park, Texas. @@ -23,29 +50,6 @@ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. - * - * Authors: - * Rickard E. (Rik) Faith - * Gareth Hughes - */ - -/* - * To use this template, you must at least define the following (samples - * given for the MGA driver): - * - * #define DRIVER_AUTHOR "VA Linux Systems, Inc." - * - * #define DRIVER_NAME "mga" - * #define DRIVER_DESC "Matrox G200/G400" - * #define DRIVER_DATE "20001127" - * - * #define DRIVER_MAJOR 2 - * #define DRIVER_MINOR 0 - * #define DRIVER_PATCHLEVEL 2 - * - * #define DRIVER_IOCTL_COUNT DRM_ARRAY_SIZE( mga_ioctls ) - * - * #define DRM(x) mga_##x */ #ifndef __MUST_HAVE_AGP @@ -84,6 +88,10 @@ #ifndef __HAVE_SG #define __HAVE_SG 0 #endif +/* __HAVE_KERNEL_CTX_SWITCH isn't used by any of the drm modules in + * the DRI cvs tree, but it is required by the kernel tree's sparc + * driver. + */ #ifndef __HAVE_KERNEL_CTX_SWITCH #define __HAVE_KERNEL_CTX_SWITCH 0 #endif @@ -134,12 +142,13 @@ #endif #ifndef MODULE -/* DRM(options) is called by the kernel to parse command-line options - * passed via the boot-loader (e.g., LILO). It calls the insmod option - * routine, drm_parse_drm. - */ -/* Use an additional macro to avoid preprocessor troubles */ +/** Use an additional macro to avoid preprocessor troubles */ #define DRM_OPTIONS_FUNC DRM(options) +/** + * Called by the kernel to parse command-line options passed via the + * boot-loader (e.g., LILO). It calls the insmod option routine, + * parse_options(). + */ static int __init DRM(options)( char *str ) { DRM(parse_options)( str ); @@ -150,7 +159,7 @@ #undef DRM_OPTIONS_FUNC #endif -/* +/** * The default number of instances (minor numbers) to initialize. */ #ifndef DRIVER_NUM_CARDS @@ -163,6 +172,7 @@ DRIVER_FOPS; +/** Ioctl table */ static drm_ioctl_desc_t DRM(ioctls)[] = { [DRM_IOCTL_NR(DRM_IOCTL_VERSION)] = { DRM(version), 0, 0 }, [DRM_IOCTL_NR(DRM_IOCTL_GET_UNIQUE)] = { DRM(getunique), 0, 0 }, @@ -348,7 +358,8 @@ DRM_DEBUG( "\n" ); - /* The kernel's context could be created here, but is now created + /* + * The kernel's context could be created here, but is now created * in drm_dma_enqueue. This is more resource-efficient for * hardware that does not do DMA, but may mean that * drm_select_queue fails between the time the interrupt is @@ -359,6 +370,15 @@ } +/** + * Take down the DRM device. + * + * \param dev DRM device structure. + * + * Frees every resource in \p dev. + * + * \sa drm_device and setup(). + */ static int DRM(takedown)( drm_device_t *dev ) { drm_magic_entry_t *pt, *next; @@ -516,8 +536,12 @@ return 0; } -/* +/** * Figure out how many instances to initialize. + * + * \return number of cards found. + * + * Searches for every PCI card in \c DRIVER_CARD_LIST with matching vendor and device ids. */ static int drm_count_cards(void) { @@ -551,8 +575,18 @@ return num; } -/* drm_init is called via init_module at module load time, or via +/** + * Module initialization. Called via init_module at module load time, or via * linux/init/main.c (this is not currently supported). + * + * \return zero on success or a negative number on failure. + * + * Allocates and initialize an array of drm_device structures, and attempts to + * initialize all available devices, using consecutive minors, registering the + * stubs and initializing the AGP device. + * + * Expands the \c DRIVER_PREINIT and \c DRIVER_POST_INIT macros before and + * after the initialization for driver customization. */ static int __init drm_init( void ) { @@ -641,7 +675,12 @@ return 0; } -/* drm_cleanup is called via cleanup_module at module unload time. +/** + * Called via cleanup_module() at module unload time. + * + * Cleans up all DRM device, calling takedown(). + * + * \sa drm_init(). */ static void __exit drm_cleanup( void ) { @@ -694,6 +733,17 @@ module_exit( drm_cleanup ); +/** + * Get version information + * + * \param inode device inode. + * \param filp file pointer. + * \param cmd command. + * \param arg user argument, pointing to a drm_version structure. + * \return zero on success or negative number on failure. + * + * Fills in the version information in \p arg. + */ int DRM(version)( struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg ) { @@ -729,6 +779,17 @@ return 0; } +/** + * Open file. + * + * \param inode device inode + * \param filp file pointer. + * \return zero on success or a negative number on failure. + * + * Searches the DRM device with the same minor number, calls open_helper(), and + * increments the device open count. If the open count was previous at zero, + * i.e., it's the first that the device is open, then calls setup(). + */ int DRM(open)( struct inode *inode, struct file *filp ) { drm_device_t *dev = NULL; @@ -759,6 +820,18 @@ return retcode; } +/** + * Release file. + * + * \param inode device inode + * \param filp file pointer. + * \return zero on success or a negative number on failure. + * + * If the hardware lock is held then free it, and take it again for the kernel + * context since it's necessary to reclaim buffers. Unlink the file private + * data from its list and free it. Decreases the open count and if it reaches + * zero calls takedown(). + */ int DRM(release)( struct inode *inode, struct file *filp ) { drm_file_t *priv = filp->private_data; @@ -885,7 +958,17 @@ return retcode; } -/* DRM(ioctl) is called whenever a process performs an ioctl on /dev/drm. +/** + * Called whenever a process performs an ioctl on /dev/drm. + * + * \param inode device inode. + * \param filp file pointer. + * \param cmd command. + * \param arg user argument. + * \return zero on success or negative number on failure. + * + * Looks up the ioctl function in the ::ioctls table, checking for root + * previleges if so required, and dispatches to the respective function. */ int DRM(ioctl)( struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg ) @@ -926,6 +1009,17 @@ return retcode; } +/** + * Lock ioctl. + * + * \param inode device inode. + * \param filp file pointer. + * \param cmd command. + * \param arg user argument, pointing to a drm_lock structure. + * \return zero on success or negative number on failure. + * + * Add the current task to the lock wait queue, and attempt to take to lock. + */ int DRM(lock)( struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg ) { @@ -1018,6 +1112,10 @@ DRIVER_DMA_QUIESCENT(); } #endif + /* __HAVE_KERNEL_CTX_SWITCH isn't used by any of the + * drm modules in the DRI cvs tree, but it is required + * by the Sparc driver. + */ #if __HAVE_KERNEL_CTX_SWITCH if ( dev->last_context != lock.context ) { DRM(context_switch)(dev, dev->last_context, @@ -1031,7 +1129,17 @@ return ret; } - +/** + * Unlock ioctl. + * + * \param inode device inode. + * \param filp file pointer. + * \param cmd command. + * \param arg user argument, pointing to a drm_lock structure. + * \return zero on success or negative number on failure. + * + * Transfer and free the lock. + */ int DRM(unlock)( struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg ) { @@ -1050,6 +1158,10 @@ atomic_inc( &dev->counts[_DRM_STAT_UNLOCKS] ); + /* __HAVE_KERNEL_CTX_SWITCH isn't used by any of the drm + * modules in the DRI cvs tree, but it is required by the + * Sparc driver. + */ #if __HAVE_KERNEL_CTX_SWITCH /* We no longer really hold it, but if we are the next * agent to request it then we should just be able to @@ -1075,13 +1187,9 @@ DRM(dma_schedule)( dev, 1 ); #endif - /* FIXME: Do we ever really need to check this??? - */ - if ( 1 /* !dev->context_flag */ ) { - if ( DRM(lock_free)( dev, &dev->lock.hw_lock->lock, - DRM_KERNEL_CONTEXT ) ) { - DRM_ERROR( "\n" ); - } + if ( DRM(lock_free)( dev, &dev->lock.hw_lock->lock, + DRM_KERNEL_CONTEXT ) ) { + DRM_ERROR( "\n" ); } #endif /* !__HAVE_KERNEL_CTX_SWITCH */ diff -Nru a/drivers/char/drm/drm_fops.h b/drivers/char/drm/drm_fops.h --- a/drivers/char/drm/drm_fops.h Fri Apr 25 15:57:57 2003 +++ b/drivers/char/drm/drm_fops.h Thu Jul 10 23:18:01 2003 @@ -1,4 +1,13 @@ -/* drm_fops.h -- File operations for DRM -*- linux-c -*- +/** + * \file drm_fops.h + * File operations for DRM + * + * \author Rickard E. (Rik) Faith + * \author Daryll Strauss + * \author Gareth Hughes + */ + +/* * Created: Mon Jan 4 08:58:31 1999 by faith@valinux.com * * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. @@ -23,18 +32,23 @@ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. - * - * Authors: - * Rickard E. (Rik) Faith - * Daryll Strauss - * Gareth Hughes */ #include "drmP.h" #include -/* drm_open is called whenever a process opens /dev/drm. */ +/** + * Called whenever a process opens /dev/drm. + * + * \param inode device inode. + * \param filp file pointer. + * \param dev device. + * \return zero on success or a negative number on failure. + * + * Creates and initializes a drm_file structure for the file private data in \p + * filp and add it into the double linked list in \p dev. + */ int DRM(open_helper)(struct inode *inode, struct file *filp, drm_device_t *dev) { int minor = minor(inode->i_rdev); @@ -90,6 +104,7 @@ return 0; } +/** No-op. */ int DRM(flush)(struct file *filp) { drm_file_t *priv = filp->private_data; @@ -100,6 +115,7 @@ return 0; } +/** No-op. */ int DRM(fasync)(int fd, struct file *filp, int on) { drm_file_t *priv = filp->private_data; @@ -113,6 +129,7 @@ } #if !__HAVE_DRIVER_FOPS_POLL +/** No-op. */ unsigned int DRM(poll)(struct file *filp, struct poll_table_struct *wait) { return 0; @@ -121,6 +138,7 @@ #if !__HAVE_DRIVER_FOPS_READ +/** No-op. */ ssize_t DRM(read)(struct file *filp, char *buf, size_t count, loff_t *off) { return 0; diff -Nru a/drivers/char/drm/drm_init.h b/drivers/char/drm/drm_init.h --- a/drivers/char/drm/drm_init.h Fri Apr 25 15:57:57 2003 +++ b/drivers/char/drm/drm_init.h Thu Jul 10 23:18:01 2003 @@ -1,4 +1,12 @@ -/* drm_init.h -- Setup/Cleanup for DRM -*- linux-c -*- +/** + * \file drm_init.h + * Setup/Cleanup for DRM + * + * \author Rickard E. (Rik) Faith + * \author Gareth Hughes + */ + +/* * Created: Mon Jan 4 08:58:31 1999 by faith@valinux.com * * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. @@ -23,22 +31,23 @@ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. - * - * Authors: - * Rickard E. (Rik) Faith - * Gareth Hughes */ #include "drmP.h" +/** Debug flags. Set by parse_option(). */ #if 0 int DRM(flags) = DRM_FLAG_DEBUG; #else int DRM(flags) = 0; #endif -/* drm_parse_option parses a single option. See description for - * drm_parse_options for details. +/** + * Parse a single option. + * + * \param s option string. + * + * \sa See parse_options() for details. */ static void DRM(parse_option)(char *s) { @@ -58,26 +67,33 @@ return; } -/* drm_parse_options parse the insmod "drm_opts=" options, or the command-line - * options passed to the kernel via LILO. The grammar of the format is as +/** + * Parse the insmod "drm_opts=" options, or the command-line + * options passed to the kernel via LILO. + * + * \param s contains option_list without the 'drm_opts=' part. + * + * The grammar of the format is as * follows: * + * \code * drm ::= 'drm_opts=' option_list * option_list ::= option [ ';' option_list ] * option ::= 'device:' major * | 'debug' * | 'noctx' * major ::= INTEGER + * \endcode * - * Note that 's' contains option_list without the 'drm_opts=' part. - * - * device=major,minor specifies the device number used for /dev/drm - * if major == 0 then the misc device is used - * if major == 0 and minor == 0 then dynamic misc allocation is used - * debug=on specifies that debugging messages will be printk'd - * debug=trace specifies that each function call will be logged via printk - * debug=off turns off all debugging options + * - device=major,minor specifies the device number used for /dev/drm + * - if major == 0 then the misc device is used + * - if major == 0 and minor == 0 then dynamic misc allocation is used + * - debug=on specifies that debugging messages will be printk'd + * - debug=trace specifies that each function call will be logged via printk + * - debug=off turns off all debugging options * + * \todo Actually only the \e presence of the 'debug' option is currently + * checked. */ void DRM(parse_options)(char *s) @@ -95,8 +111,10 @@ } } -/* drm_cpu_valid returns non-zero if the DRI will run on this CPU, and 0 - * otherwise. +/** + * Check whether DRI will run on this CPU. + * + * \return non-zero if the DRI will run on this CPU, or zero otherwise. */ int DRM(cpu_valid)(void) { diff -Nru a/drivers/char/drm/drm_ioctl.h b/drivers/char/drm/drm_ioctl.h --- a/drivers/char/drm/drm_ioctl.h Fri Apr 25 15:57:57 2003 +++ b/drivers/char/drm/drm_ioctl.h Thu Jul 10 23:18:01 2003 @@ -1,4 +1,12 @@ -/* drm_ioctl.h -- IOCTL processing for DRM -*- linux-c -*- +/** + * \file drm_ioctl.h + * IOCTL processing for DRM + * + * \author Rickard E. (Rik) Faith + * \author Gareth Hughes + */ + +/* * Created: Fri Jan 8 09:01:26 1999 by faith@valinux.com * * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. @@ -23,15 +31,22 @@ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. - * - * Authors: - * Rickard E. (Rik) Faith - * Gareth Hughes */ #include "drmP.h" +/** + * Get interrupt from bus id. + * + * \param inode device inode. + * \param filp file pointer. + * \param cmd command. + * \param arg user argument, pointing to a drm_irq_busid structure. + * \return zero on success or a negative number on failure. + * + * Finds the PCI device with the specified bus id and gets its IRQ number. + */ int DRM(irq_busid)(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) { @@ -84,6 +99,17 @@ return 0; } +/** + * Get the bus id. + * + * \param inode device inode. + * \param filp file pointer. + * \param cmd command. + * \param arg user argument, pointing to a drm_unique structure. + * \return zero on success or a negative number on failure. + * + * Copies the bus id from drm_device::unique into user space. + */ int DRM(getunique)(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) { @@ -103,6 +129,18 @@ return 0; } +/** + * Set the bus id. + * + * \param inode device inode. + * \param filp file pointer. + * \param cmd command. + * \param arg user argument, pointing to a drm_unique structure. + * \return zero on success or a negative number on failure. + * + * Copies the bus id from userspace into drm_device::unique, and searches for + * the respective PCI device, updating drm_device::pdev. + */ int DRM(setunique)(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) { @@ -180,6 +218,19 @@ } +/** + * Get a mapping information. + * + * \param inode device inode. + * \param filp file pointer. + * \param cmd command. + * \param arg user argument, pointing to a drm_map structure. + * + * \return zero on success or a negative number on failure. + * + * Searches for the mapping with the specified offset and copies its information + * into userspace + */ int DRM(getmap)( struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg ) { @@ -226,6 +277,19 @@ return 0; } +/** + * Get client information. + * + * \param inode device inode. + * \param filp file pointer. + * \param cmd command. + * \param arg user argument, pointing to a drm_client structure. + * + * \return zero on success or a negative number on failure. + * + * Searches for the client with the specified index and copies its information + * into userspace + */ int DRM(getclient)( struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg ) { @@ -259,6 +323,16 @@ return 0; } +/** + * Get statistics information. + * + * \param inode device inode. + * \param filp file pointer. + * \param cmd command. + * \param arg user argument, pointing to a drm_stats structure. + * + * \return zero on success or a negative number on failure. + */ int DRM(getstats)( struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg ) { diff -Nru a/drivers/char/drm/drm_lock.h b/drivers/char/drm/drm_lock.h --- a/drivers/char/drm/drm_lock.h Fri Apr 25 15:57:57 2003 +++ b/drivers/char/drm/drm_lock.h Thu Jul 10 23:18:01 2003 @@ -1,4 +1,12 @@ -/* lock.c -- IOCTLs for locking -*- linux-c -*- +/** + * \file drm_lock.h + * IOCTLs for locking + * + * \author Rickard E. (Rik) Faith + * \author Gareth Hughes + */ + +/* * Created: Tue Feb 2 08:37:54 1999 by faith@valinux.com * * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. @@ -23,14 +31,11 @@ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. - * - * Authors: - * Rickard E. (Rik) Faith - * Gareth Hughes */ #include "drmP.h" +/** No-op ioctl. */ int DRM(noop)(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) { @@ -38,7 +43,15 @@ return 0; } - +/** + * Take the heavyweight lock. + * + * \param lock lock pointer. + * \param context locking context. + * \return one if the lock is held, or zero otherwise. + * + * Attempt to mark the lock as held by the given context, via the \p cmpxchg instruction. + */ int DRM(lock_take)(__volatile__ unsigned int *lock, unsigned int context) { unsigned int old, new, prev; @@ -65,8 +78,18 @@ return 0; } -/* This takes a lock forcibly and hands it to context. Should ONLY be used - inside *_unlock to give lock to kernel before calling *_dma_schedule. */ +/** + * This takes a lock forcibly and hands it to context. Should ONLY be used + * inside *_unlock to give lock to kernel before calling *_dma_schedule. + * + * \param dev DRM device. + * \param lock lock pointer. + * \param context locking context. + * \return always one. + * + * Resets the lock file pointer. + * Marks the lock as held by the given context, via the \p cmpxchg instruction. + */ int DRM(lock_transfer)(drm_device_t *dev, __volatile__ unsigned int *lock, unsigned int context) { @@ -81,6 +104,17 @@ return 1; } +/** + * Free lock. + * + * \param dev DRM device. + * \param lock lock. + * \param context context. + * + * Resets the lock file pointer. + * Marks the lock as not held, via the \p cmpxchg instruction. Wakes any task + * waiting on the lock queue. + */ int DRM(lock_free)(drm_device_t *dev, __volatile__ unsigned int *lock, unsigned int context) { @@ -102,18 +136,17 @@ return 0; } -/* If we get here, it means that the process has called DRM_IOCTL_LOCK - without calling DRM_IOCTL_UNLOCK. - - If the lock is not held, then let the signal proceed as usual. - - If the lock is held, then set the contended flag and keep the signal - blocked. - - - Return 1 if the signal should be delivered normally. - Return 0 if the signal should be blocked. */ - +/** + * If we get here, it means that the process has called DRM_IOCTL_LOCK + * without calling DRM_IOCTL_UNLOCK. + * + * If the lock is not held, then let the signal proceed as usual. If the lock + * is held, then set the contended flag and keep the signal blocked. + * + * \param priv pointer to a drm_sigdata structure. + * \return one if the signal should be delivered normally, or zero if the + * signal should be blocked. + */ int DRM(notifier)(void *priv) { drm_sigdata_t *s = (drm_sigdata_t *)priv; diff -Nru a/drivers/char/drm/drm_memory.h b/drivers/char/drm/drm_memory.h --- a/drivers/char/drm/drm_memory.h Mon May 26 12:57:49 2003 +++ b/drivers/char/drm/drm_memory.h Thu Jul 10 23:18:01 2003 @@ -1,4 +1,12 @@ -/* drm_memory.h -- Memory management wrappers for DRM -*- linux-c -*- +/** + * \file drm_memory.h + * Memory management wrappers for DRM + * + * \author Rickard E. (Rik) Faith + * \author Gareth Hughes + */ + +/* * Created: Thu Feb 4 14:00:34 1999 by faith@valinux.com * * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. @@ -23,17 +31,14 @@ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. - * - * Authors: - * Rickard E. (Rik) Faith - * Gareth Hughes */ #include #include #include "drmP.h" -/* Cut down version of drm_memory_debug.h, which used to be called +/** + * Cut down version of drm_memory_debug.h, which used to be called * drm_memory.h. If you want the debug functionality, change 0 to 1 * below. */ @@ -188,22 +193,38 @@ #if DEBUG_MEMORY #include "drm_memory_debug.h" #else + +/** No-op. */ void DRM(mem_init)(void) { } -/* drm_mem_info is called whenever a process reads /dev/drm/mem. */ +/** + * Called when "/proc/dri/%dev%/mem" is read. + * + * \param buf output buffer. + * \param start start of output data. + * \param offset requested start offset. + * \param len requested number of bytes. + * \param eof whether there is no more data to return. + * \param data private data. + * \return number of written bytes. + * + * No-op. + */ int DRM(mem_info)(char *buf, char **start, off_t offset, int len, int *eof, void *data) { return 0; } +/** Wrapper around kmalloc() */ void *DRM(alloc)(size_t size, int area) { return kmalloc(size, GFP_KERNEL); } +/** Wrapper around kmalloc() and kfree() */ void *DRM(realloc)(void *oldpt, size_t oldsize, size_t size, int area) { void *pt; @@ -216,11 +237,21 @@ return pt; } +/** Wrapper around kfree() */ void DRM(free)(void *pt, size_t size, int area) { kfree(pt); } +/** + * Allocate pages. + * + * \param order size order. + * \param area memory area. (Not used.) + * \return page address on success, or zero on failure. + * + * Allocate and reserve free pages. + */ unsigned long DRM(alloc_pages)(int order, int area) { unsigned long address; @@ -245,6 +276,15 @@ return address; } +/** + * Free pages. + * + * \param address address of the pages to free. + * \param order size order. + * \param area memory area. (Not used.) + * + * Unreserve and free pages allocated by alloc_pages(). + */ void DRM(free_pages)(unsigned long address, int order, int area) { unsigned long bytes = PAGE_SIZE << order; @@ -264,37 +304,44 @@ free_pages(address, order); } +/** Wrapper around drm_ioremap() */ void *DRM(ioremap)(unsigned long offset, unsigned long size, drm_device_t *dev) { return drm_ioremap(offset, size, dev); } +/** Wrapper around drm_ioremap_nocache() */ void *DRM(ioremap_nocache)(unsigned long offset, unsigned long size, drm_device_t *dev) { return drm_ioremap_nocache(offset, size, dev); } +/** Wrapper around drm_iounmap() */ void DRM(ioremapfree)(void *pt, unsigned long size, drm_device_t *dev) { drm_ioremapfree(pt, size, dev); } #if __REALLY_HAVE_AGP +/** Wrapper around agp_allocate_memory() */ struct agp_memory *DRM(alloc_agp)(int pages, u32 type) { return DRM(agp_allocate_memory)(pages, type); } +/** Wrapper around agp_free_memory() */ int DRM(free_agp)(struct agp_memory *handle, int pages) { return DRM(agp_free_memory)(handle) ? 0 : -EINVAL; } +/** Wrapper around agp_bind_memory() */ int DRM(bind_agp)(struct agp_memory *handle, unsigned int start) { return DRM(agp_bind_memory)(handle, start); } +/** Wrapper around agp_unbind_memory() */ int DRM(unbind_agp)(struct agp_memory *handle) { return DRM(agp_unbind_memory)(handle); diff -Nru a/drivers/char/drm/drm_memory_debug.h b/drivers/char/drm/drm_memory_debug.h --- a/drivers/char/drm/drm_memory_debug.h Sat May 24 12:57:42 2003 +++ b/drivers/char/drm/drm_memory_debug.h Thu Jul 10 23:18:01 2003 @@ -1,6 +1,12 @@ -/* drm_memory.h -- Memory management wrappers for DRM -*- linux-c -*- - * Created: Thu Feb 4 14:00:34 1999 by faith@valinux.com +/** + * \file drm_memory.h + * Memory management wrappers for DRM. * + * \author Rickard E. (Rik) Faith + * \author Gareth Hughes + */ + +/* * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. * All Rights Reserved. @@ -23,10 +29,6 @@ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. - * - * Authors: - * Rickard E. (Rik) Faith - * Gareth Hughes */ #include diff -Nru a/drivers/char/drm/drm_os_linux.h b/drivers/char/drm/drm_os_linux.h --- a/drivers/char/drm/drm_os_linux.h Mon Apr 28 20:01:25 2003 +++ b/drivers/char/drm/drm_os_linux.h Thu Jul 10 23:18:01 2003 @@ -1,35 +1,55 @@ +/** + * \file drm_os_linux.h + * OS abstraction macros. + */ + #include /* For task queue support */ #include +/** File pointer type */ #define DRMFILE struct file * +/** Ioctl arguments */ #define DRM_IOCTL_ARGS struct inode *inode, struct file *filp, unsigned int cmd, unsigned long data #define DRM_ERR(d) -(d) +/** Current process ID */ #define DRM_CURRENTPID current->pid #define DRM_UDELAY(d) udelay(d) +/** Read a byte from a MMIO region */ #define DRM_READ8(map, offset) readb(((unsigned long)(map)->handle) + (offset)) +/** Read a dword from a MMIO region */ #define DRM_READ32(map, offset) readl(((unsigned long)(map)->handle) + (offset)) +/** Write a byte into a MMIO region */ #define DRM_WRITE8(map, offset, val) writeb(val, ((unsigned long)(map)->handle) + (offset)) +/** Write a dword into a MMIO region */ #define DRM_WRITE32(map, offset, val) writel(val, ((unsigned long)(map)->handle) + (offset)) +/** Read memory barrier */ #define DRM_READMEMORYBARRIER() rmb() +/** Write memory barrier */ #define DRM_WRITEMEMORYBARRIER() wmb() +/** Read/write memory barrier */ #define DRM_MEMORYBARRIER() mb() +/** DRM device local declaration */ #define DRM_DEVICE drm_file_t *priv = filp->private_data; \ drm_device_t *dev = priv->dev - + +/** IRQ handler arguments */ #define DRM_IRQ_ARGS int irq, void *arg, struct pt_regs *regs +/** Task queue handler arguments */ #define DRM_TASKQUEUE_ARGS void *arg -/* For data going from/to the kernel through the ioctl argument */ +/** For data going into the kernel through the ioctl argument */ #define DRM_COPY_FROM_USER_IOCTL(arg1, arg2, arg3) \ if ( copy_from_user(&arg1, arg2, arg3) ) \ return -EFAULT +/** For data going from the kernel through the ioctl argument */ #define DRM_COPY_TO_USER_IOCTL(arg1, arg2, arg3) \ if ( copy_to_user(arg1, &arg2, arg3) ) \ return -EFAULT -/* Other copying of data from/to kernel space */ +/** Other copying of data to kernel space */ #define DRM_COPY_FROM_USER(arg1, arg2, arg3) \ copy_from_user(arg1, arg2, arg3) +/** Other copying of data from kernel space */ #define DRM_COPY_TO_USER(arg1, arg2, arg3) \ copy_to_user(arg1, arg2, arg3) /* Macros for copyfrom user, but checking readability only once */ @@ -41,10 +61,16 @@ __get_user(val, uaddr) -/* malloc/free without the overhead of DRM(alloc) */ +/** 'malloc' without the overhead of DRM(alloc)() */ #define DRM_MALLOC(x) kmalloc(x, GFP_KERNEL) +/** 'free' without the overhead of DRM(free)() */ #define DRM_FREE(x,size) kfree(x) +/** + * Get the pointer to the SAREA. + * + * Searches the SAREA on the mapping lists and points drm_device::sarea to it. + */ #define DRM_GETSAREA() \ do { \ drm_map_list_t *entry; \ diff -Nru a/drivers/char/drm/drm_proc.h b/drivers/char/drm/drm_proc.h --- a/drivers/char/drm/drm_proc.h Sat May 24 12:57:42 2003 +++ b/drivers/char/drm/drm_proc.h Thu Jul 10 23:18:01 2003 @@ -1,4 +1,16 @@ -/* drm_proc.h -- /proc support for DRM -*- linux-c -*- +/** + * \file drm_proc.h + * /proc support for DRM + * + * \author Rickard E. (Rik) Faith + * \author Gareth Hughes + * + * \par Acknowledgements: + * Matthew J Sottek sent in a patch to fix + * the problem with the proc files not outputting all their information. + */ + +/* * Created: Mon Jan 11 09:48:47 1999 by faith@valinux.com * * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. @@ -23,14 +35,6 @@ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. - * - * Authors: - * Rickard E. (Rik) Faith - * Gareth Hughes - * - * Acknowledgements: - * Matthew J Sottek sent in a patch to fix - * the problem with the proc files not outputting all their information. */ #include "drmP.h" @@ -50,9 +54,12 @@ int request, int *eof, void *data); #endif +/** + * Proc file list. + */ struct drm_proc_list { - const char *name; - int (*f)(char *, char **, off_t, int, int *, void *); + const char *name; /**< file name */ + int (*f)(char *, char **, off_t, int, int *, void *); /**< proc callback*/ } DRM(proc_list)[] = { { "name", DRM(name_info) }, { "mem", DRM(mem_info) }, @@ -66,6 +73,19 @@ }; #define DRM_PROC_ENTRIES (sizeof(DRM(proc_list))/sizeof(DRM(proc_list)[0])) +/** + * Initialize the DRI proc filesystem for a device. + * + * \param dev DRM device. + * \param minor device minor number. + * \param root DRI proc dir entry. + * \param dev_root resulting DRI device proc dir entry. + * \return root entry pointer on success, or NULL on failure. + * + * Create the DRI proc root entry "/proc/dri", the device proc root entry + * "/proc/dri/%minor%/", and each entry in proc_list as + * "/proc/dri/%minor%/%name%". + */ struct proc_dir_entry *DRM(proc_init)(drm_device_t *dev, int minor, struct proc_dir_entry *root, struct proc_dir_entry **dev_root) @@ -83,7 +103,7 @@ sprintf(name, "%d", minor); *dev_root = create_proc_entry(name, S_IFDIR, root); if (!*dev_root) { - DRM_ERROR("Cannot create /proc/%s\n", name); + DRM_ERROR("Cannot create /proc/dri/%s\n", name); return NULL; } @@ -108,6 +128,16 @@ } +/** + * Cleanup the proc filesystem resources. + * + * \param minor device minor number. + * \param root DRI proc dir entry. + * \param dev_root DRI device proc dir entry. + * \return always zero. + * + * Remove all proc entries created by proc_init(). + */ int DRM(proc_cleanup)(int minor, struct proc_dir_entry *root, struct proc_dir_entry *dev_root) { @@ -125,6 +155,19 @@ return 0; } +/** + * Called when "/proc/dri/.../name" is read. + * + * \param buf output buffer. + * \param start start of output data. + * \param offset requested start offset. + * \param request requested number of bytes. + * \param eof whether there is no more data to return. + * \param data private data. + * \return number of written bytes. + * + * Prints the device name together with the bus id if available. + */ static int DRM(name_info)(char *buf, char **start, off_t offset, int request, int *eof, void *data) { @@ -151,6 +194,19 @@ return len - offset; } +/** + * Called when "/proc/dri/.../vm" is read. + * + * \param buf output buffer. + * \param start start of output data. + * \param offset requested start offset. + * \param request requested number of bytes. + * \param eof whether there is no more data to return. + * \param data private data. + * \return number of written bytes. + * + * Prints information about all mappings in drm_device::maplist. + */ static int DRM(_vm_info)(char *buf, char **start, off_t offset, int request, int *eof, void *data) { @@ -204,6 +260,9 @@ return len - offset; } +/** + * Simply calls _vm_info() while holding the drm_device::struct_sem lock. + */ static int DRM(vm_info)(char *buf, char **start, off_t offset, int request, int *eof, void *data) { @@ -216,7 +275,17 @@ return ret; } - +/** + * Called when "/proc/dri/.../queues" is read. + * + * \param buf output buffer. + * \param start start of output data. + * \param offset requested start offset. + * \param request requested number of bytes. + * \param eof whether there is no more data to return. + * \param data private data. + * \return number of written bytes. + */ static int DRM(_queues_info)(char *buf, char **start, off_t offset, int request, int *eof, void *data) { @@ -261,6 +330,9 @@ return len - offset; } +/** + * Simply calls _queues_info() while holding the drm_device::struct_sem lock. + */ static int DRM(queues_info)(char *buf, char **start, off_t offset, int request, int *eof, void *data) { @@ -273,9 +345,17 @@ return ret; } -/* drm_bufs_info is called whenever a process reads - /dev/dri//bufs. */ - +/** + * Called when "/proc/dri/.../bufs" is read. + * + * \param buf output buffer. + * \param start start of output data. + * \param offset requested start offset. + * \param request requested number of bytes. + * \param eof whether there is no more data to return. + * \param data private data. + * \return number of written bytes. + */ static int DRM(_bufs_info)(char *buf, char **start, off_t offset, int request, int *eof, void *data) { @@ -320,6 +400,9 @@ return len - offset; } +/** + * Simply calls _bufs_info() while holding the drm_device::struct_sem lock. + */ static int DRM(bufs_info)(char *buf, char **start, off_t offset, int request, int *eof, void *data) { @@ -332,7 +415,17 @@ return ret; } - +/** + * Called when "/proc/dri/.../clients" is read. + * + * \param buf output buffer. + * \param start start of output data. + * \param offset requested start offset. + * \param request requested number of bytes. + * \param eof whether there is no more data to return. + * \param data private data. + * \return number of written bytes. + */ static int DRM(_clients_info)(char *buf, char **start, off_t offset, int request, int *eof, void *data) { @@ -364,6 +457,9 @@ return len - offset; } +/** + * Simply calls _clients_info() while holding the drm_device::struct_sem lock. + */ static int DRM(clients_info)(char *buf, char **start, off_t offset, int request, int *eof, void *data) { diff -Nru a/drivers/char/drm/drm_sarea.h b/drivers/char/drm/drm_sarea.h --- a/drivers/char/drm/drm_sarea.h Thu Nov 14 18:19:43 2002 +++ b/drivers/char/drm/drm_sarea.h Thu Jul 10 23:18:01 2003 @@ -1,5 +1,11 @@ -/* sarea.h -- SAREA definitions -*- linux-c -*- +/** + * \file drm_sarea.h + * \brief SAREA definitions * + * \author Michel Dänzer + */ + +/* * Copyright 2002 Tungsten Graphics, Inc., Cedar Park, Texas. * All Rights Reserved. * @@ -21,22 +27,22 @@ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. - * - * Authors: - * Michel Dänzer */ #ifndef _DRM_SAREA_H_ #define _DRM_SAREA_H_ +/** Maximum number of drawables in the SAREA */ #define SAREA_MAX_DRAWABLES 256 -typedef struct _drm_sarea_drawable_t { +/** SAREA drawable */ +typedef struct drm_sarea_drawable { unsigned int stamp; unsigned int flags; } drm_sarea_drawable_t; -typedef struct _dri_sarea_frame_t { +/** SAREA frame */ +typedef struct drm_sarea_frame { unsigned int x; unsigned int y; unsigned int width; @@ -44,13 +50,14 @@ unsigned int fullscreen; } drm_sarea_frame_t; -typedef struct _drm_sarea_t { - /* first thing is always the drm locking structure */ +/** SAREA */ +typedef struct drm_sarea { + /** first thing is always the DRM locking structure */ drm_hw_lock_t lock; - /* NOT_DONE: Use readers/writer lock for drawable_lock */ + /** \todo Use readers/writer lock for drm_sarea::drawable_lock */ drm_hw_lock_t drawable_lock; - drm_sarea_drawable_t drawableTable[SAREA_MAX_DRAWABLES]; - drm_sarea_frame_t frame; + drm_sarea_drawable_t drawableTable[SAREA_MAX_DRAWABLES]; /**< drawables */ + drm_sarea_frame_t frame; /**< frame */ drm_context_t dummy_context; } drm_sarea_t; diff -Nru a/drivers/char/drm/drm_scatter.h b/drivers/char/drm/drm_scatter.h --- a/drivers/char/drm/drm_scatter.h Thu Mar 13 16:52:15 2003 +++ b/drivers/char/drm/drm_scatter.h Thu Jul 10 23:18:01 2003 @@ -1,4 +1,11 @@ -/* drm_scatter.h -- IOCTLs to manage scatter/gather memory -*- linux-c -*- +/** + * \file drm_scatter.h + * IOCTLs to manage scatter/gather memory + * + * \author Gareth Hughes + */ + +/* * Created: Mon Dec 18 23:20:54 2000 by gareth@valinux.com * * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. @@ -22,9 +29,6 @@ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. - * - * Authors: - * Gareth Hughes */ #include diff -Nru a/drivers/char/drm/drm_stub.h b/drivers/char/drm/drm_stub.h --- a/drivers/char/drm/drm_stub.h Thu Mar 13 16:52:15 2003 +++ b/drivers/char/drm/drm_stub.h Thu Jul 10 23:18:01 2003 @@ -1,4 +1,11 @@ -/* drm_stub.h -- -*- linux-c -*- +/** + * \file drm_stub.h + * Stub support + * + * \author Rickard E. (Rik) Faith + */ + +/* * Created: Fri Jan 19 10:48:35 2001 by faith@acm.org * * Copyright 2001 VA Linux Systems, Inc., Sunnyvale, California. @@ -22,30 +29,37 @@ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. - * - * Authors: - * Rickard E. (Rik) Faith - * */ #include "drmP.h" #define DRM_STUB_MAXCARDS 16 /* Enough for one machine */ +/** Stub list. One for each minor. */ static struct drm_stub_list { const char *name; - struct file_operations *fops; - struct proc_dir_entry *dev_root; + struct file_operations *fops; /**< file operations */ + struct proc_dir_entry *dev_root; /**< proc directory entry */ } *DRM(stub_list); static struct proc_dir_entry *DRM(stub_root); +/** Stub information */ static struct drm_stub_info { int (*info_register)(const char *name, struct file_operations *fops, drm_device_t *dev); int (*info_unregister)(int minor); } DRM(stub_info); +/** + * File \c open operation. + * + * \param inode device inode. + * \param filp file pointer. + * + * Puts the drm_stub_list::fops corresponding to the device minor number into + * \p filp, call the \c open method, and restore the file operations. + */ static int DRM(stub_open)(struct inode *inode, struct file *filp) { int minor = minor(inode->i_rdev); @@ -64,11 +78,24 @@ return err; } +/** File operations structure */ static struct file_operations DRM(stub_fops) = { .owner = THIS_MODULE, .open = DRM(stub_open) }; +/** + * Get a device minor number. + * + * \param name driver name. + * \param fops file operations. + * \param dev DRM device. + * \return minor number on success, or a negative number on failure. + * + * Allocate and initialize ::stub_list if one doesn't exist already. Search an + * empty entry and initialize it to the given parameters, and create the proc + * init entry via proc_init(). + */ static int DRM(stub_getminor)(const char *name, struct file_operations *fops, drm_device_t *dev) { @@ -96,6 +123,16 @@ return -1; } +/** + * Put a device minor number. + * + * \param minor minor number. + * \return always zero. + * + * Cleans up the proc resources. If a minor is zero then release the foreign + * "drm" data, otherwise unregisters the "drm" data, frees the stub list and + * unregisters the character device. + */ static int DRM(stub_putminor)(int minor) { if (minor < 0 || minor >= DRM_STUB_MAXCARDS) return -1; @@ -115,7 +152,20 @@ return 0; } - +/** + * Register. + * + * \param name driver name. + * \param fops file operations + * \param dev DRM device. + * \return zero on success or a negative number on failure. + * + * Attempt to register the char device and get the foreign "drm" data. If + * successful then another module already registered so gets the stub info, + * otherwise use this module stub info and make it available for other modules. + * + * Finally calls stub_info::info_register. + */ int DRM(stub_register)(const char *name, struct file_operations *fops, drm_device_t *dev) { @@ -141,6 +191,13 @@ return -1; } +/** + * Unregister. + * + * \param minor + * + * Calls drm_stub_info::unregister. + */ int DRM(stub_unregister)(int minor) { DRM_DEBUG("%d\n", minor); diff -Nru a/drivers/char/drm/drm_vm.h b/drivers/char/drm/drm_vm.h --- a/drivers/char/drm/drm_vm.h Sat May 24 12:57:42 2003 +++ b/drivers/char/drm/drm_vm.h Thu Jul 10 23:18:01 2003 @@ -1,4 +1,12 @@ -/* drm_vm.h -- Memory mapping for DRM -*- linux-c -*- +/** + * \file drm_vm.h + * Memory mapping for DRM + * + * \author Rickard E. (Rik) Faith + * \author Gareth Hughes + */ + +/* * Created: Mon Jan 4 08:58:31 1999 by faith@valinux.com * * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. @@ -23,38 +31,49 @@ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. - * - * Authors: - * Rickard E. (Rik) Faith - * Gareth Hughes */ #include "drmP.h" +/** AGP virtual memory operations */ struct vm_operations_struct DRM(vm_ops) = { .nopage = DRM(vm_nopage), .open = DRM(vm_open), .close = DRM(vm_close), }; +/** Shared virtual memory operations */ struct vm_operations_struct DRM(vm_shm_ops) = { .nopage = DRM(vm_shm_nopage), .open = DRM(vm_open), .close = DRM(vm_shm_close), }; +/** DMA virtual memory operations */ struct vm_operations_struct DRM(vm_dma_ops) = { .nopage = DRM(vm_dma_nopage), .open = DRM(vm_open), .close = DRM(vm_close), }; +/** Scatter-gather virtual memory operations */ struct vm_operations_struct DRM(vm_sg_ops) = { .nopage = DRM(vm_sg_nopage), .open = DRM(vm_open), .close = DRM(vm_close), }; +/** + * \c nopage method for AGP virtual memory. + * + * \param vma virtual memory area. + * \param address access address. + * \param write_access sharing. + * \return pointer to the page structure. + * + * Find the right map and if it's AGP memory find the real physical page to + * map, get the page, increment the use count and return it. + */ struct page *DRM(vm_nopage)(struct vm_area_struct *vma, unsigned long address, int write_access) @@ -122,6 +141,17 @@ return NOPAGE_SIGBUS; /* Disallow mremap */ } +/** + * \c nopage method for shared virtual memory. + * + * \param vma virtual memory area. + * \param address access address. + * \param write_access sharing. + * \return pointer to the page structure. + * + * Get the the mapping, find the real physical page to map, get the page, and + * return it. + */ struct page *DRM(vm_shm_nopage)(struct vm_area_struct *vma, unsigned long address, int write_access) @@ -145,10 +175,15 @@ return page; } -/* Special close routine which deletes map information if we are the last + +/** + * \c close method for shared virtual memory. + * + * \param vma virtual memory area. + * + * Deletes map information if we are the last * person to close a mapping and it's not in the global maplist. */ - void DRM(vm_shm_close)(struct vm_area_struct *vma) { drm_file_t *priv = vma->vm_file->private_data; @@ -221,6 +256,16 @@ up(&dev->struct_sem); } +/** + * \c nopage method for DMA virtual memory. + * + * \param vma virtual memory area. + * \param address access address. + * \param write_access sharing. + * \return pointer to the page structure. + * + * Determine the page number from the page offset and get it from drm_device_dma::pagelist. + */ struct page *DRM(vm_dma_nopage)(struct vm_area_struct *vma, unsigned long address, int write_access) @@ -247,6 +292,16 @@ return page; } +/** + * \c nopage method for scatter-gather virtual memory. + * + * \param vma virtual memory area. + * \param address access address. + * \param write_access sharing. + * \return pointer to the page structure. + * + * Determine the map offset from the page offset and get it from drm_sg_mem::pagelist. + */ struct page *DRM(vm_sg_nopage)(struct vm_area_struct *vma, unsigned long address, int write_access) @@ -274,6 +329,14 @@ return page; } +/** + * \c open method for shared virtual memory. + * + * \param vma virtual memory area. + * + * Create a new drm_vma_entry structure as the \p vma private data entry and + * add it to drm_device::vmalist. + */ void DRM(vm_open)(struct vm_area_struct *vma) { drm_file_t *priv = vma->vm_file->private_data; @@ -295,6 +358,14 @@ } } +/** + * \c close method for all virtual memory types. + * + * \param vma virtual memory area. + * + * Search the \p vma private data entry in drm_device::vmalist, unlink it, and + * free it. + */ void DRM(vm_close)(struct vm_area_struct *vma) { drm_file_t *priv = vma->vm_file->private_data; @@ -320,6 +391,16 @@ up(&dev->struct_sem); } +/** + * mmap DMA memory. + * + * \param filp file pointer. + * \param vma virtual memory area. + * \return zero on success or a negative number on failure. + * + * Sets the virtual memory area operations structure to vm_dma_ops, the file + * pointer, and calls vm_open(). + */ int DRM(mmap_dma)(struct file *filp, struct vm_area_struct *vma) { drm_file_t *priv = filp->private_data; @@ -366,6 +447,19 @@ #endif #endif +/** + * mmap DMA memory. + * + * \param filp file pointer. + * \param vma virtual memory area. + * \return zero on success or a negative number on failure. + * + * If the virtual memory area has no offset associated with it then it's a DMA + * area, so calls mmap_dma(). Otherwise searches the map in drm_device::maplist, + * checks that the restricted flag is not set, sets the virtual memory operations + * according to the mapping type and remaps the pages. Finally sets the file + * pointer and calls vm_open(). + */ int DRM(mmap)(struct file *filp, struct vm_area_struct *vma) { drm_file_t *priv = filp->private_data; diff -Nru a/drivers/char/drm/i810.h b/drivers/char/drm/i810.h --- a/drivers/char/drm/i810.h Mon Apr 28 19:59:49 2003 +++ b/drivers/char/drm/i810.h Thu Jul 10 23:26:02 2003 @@ -45,7 +45,7 @@ #define DRIVER_NAME "i810" #define DRIVER_DESC "Intel i810" -#define DRIVER_DATE "20020211" +#define DRIVER_DATE "20030605" /* Interface history * @@ -54,10 +54,11 @@ * - XFree86 4.2 * 1.2.1 - Disable copying code (leave stub ioctls for backwards compatibility) * - Remove requirement for interrupt (leave stubs again) + * 1.3 - Add page flipping. */ #define DRIVER_MAJOR 1 -#define DRIVER_MINOR 2 -#define DRIVER_PATCHLEVEL 1 +#define DRIVER_MINOR 3 +#define DRIVER_PATCHLEVEL 0 #define DRIVER_IOCTLS \ [DRM_IOCTL_NR(DRM_IOCTL_I810_INIT)] = { i810_dma_init, 1, 1 }, \ @@ -73,8 +74,9 @@ [DRM_IOCTL_NR(DRM_IOCTL_I810_FSTATUS)] = { i810_fstatus, 1, 0 }, \ [DRM_IOCTL_NR(DRM_IOCTL_I810_OV0FLIP)] = { i810_ov0_flip, 1, 0 }, \ [DRM_IOCTL_NR(DRM_IOCTL_I810_MC)] = { i810_dma_mc, 1, 1 }, \ - [DRM_IOCTL_NR(DRM_IOCTL_I810_RSTATUS)] = { i810_rstatus, 1, 0 } - + [DRM_IOCTL_NR(DRM_IOCTL_I810_RSTATUS)] = { i810_rstatus, 1, 0 }, \ + [DRM_IOCTL_NR(DRM_IOCTL_I810_FLIP)] = { i810_flip_bufs, 1, 0 } + #define __HAVE_COUNTERS 4 #define __HAVE_COUNTER6 _DRM_STAT_IRQ diff -Nru a/drivers/char/drm/i810_dma.c b/drivers/char/drm/i810_dma.c --- a/drivers/char/drm/i810_dma.c Sat May 24 12:57:42 2003 +++ b/drivers/char/drm/i810_dma.c Thu Jul 10 23:26:02 2003 @@ -282,10 +282,12 @@ ring->head = I810_READ(LP_RING + RING_HEAD) & HEAD_ADDR; ring->space = ring->head - (ring->tail+8); if (ring->space < 0) ring->space += ring->Size; - - if (ring->head != last_head) - end = jiffies + (HZ*3); - + + if (ring->head != last_head) { + end = jiffies + (HZ*3); + last_head = ring->head; + } + iters++; if(time_before(end, jiffies)) { DRM_ERROR("space: %d wanted %d\n", ring->space, n); @@ -403,6 +405,7 @@ dev_priv->pitch = init->pitch; dev_priv->back_offset = init->back_offset; dev_priv->depth_offset = init->depth_offset; + dev_priv->front_offset = init->front_offset; dev_priv->overlay_offset = init->overlay_offset; dev_priv->overlay_physical = init->overlay_physical; @@ -582,6 +585,8 @@ drm_i810_private_t *dev_priv = dev->dev_private; drm_i810_sarea_t *sarea_priv = dev_priv->sarea_priv; unsigned int dirty = sarea_priv->dirty; + + DRM_DEBUG("%s %x\n", __FUNCTION__, dirty); if (dirty & I810_UPLOAD_BUFFERS) { i810EmitDestVerified( dev, sarea_priv->BufferState ); @@ -620,6 +625,14 @@ int cpp = 2; int i; RING_LOCALS; + + if ( dev_priv->current_page == 1 ) { + unsigned int tmp = flags; + + flags &= ~(I810_FRONT | I810_BACK); + if (tmp & I810_FRONT) flags |= I810_BACK; + if (tmp & I810_BACK) flags |= I810_FRONT; + } i810_kernel_lost_context(dev); @@ -685,10 +698,11 @@ drm_clip_rect_t *pbox = sarea_priv->boxes; int pitch = dev_priv->pitch; int cpp = 2; - int ofs = dev_priv->back_offset; int i; RING_LOCALS; + DRM_DEBUG("swapbuffers\n"); + i810_kernel_lost_context(dev); if (nbox > I810_NR_SAREA_CLIPRECTS) @@ -699,7 +713,7 @@ unsigned int w = pbox->x2 - pbox->x1; unsigned int h = pbox->y2 - pbox->y1; unsigned int dst = pbox->x1*cpp + pbox->y1*pitch; - unsigned int start = ofs + dst; + unsigned int start = dst; if (pbox->x1 > pbox->x2 || pbox->y1 > pbox->y2 || @@ -711,9 +725,15 @@ OUT_RING( BR00_BITBLT_CLIENT | BR00_OP_SRC_COPY_BLT | 0x4 ); OUT_RING( pitch | (0xCC << 16)); OUT_RING( (h << 16) | (w * cpp)); - OUT_RING( dst ); + if (dev_priv->current_page == 0) + OUT_RING(dev_priv->front_offset + start); + else + OUT_RING(dev_priv->back_offset + start); OUT_RING( pitch ); - OUT_RING( start ); + if (dev_priv->current_page == 0) + OUT_RING(dev_priv->back_offset + start); + else + OUT_RING(dev_priv->front_offset + start); ADVANCE_LP_RING(); } } @@ -800,6 +820,52 @@ } } +static void i810_dma_dispatch_flip( drm_device_t *dev ) +{ + drm_i810_private_t *dev_priv = dev->dev_private; + int pitch = dev_priv->pitch; + RING_LOCALS; + + DRM_DEBUG( "%s: page=%d pfCurrentPage=%d\n", + __FUNCTION__, + dev_priv->current_page, + dev_priv->sarea_priv->pf_current_page); + + i810_kernel_lost_context(dev); + + BEGIN_LP_RING( 2 ); + OUT_RING( INST_PARSER_CLIENT | INST_OP_FLUSH | INST_FLUSH_MAP_CACHE ); + OUT_RING( 0 ); + ADVANCE_LP_RING(); + + BEGIN_LP_RING( I810_DEST_SETUP_SIZE + 2 ); + /* On i815 at least ASYNC is buggy */ + /* pitch<<5 is from 11.2.8 p158, + its the pitch / 8 then left shifted 8, + so (pitch >> 3) << 8 */ + OUT_RING( CMD_OP_FRONTBUFFER_INFO | (pitch<<5) /*| ASYNC_FLIP */ ); + if ( dev_priv->current_page == 0 ) { + OUT_RING( dev_priv->back_offset ); + dev_priv->current_page = 1; + } else { + OUT_RING( dev_priv->front_offset ); + dev_priv->current_page = 0; + } + OUT_RING(0); + ADVANCE_LP_RING(); + + BEGIN_LP_RING(2); + OUT_RING( CMD_OP_WAIT_FOR_EVENT | WAIT_FOR_PLANE_A_FLIP ); + OUT_RING( 0 ); + ADVANCE_LP_RING(); + + /* Increment the frame counter. The client-side 3D driver must + * throttle the framerate by waiting for this value before + * performing the swapbuffer ioctl. + */ + dev_priv->sarea_priv->pf_current_page = dev_priv->current_page; + +} void i810_dma_quiescent(drm_device_t *dev) { @@ -1191,3 +1257,47 @@ } +/* Not sure why this isn't set all the time: + */ +static void i810_do_init_pageflip( drm_device_t *dev ) +{ + drm_i810_private_t *dev_priv = dev->dev_private; + + DRM_DEBUG("%s\n", __FUNCTION__); + dev_priv->page_flipping = 1; + dev_priv->current_page = 0; + dev_priv->sarea_priv->pf_current_page = dev_priv->current_page; +} + +int i810_do_cleanup_pageflip( drm_device_t *dev ) +{ + drm_i810_private_t *dev_priv = dev->dev_private; + + DRM_DEBUG("%s\n", __FUNCTION__); + if (dev_priv->current_page != 0) + i810_dma_dispatch_flip( dev ); + + dev_priv->page_flipping = 0; + return 0; +} + +int i810_flip_bufs(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_i810_private_t *dev_priv = dev->dev_private; + + DRM_DEBUG("%s\n", __FUNCTION__); + + if(!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) { + DRM_ERROR("i810_flip_buf called without lock held\n"); + return -EINVAL; + } + + if (!dev_priv->page_flipping) + i810_do_init_pageflip( dev ); + + i810_dma_dispatch_flip( dev ); + return 0; +} diff -Nru a/drivers/char/drm/i810_drm.h b/drivers/char/drm/i810_drm.h --- a/drivers/char/drm/i810_drm.h Sun Mar 2 23:30:02 2003 +++ b/drivers/char/drm/i810_drm.h Thu Jul 10 23:26:02 2003 @@ -166,6 +166,9 @@ int vertex_prim; + int pf_enabled; /* is pageflipping allowed? */ + int pf_active; + int pf_current_page; /* which buffer is being displayed? */ } drm_i810_sarea_t; /* WARNING: If you change any of these defines, make sure to change the @@ -189,6 +192,7 @@ #define DRM_IOCTL_I810_OV0FLIP DRM_IO ( 0x4b) #define DRM_IOCTL_I810_MC DRM_IOW( 0x4c, drm_i810_mc_t) #define DRM_IOCTL_I810_RSTATUS DRM_IO ( 0x4d ) +#define DRM_IOCTL_I810_FLIP DRM_IO ( 0x4e ) typedef struct _drm_i810_clear { int clear_color; diff -Nru a/drivers/char/drm/i810_drv.h b/drivers/char/drm/i810_drv.h --- a/drivers/char/drm/i810_drv.h Mon Apr 28 19:59:49 2003 +++ b/drivers/char/drm/i810_drv.h Thu Jul 10 23:26:02 2003 @@ -75,7 +75,20 @@ int overlay_physical; int w, h; int pitch; + int back_pitch; + int depth_pitch; + int do_boxes; + int dma_used; + + int current_page; + int page_flipping; + + wait_queue_head_t irq_queue; + atomic_t irq_received; + atomic_t irq_emitted; + + int front_offset; } drm_i810_private_t; /* i810_dma.c */ @@ -124,6 +137,8 @@ int i810_clear_bufs(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg); +int i810_flip_bufs(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); #define I810_BASE(reg) ((unsigned long) \ dev_priv->mmio_map->handle) @@ -225,12 +240,15 @@ #define CMD_OP_Z_BUFFER_INFO ((0x0<<29)|(0x16<<23)) #define CMD_OP_DESTBUFFER_INFO ((0x0<<29)|(0x15<<23)) #define CMD_OP_FRONTBUFFER_INFO ((0x0<<29)|(0x14<<23)) +#define CMD_OP_WAIT_FOR_EVENT ((0x0<<29)|(0x03<<23)) #define BR00_BITBLT_CLIENT 0x40000000 #define BR00_OP_COLOR_BLT 0x10000000 #define BR00_OP_SRC_COPY_BLT 0x10C00000 #define BR13_SOLID_PATTERN 0x80000000 - +#define WAIT_FOR_PLANE_A_SCANLINES (1<<1) +#define WAIT_FOR_PLANE_A_FLIP (1<<2) +#define WAIT_FOR_VBLANK (1<<3) #endif diff -Nru a/drivers/char/drm/r128.h b/drivers/char/drm/r128.h --- a/drivers/char/drm/r128.h Mon Apr 28 19:59:49 2003 +++ b/drivers/char/drm/r128.h Thu Jul 10 23:30:21 2003 @@ -47,13 +47,17 @@ #define DRIVER_NAME "r128" #define DRIVER_DESC "ATI Rage 128" -#define DRIVER_DATE "20021029" +#define DRIVER_DATE "20030526" #define DRIVER_MAJOR 2 -#define DRIVER_MINOR 3 +#define DRIVER_MINOR 4 #define DRIVER_PATCHLEVEL 0 - +/* Interface history: + * + * ?? - ?? + * 2.4 - Add support for ycbcr textures (no new ioctls) + */ #define DRIVER_IOCTLS \ [DRM_IOCTL_NR(DRM_IOCTL_DMA)] = { r128_cce_buffers, 1, 0 }, \ [DRM_IOCTL_NR(DRM_IOCTL_R128_INIT)] = { r128_cce_init, 1, 1 }, \ diff -Nru a/drivers/char/drm/r128_drv.h b/drivers/char/drm/r128_drv.h --- a/drivers/char/drm/r128_drv.h Mon Apr 28 20:01:25 2003 +++ b/drivers/char/drm/r128_drv.h Thu Jul 10 23:30:21 2003 @@ -343,13 +343,20 @@ #define R128_CCE_VC_CNTL_PRIM_WALK_RING 0x00000030 #define R128_CCE_VC_CNTL_NUM_SHIFT 16 +#define R128_DATATYPE_VQ 0 +#define R128_DATATYPE_CI4 1 #define R128_DATATYPE_CI8 2 #define R128_DATATYPE_ARGB1555 3 #define R128_DATATYPE_RGB565 4 #define R128_DATATYPE_RGB888 5 #define R128_DATATYPE_ARGB8888 6 #define R128_DATATYPE_RGB332 7 +#define R128_DATATYPE_Y8 8 #define R128_DATATYPE_RGB8 9 +#define R128_DATATYPE_CI16 10 +#define R128_DATATYPE_YVYU422 11 +#define R128_DATATYPE_VYUY422 12 +#define R128_DATATYPE_AYUV444 14 #define R128_DATATYPE_ARGB4444 15 /* Constants */ diff -Nru a/drivers/char/drm/r128_state.c b/drivers/char/drm/r128_state.c --- a/drivers/char/drm/r128_state.c Sat Mar 29 22:34:33 2003 +++ b/drivers/char/drm/r128_state.c Thu Jul 10 23:30:21 2003 @@ -802,6 +802,8 @@ case R128_DATATYPE_ARGB1555: case R128_DATATYPE_RGB565: case R128_DATATYPE_ARGB4444: + case R128_DATATYPE_YVYU422: + case R128_DATATYPE_VYUY422: dword_shift = 1; break; case R128_DATATYPE_CI8: diff -Nru a/drivers/char/drm/radeon.h b/drivers/char/drm/radeon.h --- a/drivers/char/drm/radeon.h Sat May 24 12:57:42 2003 +++ b/drivers/char/drm/radeon.h Thu Jul 10 23:33:48 2003 @@ -51,7 +51,7 @@ #define DRIVER_DATE "20020828" #define DRIVER_MAJOR 1 -#define DRIVER_MINOR 8 +#define DRIVER_MINOR 9 #define DRIVER_PATCHLEVEL 0 /* Interface history: diff -Nru a/drivers/char/drm/radeon_drm.h b/drivers/char/drm/radeon_drm.h --- a/drivers/char/drm/radeon_drm.h Sat May 24 12:57:42 2003 +++ b/drivers/char/drm/radeon_drm.h Thu Jul 10 23:33:48 2003 @@ -141,7 +141,10 @@ #define R200_EMIT_PP_CUBIC_OFFSETS_4 70 #define R200_EMIT_PP_CUBIC_FACES_5 71 #define R200_EMIT_PP_CUBIC_OFFSETS_5 72 -#define RADEON_MAX_STATE_PACKETS 73 +#define RADEON_EMIT_PP_TEX_SIZE_0 73 +#define RADEON_EMIT_PP_TEX_SIZE_1 74 +#define RADEON_EMIT_PP_TEX_SIZE_2 75 +#define RADEON_MAX_STATE_PACKETS 76 /* Commands understood by cmd_buffer ioctl. More can be added but diff -Nru a/drivers/char/drm/radeon_drv.c b/drivers/char/drm/radeon_drv.c --- a/drivers/char/drm/radeon_drv.c Tue Oct 29 14:29:22 2002 +++ b/drivers/char/drm/radeon_drv.c Thu Jul 10 23:33:48 2003 @@ -1,6 +1,11 @@ -/* radeon_drv.c -- ATI Radeon driver -*- linux-c -*- - * Created: Wed Feb 14 17:10:04 2001 by gareth@valinux.com +/** + * \file radeon_drv.c + * ATI Radeon driver * + * \author Gareth Hughes + */ + +/* * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. * All Rights Reserved. * @@ -22,10 +27,8 @@ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. - * - * Authors: - * Gareth Hughes */ + #include #include "radeon.h" diff -Nru a/drivers/char/drm/radeon_drv.h b/drivers/char/drm/radeon_drv.h --- a/drivers/char/drm/radeon_drv.h Sat May 24 12:57:42 2003 +++ b/drivers/char/drm/radeon_drv.h Thu Jul 10 23:33:48 2003 @@ -669,6 +669,10 @@ #define R200_RE_POINTSIZE 0x2648 #define R200_SE_TCL_INPUT_VTX_VECTOR_ADDR_0 0x2254 +#define RADEON_PP_TEX_SIZE_0 0x1d04 /* NPOT */ +#define RADEON_PP_TEX_SIZE_1 0x1d0c +#define RADEON_PP_TEX_SIZE_2 0x1d14 + #define SE_VAP_CNTL__TCL_ENA_MASK 0x00000001 #define SE_VAP_CNTL__FORCE_W_TO_ONE_MASK 0x00010000 diff -Nru a/drivers/char/drm/radeon_state.c b/drivers/char/drm/radeon_state.c --- a/drivers/char/drm/radeon_state.c Fri Apr 25 15:57:58 2003 +++ b/drivers/char/drm/radeon_state.c Thu Jul 10 23:33:48 2003 @@ -292,6 +292,9 @@ { R200_PP_CUBIC_OFFSET_F1_4, 5, "R200_PP_CUBIC_OFFSET_F1_4" }, { R200_PP_CUBIC_FACES_5, 1, "R200_PP_CUBIC_FACES_5" }, { R200_PP_CUBIC_OFFSET_F1_5, 5, "R200_PP_CUBIC_OFFSET_F1_5" }, + { RADEON_PP_TEX_SIZE_0, 2, "RADEON_PP_TEX_SIZE_0" }, + { RADEON_PP_TEX_SIZE_1, 2, "RADEON_PP_TEX_SIZE_1" }, + { RADEON_PP_TEX_SIZE_2, 2, "RADEON_PP_TEX_SIZE_1" }, }; @@ -885,15 +888,14 @@ static void radeon_cp_dispatch_vertex( drm_device_t *dev, drm_buf_t *buf, - drm_radeon_tcl_prim_t *prim, - drm_clip_rect_t *boxes, - int nbox ) + drm_radeon_tcl_prim_t *prim ) { drm_radeon_private_t *dev_priv = dev->dev_private; - drm_clip_rect_t box; + drm_radeon_sarea_t *sarea_priv = dev_priv->sarea_priv; int offset = dev_priv->agp_buffers_offset + buf->offset + prim->start; int numverts = (int)prim->numverts; + int nbox = sarea_priv->nbox; int i = 0; RING_LOCALS; @@ -913,10 +915,8 @@ do { /* Emit the next cliprect */ if ( i < nbox ) { - if (DRM_COPY_FROM_USER_UNCHECKED( &box, &boxes[i], sizeof(box) )) - return; - - radeon_emit_clip_rect( dev_priv, &box ); + radeon_emit_clip_rect( dev_priv, + &sarea_priv->boxes[i] ); } /* Emit the vertex buffer rendering commands */ @@ -995,18 +995,17 @@ static void radeon_cp_dispatch_indices( drm_device_t *dev, drm_buf_t *elt_buf, - drm_radeon_tcl_prim_t *prim, - drm_clip_rect_t *boxes, - int nbox ) + drm_radeon_tcl_prim_t *prim ) { drm_radeon_private_t *dev_priv = dev->dev_private; - drm_clip_rect_t box; + drm_radeon_sarea_t *sarea_priv = dev_priv->sarea_priv; int offset = dev_priv->agp_buffers_offset + prim->offset; u32 *data; int dwords; int i = 0; int start = prim->start + RADEON_INDEX_PRIM_OFFSET; int count = (prim->finish - start) / sizeof(u16); + int nbox = sarea_priv->nbox; DRM_DEBUG("hwprim 0x%x vfmt 0x%x %d..%d offset: %x nr %d\n", prim->prim, @@ -1045,12 +1044,9 @@ (count << RADEON_NUM_VERTICES_SHIFT) ); do { - if ( i < nbox ) { - if (DRM_COPY_FROM_USER_UNCHECKED( &box, &boxes[i], sizeof(box) )) - return; - - radeon_emit_clip_rect( dev_priv, &box ); - } + if ( i < nbox ) + radeon_emit_clip_rect( dev_priv, + &sarea_priv->boxes[i] ); radeon_cp_dispatch_indirect( dev, elt_buf, prim->start, @@ -1450,9 +1446,7 @@ prim.numverts = vertex.count; prim.vc_format = dev_priv->sarea_priv->vc_format; - radeon_cp_dispatch_vertex( dev, buf, &prim, - dev_priv->sarea_priv->boxes, - dev_priv->sarea_priv->nbox ); + radeon_cp_dispatch_vertex( dev, buf, &prim ); } if (vertex.discard) { @@ -1550,9 +1544,7 @@ prim.numverts = RADEON_MAX_VB_VERTS; /* duh */ prim.vc_format = dev_priv->sarea_priv->vc_format; - radeon_cp_dispatch_indices( dev, buf, &prim, - dev_priv->sarea_priv->boxes, - dev_priv->sarea_priv->nbox ); + radeon_cp_dispatch_indices( dev, buf, &prim ); if (elts.discard) { radeon_cp_discard_buffer( dev, buf ); } @@ -1769,16 +1761,12 @@ tclprim.offset = prim.numverts * 64; tclprim.numverts = RADEON_MAX_VB_VERTS; /* duh */ - radeon_cp_dispatch_indices( dev, buf, &tclprim, - sarea_priv->boxes, - sarea_priv->nbox); + radeon_cp_dispatch_indices( dev, buf, &tclprim ); } else { tclprim.numverts = prim.numverts; tclprim.offset = 0; /* not used */ - radeon_cp_dispatch_vertex( dev, buf, &tclprim, - sarea_priv->boxes, - sarea_priv->nbox); + radeon_cp_dispatch_vertex( dev, buf, &tclprim ); } if (sarea_priv->nbox == 1) diff -Nru a/drivers/char/dtlk.c b/drivers/char/dtlk.c --- a/drivers/char/dtlk.c Wed May 7 08:47:29 2003 +++ b/drivers/char/dtlk.c Fri Jul 11 06:34:28 2003 @@ -55,7 +55,7 @@ #include #include /* for verify_area */ #include /* for -EBUSY */ -#include /* for check_region, request_region */ +#include /* for request_region */ #include /* for loops_per_jiffy */ #include /* for inb_p, outb_p, inb, outb, etc. */ #include /* for get_user, etc. */ diff -Nru a/drivers/char/genrtc.c b/drivers/char/genrtc.c --- a/drivers/char/genrtc.c Sat Apr 12 21:10:27 2003 +++ b/drivers/char/genrtc.c Fri Jul 11 06:29:57 2003 @@ -355,8 +355,6 @@ if (gen_rtc_status & RTC_IS_OPEN) return -EBUSY; - MOD_INC_USE_COUNT; - gen_rtc_status |= RTC_IS_OPEN; gen_rtc_irq_data = 0; irq_active = 0; @@ -374,8 +372,6 @@ gen_clear_rtc_irq_bit(RTC_PIE|RTC_AIE|RTC_UIE); gen_rtc_status &= ~RTC_IS_OPEN; - MOD_DEC_USE_COUNT; - return 0; } diff -Nru a/drivers/eisa/Kconfig b/drivers/eisa/Kconfig --- a/drivers/eisa/Kconfig Mon Apr 21 09:20:23 2003 +++ b/drivers/eisa/Kconfig Sat Jun 14 08:40:13 2003 @@ -14,7 +14,7 @@ config EISA_PCI_EISA bool "Generic PCI/EISA bridge" - depends on PCI && EISA + depends on !PARISC && PCI && EISA default y ---help--- Activate this option if your system contains a PCI to EISA diff -Nru a/drivers/ide/ide-cd.c b/drivers/ide/ide-cd.c --- a/drivers/ide/ide-cd.c Sat Jun 14 17:46:49 2003 +++ b/drivers/ide/ide-cd.c Thu Jun 19 08:56:39 2003 @@ -749,12 +749,17 @@ by transferring the semaphore from the packet command request to the request sense request. */ + rq->flags |= REQ_FAILED; if ((stat & ERR_STAT) != 0) { wait = rq->waiting; rq->waiting = NULL; + if ((rq->flags & REQ_BLOCK_PC) != 0) { + cdrom_queue_request_sense(drive, wait, + rq->sense, rq); + return 1; /* REQ_BLOCK_PC self-cares */ + } } - rq->flags |= REQ_FAILED; cdrom_end_request(drive, 0); if ((stat & ERR_STAT) != 0) @@ -1657,13 +1662,14 @@ dma = info->dma; if (dma) { info->dma = 0; - if ((dma_error = HWIF(drive)->ide_dma_end(drive))) { - printk("ide-cd: dma error\n"); - HWIF(drive)->ide_dma_off(drive); - } + dma_error = HWIF(drive)->ide_dma_end(drive); } if (cdrom_decode_status(drive, 0, &stat)) { + if ((stat & ERR_STAT) != 0) { + end_that_request_chunk(rq, 0, rq->data_len); + goto end_request; /* purge the whole thing... */ + } end_that_request_chunk(rq, 1, rq->data_len); return ide_stopped; } @@ -1672,8 +1678,11 @@ * using dma, transfer is complete now */ if (dma) { - if (dma_error) + if (dma_error) { + printk("ide-cd: dma error\n"); + HWIF(drive)->ide_dma_off(drive); return DRIVER(drive)->error(drive, "dma error", stat); + } end_that_request_chunk(rq, 1, rq->data_len); rq->data_len = 0; diff -Nru a/drivers/input/misc/gsc_ps2.c b/drivers/input/misc/gsc_ps2.c --- a/drivers/input/misc/gsc_ps2.c Wed Feb 12 01:41:01 2003 +++ b/drivers/input/misc/gsc_ps2.c Thu May 29 04:21:53 2003 @@ -56,12 +56,13 @@ #include /* for request_irq/free_irq */ #include #include -#include #include #include +#include #include #include +#include /* Debugging stuff */ #undef KBD_DEBUG @@ -79,6 +80,7 @@ /* PS/2 keyboard and mouse constants */ #define AUX_RECONNECT 0xAA /* PS/2 Mouse end of test successful */ #define AUX_REPLY_ACK 0xFA +#define AUX_ENABLE_DEV 0xF4 /* Enables aux device */ /* Order of the mouse bytes coming to the host */ #define PACKET_X 1 @@ -131,6 +133,12 @@ #define MOUSE_XOVFLOW 0x40 #define MOUSE_YOVFLOW 0x80 +/* Remnant of pc_keyb.h */ +#define KBD_CMD_SET_LEDS 0xED /* Sets keyboard leds */ +#define KBD_CMD_SET_RATE 0xF3 /* Sets typematic rate */ +#define KBD_CMD_ENABLE 0xF4 /* Enables scanning */ +#define KBD_CMD_DISABLE 0xF5 +#define KBD_CMD_RESET 0xFF static unsigned char hpkeyb_keycode[KBD_TBLSIZE] = { @@ -341,7 +349,7 @@ default: hpkeyb.scancode = scancode; DPRINTK("sent=%d, rel=%d\n",hpkeyb.scancode, hpkeyb.released); - input_regs(regs); + /*input_regs(regs);*/ input_report_key(&hpkeyb.dev, hpkeyb_keycode[hpkeyb.scancode], !hpkeyb.released); input_sync(&hpkeyb.dev); if (hpkeyb.escaped) @@ -388,7 +396,7 @@ if ((hpmouse.bytes[PACKET_CTRL] & (MOUSE_XOVFLOW | MOUSE_YOVFLOW))) DPRINTK("Mouse: position overflow\n"); - input_regs(regs); + /*input_regs(regs);*/ input_report_key(&hpmouse.dev, BTN_LEFT, hpmouse.bytes[PACKET_CTRL] & MOUSE_LEFTBTN); input_report_key(&hpmouse.dev, BTN_MIDDLE, hpmouse.bytes[PACKET_CTRL] & MOUSE_MIDBTN); @@ -420,7 +428,7 @@ * key value to the system. */ -static void gscps2_interrupt(int irq, void *dev, struct pt_regs *reg) +static irqreturn_t gscps2_interrupt(int irq, void *dev, struct pt_regs *reg) { /* process mouse actions */ while (gscps2_readb_status(hpmouse.addr) & GSC_STAT_RBNE) @@ -429,6 +437,8 @@ /* process keyboard scancode */ while (gscps2_readb_status(hpkeyb.addr) & GSC_STAT_RBNE) gscps2_kbd_docode(reg); + + return IRQ_HANDLED; } @@ -525,7 +535,7 @@ /* TODO These need some adjustement, are they really useful ? */ hpkeyb.dev.id.bustype = BUS_GSC; - hpkeyb.dev.id.vendor = 0x0001; + hpkeyb.dev.id.vendor = PCI_VENDOR_ID_HP; hpkeyb.dev.id.product = 0x0001; hpkeyb.dev.id.version = 0x0010; hpkeyb.initialized = 1; @@ -580,7 +590,7 @@ { u8 id; char *addr, *name; - int ret, device_found = 0; + int ret = 0, device_found = 0; unsigned long hpa = dev->hpa; if (!dev->irq) diff -Nru a/drivers/isdn/hisax/isurf.c b/drivers/isdn/hisax/isurf.c --- a/drivers/isdn/hisax/isurf.c Thu Apr 24 05:18:50 2003 +++ b/drivers/isdn/hisax/isurf.c Fri Jul 11 05:05:43 2003 @@ -237,7 +237,7 @@ struct pnp_card *pb; struct pnp_dev *pd; - cs->subtyp = 0; + card->cs->subtyp = 0; if ((pb = pnp_find_card( ISAPNP_VENDOR('S', 'I', 'E'), ISAPNP_FUNCTION(0x0010), pnp_surf))) { diff -Nru a/drivers/net/appletalk/cops.c b/drivers/net/appletalk/cops.c --- a/drivers/net/appletalk/cops.c Sun Apr 27 20:36:18 2003 +++ b/drivers/net/appletalk/cops.c Tue Jul 1 16:09:19 2003 @@ -424,7 +424,7 @@ init_timer(&cops_timer); cops_timer.function = cops_poll; cops_timer.data = (unsigned long)dev; - cops_timer.expires = jiffies + 5; + cops_timer.expires = jiffies + HZ/20; add_timer(&cops_timer); } else @@ -700,7 +700,8 @@ status = inb(ioaddr+TANG_CARD_STATUS); } while((++boguscount < 20) && (status&(TANG_RX_READY|TANG_TX_READY))); - cops_timer.expires = jiffies+5; + /* poll 20 times per second */ + cops_timer.expires = jiffies + HZ/20; add_timer(&cops_timer); return; diff -Nru a/drivers/net/appletalk/ltpc.c b/drivers/net/appletalk/ltpc.c --- a/drivers/net/appletalk/ltpc.c Sun Apr 27 20:36:18 2003 +++ b/drivers/net/appletalk/ltpc.c Tue Jul 1 16:10:07 2003 @@ -926,8 +926,9 @@ if (!dev) return; /* we've been downed */ + /* poll 20 times per second */ idle(dev); - ltpc_timer.expires = jiffies+5; + ltpc_timer.expires = jiffies + HZ/20; add_timer(<pc_timer); } @@ -1217,7 +1218,7 @@ ltpc_timer.function=ltpc_poll; ltpc_timer.data = (unsigned long) dev; - ltpc_timer.expires = jiffies + 5; + ltpc_timer.expires = jiffies + HZ/20; add_timer(<pc_timer); } diff -Nru a/drivers/net/declance.c b/drivers/net/declance.c --- a/drivers/net/declance.c Wed Jun 25 14:05:16 2003 +++ b/drivers/net/declance.c Tue Jul 1 16:16:27 2003 @@ -992,7 +992,7 @@ return; if (lp->tx_old != lp->tx_new) { - mod_timer(&lp->multicast_timer, jiffies + 4); + mod_timer(&lp->multicast_timer, jiffies + 4 * HZ/100); netif_wake_queue(dev); return; } diff -Nru a/drivers/net/dgrs.c b/drivers/net/dgrs.c --- a/drivers/net/dgrs.c Mon Jun 9 15:48:16 2003 +++ b/drivers/net/dgrs.c Thu Jul 10 16:14:45 2003 @@ -1252,18 +1252,12 @@ { DGRS_PRIV *priv; struct net_device *dev, *aux; - - /* Allocate and fill new device structure. */ - int dev_size = sizeof(struct net_device) + sizeof(DGRS_PRIV); int i, ret; - dev = (struct net_device *) kmalloc(dev_size, GFP_KERNEL); - + dev = alloc_etherdev(sizeof(DGRS_PRIV)); if (!dev) return -ENOMEM; - memset(dev, 0, dev_size); - dev->priv = ((void *)dev) + sizeof(struct net_device); priv = (DGRS_PRIV *)dev->priv; dev->base_addr = io; @@ -1279,7 +1273,7 @@ dev->init = dgrs_probe1; SET_MODULE_OWNER(dev); - ether_setup(dev); + if (register_netdev(dev) != 0) { kfree(dev); return -EIO; @@ -1302,15 +1296,18 @@ struct net_device *devN; DGRS_PRIV *privN; /* Allocate new dev and priv structures */ - devN = (struct net_device *) kmalloc(dev_size, GFP_KERNEL); - /* Make it an exact copy of dev[0]... */ + devN = alloc_etherdev(sizeof(DGRS_PRIV)); ret = -ENOMEM; if (!devN) goto fail; - memcpy(devN, dev, dev_size); - memset(devN->name, 0, sizeof(devN->name)); - devN->priv = ((void *)devN) + sizeof(struct net_device); + + /* Make it an exact copy of dev[0]... */ + *devN = *dev; + + /* copy the priv structure of dev[0] */ privN = (DGRS_PRIV *)devN->priv; + *privN = *priv; + /* ... and zero out VM areas */ privN->vmem = 0; privN->vplxdma = 0; @@ -1318,9 +1315,11 @@ devN->irq = 0; /* ... and base MAC address off address of 1st port */ devN->dev_addr[5] += i; + /* ... choose a new name */ + strncpy(devN->name, "eth%d", IFNAMSIZ); devN->init = dgrs_initclone; SET_MODULE_OWNER(devN); - ether_setup(devN); + ret = -EIO; if (register_netdev(devN)) { kfree(devN); diff -Nru a/drivers/net/e100/e100_main.c b/drivers/net/e100/e100_main.c --- a/drivers/net/e100/e100_main.c Thu Jun 26 17:35:25 2003 +++ b/drivers/net/e100/e100_main.c Tue Jul 8 17:18:07 2003 @@ -46,6 +46,13 @@ /* Change Log * + * 2.3.18 07/08/03 + * o Bug fix: read skb->len after freeing skb + * [Andrew Morton] akpm@zip.com.au + * o Bug fix: 82557 (with National PHY) timeout during init + * [Adam Kropelin] akropel1@rochester.rr.com + * o Feature add: allow to change Wake On LAN when EEPROM disabled + * * 2.3.13 05/08/03 * o Feature remove: /proc/net/PRO_LAN_Adapters support gone completely * o Feature remove: IDIAG support (use ethtool -t instead) @@ -65,20 +72,6 @@ * o Bug fix: statistic command failure would stop statistic collection. * * 2.2.21 02/11/03 - * o Removed marketing brand strings. Instead, Using generic string - * "Intel(R) PRO/100 Network Connection" for all adapters. - * o Implemented ethtool -S option - * o Strip /proc/net/PRO_LAN_Adapters files for kernel driver - * o Bug fix: Read wrong byte in EEPROM when offset is odd number - * o Bug fix: PHY loopback test fails on ICH devices - * o Bug fix: System panic on e100_close when repeating Hot Remove and - * Add in a team - * o Bug fix: Linux Bonding driver claims adapter's link loss because of - * not updating last_rx field - * o Bug fix: e100 does not check validity of MAC address - * o New feature: added ICH5 support - * - * 2.1.27 11/20/02 */ #include @@ -144,7 +137,7 @@ static inline void e100_tx_skb_free(struct e100_private *bdp, tcb_t *tcb); /* Global Data structures and variables */ char e100_copyright[] __devinitdata = "Copyright (c) 2003 Intel Corporation"; -char e100_driver_version[]="2.3.13-k1"; +char e100_driver_version[]="2.3.18-k1"; const char *e100_full_driver_name = "Intel(R) PRO/100 Network Driver"; char e100_short_driver_name[] = "e100"; static int e100nics = 0; @@ -689,17 +682,16 @@ bdp->wolsupported = 0; bdp->wolopts = 0; + if (bdp->rev_id >= D101A4_REV_ID) + bdp->wolsupported = WAKE_PHY | WAKE_MAGIC; + if (bdp->rev_id >= D101MA_REV_ID) + bdp->wolsupported |= WAKE_UCAST | WAKE_ARP; /* Check if WoL is enabled on EEPROM */ if (e100_eeprom_read(bdp, EEPROM_ID_WORD) & BIT_5) { /* Magic Packet WoL is enabled on device by default */ /* if EEPROM WoL bit is TRUE */ - bdp->wolsupported = WAKE_MAGIC; bdp->wolopts = WAKE_MAGIC; - if (bdp->rev_id >= D101A4_REV_ID) - bdp->wolsupported = WAKE_PHY | WAKE_MAGIC; - if (bdp->rev_id >= D101MA_REV_ID) - bdp->wolsupported |= WAKE_UCAST | WAKE_ARP; } printk(KERN_NOTICE "\n"); diff -Nru a/drivers/net/e100/e100_phy.c b/drivers/net/e100/e100_phy.c --- a/drivers/net/e100/e100_phy.c Thu May 8 19:16:27 2003 +++ b/drivers/net/e100/e100_phy.c Tue Jul 8 17:18:07 2003 @@ -919,6 +919,7 @@ unsigned char __devinit e100_phy_init(struct e100_private *bdp) { + e100_phy_reset(bdp); e100_phy_address_detect(bdp); e100_phy_isolate(bdp); e100_phy_id_detect(bdp); @@ -930,7 +931,6 @@ bdp->PhyDelay = 0; bdp->zlock_state = ZLOCK_INITIAL; - e100_phy_reset(bdp); e100_phy_set_speed_duplex(bdp, false); e100_fix_polarity(bdp); diff -Nru a/drivers/net/hamradio/mkiss.c b/drivers/net/hamradio/mkiss.c --- a/drivers/net/hamradio/mkiss.c Wed Jun 4 04:17:01 2003 +++ b/drivers/net/hamradio/mkiss.c Mon Jul 7 12:55:58 2003 @@ -22,6 +22,8 @@ * Matthias (DG2FEF) Added support for FlexNet CRC (on special request) * Fixed bug in ax25_close(): dev_lock_wait() was * called twice, causing a deadlock. + * Jeroen (PE1RXQ) Removed old MKISS_MAGIC stuff and calls to + * MOD_*_USE_COUNT */ #include @@ -55,15 +57,6 @@ static char banner[] __initdata = KERN_INFO "mkiss: AX.25 Multikiss, Hans Albas PE1AYX\n"; -#define NR_MKISS 4 -#define MKISS_SERIAL_TYPE_NORMAL 1 - -struct mkiss_channel { - int magic; /* magic word */ - int init; /* channel exists? */ - struct tty_struct *tty; /* link to tty control structure */ -}; - typedef struct ax25_ctrl { struct ax_disp ctrl; /* */ struct net_device dev; /* the device */ @@ -310,19 +303,11 @@ /* Send one completely decapsulated AX.25 packet to the AX.25 layer. */ static void ax_bump(struct ax_disp *ax) { - struct ax_disp *tmp_ax; struct sk_buff *skb; - struct mkiss_channel *mkiss; int count; - tmp_ax = ax; - if (ax->rbuff[0] > 0x0f) { - if (ax->mkiss != NULL) { - mkiss= ax->mkiss->tty->driver_data; - if (mkiss->magic == MKISS_DRIVER_MAGIC) - tmp_ax = ax->mkiss; - } else if (ax->rbuff[0] & 0x20) { + if (ax->rbuff[0] & 0x20) { ax->crcmode = CRC_MODE_FLEX; if (check_crc_flex(ax->rbuff, ax->rcount) < 0) { ax->rx_errors++; @@ -346,14 +331,14 @@ return; } - skb->dev = tmp_ax->dev; + skb->dev = ax->dev; memcpy(skb_put(skb,count), ax->rbuff, count); skb->mac.raw = skb->data; skb->protocol = htons(ETH_P_AX25); netif_rx(skb); - tmp_ax->dev->last_rx = jiffies; - tmp_ax->rx_packets++; - tmp_ax->rx_bytes+=count; + ax->dev->last_rx = jiffies; + ax->rx_packets++; + ax->rx_bytes+=count; } /* Encapsulate one AX.25 packet and stuff into a TTY queue. */ @@ -361,7 +346,6 @@ { unsigned char *p; int actual, count; - struct mkiss_channel *mkiss = ax->tty->driver_data; if (ax->mtu != ax->dev->mtu + 73) /* Someone has been ifconfigging */ ax_changedmtu(ax); @@ -376,37 +360,26 @@ p = icp; - if (mkiss->magic != MKISS_DRIVER_MAGIC) { - switch (ax->crcmode) { - unsigned short crc; - - case CRC_MODE_FLEX: - *p |= 0x20; - crc = calc_crc_flex(p, len); - count = kiss_esc_crc(p, (unsigned char *)ax->xbuff, crc, len+2); - break; - - default: - count = kiss_esc(p, (unsigned char *)ax->xbuff, len); - break; - } - ax->tty->flags |= (1 << TTY_DO_WRITE_WAKEUP); - actual = ax->tty->driver->write(ax->tty, 0, ax->xbuff, count); - ax->tx_packets++; - ax->tx_bytes+=actual; - ax->dev->trans_start = jiffies; - ax->xleft = count - actual; - ax->xhead = ax->xbuff + actual; - } else { - count = kiss_esc(p, (unsigned char *) ax->mkiss->xbuff, len); - ax->mkiss->tty->flags |= (1 << TTY_DO_WRITE_WAKEUP); - actual = ax->mkiss->tty->driver->write(ax->mkiss->tty, 0, ax->mkiss->xbuff, count); - ax->tx_packets++; - ax->tx_bytes+=actual; - ax->mkiss->dev->trans_start = jiffies; - ax->mkiss->xleft = count - actual; - ax->mkiss->xhead = ax->mkiss->xbuff + actual; - } + switch (ax->crcmode) { + unsigned short crc; + + case CRC_MODE_FLEX: + *p |= 0x20; + crc = calc_crc_flex(p, len); + count = kiss_esc_crc(p, (unsigned char *)ax->xbuff, crc, len+2); + break; + + default: + count = kiss_esc(p, (unsigned char *)ax->xbuff, len); + break; + } + ax->tty->flags |= (1 << TTY_DO_WRITE_WAKEUP); + actual = ax->tty->driver->write(ax->tty, 0, ax->xbuff, count); + ax->tx_packets++; + ax->tx_bytes+=actual; + ax->dev->trans_start = jiffies; + ax->xleft = count - actual; + ax->xhead = ax->xbuff + actual; } /* @@ -417,7 +390,6 @@ { int actual; struct ax_disp *ax = (struct ax_disp *) tty->disc_data; - struct mkiss_channel *mkiss; /* First make sure we're connected. */ if (ax == NULL || ax->magic != AX25_MAGIC || !netif_running(ax->dev)) @@ -428,12 +400,6 @@ */ tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP); - if (ax->mkiss != NULL) { - mkiss= ax->mkiss->tty->driver_data; - if (mkiss->magic == MKISS_DRIVER_MAGIC) - ax_unlock(ax->mkiss); - } - netif_wake_queue(ax->dev); return; } @@ -447,32 +413,12 @@ static int ax_xmit(struct sk_buff *skb, struct net_device *dev) { struct ax_disp *ax = (struct ax_disp *) dev->priv; - struct mkiss_channel *mkiss = ax->tty->driver_data; - struct ax_disp *tmp_ax; - - tmp_ax = NULL; - - if (mkiss->magic == MKISS_DRIVER_MAGIC) { - if (skb->data[0] < 0x10) - skb->data[0] = skb->data[0] + 0x10; - tmp_ax = ax->mkiss; - } if (!netif_running(dev)) { printk(KERN_ERR "mkiss: %s: xmit call when iface is down\n", dev->name); return 1; } - if (tmp_ax != NULL) - if (netif_queue_stopped(tmp_ax->dev)) - return 1; - - if (tmp_ax != NULL) - if (netif_queue_stopped(dev)) { - printk(KERN_ERR "mkiss: dev busy while serial dev is free\n"); - ax_unlock(ax); - } - if (netif_queue_stopped(dev)) { /* * May be we must check transmitter timeout here ? @@ -495,8 +441,6 @@ /* We were not busy, so we are now... :-) */ if (skb != NULL) { ax_lock(ax); - if (tmp_ax != NULL) - ax_lock(tmp_ax); ax_encaps(ax, skb->data, skb->len); kfree_skb(skb); } @@ -634,9 +578,7 @@ static int ax25_open(struct tty_struct *tty) { struct ax_disp *ax = (struct ax_disp *) tty->disc_data; - struct ax_disp *tmp_ax; - struct mkiss_channel *mkiss; - int err, cnt; + int err; /* First make sure we're not already connected. */ if (ax && ax->magic == AX25_MAGIC) @@ -649,9 +591,6 @@ ax->tty = tty; tty->disc_data = ax; - ax->mkiss = NULL; - tmp_ax = NULL; - if (tty->driver->flush_buffer) tty->driver->flush_buffer(tty); if (tty->ldisc.flush_buffer) @@ -664,29 +603,6 @@ if ((err = ax_open(ax->dev))) return err; - mkiss = ax->tty->driver_data; - - if (mkiss->magic == MKISS_DRIVER_MAGIC) { - for (cnt = 1; cnt < ax25_maxdev; cnt++) { - if (ax25_ctrls[cnt]) { - if (netif_running(&ax25_ctrls[cnt]->dev)) { - if (ax == &ax25_ctrls[cnt]->ctrl) { - cnt--; - tmp_ax = &ax25_ctrls[cnt]->ctrl; - break; - } - } - } - } - } - - if (tmp_ax != NULL) { - ax->mkiss = tmp_ax; - tmp_ax->mkiss = ax; - } - - MOD_INC_USE_COUNT; - /* Done. We have linked the TTY line to a channel. */ return ax->dev->base_addr; } @@ -705,7 +621,6 @@ ax->tty = NULL; ax_free(ax); - MOD_DEC_USE_COUNT; } diff -Nru a/drivers/net/hamradio/mkiss.h b/drivers/net/hamradio/mkiss.h --- a/drivers/net/hamradio/mkiss.h Mon Mar 17 15:42:28 2003 +++ b/drivers/net/hamradio/mkiss.h Mon Jul 7 12:50:28 2003 @@ -19,7 +19,6 @@ /* Various fields. */ struct tty_struct *tty; /* ptr to TTY structure */ struct net_device *dev; /* easy for intr handling */ - struct ax_disp *mkiss; /* mkiss txport if mkiss channel*/ /* These are pointers to the malloc()ed frame buffers. */ unsigned char *rbuff; /* receiver buffer */ @@ -60,4 +59,3 @@ }; #define AX25_MAGIC 0x5316 -#define MKISS_DRIVER_MAGIC 1215 diff -Nru a/drivers/net/irda/Kconfig b/drivers/net/irda/Kconfig --- a/drivers/net/irda/Kconfig Wed Jun 25 13:52:00 2003 +++ b/drivers/net/irda/Kconfig Wed Jul 9 05:02:24 2003 @@ -22,6 +22,7 @@ config DONGLE bool "Serial dongle support" + depends on IRTTY_SIR help Say Y here if you have an infrared device that connects to your computer's serial port. These devices are called dongles. Then say Y @@ -69,7 +70,7 @@ config IRTTY_OLD tristate "Old IrTTY (broken)" - depends on IRDA + depends on IRDA && EXPERIMENTAL help Say Y here if you want to build support for the IrTTY line discipline. If you want to compile it as a module (irtty), say M @@ -102,6 +103,7 @@ config DONGLE_OLD bool "Old Serial dongle support" + depends on IRTTY_OLD || IRPORT_SIR help Say Y here if you have an infrared device that connects to your computer's serial port. These devices are called dongles. Then say Y @@ -226,8 +228,8 @@ comment "FIR device drivers" config USB_IRDA - tristate "IrDA USB dongles (EXPERIMENTAL)" - depends on IRDA && USB && EXPERIMENTAL + tristate "IrDA USB dongles" + depends on IRDA && USB ---help--- Say Y here if you want to build support for the USB IrDA FIR Dongle device driver. If you want to compile it as a module (irda-usb), @@ -243,7 +245,7 @@ config NSC_FIR tristate "NSC PC87108/PC87338" - depends on IRDA + depends on IRDA && ISA help Say Y here if you want to build support for the NSC PC87108 and PC87338 IrDA chipsets. This driver supports SIR, @@ -255,7 +257,7 @@ config WINBOND_FIR tristate "Winbond W83977AF (IR)" - depends on IRDA + depends on IRDA && ISA help Say Y here if you want to build IrDA support for the Winbond W83977AF super-io chipset. This driver should be used for the IrDA @@ -295,7 +297,7 @@ config SMC_IRCC_OLD tristate "SMC IrCC (old driver) (EXPERIMENTAL)" - depends on EXPERIMENTAL && IRDA + depends on EXPERIMENTAL && IRDA && ISA help Say Y here if you want to build support for the SMC Infrared Communications Controller. It is used in the Fujitsu Lifebook 635t @@ -307,7 +309,7 @@ config SMC_IRCC_FIR tristate "SMSC IrCC (EXPERIMENTAL)" - depends on EXPERIMENTAL && IRDA + depends on EXPERIMENTAL && IRDA && ISA help Say Y here if you want to build support for the SMC Infrared Communications Controller. It is used in a wide variety of @@ -318,7 +320,7 @@ config ALI_FIR tristate "ALi M5123 FIR (EXPERIMENTAL)" - depends on EXPERIMENTAL && IRDA + depends on EXPERIMENTAL && IRDA && ISA help Say Y here if you want to build support for the ALi M5123 FIR Controller. The ALi M5123 FIR Controller is embedded in ALi M1543C, diff -Nru a/drivers/net/irda/irda-usb.c b/drivers/net/irda/irda-usb.c --- a/drivers/net/irda/irda-usb.c Fri May 30 10:45:57 2003 +++ b/drivers/net/irda/irda-usb.c Wed Jul 9 08:40:39 2003 @@ -74,8 +74,10 @@ /* These are the currently known IrDA USB dongles. Add new dongles here */ static struct usb_device_id dongles[] = { - /* ACTiSYS Corp, ACT-IR2000U FIR-USB Adapter */ + /* ACTiSYS Corp., ACT-IR2000U FIR-USB Adapter */ { USB_DEVICE(0x9c4, 0x011), .driver_info = IUC_SPEED_BUG | IUC_NO_WINDOW }, + /* Look like ACTiSYS, Report : IBM Corp., IBM UltraPort IrDA */ + { USB_DEVICE(0x4428, 0x012), driver_info: IUC_SPEED_BUG | IUC_NO_WINDOW }, /* KC Technology Inc., KC-180 USB IrDA Device */ { USB_DEVICE(0x50f, 0x180), .driver_info = IUC_SPEED_BUG | IUC_NO_WINDOW }, /* Extended Systems, Inc., XTNDAccess IrDA USB (ESI-9685) */ @@ -1123,7 +1125,10 @@ /* Initialize QoS for this device */ irda_init_max_qos_capabilies(&self->qos); - self->qos.baud_rate.bits = desc->wBaudRate; + /* See spec section 7.2 for meaning. + * Values are little endian (as most USB stuff), the IrDA stack + * use it in native order (see parameters.c). - Jean II */ + self->qos.baud_rate.bits = le16_to_cpu(desc->wBaudRate); self->qos.min_turn_time.bits = desc->bmMinTurnaroundTime; self->qos.additional_bofs.bits = desc->bmAdditionalBOFs; self->qos.window_size.bits = desc->bmWindowSize; @@ -1134,7 +1139,7 @@ /* Don't always trust what the dongle tell us */ if(self->capability & IUC_SIR_ONLY) - self->qos.baud_rate.bits &= 0xff; + self->qos.baud_rate.bits &= 0x00ff; if(self->capability & IUC_SMALL_PKT) self->qos.data_size.bits = 0x07; if(self->capability & IUC_NO_WINDOW) @@ -1327,13 +1332,14 @@ */ static inline void irda_usb_dump_class_desc(struct irda_class_desc *desc) { + /* Values are little endian */ printk("bLength=%x\n", desc->bLength); printk("bDescriptorType=%x\n", desc->bDescriptorType); - printk("bcdSpecRevision=%x\n", desc->bcdSpecRevision); + printk("bcdSpecRevision=%x\n", le16_to_cpu(desc->bcdSpecRevision)); printk("bmDataSize=%x\n", desc->bmDataSize); printk("bmWindowSize=%x\n", desc->bmWindowSize); printk("bmMinTurnaroundTime=%d\n", desc->bmMinTurnaroundTime); - printk("wBaudRate=%x\n", desc->wBaudRate); + printk("wBaudRate=%x\n", le16_to_cpu(desc->wBaudRate)); printk("bmAdditionalBOFs=%x\n", desc->bmAdditionalBOFs); printk("bIrdaRateSniff=%x\n", desc->bIrdaRateSniff); printk("bMaxUnicastList=%x\n", desc->bMaxUnicastList); diff -Nru a/drivers/net/irda/nsc-ircc.c b/drivers/net/irda/nsc-ircc.c --- a/drivers/net/irda/nsc-ircc.c Thu May 29 23:50:31 2003 +++ b/drivers/net/irda/nsc-ircc.c Wed Jul 9 08:47:24 2003 @@ -81,8 +81,10 @@ static int nsc_ircc_probe_108(nsc_chip_t *chip, chipio_t *info); static int nsc_ircc_probe_338(nsc_chip_t *chip, chipio_t *info); +static int nsc_ircc_probe_39x(nsc_chip_t *chip, chipio_t *info); static int nsc_ircc_init_108(nsc_chip_t *chip, chipio_t *info); static int nsc_ircc_init_338(nsc_chip_t *chip, chipio_t *info); +static int nsc_ircc_init_39x(nsc_chip_t *chip, chipio_t *info); /* These are the known NSC chips */ static nsc_chip_t chips[] = { @@ -94,6 +96,10 @@ /* Contributed by Kevin Thayer - OmniBook 6100 */ { "PC87338?", { 0x2e, 0x15c, 0x398 }, 0x08, 0x00, 0xf8, nsc_ircc_probe_338, nsc_ircc_init_338 }, + /* Contributed by Jan Frey - IBM A30/A31 */ + /* Should use nsc_ircc_probe_39x properly */ + { "PC8739x", { 0x2e, 0x4e, 0x0 }, 0x20, 0xea, 0xff, + nsc_ircc_probe_39x, nsc_ircc_init_39x }, { NULL } }; @@ -426,7 +432,7 @@ outb(0x00, cfg_base+1); /* Disable device */ /* Base Address and Interrupt Control Register (BAIC) */ - outb(0, cfg_base); + outb(CFG_108_BAIC, cfg_base); switch (info->fir_base) { case 0x3e8: outb(0x14, cfg_base+1); break; case 0x2e8: outb(0x15, cfg_base+1); break; @@ -446,7 +452,7 @@ case 15: temp = 0x07; break; default: ERROR("%s(), invalid irq", __FUNCTION__); } - outb(1, cfg_base); + outb(CFG_108_CSRT, cfg_base); switch (info->dma) { case 0: outb(0x08+temp, cfg_base+1); break; @@ -455,7 +461,7 @@ default: ERROR("%s(), invalid dma", __FUNCTION__); } - outb(2, cfg_base); /* Mode Control Register (MCTL) */ + outb(CFG_108_MCTL, cfg_base); /* Mode Control Register (MCTL) */ outb(0x03, cfg_base+1); /* Enable device */ return 0; @@ -473,7 +479,7 @@ int reg; /* Read address and interrupt control register (BAIC) */ - outb(CFG_BAIC, cfg_base); + outb(CFG_108_BAIC, cfg_base); reg = inb(cfg_base+1); switch (reg & 0x03) { @@ -495,7 +501,7 @@ info->fir_base); /* Read control signals routing register (CSRT) */ - outb(CFG_CSRT, cfg_base); + outb(CFG_108_CSRT, cfg_base); reg = inb(cfg_base+1); switch (reg & 0x07) { @@ -544,7 +550,7 @@ IRDA_DEBUG(2, "%s(), probing dma=%d\n", __FUNCTION__, info->dma); /* Read mode control register (MCTL) */ - outb(CFG_MCTL, cfg_base); + outb(CFG_108_MCTL, cfg_base); reg = inb(cfg_base+1); info->enabled = reg & 0x01; @@ -581,13 +587,194 @@ int pnp; /* Read funtion enable register (FER) */ - outb(CFG_FER, cfg_base); + outb(CFG_338_FER, cfg_base); + reg = inb(cfg_base+1); + + info->enabled = (reg >> 2) & 0x01; + + /* Check if we are in Legacy or PnP mode */ + outb(CFG_338_PNP0, cfg_base); + reg = inb(cfg_base+1); + + pnp = (reg >> 3) & 0x01; + if (pnp) { + IRDA_DEBUG(2, "(), Chip is in PnP mode\n"); + outb(0x46, cfg_base); + reg = (inb(cfg_base+1) & 0xfe) << 2; + + outb(0x47, cfg_base); + reg |= ((inb(cfg_base+1) & 0xfc) << 8); + + info->fir_base = reg; + } else { + /* Read function address register (FAR) */ + outb(CFG_338_FAR, cfg_base); + reg = inb(cfg_base+1); + + switch ((reg >> 4) & 0x03) { + case 0: + info->fir_base = 0x3f8; + break; + case 1: + info->fir_base = 0x2f8; + break; + case 2: + com = 3; + break; + case 3: + com = 4; + break; + } + + if (com) { + switch ((reg >> 6) & 0x03) { + case 0: + if (com == 3) + info->fir_base = 0x3e8; + else + info->fir_base = 0x2e8; + break; + case 1: + if (com == 3) + info->fir_base = 0x338; + else + info->fir_base = 0x238; + break; + case 2: + if (com == 3) + info->fir_base = 0x2e8; + else + info->fir_base = 0x2e0; + break; + case 3: + if (com == 3) + info->fir_base = 0x220; + else + info->fir_base = 0x228; + break; + } + } + } + info->sir_base = info->fir_base; + + /* Read PnP register 1 (PNP1) */ + outb(CFG_338_PNP1, cfg_base); + reg = inb(cfg_base+1); + + info->irq = reg >> 4; + + /* Read PnP register 3 (PNP3) */ + outb(CFG_338_PNP3, cfg_base); + reg = inb(cfg_base+1); + + info->dma = (reg & 0x07) - 1; + + /* Read power and test register (PTR) */ + outb(CFG_338_PTR, cfg_base); + reg = inb(cfg_base+1); + + info->suspended = reg & 0x01; + + return 0; +} + + +/* + * Function nsc_ircc_init_39x (chip, info) + * + * Now that we know it's a '39x (see probe below), we need to + * configure it so we can use it. + * + * The NSC '338 chip is a Super I/O chip with a "bank" architecture, + * the configuration of the different functionality (serial, parallel, + * floppy...) are each in a different bank (Logical Device Number). + * The base address, irq and dma configuration registers are common + * to all functionalities (index 0x30 to 0x7F). + * There is only one configuration register specific to the + * serial port, CFG_39X_SPC. + * JeanII + * + * Note : this code was written by Jan Frey + */ +static int nsc_ircc_init_39x(nsc_chip_t *chip, chipio_t *info) +{ + int cfg_base = info->cfg_base; + int reg1, reg2, irq, irqt, dma1, dma2; + int enabled, susp; + + /* This function should be executed with irq off to avoid + * another driver messing with the Super I/O bank - Jean II */ + + /* Access bank for SP2 */ + outb(CFG_39X_LDN, cfg_base); + outb(0x02, cfg_base+1); + + /* Read infos about SP2 */ + outb(CFG_39X_BASEH, cfg_base); + reg1 = inb(cfg_base+1); + outb(CFG_39X_BASEL, cfg_base); + reg2 = inb(cfg_base+1); + info->fir_base = (reg1 << 8) | reg2; + + outb(CFG_39X_IRQNUM, cfg_base); + irq = inb(cfg_base+1); + outb(CFG_39X_IRQSEL, cfg_base); + irqt = inb(cfg_base+1); + info->irq = irq; + + outb(CFG_39X_DMA0, cfg_base); + dma1 = inb(cfg_base+1); + outb(CFG_39X_DMA1, cfg_base); + dma2 = inb(cfg_base+1); + info->dma = dma1 -1; + + outb(CFG_39X_ACT, cfg_base); + enabled = inb(cfg_base+1) & 0x01; + + outb(CFG_39X_SPC, cfg_base); + susp = 1 - ((inb(cfg_base+1) & 0x02) >> 1); + + /* We should store those values in info ? Jean II */ + + IRDA_DEBUG(2, "%s(): io=0x%02x%02x, irq=%d (type %d), rxdma=%d, txdma=%d, enabled=%d (suspended=%d)\n", __FUNCTION__, reg1,reg2,irq,irqt,dma1,dma2,enabled,susp); + + /* Configure SP2 */ + + /* Do we want to enable the device if not enabled ? */ + + /* Enable UART bank switching (bit 7) */ + outb(CFG_39X_SPC, cfg_base); + outb(0x80, cfg_base+1); + + return 0; +} + +/* + * Function nsc_ircc_probe_39x (chip, info) + * + * Test if we really have a '39x chip at the given address + * + */ +static int nsc_ircc_probe_39x(nsc_chip_t *chip, chipio_t *info) +{ +#if 0 + int cfg_base = info->cfg_base; + int reg, com = 0; + int pnp; +#endif + + /* Why are we using the '338 configuration registers in here ? + * This probably need to be rewritten... + * Jean II */ +#if 0 + /* Read funtion enable register (FER) */ + outb(CFG_338_FER, cfg_base); reg = inb(cfg_base+1); info->enabled = (reg >> 2) & 0x01; /* Check if we are in Legacy or PnP mode */ - outb(CFG_PNP0, cfg_base); + outb(CFG_338_PNP0, cfg_base); reg = inb(cfg_base+1); pnp = (reg >> 3) & 0x01; @@ -602,7 +789,7 @@ info->fir_base = reg; } else { /* Read function address register (FAR) */ - outb(CFG_FAR, cfg_base); + outb(CFG_338_FAR, cfg_base); reg = inb(cfg_base+1); switch ((reg >> 4) & 0x03) { @@ -652,22 +839,23 @@ info->sir_base = info->fir_base; /* Read PnP register 1 (PNP1) */ - outb(CFG_PNP1, cfg_base); + outb(CFG_338_PNP1, cfg_base); reg = inb(cfg_base+1); info->irq = reg >> 4; /* Read PnP register 3 (PNP3) */ - outb(CFG_PNP3, cfg_base); + outb(CFG_338_PNP3, cfg_base); reg = inb(cfg_base+1); info->dma = (reg & 0x07) - 1; /* Read power and test register (PTR) */ - outb(CFG_PTR, cfg_base); + outb(CFG_338_PTR, cfg_base); reg = inb(cfg_base+1); info->suspended = reg & 0x01; +#endif return 0; } diff -Nru a/drivers/net/irda/old_belkin.c b/drivers/net/irda/old_belkin.c --- a/drivers/net/irda/old_belkin.c Thu Nov 21 14:06:11 2002 +++ b/drivers/net/irda/old_belkin.c Wed Jun 4 06:42:08 2003 @@ -32,8 +32,6 @@ #include #include #include -#include -#include #include #include diff -Nru a/drivers/net/pcmcia/3c574_cs.c b/drivers/net/pcmcia/3c574_cs.c --- a/drivers/net/pcmcia/3c574_cs.c Mon Jun 23 14:45:40 2003 +++ b/drivers/net/pcmcia/3c574_cs.c Mon Jun 30 14:42:46 2003 @@ -1048,7 +1048,7 @@ } if (lp->fast_poll) { lp->fast_poll--; - lp->media.expires = jiffies + 2; + lp->media.expires = jiffies + 2*HZ/100; add_timer(&lp->media); return; } diff -Nru a/drivers/net/pcmcia/3c589_cs.c b/drivers/net/pcmcia/3c589_cs.c --- a/drivers/net/pcmcia/3c589_cs.c Mon Jun 23 16:24:48 2003 +++ b/drivers/net/pcmcia/3c589_cs.c Mon Jun 30 14:43:04 2003 @@ -921,7 +921,7 @@ } if (lp->fast_poll) { lp->fast_poll--; - lp->media.expires = jiffies + 1; + lp->media.expires = jiffies + HZ/100; add_timer(&lp->media); return; } diff -Nru a/drivers/net/pcmcia/axnet_cs.c b/drivers/net/pcmcia/axnet_cs.c --- a/drivers/net/pcmcia/axnet_cs.c Tue Apr 29 15:46:18 2003 +++ b/drivers/net/pcmcia/axnet_cs.c Fri Jul 11 07:21:39 2003 @@ -258,7 +258,7 @@ if (*linkp == NULL) return; - del_timer(&link->release); + del_timer_sync(&link->release); if (link->state & DEV_CONFIG) { axnet_release((u_long)link); if (link->state & DEV_STALE_CONFIG) { @@ -706,7 +706,7 @@ link->open--; netif_stop_queue(dev); - del_timer(&info->watchdog); + del_timer_sync(&info->watchdog); if (link->state & DEV_STALE_CONFIG) mod_timer(&link->release, jiffies + HZ/20); diff -Nru a/drivers/net/pcmcia/ibmtr_cs.c b/drivers/net/pcmcia/ibmtr_cs.c --- a/drivers/net/pcmcia/ibmtr_cs.c Mon Jun 2 22:44:51 2003 +++ b/drivers/net/pcmcia/ibmtr_cs.c Fri Jul 11 07:22:01 2003 @@ -293,9 +293,9 @@ dev = info->dev; { struct tok_info *ti = (struct tok_info *)dev->priv; - del_timer(&(ti->tr_timer)); + del_timer_sync(&(ti->tr_timer)); } - del_timer(&link->release); + del_timer_sync(&link->release); if (link->state & DEV_CONFIG) { ibmtr_release((u_long)link); if (link->state & DEV_STALE_CONFIG) { diff -Nru a/drivers/net/pcmcia/nmclan_cs.c b/drivers/net/pcmcia/nmclan_cs.c --- a/drivers/net/pcmcia/nmclan_cs.c Fri Jun 27 23:20:46 2003 +++ b/drivers/net/pcmcia/nmclan_cs.c Fri Jul 11 07:58:34 2003 @@ -8,6 +8,7 @@ Written by Roger C. Pao Copyright 1995 Roger C. Pao + Linux 2.5 cleanups Copyright Red Hat 2003 This software may be used and distributed according to the terms of the GNU General Public License. @@ -68,6 +69,10 @@ History ------------------------------------------------------------------------------- Log: nmclan_cs.c,v + * 2.5.75-ac1 2003/07/11 Alan Cox + * Fixed hang on card eject as we probe it + * Cleaned up to use new style locking. + * * Revision 0.16 1995/07/01 06:42:17 rpao * Bug fix: nmclan_reset() called CardServices incorrectly. * @@ -369,6 +374,8 @@ char tx_free_frames; /* Number of free transmit frame buffers */ char tx_irq_disabled; /* MACE TX interrupt disabled */ + + spinlock_t bank_lock; /* Must be held if you step off bank 0 */ } mace_private; /* ---------------------------------------------------------------------------- @@ -482,6 +489,7 @@ link = &lp->link; link->priv = dev; + spin_lock_init(&lp->bank_lock); init_timer(&link->release); link->release.function = &nmclan_release; link->release.data = (u_long)link; @@ -561,7 +569,7 @@ if (*linkp == NULL) return; - del_timer(&link->release); + del_timer_sync(&link->release); if (link->state & DEV_CONFIG) { nmclan_release((u_long)link); if (link->state & DEV_STALE_CONFIG) { @@ -588,7 +596,7 @@ assuming that during normal operation, the MACE is always in bank 0. ---------------------------------------------------------------------------- */ -static int mace_read(ioaddr_t ioaddr, int reg) +static int mace_read(mace_private *lp, ioaddr_t ioaddr, int reg) { int data = 0xFF; unsigned long flags; @@ -598,12 +606,11 @@ data = inb(ioaddr + AM2150_MACE_BASE + reg); break; case 1: /* register 16-31 */ - save_flags(flags); - cli(); + spin_lock_irqsave(&lp->bank_lock, flags); MACEBANK(1); data = inb(ioaddr + AM2150_MACE_BASE + (reg & 0x0F)); MACEBANK(0); - restore_flags(flags); + spin_unlock_irqrestore(&lp->bank_lock, flags); break; } return (data & 0xFF); @@ -616,7 +623,7 @@ are assuming that during normal operation, the MACE is always in bank 0. ---------------------------------------------------------------------------- */ -static void mace_write(ioaddr_t ioaddr, int reg, int data) +static void mace_write(mace_private *lp, ioaddr_t ioaddr, int reg, int data) { unsigned long flags; @@ -625,12 +632,11 @@ outb(data & 0xFF, ioaddr + AM2150_MACE_BASE + reg); break; case 1: /* register 16-31 */ - save_flags(flags); - cli(); + spin_lock_irqsave(&lp->bank_lock, flags); MACEBANK(1); outb(data & 0xFF, ioaddr + AM2150_MACE_BASE + (reg & 0x0F)); MACEBANK(0); - restore_flags(flags); + spin_unlock_irqrestore(&lp->bank_lock, flags); break; } } /* mace_write */ @@ -639,22 +645,29 @@ mace_init Resets the MACE chip. ---------------------------------------------------------------------------- */ -static void mace_init(ioaddr_t ioaddr, char *enet_addr) +static int mace_init(mace_private *lp, ioaddr_t ioaddr, char *enet_addr) { int i; + int ct = 0; /* MACE Software reset */ - mace_write(ioaddr, MACE_BIUCC, 1); - while (mace_read(ioaddr, MACE_BIUCC) & 0x01) { + mace_write(lp, ioaddr, MACE_BIUCC, 1); + while (mace_read(lp, ioaddr, MACE_BIUCC) & 0x01) { /* Wait for reset bit to be cleared automatically after <= 200ns */; + if(++ct > 500) + { + printk(KERN_ERR "mace: reset failed, card removed ?\n"); + return -1; + } + udelay(1); } - mace_write(ioaddr, MACE_BIUCC, 0); + mace_write(lp, ioaddr, MACE_BIUCC, 0); /* The Am2150 requires that the MACE FIFOs operate in burst mode. */ - mace_write(ioaddr, MACE_FIFOCC, 0x0F); + mace_write(lp, ioaddr, MACE_FIFOCC, 0x0F); - mace_write(ioaddr, MACE_RCVFC, 0); /* Disable Auto Strip Receive */ - mace_write(ioaddr, MACE_IMR, 0xFF); /* Disable all interrupts until _open */ + mace_write(lp,ioaddr, MACE_RCVFC, 0); /* Disable Auto Strip Receive */ + mace_write(lp, ioaddr, MACE_IMR, 0xFF); /* Disable all interrupts until _open */ /* * Bit 2-1 PORTSEL[1-0] Port Select. @@ -670,31 +683,39 @@ */ switch (if_port) { case 1: - mace_write(ioaddr, MACE_PLSCC, 0x02); + mace_write(lp, ioaddr, MACE_PLSCC, 0x02); break; case 2: - mace_write(ioaddr, MACE_PLSCC, 0x00); + mace_write(lp, ioaddr, MACE_PLSCC, 0x00); break; default: - mace_write(ioaddr, MACE_PHYCC, /* ASEL */ 4); + mace_write(lp, ioaddr, MACE_PHYCC, /* ASEL */ 4); /* ASEL Auto Select. When set, the PORTSEL[1-0] bits are overridden, and the MACE device will automatically select the operating media interface port. */ break; } - mace_write(ioaddr, MACE_IAC, MACE_IAC_ADDRCHG | MACE_IAC_PHYADDR); + mace_write(lp, ioaddr, MACE_IAC, MACE_IAC_ADDRCHG | MACE_IAC_PHYADDR); /* Poll ADDRCHG bit */ - while (mace_read(ioaddr, MACE_IAC) & MACE_IAC_ADDRCHG) - ; + ct = 0; + while (mace_read(lp, ioaddr, MACE_IAC) & MACE_IAC_ADDRCHG) + { + if(++ ct > 500) + { + printk(KERN_ERR "mace: ADDRCHG timeout, card removed ?\n"); + return -1; + } + } /* Set PADR register */ for (i = 0; i < ETHER_ADDR_LEN; i++) - mace_write(ioaddr, MACE_PADR, enet_addr[i]); + mace_write(lp, ioaddr, MACE_PADR, enet_addr[i]); /* MAC Configuration Control Register should be written last */ /* Let set_multicast_list set this. */ - /* mace_write(ioaddr, MACE_MACCC, MACE_MACCC_ENXMT | MACE_MACCC_ENRCV); */ - mace_write(ioaddr, MACE_MACCC, 0x00); + /* mace_write(lp, ioaddr, MACE_MACCC, MACE_MACCC_ENXMT | MACE_MACCC_ENRCV); */ + mace_write(lp, ioaddr, MACE_MACCC, 0x00); + return 0; } /* mace_init */ /* ---------------------------------------------------------------------------- @@ -759,8 +780,8 @@ { char sig[2]; - sig[0] = mace_read(ioaddr, MACE_CHIPIDL); - sig[1] = mace_read(ioaddr, MACE_CHIPIDH); + sig[0] = mace_read(lp, ioaddr, MACE_CHIPIDL); + sig[1] = mace_read(lp, ioaddr, MACE_CHIPIDH); if ((sig[0] == 0x40) && ((sig[1] & 0x0F) == 0x09)) { DEBUG(0, "nmclan_cs configured: mace id=%x %x\n", sig[0], sig[1]); @@ -772,7 +793,8 @@ } } - mace_init(ioaddr, dev->dev_addr); + if(mace_init(lp, ioaddr, dev->dev_addr) == -1) + goto failed; /* The if_port symbol can be set when the module is loaded */ if (if_port <= 2) @@ -923,8 +945,8 @@ lp->tx_free_frames=AM2150_MAX_TX_FRAMES; /* Reinitialize the MACE chip for operation. */ - mace_init(dev->base_addr, dev->dev_addr); - mace_write(dev->base_addr, MACE_IMR, MACE_IMR_DEFAULT); + mace_init(lp, dev->base_addr, dev->dev_addr); + mace_write(lp, dev->base_addr, MACE_IMR, MACE_IMR_DEFAULT); /* Restore the multicast list and enable TX and RX. */ restore_multicast_list(dev); @@ -1456,9 +1478,9 @@ { mace_private *lp = (mace_private *)dev->priv; - lp->mace_stats.rcvcc += mace_read(ioaddr, MACE_RCVCC); - lp->mace_stats.rntpc += mace_read(ioaddr, MACE_RNTPC); - lp->mace_stats.mpc += mace_read(ioaddr, MACE_MPC); + lp->mace_stats.rcvcc += mace_read(lp, ioaddr, MACE_RCVCC); + lp->mace_stats.rntpc += mace_read(lp, ioaddr, MACE_RNTPC); + lp->mace_stats.mpc += mace_read(lp, ioaddr, MACE_MPC); /* At this point, mace_stats is fully updated for this call. We may now update the linux_stats. */ @@ -1608,30 +1630,30 @@ DEBUG(1, "Attempt to restore multicast list detected.\n"); - mace_write(ioaddr, MACE_IAC, MACE_IAC_ADDRCHG | MACE_IAC_LOGADDR); + mace_write(lp, ioaddr, MACE_IAC, MACE_IAC_ADDRCHG | MACE_IAC_LOGADDR); /* Poll ADDRCHG bit */ - while (mace_read(ioaddr, MACE_IAC) & MACE_IAC_ADDRCHG) + while (mace_read(lp, ioaddr, MACE_IAC) & MACE_IAC_ADDRCHG) ; /* Set LADRF register */ for (i = 0; i < MACE_LADRF_LEN; i++) - mace_write(ioaddr, MACE_LADRF, ladrf[i]); + mace_write(lp, ioaddr, MACE_LADRF, ladrf[i]); - mace_write(ioaddr, MACE_UTR, MACE_UTR_RCVFCSE | MACE_UTR_LOOP_EXTERNAL); - mace_write(ioaddr, MACE_MACCC, MACE_MACCC_ENXMT | MACE_MACCC_ENRCV); + mace_write(lp, ioaddr, MACE_UTR, MACE_UTR_RCVFCSE | MACE_UTR_LOOP_EXTERNAL); + mace_write(lp, ioaddr, MACE_MACCC, MACE_MACCC_ENXMT | MACE_MACCC_ENRCV); } else if (num_addrs < 0) { /* Promiscuous mode: receive all packets */ - mace_write(ioaddr, MACE_UTR, MACE_UTR_LOOP_EXTERNAL); - mace_write(ioaddr, MACE_MACCC, + mace_write(lp, ioaddr, MACE_UTR, MACE_UTR_LOOP_EXTERNAL); + mace_write(lp, ioaddr, MACE_MACCC, MACE_MACCC_PROM | MACE_MACCC_ENXMT | MACE_MACCC_ENRCV ); } else { /* Normal mode */ - mace_write(ioaddr, MACE_UTR, MACE_UTR_LOOP_EXTERNAL); - mace_write(ioaddr, MACE_MACCC, MACE_MACCC_ENXMT | MACE_MACCC_ENRCV); + mace_write(lp, ioaddr, MACE_UTR, MACE_UTR_LOOP_EXTERNAL); + mace_write(lp, ioaddr, MACE_MACCC, MACE_MACCC_ENXMT | MACE_MACCC_ENRCV); } } /* restore_multicast_list */ @@ -1691,20 +1713,21 @@ static void restore_multicast_list(struct net_device *dev) { ioaddr_t ioaddr = dev->base_addr; + mace_private *lp = (mace_private *)dev->priv; DEBUG(2, "%s: restoring Rx mode to %d addresses.\n", dev->name, ((mace_private *)(dev->priv))->multicast_num_addrs); if (dev->flags & IFF_PROMISC) { /* Promiscuous mode: receive all packets */ - mace_write(ioaddr, MACE_UTR, MACE_UTR_LOOP_EXTERNAL); - mace_write(ioaddr, MACE_MACCC, + mace_write(lp,ioaddr, MACE_UTR, MACE_UTR_LOOP_EXTERNAL); + mace_write(lp, ioaddr, MACE_MACCC, MACE_MACCC_PROM | MACE_MACCC_ENXMT | MACE_MACCC_ENRCV ); } else { /* Normal mode */ - mace_write(ioaddr, MACE_UTR, MACE_UTR_LOOP_EXTERNAL); - mace_write(ioaddr, MACE_MACCC, MACE_MACCC_ENXMT | MACE_MACCC_ENRCV); + mace_write(lp, ioaddr, MACE_UTR, MACE_UTR_LOOP_EXTERNAL); + mace_write(lp, ioaddr, MACE_MACCC, MACE_MACCC_ENXMT | MACE_MACCC_ENRCV); } } /* restore_multicast_list */ diff -Nru a/drivers/net/pcmcia/pcnet_cs.c b/drivers/net/pcmcia/pcnet_cs.c --- a/drivers/net/pcmcia/pcnet_cs.c Sun May 18 13:57:38 2003 +++ b/drivers/net/pcmcia/pcnet_cs.c Fri Jul 11 07:22:26 2003 @@ -357,7 +357,7 @@ if (*linkp == NULL) return; - del_timer(&link->release); + del_timer_sync(&link->release); if (link->state & DEV_CONFIG) { pcnet_release((u_long)link); if (link->state & DEV_STALE_CONFIG) { @@ -1052,7 +1052,7 @@ link->open--; netif_stop_queue(dev); - del_timer(&info->watchdog); + del_timer_sync(&info->watchdog); if (link->state & DEV_STALE_CONFIG) mod_timer(&link->release, jiffies + HZ/20); diff -Nru a/drivers/net/pcmcia/smc91c92_cs.c b/drivers/net/pcmcia/smc91c92_cs.c --- a/drivers/net/pcmcia/smc91c92_cs.c Tue Jun 24 15:07:31 2003 +++ b/drivers/net/pcmcia/smc91c92_cs.c Fri Jul 11 07:22:40 2003 @@ -433,7 +433,7 @@ if (*linkp == NULL) return; - del_timer(&link->release); + del_timer_sync(&link->release); if (link->state & DEV_CONFIG) { smc91c92_release((u_long)link); if (link->state & DEV_STALE_CONFIG) { @@ -1330,7 +1330,7 @@ outw(CTL_POWERDOWN, ioaddr + CONTROL ); link->open--; - del_timer(&smc->media); + del_timer_sync(&smc->media); if (link->state & DEV_STALE_CONFIG) mod_timer(&link->release, jiffies + HZ/20); @@ -1996,7 +1996,7 @@ } if (smc->fast_poll) { smc->fast_poll--; - smc->media.expires = jiffies + 1; + smc->media.expires = jiffies + HZ/100; add_timer(&smc->media); SMC_SELECT_BANK(saved_bank); return; diff -Nru a/drivers/net/pcmcia/xirc2ps_cs.c b/drivers/net/pcmcia/xirc2ps_cs.c --- a/drivers/net/pcmcia/xirc2ps_cs.c Fri Jun 20 13:16:19 2003 +++ b/drivers/net/pcmcia/xirc2ps_cs.c Fri Jul 11 07:22:49 2003 @@ -689,7 +689,7 @@ * the release() function is called, that will trigger a proper * detach(). */ - del_timer(&link->release); + del_timer_sync(&link->release); if (link->state & DEV_CONFIG) { xirc2ps_release((unsigned long)link); if (link->state & DEV_STALE_CONFIG) { diff -Nru a/drivers/net/plip.c b/drivers/net/plip.c --- a/drivers/net/plip.c Thu Jun 19 14:25:16 2003 +++ b/drivers/net/plip.c Tue Jul 8 08:07:44 2003 @@ -277,27 +277,10 @@ then calls us here. */ -int __init -plip_init_dev(struct net_device *dev, struct parport *pb) +static int +plip_init_netdev(struct net_device *dev) { - struct net_local *nl; - struct pardevice *pardev; - - SET_MODULE_OWNER(dev); - dev->irq = pb->irq; - dev->base_addr = pb->base; - - if (pb->irq == -1) { - printk(KERN_INFO "plip: %s has no IRQ. Using IRQ-less mode," - "which is fairly inefficient!\n", pb->name); - } - - pardev = parport_register_device(pb, dev->name, plip_preempt, - plip_wakeup, plip_interrupt, - 0, dev); - - if (!pardev) - return -ENODEV; + struct net_local *nl = dev->priv; printk(KERN_INFO "%s", version); if (dev->irq != -1) @@ -307,9 +290,6 @@ printk(KERN_INFO "%s: Parallel port at %#3lx, not using IRQ.\n", dev->name, dev->base_addr); - /* Fill in the generic fields of the device structure. */ - ether_setup(dev); - /* Then, override parts of it */ dev->hard_start_xmit = plip_tx_packet; dev->open = plip_open; @@ -322,22 +302,12 @@ memset(dev->dev_addr, 0xfc, ETH_ALEN); /* Set the private structure */ - dev->priv = kmalloc(sizeof (struct net_local), GFP_KERNEL); - if (dev->priv == NULL) { - printk(KERN_ERR "%s: out of memory\n", dev->name); - parport_unregister_device(pardev); - return -ENOMEM; - } - memset(dev->priv, 0, sizeof(struct net_local)); - nl = (struct net_local *) dev->priv; - nl->orig_hard_header = dev->hard_header; dev->hard_header = plip_hard_header; nl->orig_hard_header_cache = dev->hard_header_cache; dev->hard_header_cache = plip_hard_header_cache; - nl->pardev = pardev; nl->port_owner = 0; @@ -1299,29 +1269,52 @@ * available to use. */ static void plip_attach (struct parport *port) { - static int i; + static int unit; + struct net_device *dev; + struct net_local *nl; + char name[IFNAMSIZ]; if ((parport[0] == -1 && (!timid || !port->devices)) || plip_searchfor(parport, port->number)) { - if (i == PLIP_MAX) { + if (unit == PLIP_MAX) { printk(KERN_ERR "plip: too many devices\n"); return; } - dev_plip[i] = kmalloc(sizeof(struct net_device), - GFP_KERNEL); - if (!dev_plip[i]) { + + sprintf(name, "plip%d", unit); + dev = alloc_netdev(sizeof(struct net_local), name, + ether_setup); + if (!dev) { printk(KERN_ERR "plip: memory squeeze\n"); return; } - memset(dev_plip[i], 0, sizeof(struct net_device)); - sprintf(dev_plip[i]->name, "plip%d", i); - dev_plip[i]->priv = port; - if (plip_init_dev(dev_plip[i],port) || - register_netdev(dev_plip[i])) { - kfree(dev_plip[i]); - dev_plip[i] = NULL; + + dev->init = plip_init_netdev; + + SET_MODULE_OWNER(dev); + dev->irq = port->irq; + dev->base_addr = port->base; + if (port->irq == -1) { + printk(KERN_INFO "plip: %s has no IRQ. Using IRQ-less mode," + "which is fairly inefficient!\n", port->name); + } + + nl = dev->priv; + nl->pardev = parport_register_device(port, name, plip_preempt, + plip_wakeup, plip_interrupt, + 0, dev); + + if (!nl->pardev) { + printk(KERN_ERR "%s: parport_register failed\n", name); + kfree(dev); + return; + } + + if (register_netdev(dev)) { + printk(KERN_ERR "%s: network register failed\n", name); + kfree(dev); } else { - i++; + dev_plip[unit++] = dev; } } } @@ -1341,20 +1334,19 @@ static void __exit plip_cleanup_module (void) { + struct net_device *dev; int i; parport_unregister_driver (&plip_driver); for (i=0; i < PLIP_MAX; i++) { - if (dev_plip[i]) { - struct net_local *nl = - (struct net_local *)dev_plip[i]->priv; - unregister_netdev(dev_plip[i]); + if ((dev = dev_plip[i])) { + struct net_local *nl = dev->priv; + unregister_netdev(dev); if (nl->port_owner) parport_release(nl->pardev); parport_unregister_device(nl->pardev); - kfree(dev_plip[i]->priv); - kfree(dev_plip[i]); + kfree(dev); dev_plip[i] = NULL; } } diff -Nru a/drivers/net/sb1250-mac.c b/drivers/net/sb1250-mac.c --- a/drivers/net/sb1250-mac.c Wed Jun 25 14:09:38 2003 +++ b/drivers/net/sb1250-mac.c Tue Jul 1 16:24:20 2003 @@ -2493,7 +2493,7 @@ /* Set the timer to check for link beat. */ init_timer(&sc->sbm_timer); - sc->sbm_timer.expires = jiffies + 2; + sc->sbm_timer.expires = jiffies + 2 * HZ/100; sc->sbm_timer.data = (unsigned long)dev; sc->sbm_timer.function = &sbmac_timer; add_timer(&sc->sbm_timer); diff -Nru a/drivers/net/sk_mca.c b/drivers/net/sk_mca.c --- a/drivers/net/sk_mca.c Sun Apr 27 20:36:20 2003 +++ b/drivers/net/sk_mca.c Tue Jul 1 16:26:16 2003 @@ -537,7 +537,7 @@ ResetBoard(dev); InitBoard(dev); - njiffies = jiffies + 100; + njiffies = jiffies + HZ; do { csr0val = GetLANCE(dev, LANCE_CSR0); } diff -Nru a/drivers/net/sundance.c b/drivers/net/sundance.c --- a/drivers/net/sundance.c Thu Jun 19 17:11:29 2003 +++ b/drivers/net/sundance.c Thu Jul 10 16:05:31 2003 @@ -87,11 +87,15 @@ Version LK1.09 (D-Link): - Fix the flowctrl bug. - Set Pause bit in MII ANAR if flow control enabled. + + Version LK1.09a (ICPlus): + - Add the delay time in reading the contents of EEPROM + */ #define DRV_NAME "sundance" #define DRV_VERSION "1.01+LK1.09a" -#define DRV_RELDATE "16-May-2003" +#define DRV_RELDATE "10-Jul-2003" /* The user-configurable values. @@ -744,12 +748,14 @@ return 0; } +#define eeprom_delay(ee_addr) readl(ee_addr) /* Read the EEPROM and MII Management Data I/O (MDIO) interfaces. */ static int __devinit eeprom_read(long ioaddr, int location) { - int boguscnt = 1000; /* Typical 190 ticks. */ + int boguscnt = 10000; /* Typical 1900 ticks. */ writew(0x0200 | (location & 0xff), ioaddr + EECtrl); do { + eeprom_delay(ioaddr + EECtrl); if (! (readw(ioaddr + EECtrl) & 0x8000)) { return readw(ioaddr + EEData); } diff -Nru a/drivers/net/sunhme.c b/drivers/net/sunhme.c --- a/drivers/net/sunhme.c Wed Jul 9 21:01:34 2003 +++ b/drivers/net/sunhme.c Thu Jul 10 17:42:03 2003 @@ -1521,12 +1521,6 @@ hme_write32(hp, bregs + BMAC_IGAP1, DEFAULT_IPG1); hme_write32(hp, bregs + BMAC_IGAP2, DEFAULT_IPG2); - /* Make sure we can handle VLAN frames. */ - hme_write32(hp, bregs + BMAC_TXMAX, - ETH_DATA_LEN + ETH_HLEN + 8); - hme_write32(hp, bregs + BMAC_RXMAX, - ETH_DATA_LEN + ETH_HLEN + 8); - /* Load up the MAC address and random seed. */ HMD(("rseed/macaddr, ")); @@ -2805,8 +2799,8 @@ dev->watchdog_timeo = 5*HZ; dev->do_ioctl = &happy_meal_ioctl; - /* Happy Meal can do it all... */ - dev->features |= NETIF_F_SG | NETIF_F_HW_CSUM; + /* Happy Meal can do it all... except VLAN. */ + dev->features |= NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_VLAN_CHALLENGED; dev->irq = sdev->irqs[0]; diff -Nru a/drivers/net/tg3.c b/drivers/net/tg3.c --- a/drivers/net/tg3.c Wed Jun 11 23:56:33 2003 +++ b/drivers/net/tg3.c Thu Jul 10 16:25:44 2003 @@ -6679,10 +6679,9 @@ } /* Configure DMA attributes. */ - if (!pci_set_dma_mask(pdev, (u64) 0xffffffffffffffffULL)) { + if (!pci_set_dma_mask(pdev, 0xffffffffffffffffULL)) { pci_using_dac = 1; - if (pci_set_consistent_dma_mask(pdev, - (u64) 0xffffffffffffffff)) { + if (pci_set_consistent_dma_mask(pdev, 0xffffffffffffffffULL)) { printk(KERN_ERR PFX "Unable to obtain 64 bit DMA " "for consistent allocations\n"); goto err_out_free_res; diff -Nru a/drivers/net/tokenring/3c359.c b/drivers/net/tokenring/3c359.c --- a/drivers/net/tokenring/3c359.c Mon Jun 2 20:31:22 2003 +++ b/drivers/net/tokenring/3c359.c Thu Jul 10 16:38:47 2003 @@ -464,7 +464,7 @@ printk(KERN_INFO "3C359: Uploading Microcode: "); - for (i = start,j=0; (j < mc_size && i <= 0xffff) ; i++,j++) { + for (i = start, j = 0; j < mc_size; i++, j++) { writel(MEM_BYTE_WRITE | 0XD0000 | i, xl_mmio + MMIO_MAC_ACCESS_CMD) ; writeb(microcode[j],xl_mmio + MMIO_MACDATA) ; if (j % 1024 == 0) diff -Nru a/drivers/net/tokenring/proteon.c b/drivers/net/tokenring/proteon.c --- a/drivers/net/tokenring/proteon.c Mon Jun 2 20:31:22 2003 +++ b/drivers/net/tokenring/proteon.c Thu Jul 10 16:38:47 2003 @@ -401,7 +401,6 @@ int init_module(void) { int i, num; - struct net_device *dev; num = 0; if (io[0]) { /* Only probe addresses from command line */ diff -Nru a/drivers/net/tokenring/skisa.c b/drivers/net/tokenring/skisa.c --- a/drivers/net/tokenring/skisa.c Mon Jun 2 20:31:22 2003 +++ b/drivers/net/tokenring/skisa.c Thu Jul 10 16:38:47 2003 @@ -414,7 +414,6 @@ int init_module(void) { int i, num; - struct net_device *dev; num = 0; if (io[0]) { /* Only probe addresses from command line */ diff -Nru a/drivers/net/via-rhine.c b/drivers/net/via-rhine.c --- a/drivers/net/via-rhine.c Fri Jul 4 06:02:36 2003 +++ b/drivers/net/via-rhine.c Tue Jul 1 16:28:53 2003 @@ -1152,7 +1152,7 @@ /* Set the timer to check for link beat. */ init_timer(&np->timer); - np->timer.expires = jiffies + 2; + np->timer.expires = jiffies + 2 * HZ/100; np->timer.data = (unsigned long)dev; np->timer.function = &via_rhine_timer; /* timer handler */ add_timer(&np->timer); diff -Nru a/drivers/net/wireless/atmel_cs.c b/drivers/net/wireless/atmel_cs.c --- a/drivers/net/wireless/atmel_cs.c Thu Jun 5 21:54:48 2003 +++ b/drivers/net/wireless/atmel_cs.c Thu Jul 10 16:47:29 2003 @@ -391,7 +391,7 @@ local_info_t *dev; int last_fn, last_ret; u_char buf[64]; - int card_index = -1; + int card_index = -1, done = 0; handle = link->handle; dev = link->priv; @@ -415,13 +415,13 @@ manfid->manf == card_table[i].manf && manfid->card == card_table[i].card) { card_index = i; - goto done; + done = 1; } } } tuple.DesiredTuple = CISTPL_VERS_1; - if (CardServices(GetFirstTuple, handle, &tuple) == 0) { + if (!done && (CardServices(GetFirstTuple, handle, &tuple) == 0)) { int i, j, k; cistpl_vers_1_t *ver1; CS_CHECK(GetTupleData, handle, &tuple); @@ -446,12 +446,11 @@ goto mismatch; } card_index = i; - goto done; + break; /* done */ mismatch: - + j = 0; /* dummy stmt to shut up compiler */ } - done: } /* diff -Nru a/drivers/net/wireless/wavelan.c b/drivers/net/wireless/wavelan.c --- a/drivers/net/wireless/wavelan.c Fri Dec 6 10:10:13 2002 +++ b/drivers/net/wireless/wavelan.c Thu Jul 10 16:46:33 2003 @@ -3730,8 +3730,8 @@ int i; /* Loop counter */ /* Check if the base address if available. */ - if (check_region(ioaddr, sizeof(ha_t))) - return -EADDRINUSE; /* ioaddr already used */ + if (!request_region(ioaddr, sizeof(ha_t), "wavelan probe")) + return -EBUSY; /* ioaddr already used */ /* Reset host interface */ wv_hacr_reset(ioaddr); @@ -3739,6 +3739,8 @@ /* Read the MAC address from the parameter storage area. */ psa_read(ioaddr, HACR_DEFAULT, psaoff(0, psa_univ_mac_addr), mac, 6); + + release_region(ioaddr, sizeof(ha_t)); /* * Check the first three octets of the address for the manufacturer's code. diff -Nru a/drivers/parisc/Kconfig b/drivers/parisc/Kconfig --- a/drivers/parisc/Kconfig Sat Jun 14 11:39:40 2003 +++ b/drivers/parisc/Kconfig Sun Jun 22 14:25:53 2003 @@ -57,8 +57,11 @@ source "drivers/eisa/Kconfig" config ISA - bool + bool "ISA support" depends on EISA + help + If you want to plug an ISA card into your EISA bus, say Y here. + Most people should say N. config PCI bool "PCI support" diff -Nru a/drivers/pcmcia/cs.c b/drivers/pcmcia/cs.c --- a/drivers/pcmcia/cs.c Mon Jun 30 13:22:30 2003 +++ b/drivers/pcmcia/cs.c Thu Jul 10 22:23:58 2003 @@ -338,13 +338,13 @@ socket->erase_busy.next = socket->erase_busy.prev = &socket->erase_busy; INIT_LIST_HEAD(&socket->cis_cache); spin_lock_init(&socket->lock); - - init_socket(socket); - init_completion(&socket->thread_done); init_waitqueue_head(&socket->thread_wait); init_MUTEX(&socket->skt_sem); spin_lock_init(&socket->thread_lock); + + init_socket(socket); + ret = kernel_thread(pccardd, socket, CLONE_KERNEL); if (ret < 0) return ret; @@ -868,13 +868,19 @@ complete_and_exit(&skt->thread_done, 0); } +/* + * Yenta (at least) probes interrupts before registering the socket and + * starting the handler thread. + */ void pcmcia_parse_events(struct pcmcia_socket *s, u_int events) { - spin_lock(&s->thread_lock); - s->thread_events |= events; - spin_unlock(&s->thread_lock); + if (s->thread) { + spin_lock(&s->thread_lock); + s->thread_events |= events; + spin_unlock(&s->thread_lock); - wake_up(&s->thread_wait); + wake_up(&s->thread_wait); + } } /* pcmcia_parse_events */ diff -Nru a/drivers/pcmcia/ti113x.h b/drivers/pcmcia/ti113x.h --- a/drivers/pcmcia/ti113x.h Sat Jun 21 10:22:28 2003 +++ b/drivers/pcmcia/ti113x.h Sat Jul 12 20:28:12 2003 @@ -176,6 +176,9 @@ if (new != reg) exca_writeb(socket, I365_INTCTL, new); +#if 0 + /* THIS CAUSES HANGS! Disabled for now, do not know why */ + /* * If ISA interrupts don't work, then fall back to routing card * interrupts to the PCI interrupt of the socket. @@ -195,6 +198,7 @@ config_writel(socket, TI122X_IRQMUX, irqmux); config_writeb(socket, TI113X_DEVICE_CONTROL, devctl); } +#endif socket->socket.ss_entry->init = ti_init; return 0; diff -Nru a/drivers/scsi/g_NCR5380.c b/drivers/scsi/g_NCR5380.c --- a/drivers/scsi/g_NCR5380.c Mon May 12 07:26:12 2003 +++ b/drivers/scsi/g_NCR5380.c Fri Jul 11 07:44:06 2003 @@ -386,14 +386,21 @@ if (overrides[current_override].NCR5380_map_name != PORT_AUTO) for (i = 0; ports[i]; i++) { + if (!request_region(ports[i], 16, "ncr53c80")) + continue; if (overrides[current_override].NCR5380_map_name == ports[i]) break; + release_region(ports[i], 16); } else for (i = 0; ports[i]; i++) { - if ((!check_region(ports[i], 16)) && (inb(ports[i]) == 0xff)) + if (!request_region(ports[i], 16, "ncr53c80")) + continue; + if (inb(ports[i]) == 0xff) break; + release_region(ports[i], 16); } if (ports[i]) { + /* At this point we have our region reserved */ outb(0x59, 0x779); outb(0xb9, 0x379); outb(0xc5, 0x379); @@ -408,12 +415,15 @@ } else continue; } - - request_region(overrides[current_override].NCR5380_map_name, NCR5380_region_size, "ncr5380"); + else + { + /* Not a 53C400A style setup - just grab */ + if(!(request_region(overrides[current_override].NCR5380_map_name, NCR5380_region_size, "ncr5380"))) + continue; + } #else - if (check_mem_region(overrides[current_override].NCR5380_map_name, NCR5380_region_size)) + if(!request_mem_region(overrides[current_override].NCR5380_map_name, NCR5380_region_size, "ncr5380")) continue; - request_mem_region(overrides[current_override].NCR5380_map_name, NCR5380_region_size, "ncr5380"); #endif instance = scsi_register(tpnt, sizeof(struct NCR5380_hostdata)); if (instance == NULL) { diff -Nru a/drivers/usb/class/usblp.c b/drivers/usb/class/usblp.c --- a/drivers/usb/class/usblp.c Tue Jun 17 07:52:28 2003 +++ b/drivers/usb/class/usblp.c Mon Jul 7 12:33:32 2003 @@ -146,6 +146,7 @@ int rcomplete; /* reading is completed */ unsigned int quirks; /* quirks flags */ unsigned char used; /* True if open */ + unsigned char present; /* True if not disconnected */ unsigned char bidir; /* interface is bidirectional */ unsigned char *device_id_string; /* IEEE 1284 DEVICE ID string (ptr) */ /* first 2 bytes are (big-endian) length */ @@ -157,6 +158,7 @@ dbg("usblp=0x%p", usblp); dbg("dev=0x%p", usblp->dev); + dbg("present=%d", usblp->present); dbg("buf=0x%p", usblp->buf); dbg("readcount=%d", usblp->readcount); dbg("ifnum=%d", usblp->ifnum); @@ -253,7 +255,7 @@ { struct usblp *usblp = urb->context; - if (!usblp || !usblp->dev || !usblp->used) + if (!usblp || !usblp->dev || !usblp->used || !usblp->present) return; if (unlikely(urb->status)) @@ -267,7 +269,7 @@ { struct usblp *usblp = urb->context; - if (!usblp || !usblp->dev || !usblp->used) + if (!usblp || !usblp->dev || !usblp->used || !usblp->present) return; if (unlikely(urb->status)) @@ -332,7 +334,7 @@ goto out; } usblp = usb_get_intfdata (intf); - if (!usblp || !usblp->dev) + if (!usblp || !usblp->dev || !usblp->present) goto out; retval = -EBUSY; @@ -404,7 +406,7 @@ down (&usblp->sem); lock_kernel(); usblp->used = 0; - if (usblp->dev) { + if (usblp->present) { usblp_unlink_urbs(usblp); up(&usblp->sem); } else /* finish cleanup from disconnect */ @@ -432,7 +434,7 @@ int retval = 0; down (&usblp->sem); - if (!usblp->dev) { + if (!usblp->present) { retval = -ENODEV; goto done; } @@ -630,7 +632,7 @@ } down (&usblp->sem); - if (!usblp->dev) { + if (!usblp->present) { up (&usblp->sem); return -ENODEV; } @@ -691,7 +693,7 @@ return -EINVAL; down (&usblp->sem); - if (!usblp->dev) { + if (!usblp->present) { count = -ENODEV; goto done; } @@ -726,7 +728,7 @@ remove_wait_queue(&usblp->wait, &wait); } - if (!usblp->dev) { + if (!usblp->present) { count = -ENODEV; goto done; } @@ -916,6 +918,8 @@ usb_set_intfdata (intf, usblp); + usblp->present = 1; + return 0; abort_minor: @@ -1115,14 +1119,14 @@ down (&usblp->sem); lock_kernel(); - usblp->dev = NULL; + usblp->present = 0; usb_set_intfdata (intf, NULL); usblp_unlink_urbs(usblp); if (!usblp->used) usblp_cleanup (usblp); - else /* cleanup later, on close */ + else /* cleanup later, on release */ up (&usblp->sem); unlock_kernel(); } diff -Nru a/drivers/usb/core/inode.c b/drivers/usb/core/inode.c --- a/drivers/usb/core/inode.c Tue Jun 10 09:36:21 2003 +++ b/drivers/usb/core/inode.c Thu Jul 10 06:17:51 2003 @@ -491,10 +491,6 @@ static struct super_block *usb_get_sb(struct file_system_type *fs_type, int flags, const char *dev_name, void *data) { - if (fs_type == &usbdevice_fs_type) - printk (KERN_INFO "Please use the 'usbfs' filetype instead, " - "the 'usbdevfs' name is deprecated.\n"); - return get_sb_single(fs_type, flags, data, usbfs_fill_super); } diff -Nru a/drivers/usb/core/message.c b/drivers/usb/core/message.c --- a/drivers/usb/core/message.c Thu Jun 12 07:28:01 2003 +++ b/drivers/usb/core/message.c Mon Jul 7 08:24:11 2003 @@ -986,7 +986,7 @@ if (err < 0) { err("error getting string descriptor 0 (error=%d)", err); goto errout; - } else if (tbuf[0] < 4) { + } else if (err < 4 || tbuf[0] < 4) { err("string descriptor 0 too short"); err = -EINVAL; goto errout; diff -Nru a/drivers/usb/net/usbnet.c b/drivers/usb/net/usbnet.c --- a/drivers/usb/net/usbnet.c Wed Jun 25 11:30:59 2003 +++ b/drivers/usb/net/usbnet.c Mon Jul 7 08:53:04 2003 @@ -2391,7 +2391,7 @@ if (retval) { devdbg (dev, "drop, code %d", retval); drop: - retval = NET_XMIT_DROP; + retval = NET_XMIT_SUCCESS; dev->stats.tx_dropped++; if (skb) dev_kfree_skb_any (skb); diff -Nru a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c --- a/drivers/usb/serial/ftdi_sio.c Thu May 29 13:51:36 2003 +++ b/drivers/usb/serial/ftdi_sio.c Fri Jun 27 07:43:37 2003 @@ -4,6 +4,8 @@ * Copyright (C) 1999 - 2001 * Greg Kroah-Hartman (greg@kroah.com) * Bill Ryder (bryder@sgi.com) + * Copyright (C) 2002 + * Kuba Ober (kuba@mareimbrium.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 @@ -13,49 +15,156 @@ * See Documentation/usb/usb-serial.txt for more information on using this driver * * See http://ftdi-usb-sio.sourceforge.net for upto date testing info - * and extra documentation + * and extra documentation + * + * (27/Jun/2003) Ian Abbott + * Reworked the urb handling logic. We have no more pool, but dynamically + * allocate the urb and the transfer buffer on the fly. In testing this + * does not incure any measurable overhead. This also relies on the fact + * that we have proper reference counting logic for urbs. I nicked this + * from Greg KH's Visor driver. + * + * (23/Jun/2003) Ian Abbott + * Reduced flip buffer pushes and corrected a data length test in + * ftdi_read_bulk_callback. + * Defererence pointers after any paranoid checks, not before. + * + * (21/Jun/2003) Erik Nygren + * Added support for Home Electronics Tira-1 IR tranceiver using FT232BM chip. + * See . Only operates properly + * at 100000 and RTS-CTS, so set custom divisor mode on startup. + * Also force the Tira-1 and USB-UIRT to only use their custom baud rates. + * + * (18/Jun/2003) Ian Abbott + * Added Device ID of the USB relais from Rudolf Gugler (backported from + * Philipp Gühring's patch for 2.5.x kernel). + * Moved read transfer buffer reallocation into startup function. + * Free existing write urb and transfer buffer in startup function. + * Only use urbs in write urb pool that were successfully allocated. + * Moved some constant macros out of functions. + * Minor whitespace and comment changes. + * + * (12/Jun/2003) David Norwood + * Added support for USB-UIRT IR tranceiver using 8U232AM chip. + * See . Only + * operates properly at 312500, so set custom divisor mode on startup. + * + * (12/Jun/2003) Ian Abbott + * Added Sealevel SeaLINK+ 210x, 220x, 240x, 280x vid/pids from Tuan Hoang + * - I've eliminated some that don't seem to exist! + * Added Home Electronics Tira-1 IR transceiver pid from Chris Horn + * Some whitespace/coding-style cleanups + * + * (11/Jun/2003) Ian Abbott + * Fixed unsafe spinlock usage in ftdi_write + * + * (24/Feb/2003) Richard Shooter + * Increase read buffer size to improve read speeds at higher baud rates + * (specifically tested with up to 1Mb/sec at 1.5M baud) + * + * (23/Feb/2003) John Wilkins + * Added Xon/xoff flow control (activating support in the ftdi device) + * Added vid/pid for Videonetworks/Homechoice (UK ISP) + * + * (23/Feb/2003) Bill Ryder + * Added matrix orb device vid/pids from Wayne Wylupski + * + * (19/Feb/2003) Ian Abbott + * For TIOCSSERIAL, set alt_speed to 0 when ASYNC_SPD_MASK value has + * changed to something other than ASYNC_SPD_HI, ASYNC_SPD_VHI, + * ASYNC_SPD_SHI or ASYNC_SPD_WARP. Also, unless ASYNC_SPD_CUST is in + * force, don't bother changing baud rate when custom_divisor has changed. + * + * (18/Feb/2003) Ian Abbott + * Fixed TIOCMGET handling to include state of DTR and RTS, the state + * of which are now saved by set_dtr() and set_rts(). + * Fixed improper storage class for buf in set_dtr() and set_rts(). + * Added FT232BM chip type and support for its extra baud rates (compared + * to FT8U232AM). + * Took account of special case divisor values for highest baud rates of + * FT8U232AM and FT232BM. + * For TIOCSSERIAL, forced alt_speed to 0 when ASYNC_SPD_CUST kludge used, + * as previous alt_speed setting is now stale. + * Moved startup code common between the startup routines for the + * different chip types into a common subroutine. + * + * (17/Feb/2003) Bill Ryder + * Added write urb buffer pool on a per device basis + * Added more checking for open file on callbacks (fixed OOPS) + * Added CrystalFontz 632 and 634 PIDs + * (thanx to CrystalFontz for the sample devices - they flushed out + * some driver bugs) + * Minor debugging message changes + * Added throttle, unthrottle and chars_in_buffer functions + * Fixed FTDI_SIO (the original device) bug + * Fixed some shutdown handling + * + * + * * + * (07/Jun/2002) Kuba Ober + * Changed FTDI_SIO_BASE_BAUD_TO_DIVISOR macro into ftdi_baud_to_divisor + * function. It was getting too complex. + * Fix the divisor calculation logic which was setting divisor of 0.125 + * instead of 0.5 for fractional parts of divisor equal to 5/8, 6/8, 7/8. + * Also make it bump up the divisor to next integer in case of 7/8 - it's + * a better approximation. + * + * (25/Jul/2002) Bill Ryder inserted Dmitri's TIOCMIWAIT patch + * Not tested by me but it doesn't break anything I use. + * + * (04/Jan/2002) Kuba Ober + * Implemented 38400 baudrate kludge, where it can be substituted with other + * values. That's the only way to set custom baudrates. + * Implemented TIOCSSERIAL, TIOCGSERIAL ioctl's so that setserial is happy. + * FIXME: both baudrate things should eventually go to usbserial.c as other + * devices may need that functionality too. Actually, it can probably be + * merged in serial.c somehow - too many drivers repeat this code over + * and over. + * Fixed baudrate forgetfulness - open() used to reset baudrate to 9600 every time. + * Divisors for baudrates are calculated by a macro. + * Small code cleanups. Ugly whitespace changes for Plato's sake only ;-]. + * * (04/Nov/2001) Bill Ryder - * Fixed bug in read_bulk_callback where incorrect urb buffer was used. - * cleaned up write offset calculation - * added write_room since default values can be incorrect for sio - * changed write_bulk_callback to use same queue_task as other drivers - * (the previous version caused panics) - * Removed port iteration code since the device only has one I/O port and it - * was wrong anyway. + * Fixed bug in read_bulk_callback where incorrect urb buffer was used. + * Cleaned up write offset calculation + * Added write_room since default values can be incorrect for sio + * Changed write_bulk_callback to use same queue_task as other drivers + * (the previous version caused panics) + * Removed port iteration code since the device only has one I/O port and it + * was wrong anyway. * * (31/May/2001) gkh - * switched from using spinlock to a semaphore, which fixes lots of problems. + * Switched from using spinlock to a semaphore, which fixes lots of problems. * * (23/May/2001) Bill Ryder - * Added runtime debug patch (thanx Tyson D Sawyer). - * Cleaned up comments for 8U232 - * Added parity, framing and overrun error handling - * Added receive break handling. + * Added runtime debug patch (thanx Tyson D Sawyer). + * Cleaned up comments for 8U232 + * Added parity, framing and overrun error handling + * Added receive break handling. * * (04/08/2001) gb * Identify version on module load. * * (18/March/2001) Bill Ryder - * (Not released) - * Added send break handling. (requires kernel patch too) - * Fixed 8U232AM hardware RTS/CTS etc status reporting. - * Added flipbuf fix copied from generic device + * (Not released) + * Added send break handling. (requires kernel patch too) + * Fixed 8U232AM hardware RTS/CTS etc status reporting. + * Added flipbuf fix copied from generic device * * (12/3/2000) Bill Ryder - * Added support for 8U232AM device. - * Moved PID and VIDs into header file only. - * Turned on low-latency for the tty (device will do high baudrates) - * Added shutdown routine to close files when device removed. - * More debug and error message cleanups. - * + * Added support for 8U232AM device. + * Moved PID and VIDs into header file only. + * Turned on low-latency for the tty (device will do high baudrates) + * Added shutdown routine to close files when device removed. + * More debug and error message cleanups. * * (11/13/2000) Bill Ryder - * Added spinlock protected open code and close code. - * Multiple opens work (sort of - see webpage mentioned above). - * Cleaned up comments. Removed multiple PID/VID definitions. - * Factorised cts/dtr code - * Made use of __FUNCTION__ in dbg's + * Added spinlock protected open code and close code. + * Multiple opens work (sort of - see webpage mentioned above). + * Cleaned up comments. Removed multiple PID/VID definitions. + * Factorised cts/dtr code + * Made use of __FUNCTION__ in dbg's * * (11/01/2000) Adam J. Richter * usb_device_id table support @@ -72,16 +181,16 @@ * driver is a loadable module now. * * (04/04/2000) Bill Ryder - * Fixed bugs in TCGET/TCSET ioctls (by removing them - they are + * Fixed bugs in TCGET/TCSET ioctls (by removing them - they are * handled elsewhere in the tty io driver chain). * * (03/30/2000) Bill Ryder - * Implemented lots of ioctls - * Fixed a race condition in write - * Changed some dbg's to errs + * Implemented lots of ioctls + * Fixed a race condition in write + * Changed some dbg's to errs * * (03/26/2000) gkh - * Split driver up into device specific pieces. + * Split driver up into device specific pieces. * */ @@ -90,7 +199,6 @@ /* to talk to the device */ /* Thanx to gkh and the rest of the usb dev group for all code I have assimilated :-) */ - #include #include #include @@ -103,6 +211,7 @@ #include #include #include +#include #ifdef CONFIG_USB_SERIAL_DEBUG static int debug = 1; #else @@ -112,47 +221,244 @@ #include "usb-serial.h" #include "ftdi_sio.h" - /* * Version Information */ -#define DRIVER_VERSION "v1.2.0" -#define DRIVER_AUTHOR "Greg Kroah-Hartman , Bill Ryder " -#define DRIVER_DESC "USB FTDI RS232 Converters Driver" +#define DRIVER_VERSION "v1.4.0" +#define DRIVER_AUTHOR "Greg Kroah-Hartman , Bill Ryder , Kuba Ober " +#define DRIVER_DESC "USB FTDI Serial Converters Driver" static struct usb_device_id id_table_sio [] = { { USB_DEVICE(FTDI_VID, FTDI_SIO_PID) }, { } /* Terminating entry */ }; -/* THe 8U232AM has the same API as the sio except for: - - it can support MUCH higher baudrates (921600 at 48MHz/230400 - at 12MHz so .. it's baudrate setting codes are different - - it has a two byte status code. - - it returns characters very 16ms (the FTDI does it every 40ms) - */ +/* + * The 8U232AM has the same API as the sio except for: + * - it can support MUCH higher baudrates; up to: + * o 921600 for RS232 and 2000000 for RS422/485 at 48MHz + * o 230400 at 12MHz + * so .. 8U232AM's baudrate setting codes are different + * - it has a two byte status code. + * - it returns characters every 16ms (the FTDI does it every 40ms) + * + * the bcdDevice value is used to differentiate FT232BM and FT245BM from + * the earlier FT8U232AM and FT8U232BM. For now, include all known VID/PID + * combinations in both tables. + * FIXME: perhaps bcdDevice can also identify 12MHz devices, but I don't know + * if those ever went into mass production. [Ian Abbott] + */ + - static struct usb_device_id id_table_8U232AM [] = { - { USB_DEVICE(FTDI_VID, FTDI_8U232AM_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_RELAIS_PID) }, - { USB_DEVICE(FTDI_NF_RIC_VID, FTDI_NF_RIC_PID) }, + { USB_DEVICE_VER(FTDI_VID, FTDI_8U232AM_PID, 0, 0x3ff) }, + { USB_DEVICE_VER(FTDI_VID, FTDI_RELAIS_PID, 0, 0x3ff) }, + { USB_DEVICE_VER(FTDI_NF_RIC_VID, FTDI_NF_RIC_PID, 0, 0x3ff) }, + { USB_DEVICE_VER(FTDI_VID, FTDI_XF_634_PID, 0, 0x3ff) }, + { USB_DEVICE_VER(FTDI_VID, FTDI_XF_632_PID, 0, 0x3ff) }, + { USB_DEVICE_VER(FTDI_VID, FTDI_VNHCPCUSB_D_PID, 0, 0x3ff) }, + { USB_DEVICE_VER(FTDI_MTXORB_VID, FTDI_MTXORB_0_PID, 0, 0x3ff) }, + { USB_DEVICE_VER(FTDI_MTXORB_VID, FTDI_MTXORB_1_PID, 0, 0x3ff) }, + { USB_DEVICE_VER(FTDI_MTXORB_VID, FTDI_MTXORB_2_PID, 0, 0x3ff) }, + { USB_DEVICE_VER(FTDI_MTXORB_VID, FTDI_MTXORB_3_PID, 0, 0x3ff) }, + { USB_DEVICE_VER(FTDI_MTXORB_VID, FTDI_MTXORB_4_PID, 0, 0x3ff) }, + { USB_DEVICE_VER(FTDI_MTXORB_VID, FTDI_MTXORB_5_PID, 0, 0x3ff) }, + { USB_DEVICE_VER(FTDI_MTXORB_VID, FTDI_MTXORB_6_PID, 0, 0x3ff) }, + { USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2101_PID, 0, 0x3ff) }, + { USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2102_PID, 0, 0x3ff) }, + { USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2103_PID, 0, 0x3ff) }, + { USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2104_PID, 0, 0x3ff) }, + { USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2201_1_PID, 0, 0x3ff) }, + { USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2201_2_PID, 0, 0x3ff) }, + { USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2202_1_PID, 0, 0x3ff) }, + { USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2202_2_PID, 0, 0x3ff) }, + { USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2203_1_PID, 0, 0x3ff) }, + { USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2203_2_PID, 0, 0x3ff) }, + { USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2401_1_PID, 0, 0x3ff) }, + { USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2401_2_PID, 0, 0x3ff) }, + { USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2401_3_PID, 0, 0x3ff) }, + { USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2401_4_PID, 0, 0x3ff) }, + { USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2402_1_PID, 0, 0x3ff) }, + { USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2402_2_PID, 0, 0x3ff) }, + { USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2402_3_PID, 0, 0x3ff) }, + { USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2402_4_PID, 0, 0x3ff) }, + { USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2403_1_PID, 0, 0x3ff) }, + { USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2403_2_PID, 0, 0x3ff) }, + { USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2403_3_PID, 0, 0x3ff) }, + { USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2403_4_PID, 0, 0x3ff) }, + { USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2801_1_PID, 0, 0x3ff) }, + { USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2801_2_PID, 0, 0x3ff) }, + { USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2801_3_PID, 0, 0x3ff) }, + { USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2801_4_PID, 0, 0x3ff) }, + { USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2801_5_PID, 0, 0x3ff) }, + { USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2801_6_PID, 0, 0x3ff) }, + { USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2801_7_PID, 0, 0x3ff) }, + { USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2801_8_PID, 0, 0x3ff) }, + { USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2802_1_PID, 0, 0x3ff) }, + { USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2802_2_PID, 0, 0x3ff) }, + { USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2802_3_PID, 0, 0x3ff) }, + { USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2802_4_PID, 0, 0x3ff) }, + { USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2802_5_PID, 0, 0x3ff) }, + { USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2802_6_PID, 0, 0x3ff) }, + { USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2802_7_PID, 0, 0x3ff) }, + { USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2802_8_PID, 0, 0x3ff) }, + { USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2803_1_PID, 0, 0x3ff) }, + { USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2803_2_PID, 0, 0x3ff) }, + { USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2803_3_PID, 0, 0x3ff) }, + { USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2803_4_PID, 0, 0x3ff) }, + { USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2803_5_PID, 0, 0x3ff) }, + { USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2803_6_PID, 0, 0x3ff) }, + { USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2803_7_PID, 0, 0x3ff) }, + { USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2803_8_PID, 0, 0x3ff) }, + { } /* Terminating entry */ +}; + + +static struct usb_device_id id_table_FT232BM [] = { + { USB_DEVICE_VER(FTDI_VID, FTDI_8U232AM_PID, 0x400, 0xffff) }, + { USB_DEVICE_VER(FTDI_VID, FTDI_RELAIS_PID, 0x400, 0xffff) }, + { USB_DEVICE_VER(FTDI_NF_RIC_VID, FTDI_NF_RIC_PID, 0x400, 0xffff) }, + { USB_DEVICE_VER(FTDI_VID, FTDI_XF_634_PID, 0x400, 0xffff) }, + { USB_DEVICE_VER(FTDI_VID, FTDI_XF_632_PID, 0x400, 0xffff) }, + { USB_DEVICE_VER(FTDI_VID, FTDI_VNHCPCUSB_D_PID, 0x400, 0xffff) }, + { USB_DEVICE_VER(FTDI_MTXORB_VID, FTDI_MTXORB_0_PID, 0x400, 0xffff) }, + { USB_DEVICE_VER(FTDI_MTXORB_VID, FTDI_MTXORB_1_PID, 0x400, 0xffff) }, + { USB_DEVICE_VER(FTDI_MTXORB_VID, FTDI_MTXORB_2_PID, 0x400, 0xffff) }, + { USB_DEVICE_VER(FTDI_MTXORB_VID, FTDI_MTXORB_3_PID, 0x400, 0xffff) }, + { USB_DEVICE_VER(FTDI_MTXORB_VID, FTDI_MTXORB_4_PID, 0x400, 0xffff) }, + { USB_DEVICE_VER(FTDI_MTXORB_VID, FTDI_MTXORB_5_PID, 0x400, 0xffff) }, + { USB_DEVICE_VER(FTDI_MTXORB_VID, FTDI_MTXORB_6_PID, 0x400, 0xffff) }, + { USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2101_PID, 0x400, 0xffff) }, + { USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2102_PID, 0x400, 0xffff) }, + { USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2103_PID, 0x400, 0xffff) }, + { USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2104_PID, 0x400, 0xffff) }, + { USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2201_1_PID, 0x400, 0xffff) }, + { USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2201_2_PID, 0x400, 0xffff) }, + { USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2202_1_PID, 0x400, 0xffff) }, + { USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2202_2_PID, 0x400, 0xffff) }, + { USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2203_1_PID, 0x400, 0xffff) }, + { USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2203_2_PID, 0x400, 0xffff) }, + { USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2401_1_PID, 0x400, 0xffff) }, + { USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2401_2_PID, 0x400, 0xffff) }, + { USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2401_3_PID, 0x400, 0xffff) }, + { USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2401_4_PID, 0x400, 0xffff) }, + { USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2402_1_PID, 0x400, 0xffff) }, + { USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2402_2_PID, 0x400, 0xffff) }, + { USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2402_3_PID, 0x400, 0xffff) }, + { USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2402_4_PID, 0x400, 0xffff) }, + { USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2403_1_PID, 0x400, 0xffff) }, + { USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2403_2_PID, 0x400, 0xffff) }, + { USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2403_3_PID, 0x400, 0xffff) }, + { USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2403_4_PID, 0x400, 0xffff) }, + { USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2801_1_PID, 0x400, 0xffff) }, + { USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2801_2_PID, 0x400, 0xffff) }, + { USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2801_3_PID, 0x400, 0xffff) }, + { USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2801_4_PID, 0x400, 0xffff) }, + { USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2801_5_PID, 0x400, 0xffff) }, + { USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2801_6_PID, 0x400, 0xffff) }, + { USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2801_7_PID, 0x400, 0xffff) }, + { USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2801_8_PID, 0x400, 0xffff) }, + { USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2802_1_PID, 0x400, 0xffff) }, + { USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2802_2_PID, 0x400, 0xffff) }, + { USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2802_3_PID, 0x400, 0xffff) }, + { USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2802_4_PID, 0x400, 0xffff) }, + { USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2802_5_PID, 0x400, 0xffff) }, + { USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2802_6_PID, 0x400, 0xffff) }, + { USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2802_7_PID, 0x400, 0xffff) }, + { USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2802_8_PID, 0x400, 0xffff) }, + { USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2803_1_PID, 0x400, 0xffff) }, + { USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2803_2_PID, 0x400, 0xffff) }, + { USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2803_3_PID, 0x400, 0xffff) }, + { USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2803_4_PID, 0x400, 0xffff) }, + { USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2803_5_PID, 0x400, 0xffff) }, + { USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2803_6_PID, 0x400, 0xffff) }, + { USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2803_7_PID, 0x400, 0xffff) }, + { USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2803_8_PID, 0x400, 0xffff) }, { } /* Terminating entry */ }; -static struct usb_device_id id_table_combined [] = { +static struct usb_device_id id_table_USB_UIRT [] = { + { USB_DEVICE(FTDI_VID, FTDI_USB_UIRT_PID) }, + { } /* Terminating entry */ +}; + + +static struct usb_device_id id_table_HE_TIRA1 [] = { + { USB_DEVICE_VER(FTDI_VID, FTDI_HE_TIRA1_PID, 0x400, 0xffff) }, + { } /* Terminating entry */ +}; + + +static __devinitdata struct usb_device_id id_table_combined [] = { { USB_DEVICE(FTDI_VID, FTDI_SIO_PID) }, { USB_DEVICE(FTDI_VID, FTDI_8U232AM_PID) }, { USB_DEVICE(FTDI_VID, FTDI_RELAIS_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_XF_634_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_XF_632_PID) }, { USB_DEVICE(FTDI_NF_RIC_VID, FTDI_NF_RIC_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_VNHCPCUSB_D_PID) }, + { USB_DEVICE(FTDI_MTXORB_VID, FTDI_MTXORB_0_PID) }, + { USB_DEVICE(FTDI_MTXORB_VID, FTDI_MTXORB_1_PID) }, + { USB_DEVICE(FTDI_MTXORB_VID, FTDI_MTXORB_2_PID) }, + { USB_DEVICE(FTDI_MTXORB_VID, FTDI_MTXORB_3_PID) }, + { USB_DEVICE(FTDI_MTXORB_VID, FTDI_MTXORB_4_PID) }, + { USB_DEVICE(FTDI_MTXORB_VID, FTDI_MTXORB_5_PID) }, + { USB_DEVICE(FTDI_MTXORB_VID, FTDI_MTXORB_6_PID) }, + { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2101_PID) }, + { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2102_PID) }, + { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2103_PID) }, + { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2104_PID) }, + { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2201_1_PID) }, + { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2201_2_PID) }, + { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2202_1_PID) }, + { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2202_2_PID) }, + { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2203_1_PID) }, + { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2203_2_PID) }, + { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2401_1_PID) }, + { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2401_2_PID) }, + { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2401_3_PID) }, + { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2401_4_PID) }, + { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2402_1_PID) }, + { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2402_2_PID) }, + { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2402_3_PID) }, + { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2402_4_PID) }, + { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2403_1_PID) }, + { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2403_2_PID) }, + { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2403_3_PID) }, + { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2403_4_PID) }, + { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2801_1_PID) }, + { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2801_2_PID) }, + { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2801_3_PID) }, + { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2801_4_PID) }, + { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2801_5_PID) }, + { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2801_6_PID) }, + { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2801_7_PID) }, + { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2801_8_PID) }, + { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2802_1_PID) }, + { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2802_2_PID) }, + { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2802_3_PID) }, + { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2802_4_PID) }, + { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2802_5_PID) }, + { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2802_6_PID) }, + { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2802_7_PID) }, + { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2802_8_PID) }, + { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2803_1_PID) }, + { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2803_2_PID) }, + { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2803_3_PID) }, + { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2803_4_PID) }, + { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2803_5_PID) }, + { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2803_6_PID) }, + { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2803_7_PID) }, + { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2803_8_PID) }, + { USB_DEVICE_VER(FTDI_VID, FTDI_HE_TIRA1_PID, 0x400, 0xffff) }, + { USB_DEVICE(FTDI_VID, FTDI_USB_UIRT_PID) }, { } /* Terminating entry */ }; MODULE_DEVICE_TABLE (usb, id_table_combined); static struct usb_driver ftdi_driver = { - .owner = THIS_MODULE, .name = "ftdi_sio", .probe = usb_serial_probe, .disconnect = usb_serial_disconnect, @@ -160,31 +466,63 @@ }; +/* Constants for read urb and write urb */ +#define BUFSZ 512 +#define PKTSZ 64 + struct ftdi_private { - enum ftdi_type ftdi_type; - __u16 last_set_data_urb_value ; /* the last data state set - needed for doing a break */ - int write_offset; + ftdi_chip_type_t chip_type; + /* type of the device, either SIO or FT8U232AM */ + int baud_base; /* baud base clock for divisor setting */ + int custom_divisor; /* custom_divisor kludge, this is for baud_base (different from what goes to the chip!) */ + __u16 last_set_data_urb_value ; + /* the last data state set - needed for doing a break */ + int write_offset; /* This is the offset in the usb data block to write the serial data - + * it is different between devices + */ + int flags; /* some ASYNC_xxxx flags are supported */ + unsigned long last_dtr_rts; /* saved modem control outputs */ + wait_queue_head_t delta_msr_wait; /* Used for TIOCMIWAIT */ + char prev_status, diff_status; /* Used for TIOCMIWAIT */ + + int force_baud; /* if non-zero, force the baud rate to this value */ + int force_rtscts; /* if non-zero, force RTS-CTS to always be enabled */ }; + +/* Used for TIOCMIWAIT */ +#define FTDI_STATUS_B0_MASK (FTDI_RS0_CTS | FTDI_RS0_DSR | FTDI_RS0_RI | FTDI_RS0_RLSD) +#define FTDI_STATUS_B1_MASK (FTDI_RS_BI) +/* End TIOCMIWAIT */ + +#define FTDI_IMPL_ASYNC_FLAGS = ( ASYNC_SPD_HI | ASYNC_SPD_VHI \ + ASYNC_SPD_CUST | ASYNC_SPD_SHI | ASYNC_SPD_WARP ) + /* function prototypes for a FTDI serial converter */ -static int ftdi_sio_startup (struct usb_serial *serial); +static int ftdi_SIO_startup (struct usb_serial *serial); static int ftdi_8U232AM_startup (struct usb_serial *serial); -static void ftdi_sio_shutdown (struct usb_serial *serial); -static int ftdi_sio_open (struct usb_serial_port *port, struct file *filp); -static void ftdi_sio_close (struct usb_serial_port *port, struct file *filp); -static int ftdi_sio_write (struct usb_serial_port *port, int from_user, const unsigned char *buf, int count); -static int ftdi_sio_write_room (struct usb_serial_port *port); -static void ftdi_sio_write_bulk_callback (struct urb *urb, struct pt_regs *regs); -static void ftdi_sio_read_bulk_callback (struct urb *urb, struct pt_regs *regs); -static void ftdi_sio_set_termios (struct usb_serial_port *port, struct termios * old); -static int ftdi_sio_tiocmget (struct usb_serial_port *port, struct file *file); -static int ftdi_sio_tiocmset (struct usb_serial_port *port, struct file *file, unsigned int set, unsigned int clear); -static int ftdi_sio_ioctl (struct usb_serial_port *port, struct file * file, unsigned int cmd, unsigned long arg); -static void ftdi_sio_break_ctl (struct usb_serial_port *port, int break_state ); - -/* Should rename most ftdi_sio's to ftdi_ now since there are two devices - which share common code */ +static int ftdi_FT232BM_startup (struct usb_serial *serial); +static int ftdi_USB_UIRT_startup (struct usb_serial *serial); +static int ftdi_HE_TIRA1_startup (struct usb_serial *serial); +static void ftdi_shutdown (struct usb_serial *serial); +static int ftdi_open (struct usb_serial_port *port, struct file *filp); +static void ftdi_close (struct usb_serial_port *port, struct file *filp); +static int ftdi_write (struct usb_serial_port *port, int from_user, const unsigned char *buf, int count); +static int ftdi_write_room (struct usb_serial_port *port); +static int ftdi_chars_in_buffer (struct usb_serial_port *port); +static void ftdi_write_bulk_callback (struct urb *urb, struct pt_regs *regs); +static void ftdi_read_bulk_callback (struct urb *urb, struct pt_regs *regs); +static void ftdi_set_termios (struct usb_serial_port *port, struct termios * old); +static int ftdi_ioctl (struct usb_serial_port *port, struct file * file, unsigned int cmd, unsigned long arg); +static void ftdi_break_ctl (struct usb_serial_port *port, int break_state ); +static void ftdi_throttle (struct usb_serial_port *port); +static void ftdi_unthrottle (struct usb_serial_port *port); + +static unsigned short int ftdi_232am_baud_base_to_divisor (int baud, int base); +static unsigned short int ftdi_232am_baud_to_divisor (int baud); +static __u32 ftdi_232bm_baud_base_to_divisor (int baud, int base); +static __u32 ftdi_232bm_baud_to_divisor (int baud); -static struct usb_serial_device_type ftdi_sio_device = { +static struct usb_serial_device_type ftdi_SIO_device = { .owner = THIS_MODULE, .name = "FTDI SIO", .id_table = id_table_sio, @@ -192,77 +530,206 @@ .num_bulk_in = 1, .num_bulk_out = 1, .num_ports = 1, - .open = ftdi_sio_open, - .close = ftdi_sio_close, - .write = ftdi_sio_write, - .write_room = ftdi_sio_write_room, - .read_bulk_callback = ftdi_sio_read_bulk_callback, - .write_bulk_callback = ftdi_sio_write_bulk_callback, - .ioctl = ftdi_sio_ioctl, - .set_termios = ftdi_sio_set_termios, - .break_ctl = ftdi_sio_break_ctl, - .tiocmget = ftdi_sio_tiocmget, - .tiocmset = ftdi_sio_tiocmset, - .attach = ftdi_sio_startup, - .shutdown = ftdi_sio_shutdown, + .open = ftdi_open, + .close = ftdi_close, + .throttle = ftdi_throttle, + .unthrottle = ftdi_unthrottle, + .write = ftdi_write, + .write_room = ftdi_write_room, + .chars_in_buffer = ftdi_chars_in_buffer, + .read_bulk_callback = ftdi_read_bulk_callback, + .write_bulk_callback = ftdi_write_bulk_callback, + .ioctl = ftdi_ioctl, + .set_termios = ftdi_set_termios, + .break_ctl = ftdi_break_ctl, + .attach = ftdi_SIO_startup, + .shutdown = ftdi_shutdown, }; static struct usb_serial_device_type ftdi_8U232AM_device = { .owner = THIS_MODULE, - .name = "FTDI 8U232AM", + .name = "FTDI 8U232AM Compatible", .id_table = id_table_8U232AM, .num_interrupt_in = 0, .num_bulk_in = 1, .num_bulk_out = 1, .num_ports = 1, - .open = ftdi_sio_open, - .close = ftdi_sio_close, - .write = ftdi_sio_write, - .write_room = ftdi_sio_write_room, - .read_bulk_callback = ftdi_sio_read_bulk_callback, - .write_bulk_callback = ftdi_sio_write_bulk_callback, - .ioctl = ftdi_sio_ioctl, - .set_termios = ftdi_sio_set_termios, - .break_ctl = ftdi_sio_break_ctl, - .tiocmget = ftdi_sio_tiocmget, - .tiocmset = ftdi_sio_tiocmset, + .open = ftdi_open, + .close = ftdi_close, + .throttle = ftdi_throttle, + .unthrottle = ftdi_unthrottle, + .write = ftdi_write, + .write_room = ftdi_write_room, + .chars_in_buffer = ftdi_chars_in_buffer, + .read_bulk_callback = ftdi_read_bulk_callback, + .write_bulk_callback = ftdi_write_bulk_callback, + .ioctl = ftdi_ioctl, + .set_termios = ftdi_set_termios, + .break_ctl = ftdi_break_ctl, .attach = ftdi_8U232AM_startup, - .shutdown = ftdi_sio_shutdown, + .shutdown = ftdi_shutdown, +}; + +static struct usb_serial_device_type ftdi_FT232BM_device = { + .owner = THIS_MODULE, + .name = "FTDI FT232BM Compatible", + .id_table = id_table_FT232BM, + .num_interrupt_in = 0, + .num_bulk_in = 1, + .num_bulk_out = 1, + .num_ports = 1, + .open = ftdi_open, + .close = ftdi_close, + .throttle = ftdi_throttle, + .unthrottle = ftdi_unthrottle, + .write = ftdi_write, + .write_room = ftdi_write_room, + .chars_in_buffer = ftdi_chars_in_buffer, + .read_bulk_callback = ftdi_read_bulk_callback, + .write_bulk_callback = ftdi_write_bulk_callback, + .ioctl = ftdi_ioctl, + .set_termios = ftdi_set_termios, + .break_ctl = ftdi_break_ctl, + .attach = ftdi_FT232BM_startup, + .shutdown = ftdi_shutdown, }; +static struct usb_serial_device_type ftdi_USB_UIRT_device = { + .owner = THIS_MODULE, + .name = "USB-UIRT Infrared Receiver/Transmitter", + .id_table = id_table_USB_UIRT, + .num_interrupt_in = 0, + .num_bulk_in = 1, + .num_bulk_out = 1, + .num_ports = 1, + .open = ftdi_open, + .close = ftdi_close, + .throttle = ftdi_throttle, + .unthrottle = ftdi_unthrottle, + .write = ftdi_write, + .write_room = ftdi_write_room, + .chars_in_buffer = ftdi_chars_in_buffer, + .read_bulk_callback = ftdi_read_bulk_callback, + .write_bulk_callback = ftdi_write_bulk_callback, + .ioctl = ftdi_ioctl, + .set_termios = ftdi_set_termios, + .break_ctl = ftdi_break_ctl, + .attach = ftdi_USB_UIRT_startup, + .shutdown = ftdi_shutdown, +}; + +/* The TIRA1 is based on a FT232BM which requires a fixed baud rate of 100000 + * and which requires RTS-CTS to be enabled. */ +static struct usb_serial_device_type ftdi_HE_TIRA1_device = { + .owner = THIS_MODULE, + .name = "Home-Electronics TIRA-1 IR Transceiver", + .id_table = id_table_HE_TIRA1, + .num_interrupt_in = 0, + .num_bulk_in = 1, + .num_bulk_out = 1, + .num_ports = 1, + .open = ftdi_open, + .close = ftdi_close, + .throttle = ftdi_throttle, + .unthrottle = ftdi_unthrottle, + .write = ftdi_write, + .write_room = ftdi_write_room, + .chars_in_buffer = ftdi_chars_in_buffer, + .read_bulk_callback = ftdi_read_bulk_callback, + .write_bulk_callback = ftdi_write_bulk_callback, + .ioctl = ftdi_ioctl, + .set_termios = ftdi_set_termios, + .break_ctl = ftdi_break_ctl, + .attach = ftdi_HE_TIRA1_startup, + .shutdown = ftdi_shutdown, +}; + + + +#define WDR_TIMEOUT (HZ * 5 ) /* default urb timeout */ + +/* High and low are for DTR, RTS etc etc */ +#define HIGH 1 +#define LOW 0 /* * *************************************************************************** - * FTDI SIO Serial Converter specific driver functions + * Utlity functions * *************************************************************************** */ -#define WDR_TIMEOUT (HZ * 5 ) /* default urb timeout */ +static unsigned short int ftdi_232am_baud_base_to_divisor(int baud, int base) +{ + unsigned short int divisor; + int divisor3 = base / 2 / baud; // divisor shifted 3 bits to the left + if ((divisor3 & 0x7) == 7) divisor3 ++; // round x.7/8 up to x+1 + divisor = divisor3 >> 3; + divisor3 &= 0x7; + if (divisor3 == 1) divisor |= 0xc000; else // 0.125 + if (divisor3 >= 4) divisor |= 0x4000; else // 0.5 + if (divisor3 != 0) divisor |= 0x8000; // 0.25 + if (divisor == 1) divisor = 0; /* special case for maximum baud rate */ + return divisor; +} -/* utility functions to set and unset dtr and rts */ -#define HIGH 1 -#define LOW 0 -static int set_rts(struct usb_device *dev, - unsigned int pipe, - int high_or_low) -{ - static char buf[1]; - unsigned ftdi_high_or_low = (high_or_low? FTDI_SIO_SET_RTS_HIGH : - FTDI_SIO_SET_RTS_LOW); - return(usb_control_msg(dev, pipe, +static unsigned short int ftdi_232am_baud_to_divisor(int baud) +{ + return(ftdi_232am_baud_base_to_divisor(baud, 48000000)); +} + +static __u32 ftdi_232bm_baud_base_to_divisor(int baud, int base) +{ + static const unsigned char divfrac[8] = { 0, 3, 2, 4, 1, 5, 6, 7 }; + __u32 divisor; + int divisor3 = base / 2 / baud; // divisor shifted 3 bits to the left + divisor = divisor3 >> 3; + divisor |= (__u32)divfrac[divisor3 & 0x7] << 14; + /* Deal with special cases for highest baud rates. */ + if (divisor == 1) divisor = 0; else // 1.0 + if (divisor == 0x4001) divisor = 1; // 1.5 + return divisor; +} + +static __u32 ftdi_232bm_baud_to_divisor(int baud) +{ + return(ftdi_232bm_baud_base_to_divisor(baud, 48000000)); +} + +static int set_rts(struct usb_serial_port *port, int high_or_low) +{ + struct ftdi_private *priv = usb_get_serial_port_data(port); + char buf[1]; + unsigned ftdi_high_or_low; + if (high_or_low) { + ftdi_high_or_low = FTDI_SIO_SET_RTS_HIGH; + priv->last_dtr_rts |= TIOCM_RTS; + } else { + ftdi_high_or_low = FTDI_SIO_SET_RTS_LOW; + priv->last_dtr_rts &= ~TIOCM_RTS; + } + return(usb_control_msg(port->serial->dev, + usb_sndctrlpipe(port->serial->dev, 0), FTDI_SIO_SET_MODEM_CTRL_REQUEST, FTDI_SIO_SET_MODEM_CTRL_REQUEST_TYPE, ftdi_high_or_low, 0, buf, 0, WDR_TIMEOUT)); } -static int set_dtr(struct usb_device *dev, - unsigned int pipe, - int high_or_low) -{ - static char buf[1]; - unsigned ftdi_high_or_low = (high_or_low? FTDI_SIO_SET_DTR_HIGH : - FTDI_SIO_SET_DTR_LOW); - return(usb_control_msg(dev, pipe, + + +static int set_dtr(struct usb_serial_port *port, int high_or_low) +{ + struct ftdi_private *priv = usb_get_serial_port_data(port); + char buf[1]; + unsigned ftdi_high_or_low; + if (high_or_low) { + ftdi_high_or_low = FTDI_SIO_SET_DTR_HIGH; + priv->last_dtr_rts |= TIOCM_DTR; + } else { + ftdi_high_or_low = FTDI_SIO_SET_DTR_LOW; + priv->last_dtr_rts &= ~TIOCM_DTR; + } + return(usb_control_msg(port->serial->dev, + usb_sndctrlpipe(port->serial->dev, 0), FTDI_SIO_SET_MODEM_CTRL_REQUEST, FTDI_SIO_SET_MODEM_CTRL_REQUEST_TYPE, ftdi_high_or_low, 0, @@ -270,71 +737,409 @@ } +static __u32 get_ftdi_divisor(struct usb_serial_port * port); + + +static int change_speed(struct usb_serial_port *port) +{ + char buf[1]; + __u16 urb_value; + __u16 urb_index; + __u32 urb_index_value; + + urb_index_value = get_ftdi_divisor(port); + urb_value = (__u16)urb_index_value; + urb_index = (__u16)(urb_index_value >> 16); + + return (usb_control_msg(port->serial->dev, + usb_sndctrlpipe(port->serial->dev, 0), + FTDI_SIO_SET_BAUDRATE_REQUEST, + FTDI_SIO_SET_BAUDRATE_REQUEST_TYPE, + urb_value, urb_index, + buf, 0, 100) < 0); +} + + +static __u32 get_ftdi_divisor(struct usb_serial_port * port) +{ /* get_ftdi_divisor */ + struct ftdi_private *priv = usb_get_serial_port_data(port); + __u32 div_value = 0; + int div_okay = 1; + char *chip_name = ""; + int baud; + + /* + * The logic involved in setting the baudrate can be cleanly split in 3 steps. + * Obtaining the actual baud rate is a little tricky since unix traditionally + * somehow ignored the possibility to set non-standard baud rates. + * 1. Standard baud rates are set in tty->termios->c_cflag + * 2. If these are not enough, you can set any speed using alt_speed as follows: + * - set tty->termios->c_cflag speed to B38400 + * - set your real speed in tty->alt_speed; it gets ignored when + * alt_speed==0, (or) + * - call TIOCSSERIAL ioctl with (struct serial_struct) set as follows: + * flags & ASYNC_SPD_MASK == ASYNC_SPD_[HI, VHI, SHI, WARP], this just + * sets alt_speed to (HI: 57600, VHI: 115200, SHI: 230400, WARP: 460800) + * ** Steps 1, 2 are done courtesy of tty_get_baud_rate + * 3. You can also set baud rate by setting custom divisor as follows + * - set tty->termios->c_cflag speed to B38400 + * - call TIOCSSERIAL ioctl with (struct serial_struct) set as follows: + * o flags & ASYNC_SPD_MASK == ASYNC_SPD_CUST + * o custom_divisor set to baud_base / your_new_baudrate + * ** Step 3 is done courtesy of code borrowed from serial.c - I should really + * spend some time and separate+move this common code to serial.c, it is + * replicated in nearly every serial driver you see. + */ + + /* 1. Get the baud rate from the tty settings, this observes alt_speed hack */ + + baud = tty_get_baud_rate(port->tty); + dbg("%s - tty_get_baud_rate reports speed %d", __FUNCTION__, baud); + + /* 2. Observe async-compatible custom_divisor hack, update baudrate if needed */ + + if (baud == 38400 && + ((priv->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST) && + (priv->custom_divisor)) { + baud = priv->baud_base / priv->custom_divisor; + dbg("%s - custom divisor %d sets baud rate to %d", __FUNCTION__, priv->custom_divisor, baud); + } + + /* 3. Convert baudrate to device-specific divisor */ + + if (!baud) baud = 9600; + switch(priv->chip_type) { + case SIO: /* SIO chip */ + chip_name = "SIO"; + switch(baud) { + case 300: div_value = ftdi_sio_b300; break; + case 600: div_value = ftdi_sio_b600; break; + case 1200: div_value = ftdi_sio_b1200; break; + case 2400: div_value = ftdi_sio_b2400; break; + case 4800: div_value = ftdi_sio_b4800; break; + case 9600: div_value = ftdi_sio_b9600; break; + case 19200: div_value = ftdi_sio_b19200; break; + case 38400: div_value = ftdi_sio_b38400; break; + case 57600: div_value = ftdi_sio_b57600; break; + case 115200: div_value = ftdi_sio_b115200; break; + } /* baud */ + if (div_value == 0) { + dbg("%s - Baudrate (%d) requested is not supported", __FUNCTION__, baud); + div_value = ftdi_sio_b9600; + div_okay = 0; + } + break; + case FT8U232AM: /* 8U232AM chip */ + chip_name = "FT8U232AM"; + if (baud <= 3000000) { + div_value = ftdi_232am_baud_to_divisor(baud); + } else { + dbg("%s - Baud rate too high!", __FUNCTION__); + div_value = ftdi_232am_baud_to_divisor(9600); + div_okay = 0; + } + break; + case FT232BM: /* FT232BM chip */ + chip_name = "FT232BM"; + if (baud <= 3000000) { + div_value = ftdi_232bm_baud_to_divisor(baud); + } else { + dbg("%s - Baud rate too high!", __FUNCTION__); + div_value = ftdi_232bm_baud_to_divisor(9600); + div_okay = 0; + } + break; + } /* priv->chip_type */ + + if (div_okay) { + dbg("%s - Baud rate set to %d (divisor 0x%lX) on chip %s", + __FUNCTION__, baud, (unsigned long)div_value, chip_name); + } + + return(div_value); +} + -static int ftdi_sio_startup (struct usb_serial *serial) +static int get_serial_info(struct usb_serial_port * port, struct serial_struct * retinfo) { - struct ftdi_private *priv; + struct ftdi_private *priv = usb_get_serial_port_data(port); + struct serial_struct tmp; + + if (!retinfo) + return -EFAULT; + memset(&tmp, 0, sizeof(tmp)); + tmp.flags = priv->flags; + tmp.baud_base = priv->baud_base; + tmp.custom_divisor = priv->custom_divisor; + if (copy_to_user(retinfo, &tmp, sizeof(*retinfo))) + return -EFAULT; + return 0; +} /* get_serial_info */ + + +static int set_serial_info(struct usb_serial_port * port, struct serial_struct * newinfo) +{ /* set_serial_info */ + struct ftdi_private *priv = usb_get_serial_port_data(port); + struct serial_struct new_serial; + struct ftdi_private old_priv; + + if (copy_from_user(&new_serial, newinfo, sizeof(new_serial))) + return -EFAULT; + old_priv = * priv; + + /* Do error checking and permission checking */ + + if (!capable(CAP_SYS_ADMIN)) { + if (((new_serial.flags & ~ASYNC_USR_MASK) != + (priv->flags & ~ASYNC_USR_MASK))) + return -EPERM; + priv->flags = ((priv->flags & ~ASYNC_USR_MASK) | + (new_serial.flags & ASYNC_USR_MASK)); + priv->custom_divisor = new_serial.custom_divisor; + goto check_and_exit; + } + + if ((new_serial.baud_base != priv->baud_base) || + (new_serial.baud_base < 9600)) + return -EINVAL; + + /* Make the changes - these are privileged changes! */ + + priv->flags = ((priv->flags & ~ASYNC_FLAGS) | + (new_serial.flags & ASYNC_FLAGS)); + priv->custom_divisor = new_serial.custom_divisor; + + port->tty->low_latency = (priv->flags & ASYNC_LOW_LATENCY) ? 1 : 0; + +check_and_exit: + if ((old_priv.flags & ASYNC_SPD_MASK) != + (priv->flags & ASYNC_SPD_MASK)) { + if ((priv->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) + port->tty->alt_speed = 57600; + else if ((priv->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) + port->tty->alt_speed = 115200; + else if ((priv->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) + port->tty->alt_speed = 230400; + else if ((priv->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) + port->tty->alt_speed = 460800; + else + port->tty->alt_speed = 0; + } + if (((old_priv.flags & ASYNC_SPD_MASK) != + (priv->flags & ASYNC_SPD_MASK)) || + (((priv->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST) && + (old_priv.custom_divisor != priv->custom_divisor))) { + change_speed(port); + } + return (0); + +} /* set_serial_info */ + +/* + * *************************************************************************** + * FTDI driver specific functions + * *************************************************************************** + */ + +/* Common startup subroutine */ +/* Called from ftdi_SIO_startup, etc. */ +static int ftdi_common_startup (struct usb_serial *serial) +{ + struct usb_serial_port *port = &serial->port[0]; + struct ftdi_private *priv; + dbg("%s",__FUNCTION__); + priv = kmalloc(sizeof(struct ftdi_private), GFP_KERNEL); if (!priv){ err("%s- kmalloc(%Zd) failed.", __FUNCTION__, sizeof(struct ftdi_private)); return -ENOMEM; } + memset(priv, 0, sizeof(*priv)); - priv->ftdi_type = sio; - priv->write_offset = 1; - usb_set_serial_port_data(serial->port, priv); + init_waitqueue_head(&priv->delta_msr_wait); + /* This will push the characters through immediately rather + than queue a task to deliver them */ + priv->flags = ASYNC_LOW_LATENCY; + + /* Increase the size of read buffers */ + if (port->bulk_in_buffer) { + kfree (port->bulk_in_buffer); + } + port->bulk_in_buffer = kmalloc (BUFSZ, GFP_KERNEL); + if (!port->bulk_in_buffer) { + kfree (priv); + return -ENOMEM; + } + if (port->read_urb) { + port->read_urb->transfer_buffer = port->bulk_in_buffer; + port->read_urb->transfer_buffer_length = BUFSZ; + } + + /* Free port's existing write urb and transfer buffer. */ + if (port->write_urb) { + usb_free_urb (port->write_urb); + port->write_urb = NULL; + } + if (port->bulk_out_buffer) { + kfree (port->bulk_out_buffer); + port->bulk_out_buffer = NULL; + } + usb_set_serial_port_data(serial->port, priv); + return (0); } -static int ftdi_8U232AM_startup (struct usb_serial *serial) +/* Startup for the SIO chip */ +/* Called from usbserial:serial_probe */ +static int ftdi_SIO_startup (struct usb_serial *serial) { struct ftdi_private *priv; - + int err; - priv = kmalloc(sizeof(struct ftdi_private), GFP_KERNEL); - if (!priv){ - err("%s- kmalloc(%Zd) failed.", __FUNCTION__, sizeof(struct ftdi_private)); - return -ENOMEM; - } + dbg("%s",__FUNCTION__); - priv->ftdi_type = F8U232AM; - priv->write_offset = 0; - usb_set_serial_port_data(serial->port, priv); + err = ftdi_common_startup(serial); + if (err){ + return (err); + } + priv = usb_get_serial_port_data(serial->port); + priv->chip_type = SIO; + priv->baud_base = 12000000 / 16; + priv->write_offset = 1; + return (0); } -static void ftdi_sio_shutdown (struct usb_serial *serial) -{ - void *priv; +/* Startup for the 8U232AM chip */ +/* Called from usbserial:serial_probe */ +static int ftdi_8U232AM_startup (struct usb_serial *serial) +{ /* ftdi_8U232AM_startup */ + struct ftdi_private *priv; + int err; + + dbg("%s",__FUNCTION__); + err = ftdi_common_startup(serial); + if (err){ + return (err); + } + + priv = usb_get_serial_port_data(serial->port); + priv->chip_type = FT8U232AM; + priv->baud_base = 48000000 / 2; /* Would be / 16, but FTDI supports 0.125, 0.25 and 0.5 divisor fractions! */ + + return (0); +} /* ftdi_8U232AM_startup */ + +/* Startup for the FT232BM chip */ +/* Called from usbserial:serial_probe */ +static int ftdi_FT232BM_startup (struct usb_serial *serial) +{ /* ftdi_FT232BM_startup */ + struct ftdi_private *priv; + int err; + + dbg("%s",__FUNCTION__); + err = ftdi_common_startup(serial); + if (err){ + return (err); + } + + priv = usb_get_serial_port_data(serial->port); + priv->chip_type = FT232BM; + priv->baud_base = 48000000 / 2; /* Would be / 16, but FT232BM supports multiple of 0.125 divisor fractions! */ + + return (0); +} /* ftdi_FT232BM_startup */ + +/* Startup for the USB-UIRT device, which requires hardwired baudrate (38400 gets mapped to 312500) */ +/* Called from usbserial:serial_probe */ +static int ftdi_USB_UIRT_startup (struct usb_serial *serial) +{ /* ftdi_USB_UIRT_startup */ + struct ftdi_private *priv; + int err; + + dbg("%s",__FUNCTION__); + err = ftdi_8U232AM_startup(serial); + if (err){ + return (err); + } + + priv = usb_get_serial_port_data(serial->port); + priv->flags |= ASYNC_SPD_CUST; + priv->custom_divisor = 77; + priv->force_baud = B38400; + + return (0); +} /* ftdi_USB_UIRT_startup */ + +/* Startup for the HE-TIRA1 device, which requires hardwired + * baudrate (38400 gets mapped to 100000) */ +static int ftdi_HE_TIRA1_startup (struct usb_serial *serial) +{ /* ftdi_HE_TIRA1_startup */ + struct ftdi_private *priv; + int err; + + dbg("%s",__FUNCTION__); + err = ftdi_FT232BM_startup(serial); + if (err){ + return (err); + } + + priv = usb_get_serial_port_data(serial->port); + priv->flags |= ASYNC_SPD_CUST; + priv->custom_divisor = 240; + priv->force_baud = B38400; + priv->force_rtscts = 1; + + return (0); +} /* ftdi_HE_TIRA1_startup */ + + +/* ftdi_shutdown is called from usbserial:usb_serial_disconnect + * it is called when the usb device is disconnected + * + * usbserial:usb_serial_disconnect + * calls __serial_close for each open of the port + * shutdown is called then (ie ftdi_shutdown) + */ + + +static void ftdi_shutdown (struct usb_serial *serial) +{ /* ftdi_shutdown */ + + struct usb_serial_port *port = serial->port; + struct ftdi_private *priv = usb_get_serial_port_data(port); dbg("%s", __FUNCTION__); - priv = usb_get_serial_port_data(&serial->port[0]); - if (priv){ + /* all open ports are closed at this point + * (by usbserial.c:__serial_close, which calls ftdi_close) + */ + + if (priv) { kfree(priv); - usb_set_serial_port_data(&serial->port[0], NULL); + usb_set_serial_port_data(port, NULL); } -} - +} /* ftdi_shutdown */ -static int ftdi_sio_open (struct usb_serial_port *port, struct file *filp) -{ /* ftdi_sio_open */ +static int ftdi_open (struct usb_serial_port *port, struct file *filp) +{ /* ftdi_open */ struct termios tmp_termios; struct usb_serial *serial = port->serial; + struct ftdi_private *priv = usb_get_serial_port_data(port); + int result = 0; char buf[1]; /* Needed for the usb_control_msg I think */ dbg("%s", __FUNCTION__); - /* This will push the characters through immediately rather - than queue a task to deliver them */ - port->tty->low_latency = 1; + + 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 */ @@ -343,19 +1148,20 @@ FTDI_SIO_RESET_SIO, 0, buf, 0, WDR_TIMEOUT); - /* Setup termios defaults. According to tty_io.c the - settings are driver specific */ - port->tty->termios->c_cflag = - B9600 | CS8 | CREAD | HUPCL | CLOCAL; + /* Termios defaults are set by usb_serial_init. We don't change + port->tty->termios - this would loose speed settings, etc. + This is same behaviour as serial.c/rs_open() - Kuba */ - /* ftdi_sio_set_termios will send usb control messages */ - ftdi_sio_set_termios(port, &tmp_termios); + /* ftdi_set_termios will send usb control messages */ + ftdi_set_termios(port, &tmp_termios); + /* FIXME: Flow control might be enabled, so it should be checked - + we have no control of defaults! */ /* Turn on RTS and DTR since we are not flow controlling by default */ - if (set_dtr(serial->dev, usb_sndctrlpipe(serial->dev, 0),HIGH) < 0) { + if (set_dtr(port, HIGH) < 0) { err("%s Error from DTR HIGH urb", __FUNCTION__); } - if (set_rts(serial->dev, usb_sndctrlpipe(serial->dev, 0),HIGH) < 0){ + if (set_rts(port, HIGH) < 0){ err("%s Error from RTS HIGH urb", __FUNCTION__); } @@ -363,23 +1169,37 @@ usb_fill_bulk_urb(port->read_urb, serial->dev, usb_rcvbulkpipe(serial->dev, port->bulk_in_endpointAddress), port->read_urb->transfer_buffer, port->read_urb->transfer_buffer_length, - ftdi_sio_read_bulk_callback, port); + ftdi_read_bulk_callback, port); result = usb_submit_urb(port->read_urb, GFP_KERNEL); if (result) err("%s - failed submitting read urb, error %d", __FUNCTION__, result); + return result; -} /* ftdi_sio_open */ +} /* ftdi_open */ + -static void ftdi_sio_close (struct usb_serial_port *port, struct file *filp) -{ /* ftdi_sio_close */ - struct usb_serial *serial = port->serial; /* Checked in usbserial.c */ +/* + * usbserial:__serial_close only calls ftdi_close if the point is open + * + * This only gets called when it is the last close + * + * + */ + +static void ftdi_close (struct usb_serial_port *port, struct file *filp) +{ /* ftdi_close */ + struct usb_serial *serial; unsigned int c_cflag = port->tty->termios->c_cflag; char buf[1]; dbg("%s", __FUNCTION__); + serial = get_usb_serial ( port, __FUNCTION__); + if (!serial) + return; + if (serial->dev) { if (c_cflag & HUPCL){ /* Disable flow control */ @@ -392,38 +1212,46 @@ } /* drop DTR */ - if (set_dtr(serial->dev, usb_sndctrlpipe(serial->dev, 0), LOW) < 0){ + if (set_dtr(port, LOW) < 0){ err("Error from DTR LOW urb"); } /* drop RTS */ - if (set_rts(serial->dev, usb_sndctrlpipe(serial->dev, 0),LOW) < 0) { + if (set_rts(port, LOW) < 0) { err("Error from RTS LOW urb"); } + /* shutdown our bulk read */ + if (port->read_urb) { + usb_unlink_urb (port->read_urb); + } + /* unlink the running write urbs */ + + } /* Note change no line is hupcl is off */ + } /* if (serial->dev) */ - /* shutdown our bulk reads and writes */ - /* ***CHECK*** behaviour when there is nothing queued */ - usb_unlink_urb (port->write_urb); - usb_unlink_urb (port->read_urb); - } -} /* ftdi_sio_close */ + +} /* ftdi_close */ -/* The ftdi_sio requires the first byte to have: +/* The SIO requires the first byte to have: * B0 1 * B1 0 * B2..7 length of message excluding byte 0 + * + * The new devices do not require this byte */ -static int ftdi_sio_write (struct usb_serial_port *port, int from_user, +static int ftdi_write (struct usb_serial_port *port, int from_user, const unsigned char *buf, int count) -{ /* ftdi_sio_write */ - struct usb_serial *serial = port->serial; +{ /* ftdi_write */ + struct usb_serial *serial = get_usb_serial ( port, __FUNCTION__); struct ftdi_private *priv = usb_get_serial_port_data(port); - unsigned char *first_byte = port->write_urb->transfer_buffer; - int data_offset ; - int result; - + struct urb *urb; + unsigned char *buffer; + int data_offset ; /* will be 1 for the SIO and 0 otherwise */ + int status; + int transfer_size; + dbg("%s port %d, %d bytes", __FUNCTION__, port->number, count); if (count == 0) { @@ -434,123 +1262,209 @@ data_offset = priv->write_offset; dbg("data_offset set to %d",data_offset); - if (port->write_urb->status == -EINPROGRESS) { - dbg("%s - already writing", __FUNCTION__); - return (0); - } - - count += data_offset; - count = (count > port->bulk_out_size) ? port->bulk_out_size : count; - - /* Copy in the data to send */ - if (from_user) { - if (copy_from_user(port->write_urb->transfer_buffer + data_offset, - buf, count - data_offset )){ - return -EFAULT; + /* Determine total transfer size */ + transfer_size = count; + if (data_offset > 0) { + /* Original sio needs control bytes too... */ + transfer_size += (data_offset * + ((count + (PKTSZ - 1 - data_offset)) / + (PKTSZ - data_offset))); + } + + buffer = kmalloc (transfer_size, GFP_ATOMIC); + if (!buffer) { + err("%s ran out of kernel memory for urb ...", __FUNCTION__); + return -ENOMEM; + } + + urb = usb_alloc_urb(0, GFP_ATOMIC); + if (!urb) { + err("%s - no more free urbs", __FUNCTION__); + kfree (buffer); + return -ENOMEM; + } + + /* Copy data */ + if (data_offset > 0) { + /* Original sio requires control byte at start of each packet. */ + int user_pktsz = PKTSZ - data_offset; + int todo = count; + unsigned char *first_byte = buffer; + const unsigned char *current_position = buf; + + while (todo > 0) { + if (user_pktsz > todo) { + user_pktsz = todo; + } + /* Write the control byte at the front of the packet*/ + *first_byte = 1 | ((user_pktsz) << 2); + /* Copy data for packet */ + if (from_user) { + if (copy_from_user (first_byte + data_offset, + current_position, user_pktsz)){ + kfree (buffer); + usb_free_urb (urb); + return -EFAULT; + } + } else { + memcpy (first_byte + data_offset, + current_position, user_pktsz); + } + first_byte += user_pktsz + data_offset; + current_position += user_pktsz; + todo -= user_pktsz; } } else { - memcpy(port->write_urb->transfer_buffer + data_offset, - buf, count - data_offset ); - } - - first_byte = port->write_urb->transfer_buffer; - if (data_offset > 0){ - /* Write the control byte at the front of the packet*/ - *first_byte = 1 | ((count-data_offset) << 2) ; + /* No control byte required. */ + /* Copy in the data to send */ + if (from_user) { + if (copy_from_user (buffer, buf, count)) { + kfree (buffer); + usb_free_urb (urb); + return -EFAULT; + } + } else { + memcpy (buffer, buf, count); + } } - dbg("%s Bytes: %d, First Byte: 0x%02x", __FUNCTION__,count, first_byte[0]); - usb_serial_debug_data (__FILE__, __FUNCTION__, count, first_byte); - - /* send the data out the bulk port */ - usb_fill_bulk_urb(port->write_urb, serial->dev, + usb_serial_debug_data (__FILE__, __FUNCTION__, transfer_size, buffer); + + /* fill the buffer and send it */ + usb_fill_bulk_urb(urb, serial->dev, usb_sndbulkpipe(serial->dev, port->bulk_out_endpointAddress), - port->write_urb->transfer_buffer, count, - ftdi_sio_write_bulk_callback, port); - - result = usb_submit_urb(port->write_urb, GFP_ATOMIC); - if (result) { - err("%s - failed submitting write urb, error %d", __FUNCTION__, result); - return 0; + buffer, transfer_size, + ftdi_write_bulk_callback, port); + + status = usb_submit_urb(urb, GFP_ATOMIC); + if (status) { + err("%s - failed submitting write urb, error %d", __FUNCTION__, status); + count = status; } - dbg("%s write returning: %d", __FUNCTION__, count - data_offset); - return (count - data_offset); -} /* ftdi_sio_write */ + /* we are done with this urb, so let the host driver + * really free it when it is finished with it */ + usb_free_urb (urb); + + dbg("%s write returning: %d", __FUNCTION__, count); + return count; +} /* ftdi_write */ -static void ftdi_sio_write_bulk_callback (struct urb *urb, struct pt_regs *regs) + +/* This function may get called when the device is closed */ + +static void ftdi_write_bulk_callback (struct urb *urb, struct pt_regs *regs) { struct usb_serial_port *port = (struct usb_serial_port *)urb->context; - struct usb_serial *serial; + struct usb_serial *serial = get_usb_serial (port, __FUNCTION__); dbg("%s", __FUNCTION__); - if (port_paranoia_check (port, "ftdi_sio_write_bulk_callback")) { + if (port_paranoia_check (port, __FUNCTION__)) return; - } - - serial = port->serial; - if (serial_paranoia_check (serial, "ftdi_sio_write_bulk_callback")) { - return; - } if (urb->status) { dbg("nonzero write bulk status received: %d", urb->status); return; } - schedule_work(&port->work); -} /* ftdi_sio_write_bulk_callback */ + if (!serial) { + dbg("%s - bad serial pointer, exiting", __FUNCTION__); + return; + } + + /* Have to check for validity of queueing up the tasks */ + dbg("%s - port->open_count = %d", __FUNCTION__, port->open_count); + + if (port->open_count > 0){ + schedule_work(&port->work); + } + return; +} /* ftdi_write_bulk_callback */ -static int ftdi_sio_write_room( struct usb_serial_port *port ) + +static int ftdi_write_room( struct usb_serial_port *port ) { - struct ftdi_private *priv = usb_get_serial_port_data(port); - int room; + dbg("%s - port %d", __FUNCTION__, port->number); + + /* + * We really can take anything the user throws at us + * but let's pick a nice big number to tell the tty + * layer that we have lots of free space + */ + return 2048; +} /* ftdi_write_room */ - if ( port->write_urb->status == -EINPROGRESS) { - /* There is a race here with the _write routines but it won't hurt */ - room = 0; - } else { - room = port->bulk_out_size - priv->write_offset; - } - return(room); -} /* ftdi_sio_write_room */ +static int ftdi_chars_in_buffer (struct usb_serial_port *port) +{ /* ftdi_chars_in_buffer */ + dbg("%s - port %d", __FUNCTION__, port->number); + + /* + * We can't really account for how much data we + * have sent out, but hasn't made it through to the + * device, so just tell the tty layer that everything + * is flushed. + */ + return 0; +} /* ftdi_chars_in_buffer */ -static void ftdi_sio_read_bulk_callback (struct urb *urb, struct pt_regs *regs) -{ /* ftdi_sio_serial_buld_callback */ + + +static void ftdi_read_bulk_callback (struct urb *urb, struct pt_regs *regs) +{ /* ftdi_read_bulk_callback */ struct usb_serial_port *port = (struct usb_serial_port *)urb->context; struct usb_serial *serial; - struct tty_struct *tty = port->tty ; + struct tty_struct *tty; + struct ftdi_private *priv; char error_flag; unsigned char *data = urb->transfer_buffer; - const int data_offset = 2; int i; int result; + int need_flip; + int packet_offset; - dbg("%s - port %d", __FUNCTION__, port->number); + if (urb->number_of_packets > 0) { + err("%s transfer_buffer_length %d actual_length %d number of packets %d",__FUNCTION__, + urb->transfer_buffer_length, urb->actual_length, urb->number_of_packets ); + err("%s transfer_flags %x ", __FUNCTION__,urb->transfer_flags ); + } + + dbg("%s", __FUNCTION__); - if (port_paranoia_check (port, "ftdi_sio_read_bulk_callback")) { + if (port_paranoia_check (port, __FUNCTION__)) { return; } + if (port->open_count <= 0) + return; - serial = port->serial; - if (serial_paranoia_check (serial, "ftdi_sio_read_bulk_callback")) { + serial = get_usb_serial(port,__FUNCTION__); + if (!serial){ + dbg("%s - bad serial pointer - exiting",__FUNCTION__); + return; + } + + tty = port->tty; + if (!tty) { + dbg("%s - bad tty pointer - exiting",__FUNCTION__); return; } + priv = usb_get_serial_port_data(port); + if (urb->status) { /* This will happen at close every time so it is a dbg not an err */ - dbg("nonzero read bulk status received: %d", urb->status); + dbg("(this is ok on close) nonzero read bulk status received: %d", urb->status); return; } + /* The first two bytes of every read packet are status */ if (urb->actual_length > 2) { usb_serial_debug_data (__FILE__, __FUNCTION__, urb->actual_length, data); } else { - dbg("Just status 0o%03o0o%03o",data[0],data[1]); + dbg("Status only: %03oo %03oo",data[0],data[1]); } @@ -559,129 +1473,101 @@ /* See acm.c - you do a tty_hangup - eg tty_hangup(tty) */ /* if CD is dropped and the line is not CLOCAL then we should hangup */ - /* Handle errors and break */ - error_flag = TTY_NORMAL; - /* Although the device uses a bitmask and hence can have multiple */ - /* errors on a packet - the order here sets the priority the */ - /* error is returned to the tty layer */ - - if ( data[1] & FTDI_RS_OE ) { - error_flag = TTY_OVERRUN; - dbg("OVERRRUN error"); - } - if ( data[1] & FTDI_RS_BI ) { - error_flag = TTY_BREAK; - dbg("BREAK received"); - } - if ( data[1] & FTDI_RS_PE ) { - error_flag = TTY_PARITY; - dbg("PARITY error"); - } - if ( data[1] & FTDI_RS_FE ) { - error_flag = TTY_FRAME; - dbg("FRAMING error"); - } - if (urb->actual_length > data_offset) { - - for (i = data_offset ; i < urb->actual_length ; ++i) { - /* have to make sure we don't overflow the buffer - with tty_insert_flip_char's */ - if(tty->flip.count >= TTY_FLIPBUF_SIZE) { - tty_flip_buffer_push(tty); - } - /* Note that the error flag is duplicated for - every character received since we don't know - which character it applied to */ - tty_insert_flip_char(tty, data[i], error_flag); + need_flip = 0; + for (packet_offset=0; packet_offset < urb->actual_length; packet_offset += PKTSZ) { + /* Compare new line status to the old one, signal if different */ + if (priv != NULL) { + char new_status = data[packet_offset+0] & FTDI_STATUS_B0_MASK; + if (new_status != priv->prev_status) { + priv->diff_status |= new_status ^ priv->prev_status; + wake_up_interruptible(&priv->delta_msr_wait); + priv->prev_status = new_status; + } } - tty_flip_buffer_push(tty); - - } + /* Handle errors and break */ + error_flag = TTY_NORMAL; + /* Although the device uses a bitmask and hence can have multiple */ + /* errors on a packet - the order here sets the priority the */ + /* error is returned to the tty layer */ + + if ( data[packet_offset+1] & FTDI_RS_OE ) { + error_flag = TTY_OVERRUN; + dbg("OVERRRUN error"); + } + if ( data[packet_offset+1] & FTDI_RS_BI ) { + error_flag = TTY_BREAK; + dbg("BREAK received"); + } + if ( data[packet_offset+1] & FTDI_RS_PE ) { + error_flag = TTY_PARITY; + dbg("PARITY error"); + } + if ( data[packet_offset+1] & FTDI_RS_FE ) { + error_flag = TTY_FRAME; + dbg("FRAMING error"); + } + if (urb->actual_length > packet_offset + 2) { + for (i = 2; (i < PKTSZ) && ((i+packet_offset) < urb->actual_length); ++i) { + /* have to make sure we don't overflow the buffer + with tty_insert_flip_char's */ + if(tty->flip.count >= TTY_FLIPBUF_SIZE) { + tty_flip_buffer_push(tty); + } + /* Note that the error flag is duplicated for + every character received since we don't know + which character it applied to */ + tty_insert_flip_char(tty, data[packet_offset+i], error_flag); + } + need_flip = 1; + } #ifdef NOT_CORRECT_BUT_KEEPING_IT_FOR_NOW - /* if a parity error is detected you get status packets forever - until a character is sent without a parity error. - This doesn't work well since the application receives a never - ending stream of bad data - even though new data hasn't been sent. - Therefore I (bill) have taken this out. - However - this might make sense for framing errors and so on - so I am leaving the code in for now. - */ - else { - if (error_flag != TTY_NORMAL){ - dbg("error_flag is not normal"); + /* if a parity error is detected you get status packets forever + until a character is sent without a parity error. + This doesn't work well since the application receives a never + ending stream of bad data - even though new data hasn't been sent. + Therefore I (bill) have taken this out. + However - this might make sense for framing errors and so on + so I am leaving the code in for now. + */ + else { + if (error_flag != TTY_NORMAL){ + dbg("error_flag is not normal"); /* In this case it is just status - if that is an error send a bad character */ if(tty->flip.count >= TTY_FLIPBUF_SIZE) { tty_flip_buffer_push(tty); } tty_insert_flip_char(tty, 0xff, error_flag); - tty_flip_buffer_push(tty); + need_flip = 1; + } } - } #endif + } /* "for(packet_offset=0..." */ - /* Continue trying to always read */ - usb_fill_bulk_urb(port->read_urb, serial->dev, - usb_rcvbulkpipe(serial->dev, port->bulk_in_endpointAddress), - port->read_urb->transfer_buffer, port->read_urb->transfer_buffer_length, - ftdi_sio_read_bulk_callback, port); - - result = usb_submit_urb(port->read_urb, GFP_ATOMIC); - if (result) - err("%s - failed resubmitting read urb, error %d", __FUNCTION__, result); + /* Low latency */ + if (need_flip) { + tty_flip_buffer_push(tty); + } + + /* if the port is closed stop trying to read */ + if (port->open_count > 0){ + /* Continue trying to always read */ + usb_fill_bulk_urb(port->read_urb, serial->dev, + usb_rcvbulkpipe(serial->dev, port->bulk_in_endpointAddress), + port->read_urb->transfer_buffer, port->read_urb->transfer_buffer_length, + ftdi_read_bulk_callback, port); + + result = usb_submit_urb(port->read_urb, GFP_ATOMIC); + if (result) + err("%s - failed resubmitting read urb, error %d", __FUNCTION__, result); + } return; -} /* ftdi_sio_serial_read_bulk_callback */ - +} /* ftdi_read_bulk_callback */ -static __u16 translate_baudrate_to_ftdi(unsigned int cflag, enum ftdi_type ftdi_type) -{ /* translate_baudrate_to_ftdi */ - - __u16 urb_value = ftdi_sio_b9600; - if (ftdi_type == sio){ - switch(cflag & CBAUD){ - case B0: break; /* ignored by this */ - case B300: urb_value = ftdi_sio_b300; dbg("Set to 300"); break; - case B600: urb_value = ftdi_sio_b600; dbg("Set to 600") ; break; - case B1200: urb_value = ftdi_sio_b1200; dbg("Set to 1200") ; break; - case B2400: urb_value = ftdi_sio_b2400; dbg("Set to 2400") ; break; - case B4800: urb_value = ftdi_sio_b4800; dbg("Set to 4800") ; break; - case B9600: urb_value = ftdi_sio_b9600; dbg("Set to 9600") ; break; - case B19200: urb_value = ftdi_sio_b19200; dbg("Set to 19200") ; break; - case B38400: urb_value = ftdi_sio_b38400; dbg("Set to 38400") ; break; - case B57600: urb_value = ftdi_sio_b57600; dbg("Set to 57600") ; break; - case B115200: urb_value = ftdi_sio_b115200; dbg("Set to 115200") ; break; - default: dbg("%s - FTDI_SIO does not support the baudrate (%d) requested", - __FUNCTION__, (cflag & CBAUD)); - break; - } - } else { /* it is 8U232AM */ - switch(cflag & CBAUD){ - case B0: break; /* ignored by this */ - case B300: urb_value = ftdi_8U232AM_48MHz_b300; dbg("Set to 300"); break; - case B600: urb_value = ftdi_8U232AM_48MHz_b600; dbg("Set to 600") ; break; - case B1200: urb_value = ftdi_8U232AM_48MHz_b1200; dbg("Set to 1200") ; break; - case B2400: urb_value = ftdi_8U232AM_48MHz_b2400; dbg("Set to 2400") ; break; - case B4800: urb_value = ftdi_8U232AM_48MHz_b4800; dbg("Set to 4800") ; break; - case B9600: urb_value = ftdi_8U232AM_48MHz_b9600; dbg("Set to 9600") ; break; - case B19200: urb_value = ftdi_8U232AM_48MHz_b19200; dbg("Set to 19200") ; break; - case B38400: urb_value = ftdi_8U232AM_48MHz_b38400; dbg("Set to 38400") ; break; - case B57600: urb_value = ftdi_8U232AM_48MHz_b57600; dbg("Set to 57600") ; break; - case B115200: urb_value = ftdi_8U232AM_48MHz_b115200; dbg("Set to 115200") ; break; - case B230400: urb_value = ftdi_8U232AM_48MHz_b230400; dbg("Set to 230400") ; break; - case B460800: urb_value = ftdi_8U232AM_48MHz_b460800; dbg("Set to 460800") ; break; - case B921600: urb_value = ftdi_8U232AM_48MHz_b921600; dbg("Set to 921600") ; break; - default: dbg("%s - The baudrate (%d) requested is not implemented", - __FUNCTION__, (cflag & CBAUD)); - break; - } - } - return(urb_value); -} - -static void ftdi_sio_break_ctl( struct usb_serial_port *port, int break_state ) +static void ftdi_break_ctl( struct usb_serial_port *port, int break_state ) { struct usb_serial *serial = port->serial; struct ftdi_private *priv = usb_get_serial_port_data(port); @@ -704,32 +1590,48 @@ FTDI_SIO_SET_DATA_REQUEST_TYPE, urb_value , 0, buf, 0, WDR_TIMEOUT) < 0) { - err("%s - FAILED to enable/disable break state (state was %d)", - __FUNCTION__, break_state); + err("%s FAILED to enable/disable break state (state was %d)", __FUNCTION__,break_state); } - dbg("%s - break state is %d - urb is %d", __FUNCTION__, break_state, urb_value); + dbg("%s break state is %d - urb is %d", __FUNCTION__,break_state, urb_value); } +/* old_termios contains the original termios settings and tty->termios contains + * the new setting to be used + * WARNING: set_termios calls this with old_termios in kernel space + */ -/* As I understand this - old_termios contains the original termios settings */ -/* and tty->termios contains the new setting to be used */ -/* */ -/* WARNING: set_termios calls this with old_termios in kernel space */ - -static void ftdi_sio_set_termios (struct usb_serial_port *port, struct termios *old_termios) -{ /* ftdi_sio_set_termios */ +static void ftdi_set_termios (struct usb_serial_port *port, struct termios *old_termios) +{ /* ftdi_termios */ struct usb_serial *serial = port->serial; unsigned int cflag = port->tty->termios->c_cflag; struct ftdi_private *priv = usb_get_serial_port_data(port); __u16 urb_value; /* will hold the new flags */ char buf[1]; /* Perhaps I should dynamically alloc this? */ + // Added for xon/xoff support + unsigned int iflag = port->tty->termios->c_iflag; + unsigned char vstop; + unsigned char vstart; dbg("%s", __FUNCTION__); + /* Force baud rate if this device requires it, unless it is set to B0. */ + if (priv->force_baud && ((port->tty->termios->c_cflag & CBAUD) != B0)) { + dbg("%s: forcing baud rate for this device", __FUNCTION__); + port->tty->termios->c_cflag &= ~CBAUD; + port->tty->termios->c_cflag |= priv->force_baud; + } + + /* Force RTS-CTS if this device requires it. */ + if (priv->force_rtscts) { + dbg("%s: forcing rtscts for this device", __FUNCTION__); + port->tty->termios->c_cflag |= CRTSCTS; + } + + cflag = port->tty->termios->c_cflag; /* FIXME -For this cut I don't care if the line is really changing or not - so just do the change regardless - should be able to @@ -771,8 +1673,6 @@ } /* Now do the baudrate */ - urb_value = translate_baudrate_to_ftdi((cflag & CBAUD), priv->ftdi_type); - if ((cflag & CBAUD) == B0 ) { /* Disable flow control */ if (usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), @@ -783,24 +1683,20 @@ err("%s error from disable flowcontrol urb", __FUNCTION__); } /* Drop RTS and DTR */ - if (set_dtr(serial->dev, usb_sndctrlpipe(serial->dev, 0),LOW) < 0){ + if (set_dtr(port, LOW) < 0){ err("%s Error from DTR LOW urb", __FUNCTION__); } - if (set_rts(serial->dev, usb_sndctrlpipe(serial->dev, 0),LOW) < 0){ + if (set_rts(port, LOW) < 0){ err("%s Error from RTS LOW urb", __FUNCTION__); } } else { /* set the baudrate determined before */ - if (usb_control_msg(serial->dev, - usb_sndctrlpipe(serial->dev, 0), - FTDI_SIO_SET_BAUDRATE_REQUEST, - FTDI_SIO_SET_BAUDRATE_REQUEST_TYPE, - urb_value, 0, - buf, 0, 100) < 0) { + if (change_speed(port)) { err("%s urb failed to set baurdrate", __FUNCTION__); } } + /* Set flow control */ /* Note device also supports DTR/CD (ugh) and Xon/Xoff in hardware */ if (cflag & CRTSCTS) { @@ -815,146 +1711,290 @@ } } else { - /* CHECKME Assuming XON/XOFF handled by tty stack - not by device */ - dbg("%s Turning off hardware flow control", __FUNCTION__); - if (usb_control_msg(serial->dev, - usb_sndctrlpipe(serial->dev, 0), - FTDI_SIO_SET_FLOW_CTRL_REQUEST, - FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE, - 0, 0, - buf, 0, WDR_TIMEOUT) < 0) { - err("urb failed to clear flow control"); - } + /* + * Xon/Xoff code + * + * Check the IXOFF status in the iflag component of the termios structure + * if IXOFF is not set, the pre-xon/xoff code is executed. + */ + if (iflag & IXOFF) { + dbg("%s request to enable xonxoff iflag=%04x",__FUNCTION__,iflag); + // Try to enable the XON/XOFF on the ftdi_sio + // Set the vstart and vstop -- could have been done up above where + // a lot of other dereferencing is done but that would be very + // inefficient as vstart and vstop are not always needed + vstart=port->tty->termios->c_cc[VSTART]; + vstop=port->tty->termios->c_cc[VSTOP]; + urb_value=(vstop << 8) | (vstart); + + if (usb_control_msg(serial->dev, + usb_sndctrlpipe(serial->dev, 0), + FTDI_SIO_SET_FLOW_CTRL_REQUEST, + FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE, + urb_value , FTDI_SIO_XON_XOFF_HS, + buf, 0, WDR_TIMEOUT) < 0) { + err("urb failed to set to xon/xoff flow control"); + } + } else { + /* else clause to only run if cfag ! CRTSCTS and iflag ! XOFF */ + /* CHECKME Assuming XON/XOFF handled by tty stack - not by device */ + dbg("%s Turning off hardware flow control", __FUNCTION__); + if (usb_control_msg(serial->dev, + usb_sndctrlpipe(serial->dev, 0), + FTDI_SIO_SET_FLOW_CTRL_REQUEST, + FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE, + 0, 0, + buf, 0, WDR_TIMEOUT) < 0) { + err("urb failed to clear flow control"); + } + } } return; -} /* ftdi_sio_set_termios */ +} /* ftdi_termios */ -static int ftdi_sio_tiocmget (struct usb_serial_port *port, struct file *file) + +static int ftdi_ioctl (struct usb_serial_port *port, struct file * file, unsigned int cmd, unsigned long arg) { struct usb_serial *serial = port->serial; struct ftdi_private *priv = usb_get_serial_port_data(port); - char *buf = NULL; - int ret = -EINVAL; - int size; + + __u16 urb_value=0; /* Will hold the new flags */ + char buf[2]; + int ret, mask; - dbg("%s", __FUNCTION__); + dbg("%s cmd 0x%04x", __FUNCTION__, cmd); - buf = kmalloc(2, GFP_KERNEL); - if (!buf) - goto exit; + /* Based on code from acm.c and others */ + switch (cmd) { - if (priv->ftdi_type == sio) { - size = 1; - } else { - /* the 8U232AM returns a two byte value (the sio is a 1 byte value) - in the same - format as the data returned from the in point */ - size = 2; - } - ret = usb_control_msg(serial->dev, - usb_rcvctrlpipe(serial->dev, 0), - FTDI_SIO_GET_MODEM_STATUS_REQUEST, - FTDI_SIO_GET_MODEM_STATUS_REQUEST_TYPE, - 0, 0, buf, size, WDR_TIMEOUT); - if (ret < 0) { - err("%s Could not get modem status of device - err: %d", - __FUNCTION__, ret); - goto exit; - } - - ret = (buf[0] & FTDI_SIO_DSR_MASK ? TIOCM_DSR : 0) | - (buf[0] & FTDI_SIO_CTS_MASK ? TIOCM_CTS : 0) | - (buf[0] & FTDI_SIO_RI_MASK ? TIOCM_RI : 0) | - (buf[0] & FTDI_SIO_RLSD_MASK ? TIOCM_CD : 0); - -exit: - kfree(buf); - return ret; -} + case TIOCMGET: + dbg("%s TIOCMGET", __FUNCTION__); + switch (priv->chip_type) { + case SIO: + /* Request the status from the device */ + if ((ret = usb_control_msg(serial->dev, + usb_rcvctrlpipe(serial->dev, 0), + FTDI_SIO_GET_MODEM_STATUS_REQUEST, + FTDI_SIO_GET_MODEM_STATUS_REQUEST_TYPE, + 0, 0, + buf, 1, WDR_TIMEOUT)) < 0 ) { + err("%s Could not get modem status of device - err: %d", __FUNCTION__, + ret); + return(ret); + } + break; + case FT8U232AM: + case FT232BM: + /* the 8U232AM returns a two byte value (the sio is a 1 byte value) - in the same + format as the data returned from the in point */ + if ((ret = usb_control_msg(serial->dev, + usb_rcvctrlpipe(serial->dev, 0), + FTDI_SIO_GET_MODEM_STATUS_REQUEST, + FTDI_SIO_GET_MODEM_STATUS_REQUEST_TYPE, + 0, 0, + buf, 2, WDR_TIMEOUT)) < 0 ) { + err("%s Could not get modem status of device - err: %d", __FUNCTION__, + ret); + return(ret); + } + break; + default: + return -EFAULT; + break; + } -static int ftdi_sio_tiocmset (struct usb_serial_port *port, struct file *file, - unsigned int set, unsigned int clear) -{ - struct usb_serial *serial = port->serial; - int ret = 0; - - dbg("%s", __FUNCTION__); + return put_user((buf[0] & FTDI_SIO_DSR_MASK ? TIOCM_DSR : 0) | + (buf[0] & FTDI_SIO_CTS_MASK ? TIOCM_CTS : 0) | + (buf[0] & FTDI_SIO_RI_MASK ? TIOCM_RI : 0) | + (buf[0] & FTDI_SIO_RLSD_MASK ? TIOCM_CD : 0) | + priv->last_dtr_rts, + (unsigned long *) arg); + break; - if (set & TIOCM_RTS) - if ((ret = set_rts(serial->dev, - usb_sndctrlpipe(serial->dev, 0), - HIGH)) < 0) { - err("Urb to set RTS failed"); - goto exit; + case TIOCMSET: /* Turns on and off the lines as specified by the mask */ + dbg("%s TIOCMSET", __FUNCTION__); + if (get_user(mask, (unsigned long *) arg)) + return -EFAULT; + urb_value = ((mask & TIOCM_DTR) ? HIGH : LOW); + if ((ret = set_dtr(port, urb_value)) < 0){ + err("Error from DTR set urb (TIOCMSET)"); + return(ret); } - - if (set & TIOCM_DTR) - if ((ret = set_dtr(serial->dev, - usb_sndctrlpipe(serial->dev, 0), - HIGH)) < 0) { - err("Urb to set DTR failed"); - goto exit; + urb_value = ((mask & TIOCM_RTS) ? HIGH : LOW); + if ((ret = set_rts(port, urb_value)) < 0){ + err("Error from RTS set urb (TIOCMSET)"); + return(ret); } - - if (clear & TIOCM_RTS) - if ((ret = set_rts(serial->dev, - usb_sndctrlpipe(serial->dev, 0), - LOW)) < 0) { - err("Urb to unset RTS failed"); - goto exit; + return(0); + break; + + case TIOCMBIS: /* turns on (Sets) the lines as specified by the mask */ + dbg("%s TIOCMBIS", __FUNCTION__); + if (get_user(mask, (unsigned long *) arg)) + return -EFAULT; + if (mask & TIOCM_DTR){ + if ((ret = set_dtr(port, HIGH)) < 0) { + err("Urb to set DTR failed"); + return(ret); + } } + if (mask & TIOCM_RTS) { + if ((ret = set_rts(port, HIGH)) < 0){ + err("Urb to set RTS failed"); + return(ret); + } + } + return(0); + break; - if (clear & TIOCM_DTR) - if ((ret = set_dtr(serial->dev, - usb_sndctrlpipe(serial->dev, 0), - LOW)) < 0) { - err("Urb to unset DTR failed"); - goto exit; + case TIOCMBIC: /* turns off (Clears) the lines as specified by the mask */ + dbg("%s TIOCMBIC", __FUNCTION__); + if (get_user(mask, (unsigned long *) arg)) + return -EFAULT; + if (mask & TIOCM_DTR){ + if ((ret = set_dtr(port, LOW)) < 0){ + err("Urb to unset DTR failed"); + return(ret); + } + } + if (mask & TIOCM_RTS) { + if ((ret = set_rts(port, LOW)) < 0){ + err("Urb to unset RTS failed"); + return(ret); + } } + return(0); + break; -exit: - return ret; -} + /* + * I had originally implemented TCSET{A,S}{,F,W} and + * TCGET{A,S} here separately, however when testing I + * found that the higher layers actually do the termios + * conversions themselves and pass the call onto + * ftdi_sio_set_termios. + * + */ + + case TIOCGSERIAL: /* gets serial port data */ + return get_serial_info(port, (struct serial_struct *) arg); + + case TIOCSSERIAL: /* sets serial port data */ + return set_serial_info(port, (struct serial_struct *) arg); + + /* + * Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change + * - mask passed in arg for lines of interest + * (use |'ed TIOCM_RNG/DSR/CD/CTS for masking) + * Caller should use TIOCGICOUNT to see which one it was. + * + * This code is borrowed from linux/drivers/char/serial.c + */ + case TIOCMIWAIT: + while (priv != NULL) { + interruptible_sleep_on(&priv->delta_msr_wait); + /* see if a signal did it */ + if (signal_pending(current)) + return -ERESTARTSYS; + else { + char diff = priv->diff_status; -static int ftdi_sio_ioctl (struct usb_serial_port *port, struct file * file, unsigned int cmd, unsigned long arg) -{ - dbg("%s cmd 0x%04x", __FUNCTION__, cmd); + if (diff == 0) { + return -EIO; /* no change => error */ + } - switch (cmd) { + /* Consume all events */ + priv->diff_status = 0; + + /* Return 0 if caller wanted to know about these bits */ + if ( ((arg & TIOCM_RNG) && (diff & FTDI_RS0_RI)) || + ((arg & TIOCM_DSR) && (diff & FTDI_RS0_DSR)) || + ((arg & TIOCM_CD) && (diff & FTDI_RS0_RLSD)) || + ((arg & TIOCM_CTS) && (diff & FTDI_RS0_CTS)) ) { + return 0; + } + /* + * Otherwise caller can't care less about what happened, + * and so we continue to wait for more events. + */ + } + } + return(0); + break; default: - /* This is not an error - turns out the higher layers will do - * some ioctls itself - */ - dbg("%s arg not supported - it was 0x%04x", __FUNCTION__,cmd); - return(-ENOIOCTLCMD); break; + } - return 0; -} /* ftdi_sio_ioctl */ -static int __init ftdi_sio_init (void) + /* This is not necessarily an error - turns out the higher layers will do + * some ioctls itself (see comment above) + */ + dbg("%s arg not supported - it was 0x%04x - check /usr/include/asm/ioctls.h", __FUNCTION__, cmd); + + return(-ENOIOCTLCMD); +} /* ftdi_ioctl */ + + +static void ftdi_throttle (struct usb_serial_port *port) { + dbg("%s - port %d", __FUNCTION__, port->number); + usb_unlink_urb (port->read_urb); +} + + +static void ftdi_unthrottle (struct usb_serial_port *port) +{ + int result; + struct usb_serial *serial = port->serial; + + dbg("%s - port %d", __FUNCTION__, port->number); + + port->read_urb->dev = serial->dev; + + usb_fill_bulk_urb(port->read_urb, serial->dev, + usb_rcvbulkpipe(serial->dev, port->bulk_in_endpointAddress), + port->read_urb->transfer_buffer, port->read_urb->transfer_buffer_length, + ftdi_read_bulk_callback, port); + + result = usb_submit_urb(port->read_urb, GFP_ATOMIC); + if (result) + err("%s - failed submitting read urb, error %d", __FUNCTION__, result); +} + +static int __init ftdi_init (void) +{ + dbg("%s", __FUNCTION__); - usb_serial_register (&ftdi_sio_device); + usb_serial_register (&ftdi_SIO_device); usb_serial_register (&ftdi_8U232AM_device); + usb_serial_register (&ftdi_FT232BM_device); + usb_serial_register (&ftdi_USB_UIRT_device); + usb_serial_register (&ftdi_HE_TIRA1_device); usb_register (&ftdi_driver); + info(DRIVER_VERSION ":" DRIVER_DESC); return 0; } -static void __exit ftdi_sio_exit (void) +static void __exit ftdi_exit (void) { + dbg("%s", __FUNCTION__); + usb_deregister (&ftdi_driver); - usb_serial_deregister (&ftdi_sio_device); + usb_serial_deregister (&ftdi_HE_TIRA1_device); + usb_serial_deregister (&ftdi_USB_UIRT_device); + usb_serial_deregister (&ftdi_FT232BM_device); usb_serial_deregister (&ftdi_8U232AM_device); + usb_serial_deregister (&ftdi_SIO_device); + } -module_init(ftdi_sio_init); -module_exit(ftdi_sio_exit); +module_init(ftdi_init); +module_exit(ftdi_exit); MODULE_AUTHOR( DRIVER_AUTHOR ); MODULE_DESCRIPTION( DRIVER_DESC ); diff -Nru a/drivers/usb/serial/ftdi_sio.h b/drivers/usb/serial/ftdi_sio.h --- a/drivers/usb/serial/ftdi_sio.h Tue Feb 4 05:46:17 2003 +++ b/drivers/usb/serial/ftdi_sio.h Fri Jun 27 04:25:33 2003 @@ -14,11 +14,12 @@ * of the protocol required to talk to the device and ongoing assistence * during development. * - * Bill Ryder - bryder@sgi.com of Silicon Graphics, Inc.- wrote the + * Bill Ryder - bryder@sgi.com formerly of Silicon Graphics, Inc.- wrote the * FTDI_SIO implementation. * * Philipp Gühring - pg@futureware.at - added the Device ID of the USB relais * from Rudolf Gugler + * */ #define FTDI_VID 0x0403 /* Vendor Id */ @@ -28,6 +29,93 @@ #define FTDI_NF_RIC_VID 0x0DCD /* Vendor Id */ #define FTDI_NF_RIC_PID 0x0001 /* Product Id */ + +/* www.crystalfontz.com devices - thanx for providing free devices for evaluation ! */ +/* they use the ftdi chipset for the USB interface and the vendor id is the same */ +#define FTDI_XF_634_PID 0xFC09 /* Four line device */ +#define FTDI_XF_632_PID 0xFC08 /* Two line device */ + +/* Video Networks Limited / Homechoice in the UK use an ftdi-based device for their 1Mb */ +/* broadband internet service. The following PID is exhibited by the usb device supplied */ +/* (the VID is the standard ftdi vid (FTDI_VID) */ +#define FTDI_VNHCPCUSB_D_PID 0xfe38 /* Product Id */ + +/* + * The following are the values for the Matrix Orbital LCD displays, + * which are the FT232BM ( similar to the 8U232AM ) + */ +#define FTDI_MTXORB_VID FTDI_VID /* Matrix Orbital Product Id */ +#define FTDI_MTXORB_0_PID 0xFA00 /* Matrix Orbital Product Id */ +#define FTDI_MTXORB_1_PID 0xFA01 /* Matrix Orbital Product Id */ +#define FTDI_MTXORB_2_PID 0xFA02 /* Matrix Orbital Product Id */ +#define FTDI_MTXORB_3_PID 0xFA03 /* Matrix Orbital Product Id */ +#define FTDI_MTXORB_4_PID 0xFA04 /* Matrix Orbital Product Id */ +#define FTDI_MTXORB_5_PID 0xFA05 /* Matrix Orbital Product Id */ +#define FTDI_MTXORB_6_PID 0xFA06 /* Matrix Orbital Product Id */ + +/* + * The following are the values for the Sealevel SeaLINK+ adapters. + * (Original list sent by Tuan Hoang. Ian Abbott renamed the macros and + * removed some PIDs that don't seem to match any existing products.) + */ +#define SEALEVEL_VID 0x0c52 /* Sealevel Vendor ID */ +#define SEALEVEL_2101_PID 0x2101 /* SeaLINK+232 (2101/2105) */ +#define SEALEVEL_2102_PID 0x2102 /* SeaLINK+485 (2102) */ +#define SEALEVEL_2103_PID 0x2103 /* SeaLINK+232I (2103) */ +#define SEALEVEL_2104_PID 0x2104 /* SeaLINK+485I (2104) */ +#define SEALEVEL_2201_1_PID 0x2211 /* SeaPORT+2/232 (2201) Port 1 */ +#define SEALEVEL_2201_2_PID 0x2221 /* SeaPORT+2/232 (2201) Port 2 */ +#define SEALEVEL_2202_1_PID 0x2212 /* SeaPORT+2/485 (2202) Port 1 */ +#define SEALEVEL_2202_2_PID 0x2222 /* SeaPORT+2/485 (2202) Port 2 */ +#define SEALEVEL_2203_1_PID 0x2213 /* SeaPORT+2 (2203) Port 1 */ +#define SEALEVEL_2203_2_PID 0x2223 /* SeaPORT+2 (2203) Port 2 */ +#define SEALEVEL_2401_1_PID 0x2411 /* SeaPORT+4/232 (2401) Port 1 */ +#define SEALEVEL_2401_2_PID 0x2421 /* SeaPORT+4/232 (2401) Port 2 */ +#define SEALEVEL_2401_3_PID 0x2431 /* SeaPORT+4/232 (2401) Port 3 */ +#define SEALEVEL_2401_4_PID 0x2441 /* SeaPORT+4/232 (2401) Port 4 */ +#define SEALEVEL_2402_1_PID 0x2412 /* SeaPORT+4/485 (2402) Port 1 */ +#define SEALEVEL_2402_2_PID 0x2422 /* SeaPORT+4/485 (2402) Port 2 */ +#define SEALEVEL_2402_3_PID 0x2432 /* SeaPORT+4/485 (2402) Port 3 */ +#define SEALEVEL_2402_4_PID 0x2442 /* SeaPORT+4/485 (2402) Port 4 */ +#define SEALEVEL_2403_1_PID 0x2413 /* SeaPORT+4 (2403) Port 1 */ +#define SEALEVEL_2403_2_PID 0x2423 /* SeaPORT+4 (2403) Port 2 */ +#define SEALEVEL_2403_3_PID 0x2433 /* SeaPORT+4 (2403) Port 3 */ +#define SEALEVEL_2403_4_PID 0x2443 /* SeaPORT+4 (2403) Port 4 */ +#define SEALEVEL_2801_1_PID 0X2811 /* SeaLINK+8/232 (2801) Port 1 */ +#define SEALEVEL_2801_2_PID 0X2821 /* SeaLINK+8/232 (2801) Port 2 */ +#define SEALEVEL_2801_3_PID 0X2831 /* SeaLINK+8/232 (2801) Port 3 */ +#define SEALEVEL_2801_4_PID 0X2841 /* SeaLINK+8/232 (2801) Port 4 */ +#define SEALEVEL_2801_5_PID 0X2851 /* SeaLINK+8/232 (2801) Port 5 */ +#define SEALEVEL_2801_6_PID 0X2861 /* SeaLINK+8/232 (2801) Port 6 */ +#define SEALEVEL_2801_7_PID 0X2871 /* SeaLINK+8/232 (2801) Port 7 */ +#define SEALEVEL_2801_8_PID 0X2881 /* SeaLINK+8/232 (2801) Port 8 */ +#define SEALEVEL_2802_1_PID 0X2812 /* SeaLINK+8/485 (2802) Port 1 */ +#define SEALEVEL_2802_2_PID 0X2822 /* SeaLINK+8/485 (2802) Port 2 */ +#define SEALEVEL_2802_3_PID 0X2832 /* SeaLINK+8/485 (2802) Port 3 */ +#define SEALEVEL_2802_4_PID 0X2842 /* SeaLINK+8/485 (2802) Port 4 */ +#define SEALEVEL_2802_5_PID 0X2852 /* SeaLINK+8/485 (2802) Port 5 */ +#define SEALEVEL_2802_6_PID 0X2862 /* SeaLINK+8/485 (2802) Port 6 */ +#define SEALEVEL_2802_7_PID 0X2872 /* SeaLINK+8/485 (2802) Port 7 */ +#define SEALEVEL_2802_8_PID 0X2882 /* SeaLINK+8/485 (2802) Port 8 */ +#define SEALEVEL_2803_1_PID 0X2813 /* SeaLINK+8 (2803) Port 1 */ +#define SEALEVEL_2803_2_PID 0X2823 /* SeaLINK+8 (2803) Port 2 */ +#define SEALEVEL_2803_3_PID 0X2833 /* SeaLINK+8 (2803) Port 3 */ +#define SEALEVEL_2803_4_PID 0X2843 /* SeaLINK+8 (2803) Port 4 */ +#define SEALEVEL_2803_5_PID 0X2853 /* SeaLINK+8 (2803) Port 5 */ +#define SEALEVEL_2803_6_PID 0X2863 /* SeaLINK+8 (2803) Port 6 */ +#define SEALEVEL_2803_7_PID 0X2873 /* SeaLINK+8 (2803) Port 7 */ +#define SEALEVEL_2803_8_PID 0X2883 /* SeaLINK+8 (2803) Port 8 */ + +/* + * Home Electronics (www.home-electro.com) USB gadgets + */ +#define FTDI_HE_TIRA1_PID 0xFA78 /* Tira-1 IR tranceiver */ + +/* USB-UIRT - An infrared receiver and transmitter using the 8U232AM chip */ +/* http://home.earthlink.net/~jrhees/USBUIRT/index.htm */ +#define FTDI_USB_UIRT_PID 0xF850 /* Product Id */ + +/* Commands */ #define FTDI_SIO_RESET 0 /* Reset the port */ #define FTDI_SIO_MODEM_CTRL 1 /* Set the modem control register */ #define FTDI_SIO_SET_FLOW_CTRL 2 /* Set flow control register */ @@ -84,19 +172,61 @@ /* * BmRequestType: 0100 0000B * bRequest: FTDI_SIO_SET_BAUDRATE - * wValue: BaudRate value - see below + * wValue: BaudDivisor value - see below * wIndex: Port * wLength: 0 * Data: None + * The BaudDivisor values are calculated as follows: + * - BaseClock is either 12000000 or 48000000 depending on the device. FIXME: I wish + * I knew how to detect old chips to select proper base clock! + * - BaudDivisor is a fixed point number encoded in a funny way. + * (--WRONG WAY OF THINKING--) + * BaudDivisor is a fixed point number encoded with following bit weighs: + * (-2)(-1)(13..0). It is a radical with a denominator of 4, so values + * end with 0.0 (00...), 0.25 (10...), 0.5 (01...), and 0.75 (11...). + * (--THE REALITY--) + * The both-bits-set has quite different meaning from 0.75 - the chip designers + * have decided it to mean 0.125 instead of 0.75. + * This info looked up in FTDI application note "FT8U232 DEVICES \ Data Rates + * and Flow Control Consideration for USB to RS232". + * - BaudDivisor = (BaseClock / 16) / BaudRate, where the (=) operation should + * automagically re-encode the resulting value to take fractions into consideration. + * As all values are integers, some bit twiddling is in order: + * BaudDivisor = (BaseClock / 16 / BaudRate) | + * (((BaseClock / 2 / BaudRate) & 4) ? 0x4000 // 0.5 + * : ((BaseClock / 2 / BaudRate) & 2) ? 0x8000 // 0.25 + * : ((BaseClock / 2 / BaudRate) & 1) ? 0xc000 // 0.125 + * : 0) + * + * For the FT232BM, a 17th divisor bit was introduced to encode the multiples + * of 0.125 missing from the FT8U232AM. Bits 16 to 14 are coded as follows + * (the first four codes are the same as for the FT8U232AM, where bit 16 is + * always 0): + * 000 - add .000 to divisor + * 001 - add .500 to divisor + * 010 - add .250 to divisor + * 011 - add .125 to divisor + * 100 - add .375 to divisor + * 101 - add .625 to divisor + * 110 - add .750 to divisor + * 111 - add .875 to divisor + * Bits 15 to 0 of the 17-bit divisor are placed in the urb value. Bit 16 is + * placed in bit 0 of the urb index. + * + * Note that there are a couple of special cases to support the highest baud + * rates. If the calculated divisor value is 1, this needs to be replaced with + * 0. Additionally for the FT232BM, if the calculated divisor value is 0x4001 + * (1.5), this needs to be replaced with 0x0001 (1) (but this divisor value is + * not supported by the FT8U232AM). */ -enum ftdi_type { - sio = 1, - F8U232AM = 2, -}; +typedef enum { + SIO = 1, + FT8U232AM = 2, + FT232BM = 3, +} ftdi_chip_type_t; - -enum { +typedef enum { ftdi_sio_b300 = 0, ftdi_sio_b600 = 1, ftdi_sio_b1200 = 2, @@ -107,39 +237,12 @@ ftdi_sio_b38400 = 7, ftdi_sio_b57600 = 8, ftdi_sio_b115200 = 9 -}; - +} FTDI_SIO_baudrate_t ; -enum { - ftdi_8U232AM_12MHz_b300 = 0x09c4, - ftdi_8U232AM_12MHz_b600 = 0x04E2, - ftdi_8U232AM_12MHz_b1200 = 0x0271, - ftdi_8U232AM_12MHz_b2400 = 0x4138, - ftdi_8U232AM_12MHz_b4800 = 0x809c, - ftdi_8U232AM_12MHz_b9600 = 0xc04e, - ftdi_8U232AM_12MHz_b19200 = 0x0027, - ftdi_8U232AM_12MHz_b38400 = 0x4013, - ftdi_8U232AM_12MHz_b57600 = 0x000d, - ftdi_8U232AM_12MHz_b115200 = 0x4006, - ftdi_8U232AM_12MHz_b230400 = 0x8003, -}; -/* Apparently all devices are 48MHz */ -enum { - ftdi_8U232AM_48MHz_b300 = 0x2710, - ftdi_8U232AM_48MHz_b600 = 0x1388, - ftdi_8U232AM_48MHz_b1200 = 0x09c4, - ftdi_8U232AM_48MHz_b2400 = 0x04e2, - ftdi_8U232AM_48MHz_b4800 = 0x0271, - ftdi_8U232AM_48MHz_b9600 = 0x4138, - ftdi_8U232AM_48MHz_b19200 = 0x809c, - ftdi_8U232AM_48MHz_b38400 = 0xc04e, - ftdi_8U232AM_48MHz_b57600 = 0x0034, - ftdi_8U232AM_48MHz_b115200 = 0x001a, - ftdi_8U232AM_48MHz_b230400 = 0x000d, - ftdi_8U232AM_48MHz_b460800 = 0x4006, - ftdi_8U232AM_48MHz_b921600 = 0x8003, - -}; +/* + * The ftdi_8U232AM_xxMHz_byyy constants have been removed. The encoded divisor values + * are calculated internally. + */ #define FTDI_SIO_SET_DATA_REQUEST FTDI_SIO_SET_DATA #define FTDI_SIO_SET_DATA_REQUEST_TYPE 0x40 @@ -443,6 +546,11 @@ * B7 Error in RCVR FIFO * */ +#define FTDI_RS0_CTS (1 << 4) +#define FTDI_RS0_DSR (1 << 5) +#define FTDI_RS0_RI (1 << 6) +#define FTDI_RS0_RLSD (1 << 7) + #define FTDI_RS_DR 1 #define FTDI_RS_OE (1<<1) #define FTDI_RS_PE (1<<2) diff -Nru a/drivers/usb/storage/datafab.c b/drivers/usb/storage/datafab.c --- a/drivers/usb/storage/datafab.c Sun Jun 1 12:24:07 2003 +++ b/drivers/usb/storage/datafab.c Fri Jul 4 06:23:54 2003 @@ -94,7 +94,7 @@ unsigned char *dest, int use_sg) { - unsigned char command[8] = { 0, 0, 0, 0, 0, 0xE0, 0x20, 0x01 }; + unsigned char *command = us->iobuf; unsigned char *buffer = NULL; unsigned char *ptr; unsigned char thistime; @@ -116,8 +116,6 @@ return rc; } - command[5] += (info->lun << 4); - totallen = sectors * info->ssize; do { @@ -138,10 +136,13 @@ command[3] = (sector >> 8) & 0xFF; command[4] = (sector >> 16) & 0xFF; + command[5] = 0xE0 + (info->lun << 4); command[5] |= (sector >> 24) & 0x0F; + command[6] = 0x20; + command[7] = 0x01; // send the read command - result = datafab_bulk_write(us, command, sizeof(command)); + result = datafab_bulk_write(us, command, 8); if (result != USB_STOR_XFER_GOOD) goto leave; @@ -180,8 +181,8 @@ unsigned char *src, int use_sg) { - unsigned char command[8] = { 0, 0, 0, 0, 0, 0xE0, 0x30, 0x02 }; - unsigned char reply[2] = { 0, 0 }; + unsigned char *command = us->iobuf; + unsigned char *reply = us->iobuf; unsigned char *buffer = NULL; unsigned char *ptr; unsigned char thistime; @@ -202,8 +203,6 @@ return rc; } - command[5] += (info->lun << 4); - // If we're using scatter-gather, we have to create a new // buffer to read all of the data in first, since a // scatter-gather buffer could in theory start in the middle @@ -237,10 +236,13 @@ command[3] = (sector >> 8) & 0xFF; command[4] = (sector >> 16) & 0xFF; + command[5] = 0xE0 + (info->lun << 4); command[5] |= (sector >> 24) & 0x0F; + command[6] = 0x30; + command[7] = 0x02; // send the command - result = datafab_bulk_write(us, command, sizeof(command)); + result = datafab_bulk_write(us, command, 8); if (result != USB_STOR_XFER_GOOD) goto leave; @@ -250,7 +252,7 @@ goto leave; // read the result - result = datafab_bulk_read(us, reply, sizeof(reply)); + result = datafab_bulk_read(us, reply, 2); if (result != USB_STOR_XFER_GOOD) goto leave; @@ -291,13 +293,19 @@ // // There might be a better way of doing this? - unsigned char command[8] = { 0, 1, 0, 0, 0, 0xa0, 0xec, 1 }; - unsigned char buf[512]; + static unsigned char scommand[8] = { 0, 1, 0, 0, 0, 0xa0, 0xec, 1 }; + unsigned char *command = us->iobuf; + unsigned char *buf; int count = 0, rc; if (!us || !info) return USB_STOR_TRANSPORT_ERROR; + memcpy(command, scommand, 8); + buf = kmalloc(512, GFP_NOIO); + if (!buf) + return USB_STOR_TRANSPORT_ERROR; + US_DEBUGP("datafab_determine_lun: locating...\n"); // we'll try 3 times before giving up... @@ -306,31 +314,41 @@ command[5] = 0xa0; rc = datafab_bulk_write(us, command, 8); - if (rc != USB_STOR_XFER_GOOD) - return USB_STOR_TRANSPORT_ERROR; + if (rc != USB_STOR_XFER_GOOD) { + rc = USB_STOR_TRANSPORT_ERROR; + goto leave; + } - rc = datafab_bulk_read(us, buf, sizeof(buf)); + rc = datafab_bulk_read(us, buf, 512); if (rc == USB_STOR_XFER_GOOD) { info->lun = 0; - return USB_STOR_TRANSPORT_GOOD; + rc = USB_STOR_TRANSPORT_GOOD; + goto leave; } command[5] = 0xb0; rc = datafab_bulk_write(us, command, 8); - if (rc != USB_STOR_XFER_GOOD) - return USB_STOR_TRANSPORT_ERROR; + if (rc != USB_STOR_XFER_GOOD) { + rc = USB_STOR_TRANSPORT_ERROR; + goto leave; + } - rc = datafab_bulk_read(us, buf, sizeof(buf)); + rc = datafab_bulk_read(us, buf, 512); if (rc == USB_STOR_XFER_GOOD) { info->lun = 1; - return USB_STOR_TRANSPORT_GOOD; + rc = USB_STOR_TRANSPORT_GOOD; + goto leave; } wait_ms(20); } - return USB_STOR_TRANSPORT_ERROR; + rc = USB_STOR_TRANSPORT_ERROR; + + leave: + kfree(buf); + return rc; } static int datafab_id_device(struct us_data *us, @@ -340,8 +358,9 @@ // to the ATA spec, 'Sector Count' isn't used but the Windows driver // sets this bit so we do too... // - unsigned char command[8] = { 0, 1, 0, 0, 0, 0xa0, 0xec, 1 }; - unsigned char reply[512]; + static unsigned char scommand[8] = { 0, 1, 0, 0, 0, 0xa0, 0xec, 1 }; + unsigned char *command = us->iobuf; + unsigned char *reply; int rc; if (!us || !info) @@ -353,11 +372,18 @@ return rc; } + memcpy(command, scommand, 8); + reply = kmalloc(512, GFP_NOIO); + if (!reply) + return USB_STOR_TRANSPORT_ERROR; + command[5] += (info->lun << 4); rc = datafab_bulk_write(us, command, 8); - if (rc != USB_STOR_XFER_GOOD) - return USB_STOR_TRANSPORT_ERROR; + if (rc != USB_STOR_XFER_GOOD) { + rc = USB_STOR_TRANSPORT_ERROR; + goto leave; + } // we'll go ahead and extract the media capacity while we're here... // @@ -369,10 +395,15 @@ ((u32)(reply[116]) << 16) | ((u32)(reply[115]) << 8) | ((u32)(reply[114]) ); - return USB_STOR_TRANSPORT_GOOD; + rc = USB_STOR_TRANSPORT_GOOD; + goto leave; } - - return USB_STOR_TRANSPORT_ERROR; + + rc = USB_STOR_TRANSPORT_ERROR; + + leave: + kfree(reply); + return rc; } @@ -571,8 +602,7 @@ return USB_STOR_TRANSPORT_ERROR; } - // don't bother implementing READ_6 or WRITE_6. Just set MODE_XLATE and - // let the usb storage code convert to READ_10/WRITE_10 + // don't bother implementing READ_6 or WRITE_6. // if (srb->cmnd[0] == READ_10) { block = ((u32)(srb->cmnd[2]) << 24) | ((u32)(srb->cmnd[3]) << 16) | diff -Nru a/drivers/usb/storage/freecom.c b/drivers/usb/storage/freecom.c --- a/drivers/usb/storage/freecom.c Sun Jun 1 12:09:08 2003 +++ b/drivers/usb/storage/freecom.c Fri Jul 4 06:23:54 2003 @@ -44,11 +44,6 @@ #define ERR_STAT 0x01 #define DRQ_STAT 0x08 -struct freecom_udata { - u8 buffer[64]; /* Common command block. */ -}; -typedef struct freecom_udata *freecom_udata_t; - /* All of the outgoing packets are 64 bytes long. */ struct freecom_cb_wrap { u8 Type; /* Command type. */ @@ -112,9 +107,8 @@ freecom_readdata (Scsi_Cmnd *srb, struct us_data *us, unsigned int ipipe, unsigned int opipe, int count) { - freecom_udata_t extra = (freecom_udata_t) us->extra; struct freecom_xfer_wrap *fxfr = - (struct freecom_xfer_wrap *) extra->buffer; + (struct freecom_xfer_wrap *) us->iobuf; int result; fxfr->Type = FCM_PACKET_INPUT | 0x00; @@ -147,9 +141,8 @@ freecom_writedata (Scsi_Cmnd *srb, struct us_data *us, int unsigned ipipe, unsigned int opipe, int count) { - freecom_udata_t extra = (freecom_udata_t) us->extra; struct freecom_xfer_wrap *fxfr = - (struct freecom_xfer_wrap *) extra->buffer; + (struct freecom_xfer_wrap *) us->iobuf; int result; fxfr->Type = FCM_PACKET_OUTPUT | 0x00; @@ -190,12 +183,9 @@ int result; unsigned int partial; int length; - freecom_udata_t extra; - extra = (freecom_udata_t) us->extra; - - fcb = (struct freecom_cb_wrap *) extra->buffer; - fst = (struct freecom_status *) extra->buffer; + fcb = (struct freecom_cb_wrap *) us->iobuf; + fst = (struct freecom_status *) us->iobuf; US_DEBUGP("Freecom TRANSPORT STARTED\n"); @@ -386,18 +376,11 @@ freecom_init (struct us_data *us) { int result; - char buffer[33]; + char *buffer = us->iobuf; - /* Allocate a buffer for us. The upper usb transport code will - * free this for us when cleaning up. */ - if (us->extra == NULL) { - us->extra = kmalloc (sizeof (struct freecom_udata), - GFP_KERNEL); - if (us->extra == NULL) { - US_DEBUGP("Out of memory\n"); - return USB_STOR_TRANSPORT_ERROR; - } - } + /* The DMA-mapped I/O buffer is 64 bytes long, just right for + * all our packets. No need to allocate any extra buffer space. + */ result = usb_stor_control_msg(us, us->recv_ctrl_pipe, 0x4c, 0xc0, 0x4346, 0x0, buffer, 0x20, 3*HZ); diff -Nru a/drivers/usb/storage/initializers.c b/drivers/usb/storage/initializers.c --- a/drivers/usb/storage/initializers.c Thu Jun 19 21:23:33 2003 +++ b/drivers/usb/storage/initializers.c Fri Jul 4 06:10:48 2003 @@ -47,38 +47,27 @@ * mode */ int usb_stor_euscsi_init(struct us_data *us) { - unsigned char data = 0x1; int result; US_DEBUGP("Attempting to init eUSCSI bridge...\n"); + us->iobuf[0] = 0x1; result = usb_stor_control_msg(us, us->send_ctrl_pipe, 0x0C, USB_RECIP_INTERFACE | USB_TYPE_VENDOR, - 0x01, 0x0, &data, 0x1, 5*HZ); + 0x01, 0x0, us->iobuf, 0x1, 5*HZ); US_DEBUGP("-- result is %d\n", result); - US_DEBUGP("-- data afterwards is %d\n", data); + US_DEBUGP("-- data afterwards is %d\n", us->iobuf[0]); return 0; } /* This function is required to activate all four slots on the UCR-61S2B * flash reader */ - int usb_stor_ucr61s2b_init(struct us_data *us) { - struct bulk_cb_wrap *bcb; - struct bulk_cs_wrap *bcs; + struct bulk_cb_wrap *bcb = (struct bulk_cb_wrap*) us->iobuf; + struct bulk_cs_wrap *bcs = (struct bulk_cs_wrap*) us->iobuf; int res, partial; - bcb = kmalloc(sizeof *bcb, in_interrupt() ? GFP_ATOMIC : GFP_NOIO); - if (!bcb) { - return(-1); - } - bcs = kmalloc(sizeof *bcs, in_interrupt() ? GFP_ATOMIC : GFP_NOIO); - if (!bcs) { - kfree(bcb); - return(-1); - } - US_DEBUGP("Sending UCR-61S2B initialization packet...\n"); bcb->Signature = cpu_to_le32(US_BULK_CB_SIGN); @@ -91,19 +80,12 @@ res = usb_stor_bulk_transfer_buf(us, us->send_bulk_pipe, bcb, US_BULK_CB_WRAP_LEN, &partial); - US_DEBUGP("-- result is %d\n", res); - kfree(bcb); - - if(res) { - kfree(bcs); - return(res); - } + if(res) + return res; + US_DEBUGP("Getting status packet...\n"); res = usb_stor_bulk_transfer_buf(us, us->recv_bulk_pipe, bcs, US_BULK_CS_WRAP_LEN, &partial); - US_DEBUGP("-- result of status read is %d\n", res); - - kfree(bcs); - return(res ? -1 : 0); + return (res ? -1 : 0); } diff -Nru a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h --- a/drivers/usb/storage/unusual_devs.h Tue Jul 1 06:26:58 2003 +++ b/drivers/usb/storage/unusual_devs.h Wed Jul 2 05:27:36 2003 @@ -90,12 +90,6 @@ US_SC_SCSI, US_PR_DPCM_USB, NULL, 0 ), #endif -/* Made with the help of Edd Dumbill */ -UNUSUAL_DEV( 0x0451, 0x5409, 0x0001, 0x0001, - "Frontier Labs", - "Nex II Digital", - US_SC_SCSI, US_PR_BULK, NULL, 0), - /* Patch submitted by Philipp Friedrich */ UNUSUAL_DEV( 0x0482, 0x0100, 0x0100, 0x0100, "Kyocera", @@ -276,7 +270,7 @@ UNUSUAL_DEV( 0x054c, 0x0032, 0x0000, 0x9999, "Sony", "Memorystick MSC-U01N", - US_SC_UFI, US_PR_CB, NULL, + US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_SINGLE_LUN ), UNUSUAL_DEV( 0x054c, 0x0069, 0x0000, 0x9999, @@ -301,7 +295,7 @@ UNUSUAL_DEV( 0x057b, 0x0000, 0x0300, 0x9999, "Y-E Data", "Flashbuster-U", - US_SC_UFI, US_PR_CBI, NULL, + US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_SINGLE_LUN), UNUSUAL_DEV( 0x059f, 0xa601, 0x0200, 0x0200, diff -Nru a/drivers/usb/usb-skeleton.c b/drivers/usb/usb-skeleton.c --- a/drivers/usb/usb-skeleton.c Wed Jun 25 03:10:46 2003 +++ b/drivers/usb/usb-skeleton.c Mon Jul 7 03:13:30 2003 @@ -646,9 +646,6 @@ dev = usb_get_intfdata (interface); usb_set_intfdata (interface, NULL); - if (!dev) - return; - down (&dev->sem); /* disable open() */ diff -Nru a/drivers/video/sis/init301.c b/drivers/video/sis/init301.c --- a/drivers/video/sis/init301.c Fri Mar 7 11:19:01 2003 +++ b/drivers/video/sis/init301.c Fri Jul 11 05:43:45 2003 @@ -5282,7 +5282,7 @@ #ifdef SIS315H /* 310/325 series */ if(SiS_Pr->SiS_IF_DEF_CH70xx != 0) { - temp = temp = SiS_GetCH701x(SiS_Pr,0x61); + temp = SiS_GetCH701x(SiS_Pr,0x61); if(temp < 1) { SiS_SetCH701x(SiS_Pr,0xac76); SiS_SetCH701x(SiS_Pr,0x0066); diff -Nru a/fs/aio.c b/fs/aio.c --- a/fs/aio.c Sun Jul 6 18:51:59 2003 +++ b/fs/aio.c Thu Jul 10 22:23:44 2003 @@ -679,12 +679,11 @@ /* after flagging the request as done, we * must never even look at it again */ - barrier(); + smp_wmb(); /* make event visible before updating tail */ info->tail = tail; ring->tail = tail; - wmb(); put_aio_ring_event(event, KM_IRQ0); kunmap_atomic(ring, KM_IRQ1); @@ -721,7 +720,7 @@ dprintk("in aio_read_evt h%lu t%lu m%lu\n", (unsigned long)ring->head, (unsigned long)ring->tail, (unsigned long)ring->nr); - barrier(); + if (ring->head == ring->tail) goto out; @@ -732,7 +731,7 @@ struct io_event *evp = aio_ring_event(info, head, KM_USER1); *ent = *evp; head = (head + 1) % info->nr; - barrier(); + smp_mb(); /* finish reading the event before updatng the head */ ring->head = head; ret = 1; put_aio_ring_event(evp, KM_USER1); diff -Nru a/fs/attr.c b/fs/attr.c --- a/fs/attr.c Wed Jul 2 21:21:22 2003 +++ b/fs/attr.c Thu Jul 10 22:22:59 2003 @@ -65,7 +65,7 @@ int error = 0; if (ia_valid & ATTR_SIZE) { - if (attr->ia_size != inode->i_size) { + if (attr->ia_size != i_size_read(inode)) { error = vmtruncate(inode, attr->ia_size); if (error || (ia_valid == ATTR_SIZE)) goto out; diff -Nru a/fs/binfmt_aout.c b/fs/binfmt_aout.c --- a/fs/binfmt_aout.c Sat Feb 15 19:30:17 2003 +++ b/fs/binfmt_aout.c Thu Jul 10 22:22:59 2003 @@ -269,7 +269,7 @@ if ((N_MAGIC(ex) != ZMAGIC && N_MAGIC(ex) != OMAGIC && N_MAGIC(ex) != QMAGIC && N_MAGIC(ex) != NMAGIC) || N_TRSIZE(ex) || N_DRSIZE(ex) || - bprm->file->f_dentry->d_inode->i_size < ex.a_text+ex.a_data+N_SYMSIZE(ex)+N_TXTOFF(ex)) { + i_size_read(bprm->file->f_dentry->d_inode) < ex.a_text+ex.a_data+N_SYMSIZE(ex)+N_TXTOFF(ex)) { return -ENOEXEC; } @@ -454,7 +454,7 @@ /* We come in here for the regular a.out style of shared libraries */ if ((N_MAGIC(ex) != ZMAGIC && N_MAGIC(ex) != QMAGIC) || N_TRSIZE(ex) || N_DRSIZE(ex) || ((ex.a_entry & 0xfff) && N_MAGIC(ex) == ZMAGIC) || - inode->i_size < ex.a_text+ex.a_data+N_SYMSIZE(ex)+N_TXTOFF(ex)) { + i_size_read(inode) < ex.a_text+ex.a_data+N_SYMSIZE(ex)+N_TXTOFF(ex)) { goto out; } diff -Nru a/fs/binfmt_elf.c b/fs/binfmt_elf.c --- a/fs/binfmt_elf.c Wed Jun 25 16:30:52 2003 +++ b/fs/binfmt_elf.c Wed Jul 2 09:01:11 2003 @@ -114,7 +114,7 @@ #define STACK_ADD(sp, items) ((elf_addr_t *)(sp) + (items)) #define STACK_ROUND(sp, items) \ ((15 + (unsigned long) ((sp) + (items))) &~ 15UL) -#define STACK_ALLOC(sp, len) ({ elf_addr_t *old_sp = sp; sp += len; old_sp; }) +#define STACK_ALLOC(sp, len) ({ elf_addr_t *old_sp = (elf_addr_t *)sp; sp += len; old_sp; }) #else #define STACK_ADD(sp, items) ((elf_addr_t *)(sp) - (items)) #define STACK_ROUND(sp, items) \ diff -Nru a/fs/block_dev.c b/fs/block_dev.c --- a/fs/block_dev.c Wed Jul 2 21:21:28 2003 +++ b/fs/block_dev.c Thu Jul 10 22:22:59 2003 @@ -29,7 +29,7 @@ static sector_t max_block(struct block_device *bdev) { sector_t retval = ~((sector_t)0); - loff_t sz = bdev->bd_inode->i_size; + loff_t sz = i_size_read(bdev->bd_inode); if (sz) { unsigned int size = block_size(bdev); @@ -161,7 +161,7 @@ bd_inode = file->f_dentry->d_inode->i_bdev->bd_inode; down(&bd_inode->i_sem); - size = bd_inode->i_size; + size = i_size_read(bd_inode); switch (origin) { case 2: @@ -487,7 +487,7 @@ static void bd_set_size(struct block_device *bdev, loff_t size) { unsigned bsize = bdev_hardsect_size(bdev); - bdev->bd_inode->i_size = size; + i_size_write(bdev->bd_inode, size); while (bsize < PAGE_CACHE_SIZE) { if (size & bsize) break; diff -Nru a/fs/buffer.c b/fs/buffer.c --- a/fs/buffer.c Sun Jul 6 18:06:47 2003 +++ b/fs/buffer.c Thu Jul 10 22:22:59 2003 @@ -1721,7 +1721,7 @@ BUG_ON(!PageLocked(page)); - last_block = (inode->i_size - 1) >> inode->i_blkbits; + last_block = (i_size_read(inode) - 1) >> inode->i_blkbits; if (!page_has_buffers(page)) { if (!PageUptodate(page)) @@ -2057,7 +2057,7 @@ head = page_buffers(page); iblock = (sector_t)page->index << (PAGE_CACHE_SHIFT - inode->i_blkbits); - lblock = (inode->i_size+blocksize-1) >> inode->i_blkbits; + lblock = (i_size_read(inode)+blocksize-1) >> inode->i_blkbits; bh = head; nr = 0; i = 0; @@ -2282,8 +2282,12 @@ struct inode *inode = page->mapping->host; loff_t pos = ((loff_t)page->index << PAGE_CACHE_SHIFT) + to; __block_commit_write(inode,page,from,to); + /* + * No need to use i_size_read() here, the i_size + * cannot change under us because we hold i_sem. + */ if (pos > inode->i_size) { - inode->i_size = pos; + i_size_write(inode, pos); mark_inode_dirty(inode); } return 0; @@ -2435,7 +2439,7 @@ set_page_dirty(page); if (pos > inode->i_size) { - inode->i_size = pos; + i_size_write(inode, pos); mark_inode_dirty(inode); } return 0; @@ -2565,7 +2569,8 @@ struct writeback_control *wbc) { struct inode * const inode = page->mapping->host; - const unsigned long end_index = inode->i_size >> PAGE_CACHE_SHIFT; + loff_t i_size = i_size_read(inode); + const unsigned long end_index = i_size >> PAGE_CACHE_SHIFT; unsigned offset; void *kaddr; @@ -2574,7 +2579,7 @@ return __block_write_full_page(inode, page, get_block, wbc); /* Is the page fully outside i_size? (truncate in progress) */ - offset = inode->i_size & (PAGE_CACHE_SIZE-1); + offset = i_size & (PAGE_CACHE_SIZE-1); if (page->index >= end_index+1 || !offset) { /* * The page may have dirty, unmapped buffers. For example, diff -Nru a/fs/cifs/CHANGES b/fs/cifs/CHANGES --- a/fs/cifs/CHANGES Sun Jul 6 22:36:33 2003 +++ b/fs/cifs/CHANGES Fri Jul 11 19:24:02 2003 @@ -1,3 +1,9 @@ +Version 0.82 +------------ +Add support for mknod of block or character devices. Fix oplock +code (distributed caching) to properly send response to oplock +break from server. + Version 0.81 ------------ Finish up CIFS packet digital signing for the default diff -Nru a/fs/cifs/README b/fs/cifs/README --- a/fs/cifs/README Fri Jul 4 16:31:08 2003 +++ b/fs/cifs/README Fri Jul 11 19:24:02 2003 @@ -76,8 +76,9 @@ case sensitive = yes delete readonly = yes Some administrators also change the "map archive" and the "create mask" parameters -from their defaults. For more information on these see the manual pages -("man smb.conf") on the Samba server system. Note that the cifs vfs, unlike the +from their default values. Creating special devices (mknod) remotely may require +specifying a mkdev function to Samba. For more information on these see the manual +pages ("man smb.conf") on the Samba server system. Note that the cifs vfs, unlike the smbfs vfs, does not read the smb.conf on the client system (the few optional settings are passed in on mount via -o parameters instead). Note that Samba 2.2.7 or later includes a fix that allows the CIFS VFS to delete open files (required for strict diff -Nru a/fs/cifs/cifs_debug.c b/fs/cifs/cifs_debug.c --- a/fs/cifs/cifs_debug.c Fri May 16 09:07:44 2003 +++ b/fs/cifs/cifs_debug.c Tue Jul 8 12:58:43 2003 @@ -89,8 +89,8 @@ ses->serverOS, ses->serverNOS, ses->capabilities); buf += length; if(ses->server) - buf += sprintf(buf, "\tLocal Users To Same Server: %d ", - atomic_read(&ses->server->socketUseCount)); + buf += sprintf(buf, "\tLocal Users To Same Server: %d SecMode: 0x%x", + atomic_read(&ses->server->socketUseCount),ses->server->secMode); } read_unlock(&GlobalSMBSeslock); sprintf(buf, "\n"); @@ -590,6 +590,8 @@ sign_CIFS_PDUs = 0; else if (c == '1' || c == 'y' || c == 'Y') sign_CIFS_PDUs = 1; + else if (c == '2') + sign_CIFS_PDUs = 2; return count; } diff -Nru a/fs/cifs/cifsencrypt.c b/fs/cifs/cifsencrypt.c --- a/fs/cifs/cifsencrypt.c Sun Jul 6 18:24:42 2003 +++ b/fs/cifs/cifsencrypt.c Wed Jul 9 23:14:07 2003 @@ -24,6 +24,7 @@ #include "cifsglob.h" #include "cifs_debug.h" #include "md5.h" +#include "cifs_unicode.h" /* Calculate and return the CIFS signature based on the mac key and the smb pdu */ /* the 16 byte signature must be allocated by the caller */ @@ -133,8 +134,68 @@ if ((key == NULL) || (rn == NULL) || (password == NULL)) return -EINVAL; - E_md4hash(password, temp_key); /* BB may have to do another md4 of it */ + E_md4hash(password, temp_key); mdfour(key,temp_key,16); memcpy(key+16,rn, CIFS_SESSION_KEY_SIZE); return 0; -} +} + +int CalcNTLMv2_partial_mac_key(struct cifsSesInfo * ses, struct nls_table * nls_info) +{ + char temp_hash[16]; + struct HMACMD5Context ctx; + char * ucase_buf; + wchar_t * unicode_buf; + unsigned int i,user_name_len,dom_name_len; + + if(ses) + return -EINVAL; + + E_md4hash(ses->password_with_pad, temp_hash); + + hmac_md5_init_limK_to_64(temp_hash, 16, &ctx); + user_name_len = strlen(ses->userName); + if(user_name_len > MAX_USERNAME_SIZE) + return -EINVAL; + dom_name_len = strlen(ses->domainName); + if(dom_name_len > MAX_USERNAME_SIZE) + return -EINVAL; + + + ucase_buf = kmalloc((MAX_USERNAME_SIZE+1), GFP_KERNEL); + unicode_buf = kmalloc((MAX_USERNAME_SIZE+1)*4, GFP_KERNEL); + + for(i=0;icharset2upper[(int)ses->userName[i]]; + ucase_buf[i] = 0; + user_name_len = cifs_strtoUCS(unicode_buf, ucase_buf, MAX_USERNAME_SIZE*2, nls_info); + unicode_buf[user_name_len] = 0; + user_name_len++; + + for(i=0;icharset2upper[(int)ses->domainName[i]]; + ucase_buf[i] = 0; + dom_name_len = cifs_strtoUCS(unicode_buf+user_name_len, ucase_buf, MAX_USERNAME_SIZE*2, nls_info); + + unicode_buf[user_name_len + dom_name_len] = 0; + hmac_md5_update((const unsigned char *) unicode_buf, + (user_name_len+dom_name_len)*2,&ctx); + + hmac_md5_final(ses->mac_signing_key,&ctx); + kfree(ucase_buf); + kfree(unicode_buf); + return 0; +} +void CalcNTLMv2_response(const struct cifsSesInfo * ses,char * v2_session_response) +{ + struct HMACMD5Context context; + memcpy(v2_session_response + 8, ses->server->cryptKey,8); + /* gen_blob(v2_session_response + 16); */ + hmac_md5_init_limK_to_64(ses->mac_signing_key, 16, &context); + + hmac_md5_update(ses->server->cryptKey,8,&context); +/* hmac_md5_update(v2_session_response+16)client thing,8,&context); */ /* BB fix */ + + + hmac_md5_final(v2_session_response,&context); +} diff -Nru a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c --- a/fs/cifs/cifsfs.c Sun Jul 6 22:36:36 2003 +++ b/fs/cifs/cifsfs.c Fri Jul 11 19:24:02 2003 @@ -301,6 +301,7 @@ /* revalidate:cifs_revalidate, */ .setattr = cifs_setattr, .symlink = cifs_symlink, + .mknod = cifs_mknod, }; struct inode_operations cifs_file_inode_ops = { @@ -465,7 +466,14 @@ CIFS_I(pfile->f_dentry->d_inode)->write_behind_rc = rc; cFYI(1,("Oplock flush file %p rc %d",pfile,rc)); - /* send oplock break */ + if(pfile->private_data) { + rc = CIFSSMBLock(0, pTcon, + ((struct cifsFileInfo *) pfile->private_data)->netfid, + 0 /* len */ , 0 /* offset */, 0, + 0, LOCKING_ANDX_OPLOCK_RELEASE, + 0 /* wait flag */); + cFYI(1,("Oplock release rc = %d ",rc)); + } write_lock(&GlobalMid_Lock); } else break; diff -Nru a/fs/cifs/cifsfs.h b/fs/cifs/cifsfs.h --- a/fs/cifs/cifsfs.h Fri Jul 4 16:05:49 2003 +++ b/fs/cifs/cifsfs.h Thu Jul 10 20:09:12 2003 @@ -50,6 +50,7 @@ extern struct dentry *cifs_lookup(struct inode *, struct dentry *, struct nameidata *); extern int cifs_unlink(struct inode *, struct dentry *); extern int cifs_hardlink(struct dentry *, struct inode *, struct dentry *); +extern int cifs_mknod(struct inode *, struct dentry *, int, dev_t); extern int cifs_mkdir(struct inode *, struct dentry *, int); extern int cifs_rmdir(struct inode *, struct dentry *); extern int cifs_rename(struct inode *, struct dentry *, struct inode *, diff -Nru a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h --- a/fs/cifs/cifsproto.h Sun Jul 6 18:24:42 2003 +++ b/fs/cifs/cifsproto.h Thu Jul 10 20:09:12 2003 @@ -88,8 +88,7 @@ const struct nls_table *); extern int CIFSNTLMSSPAuthSessSetup(unsigned int xid, struct cifsSesInfo *ses, char *ntlm_session_key, - char *lanman_session_key,int ntlmv2_flag, - const struct nls_table *); + int ntlmv2_flag, const struct nls_table *); extern int CIFSTCon(unsigned int xid, struct cifsSesInfo *ses, const char *tree, struct cifsTconInfo *tcon, @@ -156,7 +155,7 @@ __u64 size, __u16 fileHandle,__u32 opener_pid, int AllocSizeFlag); extern int CIFSSMBUnixSetPerms(const int xid, struct cifsTconInfo *pTcon, char *full_path, __u64 mode, __u64 uid, - __u64 gid, const struct nls_table *nls_codepage); + __u64 gid, dev_t dev, const struct nls_table *nls_codepage); extern int CIFSSMBMkDir(const int xid, struct cifsTconInfo *tcon, const char *newName, @@ -229,8 +228,8 @@ extern int cifs_verify_signature(const struct smb_hdr *, const char * mac_key, __u32 expected_sequence_number); extern int cifs_calculate_mac_key(char * key,const char * rn,const char * pass); - -/* BB routines below not implemented yet BB */ +extern void CalcNTLMv2_partial_mac_key(struct cifsSesInfo *, struct nls_table *); +extern void CalcNTLMv2_response(const struct cifsSesInfo *,char * ); extern int CIFSBuildServerList(int xid, char *serverBufferList, int recordlength, int *entries, diff -Nru a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c --- a/fs/cifs/cifssmb.c Fri Jul 4 15:07:26 2003 +++ b/fs/cifs/cifssmb.c Fri Jul 11 19:24:02 2003 @@ -618,6 +618,7 @@ LOCK_REQ *pSMB = NULL; LOCK_RSP *pSMBr = NULL; int bytes_returned; + int timeout = 0; cFYI(1, ("In CIFSSMBLock")); @@ -626,6 +627,9 @@ if (rc) return rc; + if(lockType == LOCKING_ANDX_OPLOCK_RELEASE) + timeout = -1; /* no response expected */ + pSMB->NumberOfLocks = cpu_to_le32(numLock); pSMB->NumberOfUnlocks = cpu_to_le32(numUnlock); pSMB->LockType = lockType; @@ -640,7 +644,7 @@ pSMB->ByteCount = cpu_to_le16(pSMB->ByteCount); rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, - (struct smb_hdr *) pSMBr, &bytes_returned, 0); + (struct smb_hdr *) pSMBr, &bytes_returned, timeout); if (rc) { cERROR(1, ("Send error in Lock = %d", rc)); @@ -2299,7 +2303,7 @@ int CIFSSMBUnixSetPerms(const int xid, struct cifsTconInfo *tcon, char *fileName, __u64 mode, __u64 uid, __u64 gid, - const struct nls_table *nls_codepage) + dev_t device, const struct nls_table *nls_codepage) { TRANSACTION2_SPI_REQ *pSMB = NULL; TRANSACTION2_SPI_RSP *pSMBr = NULL; @@ -2358,6 +2362,9 @@ pSMB->hdr.smb_buf_length += pSMB->ByteCount; data_offset->Uid = cpu_to_le64(uid); data_offset->Gid = cpu_to_le64(gid); + /* better to leave device as zero when it is */ + data_offset->DevMajor = cpu_to_le64(MAJOR(device)); + data_offset->DevMinor = cpu_to_le64(MINOR(device)); data_offset->Permissions = cpu_to_le64(mode); pSMB->ByteCount = cpu_to_le16(pSMB->ByteCount); rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, diff -Nru a/fs/cifs/connect.c b/fs/cifs/connect.c --- a/fs/cifs/connect.c Sun Jul 6 18:24:42 2003 +++ b/fs/cifs/connect.c Wed Jul 9 23:14:07 2003 @@ -634,7 +634,6 @@ int setup_session(unsigned int xid, struct cifsSesInfo *pSesInfo, struct nls_table * nls_info) { int rc = 0; - char session_key[CIFS_SESSION_KEY_SIZE]; char ntlm_session_key[CIFS_SESSION_KEY_SIZE]; int ntlmv2_flag = FALSE; @@ -666,35 +665,44 @@ nls_info); if (!rc) { if(ntlmv2_flag) { - cFYI(1,("Able to use the more secure NTLM version 2 password hash")); - /* SMBNTv2encrypt( ...); */ /* BB fix this up - - and note that Samba client equivalent looks wrong */ - } else - SMBNTencrypt(pSesInfo->password_with_pad, - pSesInfo->server->cryptKey,ntlm_session_key); + char * v2_response; + cFYI(1,("Can use more secure NTLM version 2 password hash")); + CalcNTLMv2_partial_mac_key(pSesInfo, + nls_info); + v2_response = kmalloc(16 + 64 /* blob */, GFP_KERNEL); + if(v2_response) { + CalcNTLMv2_response(pSesInfo,v2_response); +/* cifs_calculate_ntlmv2_mac_key(pSesInfo->mac_signing_key, response, ntlm_session_key, */ + kfree(v2_response); + /* BB Put dummy sig in SessSetup PDU? */ + } else + rc = -ENOMEM; - /* BB add call to save MAC key here BB */ + } else { + SMBNTencrypt(pSesInfo->password_with_pad, + pSesInfo->server->cryptKey, + ntlm_session_key); - /* for better security the weaker lanman hash not sent - in AuthSessSetup so why bother calculating it */ - /* toUpper(nls_info, - password_with_pad); - SMBencrypt(password_with_pad, - pSesInfo->server->cryptKey, session_key); */ + cifs_calculate_mac_key(pSesInfo->mac_signing_key, + ntlm_session_key, + pSesInfo->password_with_pad); + } + /* for better security the weaker lanman hash not sent + in AuthSessSetup so we no longer calculate it */ rc = CIFSNTLMSSPAuthSessSetup(xid, - pSesInfo, - ntlm_session_key, - session_key, - ntlmv2_flag, - nls_info); + pSesInfo, + ntlm_session_key, + ntlmv2_flag, + nls_info); } } else { /* old style NTLM 0.12 session setup */ SMBNTencrypt(pSesInfo->password_with_pad, pSesInfo->server->cryptKey, ntlm_session_key); - cifs_calculate_mac_key(pSesInfo->mac_signing_key, ntlm_session_key, pSesInfo->password_with_pad); + cifs_calculate_mac_key(pSesInfo->mac_signing_key, + ntlm_session_key, pSesInfo->password_with_pad); rc = CIFSSessSetup(xid, pSesInfo, ntlm_session_key, nls_info); } @@ -1297,7 +1305,7 @@ int bytes_returned = 0; int len; - cFYI(1, ("In v2 sesssetup ")); + cFYI(1, ("In spnego sesssetup ")); smb_buffer = buf_get(); if (smb_buffer == 0) { @@ -1605,7 +1613,9 @@ SecurityBlob->NegotiateFlags = NTLMSSP_NEGOTIATE_UNICODE | NTLMSSP_NEGOTIATE_OEM | NTLMSSP_REQUEST_TARGET | NTLMSSP_NEGOTIATE_NTLM | 0x80000000 | - NTLMSSP_NEGOTIATE_ALWAYS_SIGN | NTLMSSP_NEGOTIATE_128; + /* NTLMSSP_NEGOTIATE_ALWAYS_SIGN | */ NTLMSSP_NEGOTIATE_128; + if(sign_CIFS_PDUs) + SecurityBlob->NegotiateFlags |= NTLMSSP_NEGOTIATE_SIGN; if(ntlmv2_support) SecurityBlob->NegotiateFlags |= NTLMSSP_NEGOTIATE_NTLMV2; /* setup pointers to domain name and workstation name */ @@ -1725,6 +1735,17 @@ CIFS_CRYPTO_KEY_SIZE); if(SecurityBlob2->NegotiateFlags & NTLMSSP_NEGOTIATE_NTLMV2) *pNTLMv2_flag = TRUE; + + if((SecurityBlob2->NegotiateFlags & + NTLMSSP_NEGOTIATE_ALWAYS_SIGN) + || (sign_CIFS_PDUs > 1)) + ses->server->secMode |= + SECMODE_SIGN_REQUIRED; + if ((SecurityBlob2->NegotiateFlags & + NTLMSSP_NEGOTIATE_SIGN) && (sign_CIFS_PDUs)) + ses->server->secMode |= + SECMODE_SIGN_ENABLED; + if (smb_buffer->Flags2 &= SMBFLG2_UNICODE) { if ((long) (bcc_ptr) % 2) { remaining_words = @@ -1868,7 +1889,7 @@ int CIFSNTLMSSPAuthSessSetup(unsigned int xid, struct cifsSesInfo *ses, - char *ntlm_session_key, char *lanman_session_key, int ntlmv2_flag, + char *ntlm_session_key, int ntlmv2_flag, const struct nls_table *nls_codepage) { struct smb_hdr *smb_buffer; @@ -1934,7 +1955,9 @@ SecurityBlob->NegotiateFlags = NTLMSSP_NEGOTIATE_UNICODE | NTLMSSP_REQUEST_TARGET | NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_NEGOTIATE_TARGET_INFO | - 0x80000000 | NTLMSSP_NEGOTIATE_ALWAYS_SIGN | NTLMSSP_NEGOTIATE_128; + 0x80000000 | NTLMSSP_NEGOTIATE_128; + if(sign_CIFS_PDUs) + SecurityBlob->NegotiateFlags |= /* NTLMSSP_NEGOTIATE_ALWAYS_SIGN |*/ NTLMSSP_NEGOTIATE_SIGN; if(ntlmv2_flag) SecurityBlob->NegotiateFlags |= NTLMSSP_NEGOTIATE_NTLMV2; diff -Nru a/fs/cifs/dir.c b/fs/cifs/dir.c --- a/fs/cifs/dir.c Sun Jun 29 23:49:04 2003 +++ b/fs/cifs/dir.c Thu Jul 10 20:09:12 2003 @@ -23,6 +23,7 @@ #include #include #include +#include #include "cifsfs.h" #include "cifspdu.h" #include "cifsglob.h" @@ -125,6 +126,7 @@ int rc = -ENOENT; int xid; int oplock = REQ_OPLOCK; + int desiredAccess = GENERIC_ALL; __u16 fileHandle; struct cifs_sb_info *cifs_sb; struct cifsTconInfo *pTcon; @@ -138,10 +140,23 @@ full_path = build_path_from_dentry(direntry); + + if(nd) { + cFYI(1,("In create nd flags = 0x%x for %s",nd->flags,full_path)); + cFYI(1,("Intent flags: 0x%x", nd->intent.open.flags)); + if ((nd->intent.open.flags & O_ACCMODE) == O_RDONLY) + desiredAccess = GENERIC_READ; + else if ((nd->intent.open.flags & O_ACCMODE) == O_WRONLY) + desiredAccess = GENERIC_WRITE; + else if ((nd->intent.open.flags & O_ACCMODE) == O_RDWR) + desiredAccess = GENERIC_ALL; + } + + /* BB add processing for setting the equivalent of mode - e.g. via CreateX with ACLs */ - rc = CIFSSMBOpen(xid, pTcon, full_path, FILE_OVERWRITE_IF, GENERIC_ALL - /* 0x20197 was used previously */ , CREATE_NOT_DIR, + rc = CIFSSMBOpen(xid, pTcon, full_path, FILE_OVERWRITE_IF, + desiredAccess, CREATE_NOT_DIR, &fileHandle, &oplock, cifs_sb->local_nls); if (rc) { cFYI(1, ("cifs_create returned 0x%x ", rc)); @@ -178,6 +193,43 @@ return rc; } +int cifs_mknod(struct inode *inode, struct dentry *direntry, int mode, dev_t device_number) +{ + int rc = -EPERM; + int xid; + struct cifs_sb_info *cifs_sb; + struct cifsTconInfo *pTcon; + char *full_path = NULL; + struct inode * newinode = NULL; + + xid = GetXid(); + + cifs_sb = CIFS_SB(inode->i_sb); + pTcon = cifs_sb->tcon; + + full_path = build_path_from_dentry(direntry); + + if (pTcon->ses->capabilities & CAP_UNIX) { + rc = CIFSSMBUnixSetPerms(xid, pTcon, + full_path, mode, current->euid, current->egid, + device_number, cifs_sb->local_nls); + if(!rc) { + rc = cifs_get_inode_info_unix(&newinode, full_path, + inode->i_sb); + direntry->d_op = &cifs_dentry_ops; + if(rc == 0) + d_instantiate(direntry, newinode); + } + } + + if (full_path) + kfree(full_path); + FreeXid(xid); + + return rc; +} + + struct dentry * cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry, struct nameidata *nd) { @@ -208,6 +260,13 @@ } cFYI(1, (" Full path: %s inode = 0x%p", full_path, direntry->d_inode)); + + if(nd) { /* BB remove begin */ + cFYI(1,("In lookup nd flags = 0x%x",nd->flags)); + cFYI(1,("Intent flags: 0x%x", nd->intent.open.flags)); + } +/* BB remove end BB */ + if (pTcon->ses->capabilities & CAP_UNIX) rc = cifs_get_inode_info_unix(&newInode, full_path, parent_dir_inode->i_sb); @@ -268,6 +327,12 @@ int isValid = 1; /* lock_kernel(); *//* surely we do not want to lock the kernel for a whole network round trip which could take seconds */ + + if(nd) { /* BB remove begin */ + cFYI(1,("In d_revalidate nd flags = 0x%x",nd->flags)); + cFYI(1,("Intent flags: 0x%x", nd->intent.open.flags)); + } +/* BB remove end BB */ if (direntry->d_inode) { if (cifs_revalidate(direntry)) { diff -Nru a/fs/cifs/file.c b/fs/cifs/file.c --- a/fs/cifs/file.c Tue Jun 10 17:29:01 2003 +++ b/fs/cifs/file.c Thu Jul 10 20:09:12 2003 @@ -52,12 +52,12 @@ xid = GetXid(); - cFYI(1, (" inode = 0x%p file flags are %x", inode, file->f_flags)); cifs_sb = CIFS_SB(inode->i_sb); pTcon = cifs_sb->tcon; full_path = build_path_from_dentry(file->f_dentry); + cFYI(1, (" inode = 0x%p file flags are %x for %s", inode, file->f_flags,full_path)); if ((file->f_flags & O_ACCMODE) == O_RDONLY) desiredAccess = GENERIC_READ; else if ((file->f_flags & O_ACCMODE) == O_WRONLY) @@ -125,6 +125,7 @@ CIFSSMBUnixSetPerms(xid, pTcon, full_path, inode->i_mode, (__u64)-1, (__u64)-1, + 0 /* dev */, cifs_sb->local_nls); else {/* BB implement via Windows security descriptors */ /* eg CIFSSMBWinSetPerms(xid,pTcon,full_path,mode,-1,-1,local_nls);*/ diff -Nru a/fs/cifs/inode.c b/fs/cifs/inode.c --- a/fs/cifs/inode.c Thu Jun 26 17:47:04 2003 +++ b/fs/cifs/inode.c Thu Jul 10 20:09:12 2003 @@ -397,6 +397,7 @@ CIFSSMBUnixSetPerms(xid, pTcon, full_path, mode, (__u64)-1, (__u64)-1, + 0 /* dev_t */, cifs_sb->local_nls); else { /* BB to be implemented via Windows secrty descriptors*/ /* eg CIFSSMBWinSetPerms(xid,pTcon,full_path,mode,-1,-1,local_nls);*/ @@ -702,7 +703,7 @@ if ((cifs_sb->tcon->ses->capabilities & CAP_UNIX) && (attrs->ia_valid & (ATTR_MODE | ATTR_GID | ATTR_UID))) rc = CIFSSMBUnixSetPerms(xid, pTcon, full_path, mode, uid, gid, - cifs_sb->local_nls); + 0 /* dev_t */, cifs_sb->local_nls); else if (attrs->ia_valid & ATTR_MODE) { if((mode & S_IWUGO) == 0) /* not writeable */ { if((cifsInode->cifsAttrs & ATTR_READONLY) == 0) diff -Nru a/fs/cifs/misc.c b/fs/cifs/misc.c --- a/fs/cifs/misc.c Tue May 27 17:41:17 2003 +++ b/fs/cifs/misc.c Fri Jul 11 19:24:02 2003 @@ -338,13 +338,8 @@ netfile = list_entry(tmp1,struct cifsFileInfo,tlist); if(pSMB->Fid == netfile->netfid) { struct cifsInodeInfo *pCifsInode; - /* BB Add following logic: - 2) look up inode from tcon->openFileList->file->f_dentry->d_inode - 3) flush dirty pages and cached byte range locks and mark inode - 4) depending on break type change to r/o caching or no caching - cifsinode->clientCanCacheAll = 0 - 5) inode->i_data.a_ops = &cifs_addr_ops_writethrough; - 6) send oplock break response to server */ + /* BB Add following logic to mark inode for write through + inode->i_data.a_ops = &cifs_addr_ops_writethrough; */ read_unlock(&GlobalSMBSeslock); cFYI(1,("Matching file id, processing oplock break")); pCifsInode = @@ -354,7 +349,7 @@ pCifsInode->clientCanCacheRead = FALSE; pCifsInode->oplockPending = TRUE; AllocOplockQEntry(netfile->pfile, tcon); - cFYI(1,("about to wake up oplock thd")); + cFYI(1,("about to wake up oplock thd")); wake_up_process(oplockThread); return TRUE; } diff -Nru a/fs/cifs/smbencrypt.c b/fs/cifs/smbencrypt.c --- a/fs/cifs/smbencrypt.c Fri Jul 4 15:07:26 2003 +++ b/fs/cifs/smbencrypt.c Mon Jul 7 17:27:52 2003 @@ -470,58 +470,3 @@ return TRUE; } - - -/*********************************************************** - SMB signing - setup the MAC key. -************************************************************/ - -void -cli_calculate_mac_key(__u8 * mac_key, int *pmac_key_len, - const char *ntpasswd, const unsigned char resp[24]) -{ - /* Get first 16 bytes. */ - E_md4hash(ntpasswd, mac_key); - memcpy(mac_key + 16, resp, 24); - *pmac_key_len = 40; - - /* Reset the sequence number in case we had a previous (aborted) attempt */ -/* cli->sign_info.send_seq_num = 0; */ -} - -/*********************************************************** - SMB signing - calculate a MAC to send. -************************************************************/ - -void -cli_caclulate_sign_mac(struct smb_hdr *outbuf, __u8 * mac_key, - int mac_key_len, __u32 * send_seq_num, - __u32 * reply_seq_num) -{ - unsigned char calc_md5_mac[16]; - struct MD5Context md5_ctx; - -/* if (!cli->sign_info.use_smb_signing) { - return; - } */ - - /* - * Firstly put the sequence number into the first 4 bytes. - * and zero out the next 4 bytes. - */ -/* - SIVAL(outbuf, smb_ss_field, *send_seq_num); - SIVAL(outbuf, smb_ss_field + 4, 0); */ - - /* Calculate the 16 byte MAC and place first 8 bytes into the field. */ - MD5Init(&md5_ctx); - MD5Update(&md5_ctx, mac_key, mac_key_len); - MD5Update(&md5_ctx, outbuf->Protocol, - be32_to_cpu(outbuf->smb_buf_length)); - MD5Final(calc_md5_mac, &md5_ctx); - - memcpy(outbuf->Signature.SecuritySignature, calc_md5_mac, 8); - (*send_seq_num)++; - *reply_seq_num = *send_seq_num; - (*send_seq_num)++; -} diff -Nru a/fs/cifs/transport.c b/fs/cifs/transport.c --- a/fs/cifs/transport.c Sun Jul 6 18:24:42 2003 +++ b/fs/cifs/transport.c Fri Jul 11 19:24:02 2003 @@ -199,13 +199,15 @@ if (in_buf->smb_buf_length > 12) in_buf->Flags2 = cpu_to_le16(in_buf->Flags2); - + rc = cifs_sign_smb(in_buf, ses, &midQ->sequence_number); midQ->midState = MID_REQUEST_SUBMITTED; rc = smb_send(ses->server->ssocket, in_buf, in_buf->smb_buf_length, (struct sockaddr *) &(ses->server->sockAddr)); + if (long_op == -1) + goto cifs_no_response_exit; if (long_op > 1) /* writes past end of file can take looooong time */ timeout = 300 * HZ; else if (long_op == 1) @@ -283,7 +285,7 @@ } else rc = -EIO; } - +cifs_no_response_exit: DeleteMidQEntry(midQ); /* BB what if process is killed? - BB add background daemon to clean up Mid entries from killed processes & test killing process with active mid */ diff -Nru a/fs/dcache.c b/fs/dcache.c --- a/fs/dcache.c Wed Jun 25 16:30:53 2003 +++ b/fs/dcache.c Thu Jul 10 22:23:45 2003 @@ -1451,19 +1451,24 @@ int is_subdir(struct dentry * new_dentry, struct dentry * old_dentry) { int result; + unsigned long seq; result = 0; - for (;;) { - if (new_dentry != old_dentry) { - struct dentry * parent = new_dentry->d_parent; - if (parent == new_dentry) - break; - new_dentry = parent; - continue; + do { + seq = read_seqbegin(&rename_lock); + for (;;) { + if (new_dentry != old_dentry) { + struct dentry * parent = new_dentry->d_parent; + if (parent == new_dentry) + break; + new_dentry = parent; + continue; + } + result = 1; + break; } - result = 1; - break; - } + } while (read_seqretry(&rename_lock, seq)); + return result; } diff -Nru a/fs/devfs/base.c b/fs/devfs/base.c --- a/fs/devfs/base.c Thu Jul 3 06:36:44 2003 +++ b/fs/devfs/base.c Thu Jul 10 22:24:00 2003 @@ -2208,8 +2208,46 @@ { devfs_handle_t de; wait_queue_head_t wait_queue; + atomic_t count; }; +static struct devfs_lookup_struct * +new_devfs_lookup_struct(void) +{ + struct devfs_lookup_struct *p = kmalloc(sizeof(*p), GFP_KERNEL); + + if (!p) + return NULL; + + init_waitqueue_head (&p->wait_queue); + atomic_set(&p->count, 1); + return p; +} + +static void +get_devfs_lookup_struct(struct devfs_lookup_struct *info) +{ + if (info) + atomic_inc(&info->count); + else { + printk(KERN_ERR "null devfs_lookup_struct pointer\n"); + dump_stack(); + } +} + +static void +put_devfs_lookup_struct(struct devfs_lookup_struct *info) +{ + if (info) { + if (!atomic_dec_and_test(&info->count)) + return; + kfree(info); + } else { + printk(KERN_ERR "null devfs_lookup_struct pointer\n"); + dump_stack(); + } +} + /* XXX: this doesn't handle the case where we got a negative dentry but a devfs entry has been registered in the meanwhile */ static int devfs_d_revalidate_wait (struct dentry *dentry, struct nameidata *nd) @@ -2252,11 +2290,13 @@ read_lock (&parent->u.dir.lock); if (dentry->d_fsdata) { + get_devfs_lookup_struct(lookup_info); set_current_state (TASK_UNINTERRUPTIBLE); add_wait_queue (&lookup_info->wait_queue, &wait); read_unlock (&parent->u.dir.lock); schedule (); remove_wait_queue (&lookup_info->wait_queue, &wait); + put_devfs_lookup_struct(lookup_info); } else read_unlock (&parent->u.dir.lock); return 1; @@ -2268,7 +2308,7 @@ static struct dentry *devfs_lookup (struct inode *dir, struct dentry *dentry, struct nameidata *nd) { struct devfs_entry tmp; /* Must stay in scope until devfsd idle again */ - struct devfs_lookup_struct lookup_info; + struct devfs_lookup_struct *lookup_info; struct fs_info *fs_info = dir->i_sb->s_fs_info; struct devfs_entry *parent, *de; struct inode *inode; @@ -2285,9 +2325,10 @@ read_lock (&parent->u.dir.lock); de = _devfs_search_dir (parent, dentry->d_name.name, dentry->d_name.len); read_unlock (&parent->u.dir.lock); - lookup_info.de = de; - init_waitqueue_head (&lookup_info.wait_queue); - dentry->d_fsdata = &lookup_info; + lookup_info = new_devfs_lookup_struct(); + if (!lookup_info) return ERR_PTR(-ENOMEM); + lookup_info->de = de; + dentry->d_fsdata = lookup_info; if (de == NULL) { /* Try with devfsd. For any kind of failure, leave a negative dentry so someone else can deal with it (in the case where the sysadmin @@ -2297,6 +2338,7 @@ if (try_modload (parent, fs_info, dentry->d_name.name, dentry->d_name.len, &tmp) < 0) { /* Lookup event was not queued to devfsd */ + put_devfs_lookup_struct(lookup_info); d_add (dentry, NULL); return NULL; } @@ -2308,8 +2350,7 @@ revalidation */ up (&dir->i_sem); wait_for_devfsd_finished (fs_info); /* If I'm not devfsd, must wait */ - down (&dir->i_sem); /* Grab it again because them's the rules */ - de = lookup_info.de; + de = lookup_info->de; /* If someone else has been so kind as to make the inode, we go home early */ if (dentry->d_inode) goto out; @@ -2333,11 +2374,13 @@ de->name, de->inode.ino, inode, de, current->comm); d_instantiate (dentry, inode); out: + write_lock (&parent->u.dir.lock); dentry->d_op = &devfs_dops; dentry->d_fsdata = NULL; - write_lock (&parent->u.dir.lock); - wake_up (&lookup_info.wait_queue); + wake_up (&lookup_info->wait_queue); + put_devfs_lookup_struct(lookup_info); write_unlock (&parent->u.dir.lock); + down (&dir->i_sem); /* Grab it again because them's the rules */ devfs_put (de); return retval; } /* End Function devfs_lookup */ diff -Nru a/fs/direct-io.c b/fs/direct-io.c --- a/fs/direct-io.c Tue Feb 25 09:50:46 2003 +++ b/fs/direct-io.c Thu Jul 10 22:22:59 2003 @@ -757,7 +757,7 @@ char *kaddr; if (dio->block_in_file >= - dio->inode->i_size>>blkbits) { + i_size_read(dio->inode)>>blkbits) { /* We hit eof */ page_cache_release(page); goto out; @@ -943,13 +943,15 @@ if (ret == 0) ret = dio->page_errors; if (ret == 0 && dio->result) { + loff_t i_size = i_size_read(inode); + ret = dio->result; /* * Adjust the return value if the read crossed a * non-block-aligned EOF. */ - if (rw == READ && (offset + ret > inode->i_size)) - ret = inode->i_size - offset; + if (rw == READ && (offset + ret > i_size)) + ret = i_size - offset; } kfree(dio); } diff -Nru a/fs/eventpoll.c b/fs/eventpoll.c --- a/fs/eventpoll.c Fri Jul 4 23:52:56 2003 +++ b/fs/eventpoll.c Thu Jul 10 22:24:01 2003 @@ -245,6 +245,9 @@ /* The "container" of this item */ struct eventpoll *ep; + /* The file descriptor this item refers to */ + int fd; + /* The file this item refers to */ struct file *file; @@ -285,15 +288,17 @@ static int ep_alloc_pages(char **pages, int numpages); static int ep_free_pages(char **pages, int numpages); static int ep_file_init(struct file *file, unsigned int hashbits); -static unsigned int ep_hash_index(struct eventpoll *ep, struct file *file); +static unsigned int ep_hash_index(struct eventpoll *ep, struct file *file, int fd); static struct list_head *ep_hash_entry(struct eventpoll *ep, unsigned int index); static int ep_init(struct eventpoll *ep, unsigned int hashbits); static void ep_free(struct eventpoll *ep); -static struct epitem *ep_find(struct eventpoll *ep, struct file *file); +static struct epitem *ep_find(struct eventpoll *ep, struct file *file, int fd); static void ep_use_epitem(struct epitem *epi); static void ep_release_epitem(struct epitem *epi); -static void ep_ptable_queue_proc(struct file *file, wait_queue_head_t *whead, poll_table *pt); -static int ep_insert(struct eventpoll *ep, struct epoll_event *event, struct file *tfile); +static void ep_ptable_queue_proc(struct file *file, wait_queue_head_t *whead, + poll_table *pt); +static int ep_insert(struct eventpoll *ep, struct epoll_event *event, + struct file *tfile, int fd); static int ep_modify(struct eventpoll *ep, struct epitem *epi, struct epoll_event *event); static void ep_unregister_pollwait(struct eventpoll *ep, struct epitem *epi); static int ep_unlink(struct eventpoll *ep, struct epitem *epi); @@ -580,7 +585,7 @@ down_write(&ep->sem); /* Try to lookup the file inside our hash table */ - epi = ep_find(ep, tfile); + epi = ep_find(ep, tfile, fd); error = -EINVAL; switch (op) { @@ -588,7 +593,7 @@ if (!epi) { epds.events |= POLLERR | POLLHUP; - error = ep_insert(ep, &epds, tfile); + error = ep_insert(ep, &epds, tfile, fd); } else error = -EEXIST; break; @@ -814,10 +819,11 @@ /* * Calculate the index of the hash relative to "file". */ -static unsigned int ep_hash_index(struct eventpoll *ep, struct file *file) +static unsigned int ep_hash_index(struct eventpoll *ep, struct file *file, int fd) { + unsigned long ptr = (unsigned long) file ^ (fd << ep->hashbits); - return (unsigned int) hash_ptr(file, ep->hashbits); + return (unsigned int) hash_ptr((void *) ptr, ep->hashbits); } @@ -920,7 +926,7 @@ * the returned item, so the caller must call ep_release_epitem() * after finished using the "struct epitem". */ -static struct epitem *ep_find(struct eventpoll *ep, struct file *file) +static struct epitem *ep_find(struct eventpoll *ep, struct file *file, int fd) { unsigned long flags; struct list_head *lsthead, *lnk; @@ -928,11 +934,11 @@ read_lock_irqsave(&ep->lock, flags); - lsthead = ep_hash_entry(ep, ep_hash_index(ep, file)); + lsthead = ep_hash_entry(ep, ep_hash_index(ep, file, fd)); list_for_each(lnk, lsthead) { epi = list_entry(lnk, struct epitem, llink); - if (epi->file == file) { + if (epi->file == file && epi->fd == fd) { ep_use_epitem(epi); break; } @@ -976,7 +982,8 @@ * This is the callback that is used to add our wait queue to the * target file wakeup lists. */ -static void ep_ptable_queue_proc(struct file *file, wait_queue_head_t *whead, poll_table *pt) +static void ep_ptable_queue_proc(struct file *file, wait_queue_head_t *whead, + poll_table *pt) { struct epitem *epi = EP_ITEM_FROM_EPQUEUE(pt); struct eppoll_entry *pwq; @@ -995,7 +1002,8 @@ } -static int ep_insert(struct eventpoll *ep, struct epoll_event *event, struct file *tfile) +static int ep_insert(struct eventpoll *ep, struct epoll_event *event, + struct file *tfile, int fd) { int error, revents, pwake = 0; unsigned long flags; @@ -1014,6 +1022,7 @@ INIT_LIST_HEAD(&epi->pwqlist); epi->ep = ep; epi->file = tfile; + epi->fd = fd; epi->event = *event; atomic_set(&epi->usecnt, 1); epi->nwait = 0; @@ -1046,7 +1055,7 @@ write_lock_irqsave(&ep->lock, flags); /* Add the current item to the hash table */ - list_add(&epi->llink, ep_hash_entry(ep, ep_hash_index(ep, tfile))); + list_add(&epi->llink, ep_hash_entry(ep, ep_hash_index(ep, tfile, fd))); /* If the file is already "ready" we drop it inside the ready list */ if ((revents & event->events) && !EP_IS_LINKED(&epi->rdllink)) { @@ -1065,8 +1074,8 @@ if (pwake) ep_poll_safewake(&psw, &ep->poll_wait); - DNPRINTK(3, (KERN_INFO "[%p] eventpoll: ep_insert(%p, %p)\n", - current, ep, tfile)); + DNPRINTK(3, (KERN_INFO "[%p] eventpoll: ep_insert(%p, %p, %d)\n", + current, ep, tfile, fd)); return 0; diff -Nru a/fs/exec.c b/fs/exec.c --- a/fs/exec.c Tue Jul 8 12:15:23 2003 +++ b/fs/exec.c Thu Jul 10 22:23:01 2003 @@ -923,12 +923,12 @@ if (offset != PAGE_SIZE) continue; offset = 0; - kunmap(page); + kunmap_atomic(kaddr, KM_USER0); inside: page = bprm->page[bprm->p/PAGE_SIZE]; - kaddr = kmap(page); + kaddr = kmap_atomic(page, KM_USER0); } - kunmap(page); + kunmap_atomic(kaddr, KM_USER0); bprm->argc--; } } diff -Nru a/fs/ext2/ialloc.c b/fs/ext2/ialloc.c --- a/fs/ext2/ialloc.c Wed Jul 2 21:22:40 2003 +++ b/fs/ext2/ialloc.c Thu Jul 10 22:23:36 2003 @@ -235,10 +235,7 @@ EXT2_INODE_SIZE(inode->i_sb); block = le32_to_cpu(gdp->bg_inode_table) + (offset >> EXT2_BLOCK_SIZE_BITS(inode->i_sb)); - bh = sb_getblk(inode->i_sb, block); - if (!buffer_uptodate(bh) && !buffer_locked(bh)) - ll_rw_block(READA, 1, &bh); - __brelse(bh); + sb_breadahead(inode->i_sb, block); } /* diff -Nru a/fs/ext3/inode.c b/fs/ext3/inode.c --- a/fs/ext3/inode.c Thu Jul 3 19:20:48 2003 +++ b/fs/ext3/inode.c Thu Jul 10 22:22:59 2003 @@ -1200,7 +1200,7 @@ if (!partial) SetPageUptodate(page); if (pos > inode->i_size) - inode->i_size = pos; + i_size_write(inode, pos); EXT3_I(inode)->i_state |= EXT3_STATE_JDATA; if (inode->i_size > EXT3_I(inode)->i_disksize) { EXT3_I(inode)->i_disksize = inode->i_size; @@ -1574,7 +1574,7 @@ loff_t end = offset + ret; if (end > inode->i_size) { ei->i_disksize = end; - inode->i_size = end; + i_size_write(inode, end); err = ext3_mark_inode_dirty(handle, inode); if (!ret) ret = err; diff -Nru a/fs/inode.c b/fs/inode.c --- a/fs/inode.c Sat Jun 14 16:16:09 2003 +++ b/fs/inode.c Thu Jul 10 22:22:59 2003 @@ -189,6 +189,7 @@ INIT_LIST_HEAD(&inode->i_data.i_mmap); INIT_LIST_HEAD(&inode->i_data.i_mmap_shared); spin_lock_init(&inode->i_lock); + i_size_ordered_init(inode); } static void init_once(void * foo, kmem_cache_t * cachep, unsigned long flags) diff -Nru a/fs/ioctl.c b/fs/ioctl.c --- a/fs/ioctl.c Tue May 13 13:42:27 2003 +++ b/fs/ioctl.c Thu Jul 10 22:22:59 2003 @@ -40,7 +40,7 @@ return -EBADF; return put_user(inode->i_sb->s_blocksize, (int *) arg); case FIONREAD: - return put_user(inode->i_size - filp->f_pos, (int *) arg); + return put_user(i_size_read(inode) - filp->f_pos, (int *) arg); } if (filp->f_op && filp->f_op->ioctl) return filp->f_op->ioctl(inode, filp, cmd, arg); diff -Nru a/fs/jbd/checkpoint.c b/fs/jbd/checkpoint.c --- a/fs/jbd/checkpoint.c Fri Jun 20 13:16:31 2003 +++ b/fs/jbd/checkpoint.c Thu Jul 10 22:23:54 2003 @@ -75,11 +75,12 @@ * Called under j-state_lock *only*. It will be unlocked if we have to wait * for a checkpoint to free up some space in the log. */ - -void __log_wait_for_space(journal_t *journal, int nblocks) +void __log_wait_for_space(journal_t *journal) { + int nblocks; assert_spin_locked(&journal->j_state_lock); + nblocks = jbd_space_needed(journal); while (__log_space_left(journal) < nblocks) { if (journal->j_flags & JFS_ABORT) return; @@ -91,9 +92,10 @@ * were waiting for the checkpoint lock */ spin_lock(&journal->j_state_lock); + nblocks = jbd_space_needed(journal); if (__log_space_left(journal) < nblocks) { spin_unlock(&journal->j_state_lock); - log_do_checkpoint(journal, nblocks); + log_do_checkpoint(journal); spin_lock(&journal->j_state_lock); } up(&journal->j_checkpoint_sem); @@ -279,9 +281,7 @@ * * The journal should be locked before calling this function. */ - -/* @@@ `nblocks' is unused. Should it be used? */ -int log_do_checkpoint(journal_t *journal, int nblocks) +int log_do_checkpoint(journal_t *journal) { int result; int batch_count = 0; @@ -315,7 +315,7 @@ int cleanup_ret, retry = 0; tid_t this_tid; - transaction = journal->j_checkpoint_transactions->t_cpnext; + transaction = journal->j_checkpoint_transactions; this_tid = transaction->t_tid; jh = transaction->t_checkpoint_list; last_jh = jh->b_cpprev; @@ -332,27 +332,19 @@ retry = 1; break; } - retry = __flush_buffer(journal, jh, bhs, &batch_count, - &drop_count); + retry = __flush_buffer(journal, jh, bhs, &batch_count, &drop_count); } while (jh != last_jh && !retry); - if (batch_count) { + + if (batch_count) __flush_batch(journal, bhs, &batch_count); - continue; - } - if (retry) - continue; /* - * If someone emptied the checkpoint list while we slept, we're - * done. - */ - if (!journal->j_checkpoint_transactions) - break; - /* * If someone cleaned up this transaction while we slept, we're * done */ - if (journal->j_checkpoint_transactions->t_cpnext != transaction) + if (journal->j_checkpoint_transactions != transaction) + break; + if (retry) continue; /* * Maybe it's a new transaction, but it fell at the same @@ -367,6 +359,8 @@ */ cleanup_ret = __cleanup_transaction(journal, transaction); J_ASSERT(drop_count != 0 || cleanup_ret != 0); + if (journal->j_checkpoint_transactions != transaction) + break; } spin_unlock(&journal->j_list_lock); result = cleanup_journal_tail(journal); diff -Nru a/fs/jbd/commit.c b/fs/jbd/commit.c --- a/fs/jbd/commit.c Wed Jul 2 21:23:09 2003 +++ b/fs/jbd/commit.c Thu Jul 10 22:23:55 2003 @@ -169,23 +169,10 @@ * that multiple journal_get_write_access() calls to the same * buffer are perfectly permissable. */ - { - int nr = 0; - while (commit_transaction->t_reserved_list) { - jh = commit_transaction->t_reserved_list; - JBUFFER_TRACE(jh, "reserved, unused: refile"); - journal_refile_buffer(journal, jh); - nr++; - } - if (nr) { - static int noisy; - - if (noisy < 10) { - noisy++; - printk("%s: freed %d reserved buffers\n", - __FUNCTION__, nr); - } - } + while (commit_transaction->t_reserved_list) { + jh = commit_transaction->t_reserved_list; + JBUFFER_TRACE(jh, "reserved, unused: refile"); + journal_refile_buffer(journal, jh); } /* @@ -413,6 +400,13 @@ __journal_abort_hard(journal); continue; } + + /* + * start_this_handle() uses t_outstanding_credits to determine + * the free space in the log, but this counter is changed + * by journal_next_log_block() also. + */ + commit_transaction->t_outstanding_credits--; /* Bump b_count to prevent truncate from stumbling over the shadowed buffer! @@@ This can go if we ever get diff -Nru a/fs/jbd/journal.c b/fs/jbd/journal.c --- a/fs/jbd/journal.c Fri Jun 20 13:16:31 2003 +++ b/fs/jbd/journal.c Thu Jul 10 22:23:56 2003 @@ -484,6 +484,13 @@ ret = __log_start_commit(journal, tid); if (ret && ptid) *ptid = tid; + } else if (journal->j_committing_transaction && ptid) { + /* + * If ext3_write_super() recently started a commit, then we + * have to wait for completion of that transaction + */ + *ptid = journal->j_committing_transaction->t_tid; + ret = 1; } spin_unlock(&journal->j_state_lock); return ret; @@ -1076,7 +1083,7 @@ spin_lock(&journal->j_list_lock); while (journal->j_checkpoint_transactions != NULL) { spin_unlock(&journal->j_list_lock); - log_do_checkpoint(journal, 1); + log_do_checkpoint(journal); spin_lock(&journal->j_list_lock); } @@ -1284,7 +1291,7 @@ spin_lock(&journal->j_list_lock); while (!err && journal->j_checkpoint_transactions != NULL) { spin_unlock(&journal->j_list_lock); - err = log_do_checkpoint(journal, journal->j_maxlen); + err = log_do_checkpoint(journal); spin_lock(&journal->j_list_lock); } spin_unlock(&journal->j_list_lock); diff -Nru a/fs/jbd/transaction.c b/fs/jbd/transaction.c --- a/fs/jbd/transaction.c Wed Jul 2 21:23:09 2003 +++ b/fs/jbd/transaction.c Thu Jul 10 22:23:54 2003 @@ -206,15 +206,10 @@ * Also, this test is inconsitent with the matching one in * journal_extend(). */ - needed = journal->j_max_transaction_buffers; - if (journal->j_committing_transaction) - needed += journal->j_committing_transaction-> - t_outstanding_credits; - - if (__log_space_left(journal) < needed) { + if (__log_space_left(journal) < jbd_space_needed(journal)) { jbd_debug(2, "Handle %p waiting for checkpoint...\n", handle); spin_unlock(&transaction->t_handle_lock); - __log_wait_for_space(journal, needed); + __log_wait_for_space(journal); goto repeat_locked; } diff -Nru a/fs/libfs.c b/fs/libfs.c --- a/fs/libfs.c Thu Jul 3 06:36:44 2003 +++ b/fs/libfs.c Thu Jul 10 22:22:59 2003 @@ -328,8 +328,12 @@ struct inode *inode = page->mapping->host; loff_t pos = ((loff_t)page->index << PAGE_CACHE_SHIFT) + to; + /* + * No need to use i_size_read() here, the i_size + * cannot change under us because we hold the i_sem. + */ if (pos > inode->i_size) - inode->i_size = pos; + i_size_write(inode, pos); set_page_dirty(page); return 0; } diff -Nru a/fs/locks.c b/fs/locks.c --- a/fs/locks.c Tue May 13 13:42:28 2003 +++ b/fs/locks.c Thu Jul 10 22:22:59 2003 @@ -285,7 +285,7 @@ start = filp->f_pos; break; case 2: /*SEEK_END*/ - start = filp->f_dentry->d_inode->i_size; + start = i_size_read(filp->f_dentry->d_inode); break; default: return -EINVAL; @@ -335,7 +335,7 @@ start = filp->f_pos; break; case 2: /*SEEK_END*/ - start = filp->f_dentry->d_inode->i_size; + start = i_size_read(filp->f_dentry->d_inode); break; default: return -EINVAL; diff -Nru a/fs/mpage.c b/fs/mpage.c --- a/fs/mpage.c Tue Apr 8 03:16:30 2003 +++ b/fs/mpage.c Thu Jul 10 22:23:36 2003 @@ -227,7 +227,7 @@ goto confused; block_in_file = page->index << (PAGE_CACHE_SHIFT - blkbits); - last_block = (inode->i_size + blocksize - 1) >> blkbits; + last_block = (i_size_read(inode) + blocksize - 1) >> blkbits; bh.b_page = page; for (page_block = 0; page_block < blocks_per_page; @@ -459,7 +459,7 @@ */ BUG_ON(!PageUptodate(page)); block_in_file = page->index << (PAGE_CACHE_SHIFT - blkbits); - last_block = (inode->i_size - 1) >> blkbits; + last_block = (i_size_read(inode) - 1) >> blkbits; map_bh.b_page = page; for (page_block = 0; page_block < blocks_per_page; ) { @@ -489,9 +489,9 @@ first_unmapped = page_block; - end_index = inode->i_size >> PAGE_CACHE_SHIFT; + end_index = i_size_read(inode) >> PAGE_CACHE_SHIFT; if (page->index >= end_index) { - unsigned offset = inode->i_size & (PAGE_CACHE_SIZE - 1); + unsigned offset = i_size_read(inode) & (PAGE_CACHE_SIZE - 1); char *kaddr; if (page->index > end_index || !offset) @@ -614,7 +614,6 @@ sector_t last_block_in_bio = 0; int ret = 0; int done = 0; - struct pagevec pvec; int (*writepage)(struct page *page, struct writeback_control *wbc); if (wbc->nonblocking && bdi_write_congested(bdi)) { @@ -626,7 +625,6 @@ if (get_block == NULL) writepage = mapping->a_ops->writepage; - pagevec_init(&pvec, 0); spin_lock(&mapping->page_lock); while (!list_empty(&mapping->io_pages) && !done) { struct page *page = list_entry(mapping->io_pages.prev, diff -Nru a/fs/namei.c b/fs/namei.c --- a/fs/namei.c Mon Jul 7 12:25:00 2003 +++ b/fs/namei.c Thu Jul 10 22:23:45 2003 @@ -434,19 +434,17 @@ return 1; } +/* no need for dcache_lock, as serialization is taken care in + * namespace.c + */ static int follow_mount(struct vfsmount **mnt, struct dentry **dentry) { int res = 0; while (d_mountpoint(*dentry)) { - struct vfsmount *mounted; - spin_lock(&dcache_lock); - mounted = lookup_mnt(*mnt, *dentry); - if (!mounted) { - spin_unlock(&dcache_lock); + struct vfsmount *mounted = lookup_mnt(*mnt, *dentry); + if (!mounted) break; - } - *mnt = mntget(mounted); - spin_unlock(&dcache_lock); + *mnt = mounted; dput(*dentry); mntput(mounted->mnt_parent); *dentry = dget(mounted->mnt_root); @@ -455,21 +453,21 @@ return res; } +/* no need for dcache_lock, as serialization is taken care in + * namespace.c + */ static inline int __follow_down(struct vfsmount **mnt, struct dentry **dentry) { struct vfsmount *mounted; - spin_lock(&dcache_lock); mounted = lookup_mnt(*mnt, *dentry); if (mounted) { - *mnt = mntget(mounted); - spin_unlock(&dcache_lock); + *mnt = mounted; dput(*dentry); mntput(mounted->mnt_parent); *dentry = dget(mounted->mnt_root); return 1; } - spin_unlock(&dcache_lock); return 0; } diff -Nru a/fs/namespace.c b/fs/namespace.c --- a/fs/namespace.c Sun Jun 29 23:49:25 2003 +++ b/fs/namespace.c Thu Jul 10 22:23:45 2003 @@ -26,6 +26,8 @@ extern int __init init_rootfs(void); extern int __init sysfs_init(void); +/* spinlock for vfsmount related operations, inplace of dcache_lock */ +spinlock_t vfsmount_lock __cacheline_aligned_in_smp = SPIN_LOCK_UNLOCKED; static struct list_head *mount_hashtable; static int hash_mask, hash_bits; static kmem_cache_t *mnt_cache; @@ -66,30 +68,38 @@ kmem_cache_free(mnt_cache, mnt); } +/* + * Now, lookup_mnt increments the ref count before returning + * the vfsmount struct. + */ struct vfsmount *lookup_mnt(struct vfsmount *mnt, struct dentry *dentry) { struct list_head * head = mount_hashtable + hash(mnt, dentry); struct list_head * tmp = head; - struct vfsmount *p; + struct vfsmount *p, *found = NULL; + spin_lock(&vfsmount_lock); for (;;) { tmp = tmp->next; p = NULL; if (tmp == head) break; p = list_entry(tmp, struct vfsmount, mnt_hash); - if (p->mnt_parent == mnt && p->mnt_mountpoint == dentry) + if (p->mnt_parent == mnt && p->mnt_mountpoint == dentry) { + found = mntget(p); break; + } } - return p; + spin_unlock(&vfsmount_lock); + return found; } static int check_mnt(struct vfsmount *mnt) { - spin_lock(&dcache_lock); + spin_lock(&vfsmount_lock); while (mnt->mnt_parent != mnt) mnt = mnt->mnt_parent; - spin_unlock(&dcache_lock); + spin_unlock(&vfsmount_lock); return mnt == current->namespace->root; } @@ -263,15 +273,15 @@ mnt = list_entry(kill.next, struct vfsmount, mnt_list); list_del_init(&mnt->mnt_list); if (mnt->mnt_parent == mnt) { - spin_unlock(&dcache_lock); + spin_unlock(&vfsmount_lock); } else { struct nameidata old_nd; detach_mnt(mnt, &old_nd); - spin_unlock(&dcache_lock); + spin_unlock(&vfsmount_lock); path_release(&old_nd); } mntput(mnt); - spin_lock(&dcache_lock); + spin_lock(&vfsmount_lock); } } @@ -324,17 +334,17 @@ } down_write(¤t->namespace->sem); - spin_lock(&dcache_lock); + spin_lock(&vfsmount_lock); if (atomic_read(&sb->s_active) == 1) { /* last instance - try to be smart */ - spin_unlock(&dcache_lock); + spin_unlock(&vfsmount_lock); lock_kernel(); DQUOT_OFF(sb); acct_auto_close(sb); unlock_kernel(); security_sb_umount_close(mnt); - spin_lock(&dcache_lock); + spin_lock(&vfsmount_lock); } retval = -EBUSY; if (atomic_read(&mnt->mnt_count) == 2 || flags & MNT_DETACH) { @@ -342,7 +352,7 @@ umount_tree(mnt); retval = 0; } - spin_unlock(&dcache_lock); + spin_unlock(&vfsmount_lock); if (retval) security_sb_umount_busy(mnt); up_write(¤t->namespace->sem); @@ -449,18 +459,18 @@ q = clone_mnt(p, p->mnt_root); if (!q) goto Enomem; - spin_lock(&dcache_lock); + spin_lock(&vfsmount_lock); list_add_tail(&q->mnt_list, &res->mnt_list); attach_mnt(q, &nd); - spin_unlock(&dcache_lock); + spin_unlock(&vfsmount_lock); } } return res; Enomem: if (res) { - spin_lock(&dcache_lock); + spin_lock(&vfsmount_lock); umount_tree(res); - spin_unlock(&dcache_lock); + spin_unlock(&vfsmount_lock); } return NULL; } @@ -485,7 +495,7 @@ goto out_unlock; err = -ENOENT; - spin_lock(&dcache_lock); + spin_lock(&vfsmount_lock); if (IS_ROOT(nd->dentry) || !d_unhashed(nd->dentry)) { struct list_head head; @@ -495,7 +505,7 @@ mntget(mnt); err = 0; } - spin_unlock(&dcache_lock); + spin_unlock(&vfsmount_lock); out_unlock: up(&nd->dentry->d_inode->i_sem); if (!err) @@ -532,9 +542,9 @@ if (mnt) { err = graft_tree(mnt, nd); if (err) { - spin_lock(&dcache_lock); + spin_lock(&vfsmount_lock); umount_tree(mnt); - spin_unlock(&dcache_lock); + spin_unlock(&vfsmount_lock); } else mntput(mnt); } @@ -599,7 +609,7 @@ if (IS_DEADDIR(nd->dentry->d_inode)) goto out1; - spin_lock(&dcache_lock); + spin_lock(&vfsmount_lock); if (!IS_ROOT(nd->dentry) && d_unhashed(nd->dentry)) goto out2; @@ -623,7 +633,7 @@ detach_mnt(old_nd.mnt, &parent_nd); attach_mnt(old_nd.mnt, nd); out2: - spin_unlock(&dcache_lock); + spin_unlock(&vfsmount_lock); out1: up(&nd->dentry->d_inode->i_sem); out: @@ -804,9 +814,9 @@ down_write(&tsk->namespace->sem); /* First pass: copy the tree topology */ new_ns->root = copy_tree(namespace->root, namespace->root->mnt_root); - spin_lock(&dcache_lock); + spin_lock(&vfsmount_lock); list_add_tail(&new_ns->list, &new_ns->root->mnt_list); - spin_unlock(&dcache_lock); + spin_unlock(&vfsmount_lock); /* Second pass: switch the tsk->fs->* elements */ if (fs) { @@ -1027,7 +1037,7 @@ if (new_nd.mnt->mnt_root != new_nd.dentry) goto out2; /* not a mountpoint */ tmp = old_nd.mnt; /* make sure we can reach put_old from new_root */ - spin_lock(&dcache_lock); + spin_lock(&vfsmount_lock); if (tmp != new_nd.mnt) { for (;;) { if (tmp->mnt_parent == tmp) @@ -1044,7 +1054,7 @@ detach_mnt(user_nd.mnt, &root_parent); attach_mnt(user_nd.mnt, &old_nd); attach_mnt(new_nd.mnt, &root_parent); - spin_unlock(&dcache_lock); + spin_unlock(&vfsmount_lock); chroot_fs_refs(&user_nd, &new_nd); security_sb_post_pivotroot(&user_nd, &new_nd); error = 0; @@ -1061,7 +1071,7 @@ unlock_kernel(); return error; out3: - spin_unlock(&dcache_lock); + spin_unlock(&vfsmount_lock); goto out2; } diff -Nru a/fs/nfs/inode.c b/fs/nfs/inode.c --- a/fs/nfs/inode.c Fri Jun 20 13:16:06 2003 +++ b/fs/nfs/inode.c Thu Jul 10 22:22:59 2003 @@ -1001,6 +1001,7 @@ loff_t new_isize; int invalid = 0; int mtime_update = 0; + loff_t cur_isize; dfprintk(VFS, "NFS: refresh_inode(%s/%ld ct=%d info=0x%x)\n", inode->i_sb->s_id, inode->i_ino, @@ -1087,8 +1088,9 @@ * If we have pending writebacks, things can get * messy. */ - if (nfs_have_writebacks(inode) && new_isize < inode->i_size) - new_isize = inode->i_size; + cur_isize = i_size_read(inode); + if (nfs_have_writebacks(inode) && new_isize < cur_isize) + new_isize = cur_isize; nfsi->read_cache_ctime = fattr->ctime; inode->i_ctime = fattr->ctime; @@ -1102,7 +1104,7 @@ } nfsi->read_cache_isize = new_size; - inode->i_size = new_isize; + i_size_write(inode, new_isize); if (inode->i_mode != fattr->mode || inode->i_uid != fattr->uid || diff -Nru a/fs/nfs/write.c b/fs/nfs/write.c --- a/fs/nfs/write.c Wed May 7 03:31:18 2003 +++ b/fs/nfs/write.c Thu Jul 10 22:22:59 2003 @@ -180,8 +180,8 @@ * If we've extended the file, update the inode * now so we don't invalidate the cache. */ - if (base > inode->i_size) - inode->i_size = base; + if (base > i_size_read(inode)) + i_size_write(inode, base); } while (count); if (PageError(page)) @@ -211,8 +211,8 @@ nfs_unlock_request(req); nfs_strategy(inode); end = ((loff_t)page->index<i_size < end) - inode->i_size = end; + if (i_size_read(inode) < end) + i_size_write(inode, end); out: return status; @@ -227,9 +227,10 @@ struct inode *inode = page->mapping->host; unsigned long end_index; unsigned offset = PAGE_CACHE_SIZE; + loff_t i_size = i_size_read(inode); int err; - end_index = inode->i_size >> PAGE_CACHE_SHIFT; + end_index = i_size >> PAGE_CACHE_SHIFT; /* Ensure we've flushed out any previous writes */ nfs_wb_page(inode,page); @@ -238,7 +239,7 @@ if (page->index < end_index) goto do_it; /* things got complicated... */ - offset = inode->i_size & (PAGE_CACHE_SIZE-1); + offset = i_size & (PAGE_CACHE_SIZE-1); /* OK, are we completely out? */ err = -EIO; @@ -701,8 +702,8 @@ status = 0; end = ((loff_t)page->index<i_size < end) - inode->i_size = end; + if (i_size_read(inode) < end) + i_size_write(inode, end); /* If we wrote past the end of the page. * Call the strategy routine so it can send out a bunch @@ -716,7 +717,7 @@ nfs_unlock_request(req); done: dprintk("NFS: nfs_updatepage returns %d (isize %Ld)\n", - status, (long long)inode->i_size); + status, (long long)i_size_read(inode)); if (status < 0) ClearPageUptodate(page); return status; @@ -951,7 +952,7 @@ end = req_offset(last) + last->wb_bytes; len = end - start; /* If 'len' is not a 32-bit quantity, pass '0' in the COMMIT call */ - if (end >= inode->i_size || len < 0 || len > (~((u32)0) >> 1)) + if (end >= i_size_read(inode) || len < 0 || len > (~((u32)0) >> 1)) len = 0; data->inode = inode; diff -Nru a/fs/nfsd/nfssvc.c b/fs/nfsd/nfssvc.c --- a/fs/nfsd/nfssvc.c Mon Jun 23 22:50:30 2003 +++ b/fs/nfsd/nfssvc.c Thu Jul 10 22:23:39 2003 @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -168,6 +169,7 @@ nfsd(struct svc_rqst *rqstp) { struct svc_serv *serv = rqstp->rq_server; + struct fs_struct *fsp; int err; struct nfsd_list me; sigset_t shutdown_mask, allowed_mask; @@ -178,6 +180,18 @@ daemonize("nfsd"); current->rlim[RLIMIT_FSIZE].rlim_cur = RLIM_INFINITY; + /* After daemonize() this kernel thread shares current->fs + * with the init process. We need to create files with a + * umask of 0 instead of init's umask. */ + fsp = copy_fs_struct(current->fs); + if (!fsp) { + printk("Unable to start nfsd thread: out of memory\n"); + goto out; + } + exit_fs(current); + current->fs = fsp; + current->fs->umask = 0; + siginitsetinv(&shutdown_mask, SHUTDOWN_SIGS); siginitsetinv(&allowed_mask, ALLOWED_SIGS); @@ -262,6 +276,7 @@ list_del(&me.list); nfsdstats.th_cnt --; +out: /* Release the thread */ svc_exit_thread(rqstp); diff -Nru a/fs/open.c b/fs/open.c --- a/fs/open.c Sun Jun 29 23:49:26 2003 +++ b/fs/open.c Thu Jul 10 22:22:59 2003 @@ -1008,7 +1008,7 @@ */ int generic_file_open(struct inode * inode, struct file * filp) { - if (!(filp->f_flags & O_LARGEFILE) && inode->i_size > MAX_NON_LFS) + if (!(filp->f_flags & O_LARGEFILE) && i_size_read(inode) > MAX_NON_LFS) return -EFBIG; return 0; } diff -Nru a/fs/proc/base.c b/fs/proc/base.c --- a/fs/proc/base.c Sun Jul 6 18:06:49 2003 +++ b/fs/proc/base.c Thu Jul 10 22:23:45 2003 @@ -307,20 +307,22 @@ base = dget(current->fs->root); read_unlock(¤t->fs->lock); - spin_lock(&dcache_lock); + spin_lock(&vfsmount_lock); de = root; mnt = vfsmnt; while (vfsmnt != our_vfsmnt) { - if (vfsmnt == vfsmnt->mnt_parent) + if (vfsmnt == vfsmnt->mnt_parent) { + spin_unlock(&vfsmount_lock); goto out; + } de = vfsmnt->mnt_mountpoint; vfsmnt = vfsmnt->mnt_parent; } + spin_unlock(&vfsmount_lock); if (!is_subdir(de, base)) goto out; - spin_unlock(&dcache_lock); exit: dput(base); @@ -329,7 +331,6 @@ mntput(mnt); return res; out: - spin_unlock(&dcache_lock); res = -EACCES; goto exit; } diff -Nru a/fs/proc/generic.c b/fs/proc/generic.c --- a/fs/proc/generic.c Thu Jul 3 06:36:44 2003 +++ b/fs/proc/generic.c Thu Jul 10 22:22:53 2003 @@ -566,22 +566,6 @@ return ent; } -struct proc_dir_entry *proc_mknod(const char *name, mode_t mode, - struct proc_dir_entry *parent, kdev_t rdev) -{ - struct proc_dir_entry *ent; - - ent = proc_create(&parent,name,mode,1); - if (ent) { - ent->rdev = rdev; - if (proc_register(parent, ent) < 0) { - kfree(ent); - ent = NULL; - } - } - return ent; -} - struct proc_dir_entry *proc_mkdir(const char *name, struct proc_dir_entry *parent) { struct proc_dir_entry *ent; diff -Nru a/fs/proc/inode.c b/fs/proc/inode.c --- a/fs/proc/inode.c Tue May 27 13:07:01 2003 +++ b/fs/proc/inode.c Thu Jul 10 22:22:53 2003 @@ -204,8 +204,6 @@ inode->i_op = de->proc_iops; if (de->proc_fops) inode->i_fop = de->proc_fops; - else if (S_ISBLK(de->mode)||S_ISCHR(de->mode)||S_ISFIFO(de->mode)) - init_special_inode(inode,de->mode,kdev_t_to_nr(de->rdev)); } out: diff -Nru a/fs/proc/root.c b/fs/proc/root.c --- a/fs/proc/root.c Thu Jul 3 06:36:44 2003 +++ b/fs/proc/root.c Thu Jul 10 22:22:53 2003 @@ -153,7 +153,6 @@ EXPORT_SYMBOL(proc_sys_root); #endif EXPORT_SYMBOL(proc_symlink); -EXPORT_SYMBOL(proc_mknod); EXPORT_SYMBOL(proc_mkdir); EXPORT_SYMBOL(create_proc_entry); EXPORT_SYMBOL(remove_proc_entry); diff -Nru a/fs/quota_v1.c b/fs/quota_v1.c --- a/fs/quota_v1.c Sun Mar 30 14:25:12 2003 +++ b/fs/quota_v1.c Thu Jul 10 22:22:59 2003 @@ -132,12 +132,14 @@ mm_segment_t fs; ssize_t size; loff_t offset = 0; + loff_t isize; static const uint quota_magics[] = V2_INITQMAGICS; - if (!inode->i_size) + isize = i_size_read(inode); + if (!isize) return 0; - blocks = inode->i_size >> BLOCK_SIZE_BITS; - off = inode->i_size & (BLOCK_SIZE - 1); + blocks = isize >> BLOCK_SIZE_BITS; + off = isize & (BLOCK_SIZE - 1); if ((blocks % sizeof(struct v1_disk_dqblk) * BLOCK_SIZE + off) % sizeof(struct v1_disk_dqblk)) return 0; /* Doublecheck whether we didn't get file with new format - with old quotactl() this could happen */ diff -Nru a/fs/read_write.c b/fs/read_write.c --- a/fs/read_write.c Wed May 7 21:20:23 2003 +++ b/fs/read_write.c Thu Jul 10 22:22:59 2003 @@ -55,7 +55,7 @@ lock_kernel(); switch (origin) { case 2: - offset += file->f_dentry->d_inode->i_size; + offset += i_size_read(file->f_dentry->d_inode); break; case 1: offset += file->f_pos; @@ -84,7 +84,7 @@ lock_kernel(); switch (origin) { case 2: - offset += file->f_dentry->d_inode->i_size; + offset += i_size_read(file->f_dentry->d_inode); break; case 1: offset += file->f_pos; diff -Nru a/fs/reiserfs/bitmap.c b/fs/reiserfs/bitmap.c --- a/fs/reiserfs/bitmap.c Sat May 17 14:09:36 2003 +++ b/fs/reiserfs/bitmap.c Thu Jul 10 22:22:55 2003 @@ -43,7 +43,7 @@ test_bit(_ALLOC_ ## optname , &SB_ALLOC_OPTS(s)) static inline void get_bit_address (struct super_block * s, - unsigned long block, int * bmap_nr, int * offset) + b_blocknr_t block, int * bmap_nr, int * offset) { /* It is in the bitmap block number equal to the block * number divided by the number of bits in a block. */ @@ -54,7 +54,7 @@ } #ifdef CONFIG_REISERFS_CHECK -int is_reusable (struct super_block * s, unsigned long block, int bit_value) +int is_reusable (struct super_block * s, b_blocknr_t block, int bit_value) { int i, j; @@ -107,7 +107,7 @@ static inline int is_block_in_journal (struct super_block * s, int bmap, int off, int *next) { - unsigned long tmp; + b_blocknr_t tmp; if (reiserfs_in_journal (s, bmap, off, 1, &tmp)) { if (tmp) { /* hint supplied */ @@ -235,7 +235,7 @@ /* Tries to find contiguous zero bit window (given size) in given region of * bitmap and place new blocks there. Returns number of allocated blocks. */ static int scan_bitmap (struct reiserfs_transaction_handle *th, - unsigned long *start, unsigned long finish, + b_blocknr_t *start, b_blocknr_t finish, int min, int max, int unfm, unsigned long file_block) { int nr_allocated=0; @@ -281,7 +281,7 @@ } static void _reiserfs_free_block (struct reiserfs_transaction_handle *th, - unsigned long block) + b_blocknr_t block) { struct super_block * s = th->t_super; struct reiserfs_super_block * rs; @@ -327,7 +327,7 @@ } void reiserfs_free_block (struct reiserfs_transaction_handle *th, - unsigned long block) + b_blocknr_t block) { struct super_block * s = th->t_super; @@ -340,7 +340,7 @@ /* preallocated blocks don't need to be run through journal_mark_freed */ void reiserfs_free_prealloc_block (struct reiserfs_transaction_handle *th, - unsigned long block) { + b_blocknr_t block) { RFALSE(!th->t_super, "vs-4060: trying to free block on nonexistent device"); RFALSE(is_reusable (th->t_super, block, 1) == 0, "vs-4070: can not free such block"); _reiserfs_free_block(th, block) ; @@ -589,15 +589,15 @@ static inline int old_hashed_relocation (reiserfs_blocknr_hint_t * hint) { - unsigned long border; - unsigned long hash_in; + b_blocknr_t border; + u32 hash_in; if (hint->formatted_node || hint->inode == NULL) { return 0; } hash_in = le32_to_cpu((INODE_PKEY(hint->inode))->k_dir_id); - border = hint->beg + (unsigned long) keyed_hash(((char *) (&hash_in)), 4) % (hint->end - hint->beg - 1); + border = hint->beg + (u32) keyed_hash(((char *) (&hash_in)), 4) % (hint->end - hint->beg - 1); if (border > hint->search_start) hint->search_start = border; @@ -606,7 +606,7 @@ static inline int old_way (reiserfs_blocknr_hint_t * hint) { - unsigned long border; + b_blocknr_t border; if (hint->formatted_node || hint->inode == NULL) { return 0; @@ -622,7 +622,7 @@ static inline void hundredth_slices (reiserfs_blocknr_hint_t * hint) { struct key * key = &hint->key; - unsigned long slice_start; + b_blocknr_t slice_start; slice_start = (keyed_hash((char*)(&key->k_dir_id),4) % 100) * (hint->end / 100); if ( slice_start > hint->search_start || slice_start + (hint->end / 100) <= hint->search_start) { @@ -910,7 +910,7 @@ int reiserfs_can_fit_pages ( struct super_block *sb /* superblock of filesystem to estimate space */ ) { - unsigned long space; + b_blocknr_t space; spin_lock(&REISERFS_SB(sb)->bitmap_lock); space = (SB_FREE_BLOCKS(sb) - REISERFS_SB(sb)->reserved_blocks) >> ( PAGE_CACHE_SHIFT - sb->s_blocksize_bits); diff -Nru a/fs/reiserfs/do_balan.c b/fs/reiserfs/do_balan.c --- a/fs/reiserfs/do_balan.c Sat May 17 14:09:35 2003 +++ b/fs/reiserfs/do_balan.c Thu Jul 10 22:22:55 2003 @@ -1250,12 +1250,12 @@ static void free_thrown(struct tree_balance *tb) { int i ; - unsigned long blocknr ; + b_blocknr_t blocknr ; for (i = 0; i < sizeof (tb->thrown)/sizeof (tb->thrown[0]); i++) { if (tb->thrown[i]) { blocknr = tb->thrown[i]->b_blocknr ; if (buffer_dirty (tb->thrown[i])) - printk ("free_thrown deals with dirty buffer %ld\n", blocknr); + printk ("free_thrown deals with dirty buffer %d\n", blocknr); brelse(tb->thrown[i]) ; /* incremented in store_thrown */ reiserfs_free_block (tb->transaction_handle, blocknr); } @@ -1339,7 +1339,7 @@ #ifdef CONFIG_REISERFS_CHECK -int is_reusable (struct super_block * s, unsigned long block, int bit_value); +int is_reusable (struct super_block * s, b_blocknr_t block, int bit_value); static void check_internal_node (struct super_block * s, struct buffer_head * bh, char * mes) { struct disk_child * dc; diff -Nru a/fs/reiserfs/fix_node.c b/fs/reiserfs/fix_node.c --- a/fs/reiserfs/fix_node.c Mon Sep 2 23:28:56 2002 +++ b/fs/reiserfs/fix_node.c Thu Jul 10 22:22:55 2003 @@ -758,7 +758,7 @@ ) { struct buffer_head * p_s_new_bh, * p_s_Sh = PATH_H_PBUFFER (p_s_tb->tb_path, n_h); - unsigned long * p_n_blocknr, + b_blocknr_t * p_n_blocknr, a_n_blocknrs[MAX_AMOUNT_NEEDED] = {0, }; int n_counter, n_number_of_freeblk, @@ -879,7 +879,7 @@ ) { struct buffer_head * p_s_father, * left; struct super_block * p_s_sb = p_s_tb->tb_sb; - unsigned long n_left_neighbor_blocknr; + b_blocknr_t n_left_neighbor_blocknr; int n_left_neighbor_position; if ( ! p_s_tb->FL[n_h] ) /* Father of the left neighbor does not exist. */ @@ -2501,7 +2501,7 @@ /* deal with list of allocated (used and unused) nodes */ for ( i = 0; i < MAX_FEB_SIZE; i++ ) { if ( tb->FEB[i] ) { - unsigned long blocknr = tb->FEB[i]->b_blocknr ; + b_blocknr_t blocknr = tb->FEB[i]->b_blocknr ; /* de-allocated block which was not used by balancing and bforget about buffer for it */ brelse (tb->FEB[i]); diff -Nru a/fs/reiserfs/inode.c b/fs/reiserfs/inode.c --- a/fs/reiserfs/inode.c Wed Jun 4 00:50:34 2003 +++ b/fs/reiserfs/inode.c Thu Jul 10 22:22:55 2003 @@ -506,7 +506,7 @@ struct buffer_head * bh_result, int create) { int repeat, retval; - b_blocknr_t allocated_block_nr = 0;// b_blocknr_t is unsigned long + b_blocknr_t allocated_block_nr = 0;// b_blocknr_t is (unsigned) 32 bit int INITIALIZE_PATH(path); int pos_in_item; struct cpu_key key; diff -Nru a/fs/reiserfs/journal.c b/fs/reiserfs/journal.c --- a/fs/reiserfs/journal.c Wed Jun 4 00:50:34 2003 +++ b/fs/reiserfs/journal.c Thu Jul 10 22:22:55 2003 @@ -507,7 +507,7 @@ */ int reiserfs_in_journal(struct super_block *p_s_sb, int bmap_nr, int bit_nr, int search_all, - unsigned long *next_zero_bit) { + b_blocknr_t *next_zero_bit) { struct reiserfs_journal_cnode *cn ; struct reiserfs_list_bitmap *jb ; int i ; @@ -761,7 +761,7 @@ */ static struct reiserfs_journal_list *find_newer_jl_for_cn(struct reiserfs_journal_cnode *cn) { struct super_block *sb = cn->sb; - unsigned long blocknr = cn->blocknr ; + b_blocknr_t blocknr = cn->blocknr ; cn = cn->hprev ; while(cn) { @@ -791,7 +791,7 @@ while(cn) { if (cn->blocknr != 0) { if (debug) { - printk("block %lu, bh is %d, state %ld\n", cn->blocknr, cn->bh ? 1: 0, + printk("block %u, bh is %d, state %ld\n", cn->blocknr, cn->bh ? 1: 0, cn->state) ; } cn->state = 0 ; @@ -1105,7 +1105,7 @@ { struct reiserfs_journal_list *pjl ; /* previous list for this cn */ struct reiserfs_journal_cnode *cn, *walk_cn ; - unsigned long blocknr ; + b_blocknr_t blocknr ; int run = 0 ; int orig_trans_id = jl->j_trans_id ; struct buffer_head *saved_bh ; @@ -2421,7 +2421,7 @@ ** ** returns 1 if it cleaned and relsed the buffer. 0 otherwise */ -static int remove_from_transaction(struct super_block *p_s_sb, unsigned long blocknr, int already_cleaned) { +static int remove_from_transaction(struct super_block *p_s_sb, b_blocknr_t blocknr, int already_cleaned) { struct buffer_head *bh ; struct reiserfs_journal_cnode *cn ; int ret = 0; @@ -2474,7 +2474,7 @@ */ static int can_dirty(struct reiserfs_journal_cnode *cn) { struct super_block *sb = cn->sb; - unsigned long blocknr = cn->blocknr ; + b_blocknr_t blocknr = cn->blocknr ; struct reiserfs_journal_cnode *cur = cn->hprev ; int can_dirty = 1 ; @@ -2710,7 +2710,7 @@ ** ** Then remove it from the current transaction, decrementing any counters and filing it on the clean list. */ -int journal_mark_freed(struct reiserfs_transaction_handle *th, struct super_block *p_s_sb, unsigned long blocknr) { +int journal_mark_freed(struct reiserfs_transaction_handle *th, struct super_block *p_s_sb, b_blocknr_t blocknr) { struct reiserfs_journal_cnode *cn = NULL ; struct buffer_head *bh = NULL ; struct reiserfs_list_bitmap *jb = NULL ; @@ -2719,7 +2719,7 @@ if (reiserfs_dont_log(th->t_super)) { bh = sb_find_get_block(p_s_sb, blocknr) ; if (bh && buffer_dirty (bh)) { - printk ("journal_mark_freed(dont_log): dirty buffer on hash list: %lx %ld\n", bh->b_state, blocknr); + printk ("journal_mark_freed(dont_log): dirty buffer on hash list: %lx %d\n", bh->b_state, blocknr); BUG (); } brelse (bh); diff -Nru a/fs/reiserfs/stree.c b/fs/reiserfs/stree.c --- a/fs/reiserfs/stree.c Mon Feb 24 10:10:17 2003 +++ b/fs/reiserfs/stree.c Thu Jul 10 22:22:55 2003 @@ -102,7 +102,7 @@ int n_key_length = REISERFS_SHORT_KEY_LEN; p_s_le_u32 = (__u32 *)le_key; - p_s_cpu_u32 = (__u32 *)cpu_key; + p_s_cpu_u32 = (__u32 *)&cpu_key->on_disk_key; for( ; n_key_length--; ++p_s_le_u32, ++p_s_cpu_u32 ) { if ( le32_to_cpu (*p_s_le_u32) < *p_s_cpu_u32 ) return -1; diff -Nru a/fs/reiserfs/tail_conversion.c b/fs/reiserfs/tail_conversion.c --- a/fs/reiserfs/tail_conversion.c Fri Jun 27 23:20:15 2003 +++ b/fs/reiserfs/tail_conversion.c Thu Jul 10 22:22:54 2003 @@ -191,7 +191,7 @@ bh = next ; } while (bh != head) ; if ( PAGE_SIZE == bh->b_size ) { - ClearPageDirty(page); + clear_page_dirty(page); } } } diff -Nru a/fs/stat.c b/fs/stat.c --- a/fs/stat.c Sun May 4 22:49:54 2003 +++ b/fs/stat.c Thu Jul 10 22:22:59 2003 @@ -28,7 +28,7 @@ stat->atime = inode->i_atime; stat->mtime = inode->i_mtime; stat->ctime = inode->i_ctime; - stat->size = inode->i_size; + stat->size = i_size_read(inode); stat->blocks = inode->i_blocks; stat->blksize = inode->i_blksize; } diff -Nru a/include/asm-generic/div64.h b/include/asm-generic/div64.h --- a/include/asm-generic/div64.h Tue Jul 8 13:45:21 2003 +++ b/include/asm-generic/div64.h Sat Jul 12 04:18:18 2003 @@ -32,11 +32,15 @@ #elif BITS_PER_LONG == 32 -extern uint32_t __div64_32(uint64_t *dividend, uint32_t divisor) __attribute_pure__; +extern uint32_t __div64_32(uint64_t *dividend, uint32_t divisor); +/* The unnecessary pointer compare is there + * to check for type safety (n must be 64bit) + */ # define do_div(n,base) ({ \ uint32_t __base = (base); \ uint32_t __rem; \ + (void)(((typeof((n)) *)0) == ((uint64_t *)0)); \ if (likely(((n) >> 32) == 0)) { \ __rem = (uint32_t)(n) % __base; \ (n) = (uint32_t)(n) / __base; \ diff -Nru a/include/asm-i386/unistd.h b/include/asm-i386/unistd.h --- a/include/asm-i386/unistd.h Mon Jul 7 12:32:37 2003 +++ b/include/asm-i386/unistd.h Fri Jul 11 22:45:21 2003 @@ -276,8 +276,9 @@ #define __NR_statfs64 268 #define __NR_fstatfs64 269 #define __NR_tgkill 270 +#define __NR_utimes 271 -#define NR_syscalls 271 +#define NR_syscalls 272 /* user-visible error numbers are in the range -1 - -124: see */ diff -Nru a/include/asm-parisc/byteorder.h b/include/asm-parisc/byteorder.h --- a/include/asm-parisc/byteorder.h Tue Feb 5 09:39:57 2002 +++ b/include/asm-parisc/byteorder.h Thu Jul 10 07:21:28 2003 @@ -30,9 +30,9 @@ */ static __inline__ __const__ __u64 ___arch__swab64(__u64 x) { __u64 temp; - __asm__("permh 3210, %0, %0\n\t" + __asm__("permh,3210 %0, %0\n\t" "hshl %0, 8, %1\n\t" - "hshr u, %0, 8, %0\n\t" + "hshr,u %0, 8, %0\n\t" "or %1, %0, %0" : "=r" (x), "=&r" (temp) : "0" (x)); diff -Nru a/include/asm-parisc/parisc-device.h b/include/asm-parisc/parisc-device.h --- a/include/asm-parisc/parisc-device.h Tue Dec 24 09:39:09 2002 +++ b/include/asm-parisc/parisc-device.h Wed Jul 2 08:57:59 2003 @@ -33,6 +33,7 @@ char *name; const struct parisc_device_id *id_table; int (*probe) (struct parisc_device *dev); /* New device discovered */ + int (*remove) (struct parisc_device *dev); struct device_driver drv; }; diff -Nru a/include/asm-parisc/pgtable.h b/include/asm-parisc/pgtable.h --- a/include/asm-parisc/pgtable.h Mon May 5 08:34:24 2003 +++ b/include/asm-parisc/pgtable.h Thu Jul 10 07:21:28 2003 @@ -368,11 +368,11 @@ /* Encode and de-code a swap entry */ #define __swp_type(x) ((x).val & 0x1f) -#define __swp_offset(x) ( (((x).val >> 5) & 0xf) | \ - (((x).val >> 7) & ~0xf) ) +#define __swp_offset(x) ( (((x).val >> 6) & 0x7) | \ + (((x).val >> 8) & ~0x7) ) #define __swp_entry(type, offset) ((swp_entry_t) { (type) | \ - ((offset & 0xf) << 5) | \ - ((offset & ~0xf) << 7) }) + ((offset & 0x7) << 6) | \ + ((offset & ~0x7) << 8) }) #define __pte_to_swp_entry(pte) ((swp_entry_t) { pte_val(pte) }) #define __swp_entry_to_pte(x) ((pte_t) { (x).val }) diff -Nru a/include/asm-parisc/processor.h b/include/asm-parisc/processor.h --- a/include/asm-parisc/processor.h Sun Mar 23 05:14:52 2003 +++ b/include/asm-parisc/processor.h Wed Jul 2 08:57:59 2003 @@ -142,6 +142,7 @@ */ unsigned long thread_saved_pc(struct task_struct *t); +void show_trace(struct task_struct *task, unsigned long *stack); /* * Start user thread in another space. diff -Nru a/include/asm-v850/statfs.h b/include/asm-v850/statfs.h --- a/include/asm-v850/statfs.h Fri Nov 1 08:38:12 2002 +++ b/include/asm-v850/statfs.h Thu Jul 10 18:06:18 2003 @@ -1,25 +1,6 @@ #ifndef __V850_STATFS_H__ #define __V850_STATFS_H__ -#ifndef __KERNEL_STRICT_NAMES - -#include - -typedef __kernel_fsid_t fsid_t; - -#endif - -struct statfs { - long f_type; - long f_bsize; - long f_blocks; - long f_bfree; - long f_bavail; - long f_files; - long f_ffree; - __kernel_fsid_t f_fsid; - long f_namelen; - long f_spare[6]; -}; +#include #endif /* __V850_STATFS_H__ */ diff -Nru a/include/linux/ac97_codec.h b/include/linux/ac97_codec.h --- a/include/linux/ac97_codec.h Tue Nov 26 07:43:52 2002 +++ b/include/linux/ac97_codec.h Fri Jul 11 09:17:37 2003 @@ -214,6 +214,9 @@ (CODEC)->supported_mixers & (1< +#define __NEED_I_SIZE_ORDERED +#define i_size_ordered_init(inode) seqcount_init(&inode->i_size_seqcount) +#else +#define i_size_ordered_init(inode) do { } while (0) +#endif + struct inode { struct hlist_node i_hash; struct list_head i_list; @@ -399,7 +410,59 @@ union { void *generic_ip; } u; +#ifdef __NEED_I_SIZE_ORDERED + seqcount_t i_size_seqcount; +#endif }; + +/* + * NOTE: in a 32bit arch with a preemptable kernel and + * an UP compile the i_size_read/write must be atomic + * with respect to the local cpu (unlike with preempt disabled), + * but they don't need to be atomic with respect to other cpus like in + * true SMP (so they need either to either locally disable irq around + * the read or for example on x86 they can be still implemented as a + * cmpxchg8b without the need of the lock prefix). For SMP compiles + * and 64bit archs it makes no difference if preempt is enabled or not. + */ +static inline loff_t i_size_read(struct inode *inode) +{ +#if BITS_PER_LONG==32 && defined(CONFIG_SMP) + loff_t i_size; + unsigned int seq; + + do { + seq = read_seqcount_begin(&inode->i_size_seqcount); + i_size = inode->i_size; + } while (read_seqcount_retry(&inode->i_size_seqcount, seq)); + return i_size; +#elif BITS_PER_LONG==32 && defined(CONFIG_PREEMPT) + loff_t i_size; + + preempt_disable(); + i_size = inode->i_size; + preempt_enable(); + return i_size; +#else + return inode->i_size; +#endif +} + + +static inline void i_size_write(struct inode *inode, loff_t i_size) +{ +#if BITS_PER_LONG==32 && defined(CONFIG_SMP) + write_seqcount_begin(&inode->i_size_seqcount); + inode->i_size = i_size; + write_seqcount_end(&inode->i_size_seqcount); +#elif BITS_PER_LONG==32 && defined(CONFIG_PREEMPT) + preempt_disable(); + inode->i_size = i_size; + preempt_enable(); +#else + inode->i_size = i_size; +#endif +} struct fown_struct { rwlock_t lock; /* protects pid, uid, euid fields */ diff -Nru a/include/linux/irda.h b/include/linux/irda.h --- a/include/linux/irda.h Sun Apr 6 06:03:54 2003 +++ b/include/linux/irda.h Wed Jun 4 07:16:33 2003 @@ -25,7 +25,11 @@ #ifndef KERNEL_IRDA_H #define KERNEL_IRDA_H -#include /* only for sa_family_t */ +/* Please do *not* add any #include in this file, this file is + * included as-is in user space. + * Please fix the calling file to properly included needed files before + * this one, or preferably to include instead. + * Jean II */ /* Hint bit positions for first hint byte */ #define HINT_PNP 0x01 diff -Nru a/include/linux/jbd.h b/include/linux/jbd.h --- a/include/linux/jbd.h Tue Jun 17 23:04:25 2003 +++ b/include/linux/jbd.h Thu Jul 10 22:23:54 2003 @@ -992,9 +992,9 @@ int __log_start_commit(journal_t *journal, tid_t tid); int journal_start_commit(journal_t *journal, tid_t *tid); int log_wait_commit(journal_t *journal, tid_t tid); -int log_do_checkpoint(journal_t *journal, int nblocks); +int log_do_checkpoint(journal_t *journal); -void __log_wait_for_space(journal_t *journal, int nblocks); +void __log_wait_for_space(journal_t *journal); extern void __journal_drop_transaction(journal_t *, transaction_t *); extern int cleanup_journal_tail(journal_t *); @@ -1052,6 +1052,19 @@ } extern int journal_blocks_per_page(struct inode *inode); + +/* + * Return the minimum number of blocks which must be free in the journal + * before a new transaction may be started. Must be called under j_state_lock. + */ +static inline int jbd_space_needed(journal_t *journal) +{ + int nblocks = journal->j_max_transaction_buffers; + if (journal->j_committing_transaction) + nblocks += journal->j_committing_transaction-> + t_outstanding_credits; + return nblocks; +} /* * Definitions which augment the buffer_head layer diff -Nru a/include/linux/loop.h b/include/linux/loop.h --- a/include/linux/loop.h Tue Jun 24 12:15:44 2003 +++ b/include/linux/loop.h Thu Jul 10 17:46:23 2003 @@ -36,7 +36,8 @@ int (*transfer)(struct loop_device *, int cmd, char *raw_buf, char *loop_buf, int size, sector_t real_block); - char lo_name[LO_NAME_SIZE]; + char lo_file_name[LO_NAME_SIZE]; + char lo_crypt_name[LO_NAME_SIZE]; char lo_encrypt_key[LO_KEY_SIZE]; int lo_encrypt_key_size; struct loop_func_table *lo_encryption; @@ -49,7 +50,6 @@ struct block_device *lo_device; unsigned lo_blocksize; void *key_data; - char key_reserved[48]; /* for use by the filter modules */ int old_gfp_mask; @@ -102,7 +102,8 @@ __u32 lo_encrypt_type; __u32 lo_encrypt_key_size; /* ioctl w/o */ __u32 lo_flags; /* ioctl r/o */ - __u8 lo_name[LO_NAME_SIZE]; + __u8 lo_file_name[LO_NAME_SIZE]; + __u8 lo_crypt_name[LO_NAME_SIZE]; __u8 lo_encrypt_key[LO_KEY_SIZE]; /* ioctl w/o */ __u64 lo_init[2]; }; diff -Nru a/include/linux/mount.h b/include/linux/mount.h --- a/include/linux/mount.h Sun May 25 17:57:20 2003 +++ b/include/linux/mount.h Thu Jul 10 22:23:45 2003 @@ -54,6 +54,7 @@ extern struct vfsmount *alloc_vfsmnt(const char *name); extern struct vfsmount *do_kern_mount(const char *fstype, int flags, const char *name, void *data); +extern spinlock_t vfsmount_lock; #endif #endif /* _LINUX_MOUNT_H */ diff -Nru a/include/linux/nbd.h b/include/linux/nbd.h --- a/include/linux/nbd.h Sun Dec 8 17:58:04 2002 +++ b/include/linux/nbd.h Thu Jul 10 22:23:53 2003 @@ -5,6 +5,9 @@ * 2001 Copyright (C) Steven Whitehouse * New nbd_end_request() for compatibility with new linux block * layer code. + * 2003/06/24 Louis D. Langholtz + * Removed unneeded blksize_bits field from nbd_device struct. + * Cleanup PARANOIA usage & code. */ #ifndef LINUX_NBD_H @@ -26,31 +29,27 @@ NBD_CMD_DISC = 2 }; - -#ifdef PARANOIA -extern int requests_in; -extern int requests_out; -#endif - #define nbd_cmd(req) ((req)->cmd[0]) - #define MAX_NBD 128 +/* Define PARANOIA to include extra sanity checking code in here & driver */ +#define PARANOIA + struct nbd_device { - int refcnt; int flags; int harderror; /* Code of hard error */ #define NBD_READ_ONLY 0x0001 #define NBD_WRITE_NOCHK 0x0002 struct socket * sock; struct file * file; /* If == NULL, device is not ready, yet */ +#ifdef PARANOIA int magic; /* FIXME: not if debugging is off */ +#endif spinlock_t queue_lock; struct list_head queue_head;/* Requests are added here... */ struct semaphore tx_lock; struct gendisk *disk; int blksize; - int blksize_bits; u64 bytesize; }; diff -Nru a/include/linux/proc_fs.h b/include/linux/proc_fs.h --- a/include/linux/proc_fs.h Thu Jul 3 06:36:45 2003 +++ b/include/linux/proc_fs.h Thu Jul 10 22:22:53 2003 @@ -71,7 +71,6 @@ write_proc_t *write_proc; atomic_t count; /* use count */ int deleted; /* delete flag */ - kdev_t rdev; }; struct kcore_list { @@ -141,8 +140,6 @@ extern struct proc_dir_entry *proc_symlink(const char *, struct proc_dir_entry *, const char *); -extern struct proc_dir_entry *proc_mknod(const char *,mode_t, - struct proc_dir_entry *,kdev_t); extern struct proc_dir_entry *proc_mkdir(const char *,struct proc_dir_entry *); static inline struct proc_dir_entry *create_proc_read_entry(const char *name, @@ -209,8 +206,6 @@ static inline struct proc_dir_entry *proc_symlink(const char *name, struct proc_dir_entry *parent,char *dest) {return NULL;} -static inline struct proc_dir_entry *proc_mknod(const char *name,mode_t mode, - struct proc_dir_entry *parent,kdev_t rdev) {return NULL;} static inline struct proc_dir_entry *proc_mkdir(const char *name, struct proc_dir_entry *parent) {return NULL;} diff -Nru a/include/linux/reiserfs_fs.h b/include/linux/reiserfs_fs.h --- a/include/linux/reiserfs_fs.h Wed Jun 4 00:50:34 2003 +++ b/include/linux/reiserfs_fs.h Thu Jul 10 22:22:55 2003 @@ -269,7 +269,7 @@ #define NO_BALANCING_NEEDED (-4) #define NO_MORE_UNUSED_CONTIGUOUS_BLOCKS (-5) -typedef unsigned long b_blocknr_t; +typedef __u32 b_blocknr_t; typedef __u32 unp_t; struct unfm_nodeinfo { @@ -570,7 +570,7 @@ static inline int uniqueness2type (__u32 uniqueness) CONSTF; static inline int uniqueness2type (__u32 uniqueness) { - switch (uniqueness) { + switch ((int)uniqueness) { case V1_SD_UNIQUENESS: return TYPE_STAT_DATA; case V1_INDIRECT_UNIQUENESS: return TYPE_INDIRECT; case V1_DIRECT_UNIQUENESS: return TYPE_DIRECT; @@ -1730,11 +1730,11 @@ int journal_end(struct reiserfs_transaction_handle *, struct super_block *, unsigned long) ; int journal_end_sync(struct reiserfs_transaction_handle *, struct super_block *, unsigned long) ; int journal_mark_dirty_nolog(struct reiserfs_transaction_handle *, struct super_block *, struct buffer_head *bh) ; -int journal_mark_freed(struct reiserfs_transaction_handle *, struct super_block *, unsigned long blocknr) ; +int journal_mark_freed(struct reiserfs_transaction_handle *, struct super_block *, b_blocknr_t blocknr) ; int push_journal_writer(char *w) ; int pop_journal_writer(int windex) ; int journal_transaction_should_end(struct reiserfs_transaction_handle *, int) ; -int reiserfs_in_journal(struct super_block *p_s_sb, int bmap_nr, int bit_nr, int searchall, unsigned long *next) ; +int reiserfs_in_journal(struct super_block *p_s_sb, int bmap_nr, int bit_nr, int searchall, b_blocknr_t *next) ; int journal_begin(struct reiserfs_transaction_handle *, struct super_block *p_s_sb, unsigned long) ; void flush_async_commits(struct super_block *p_s_sb) ; @@ -2105,8 +2105,8 @@ typedef struct __reiserfs_blocknr_hint reiserfs_blocknr_hint_t; int reiserfs_parse_alloc_options (struct super_block *, char *); -int is_reusable (struct super_block * s, unsigned long block, int bit_value); -void reiserfs_free_block (struct reiserfs_transaction_handle *th, unsigned long); +int is_reusable (struct super_block * s, b_blocknr_t block, int bit_value); +void reiserfs_free_block (struct reiserfs_transaction_handle *th, b_blocknr_t); int reiserfs_allocate_blocknrs(reiserfs_blocknr_hint_t *, b_blocknr_t * , int, int); extern inline int reiserfs_new_form_blocknrs (struct tree_balance * tb, b_blocknr_t *new_blocknrs, int amount_needed) diff -Nru a/include/linux/reiserfs_fs_sb.h b/include/linux/reiserfs_fs_sb.h --- a/include/linux/reiserfs_fs_sb.h Wed Jun 4 00:50:34 2003 +++ b/include/linux/reiserfs_fs_sb.h Thu Jul 10 22:22:55 2003 @@ -133,7 +133,7 @@ struct reiserfs_journal_cnode { struct buffer_head *bh ; /* real buffer head */ struct super_block *sb ; /* dev of real buffer head */ - unsigned long blocknr ; /* block number of real buffer head, == 0 when buffer on disk */ + __u32 blocknr ; /* block number of real buffer head, == 0 when buffer on disk */ long state ; struct reiserfs_journal_list *jlist ; /* journal list this cnode lives in */ struct reiserfs_journal_cnode *next ; /* next in transaction list */ diff -Nru a/include/linux/seqlock.h b/include/linux/seqlock.h --- a/include/linux/seqlock.h Wed Feb 5 08:05:33 2003 +++ b/include/linux/seqlock.h Thu Jul 10 22:22:58 2003 @@ -94,6 +94,57 @@ return (iv & 1) | (sl->sequence ^ iv); } + +/* + * Version using sequence counter only. + * This can be used when code has its own mutex protecting the + * updating starting before the write_seqcountbeqin() and ending + * after the write_seqcount_end(). + */ + +typedef struct seqcount { + unsigned sequence; +} seqcount_t; + +#define SEQCNT_ZERO { 0 } +#define seqcount_init(x) do { *(x) = (seqcount_t) SEQCNT_ZERO; } while (0) + +/* Start of read using pointer to a sequence counter only. */ +static inline unsigned read_seqcount_begin(const seqcount_t *s) +{ + unsigned ret = s->sequence; + smp_rmb(); + return ret; +} + +/* Test if reader processed invalid data. + * Equivalent to: iv is odd or sequence number has changed. + * (iv & 1) || (*s != iv) + * Using xor saves one conditional branch. + */ +static inline int read_seqcount_retry(const seqcount_t *s, unsigned iv) +{ + smp_rmb(); + return (iv & 1) | (s->sequence ^ iv); +} + + +/* + * Sequence counter only version assumes that callers are using their + * own mutexing. + */ +static inline void write_seqcount_begin(seqcount_t *s) +{ + s->sequence++; + smp_wmb(); +} + +static inline void write_seqcount_end(seqcount_t *s) +{ + smp_wmb(); + s->sequence++; +} + /* * Possible sw/hw IRQ protected versions of the interfaces. */ diff -Nru a/include/linux/sysctl.h b/include/linux/sysctl.h --- a/include/linux/sysctl.h Thu Jun 5 23:37:53 2003 +++ b/include/linux/sysctl.h Wed Jul 9 14:15:45 2003 @@ -6,16 +6,10 @@ **************************************************************** **************************************************************** ** - ** WARNING: ** The values in this file are exported to user space via - ** the sysctl() binary interface. Do *NOT* change the - ** numbering of any existing values here, and do not change - ** any numbers within any one set of values. If you have - ** to redefine an existing interface, use a new number for it. - ** The kernel will then return ENOTDIR to any application using - ** the old binary interface. - ** - ** --sct + ** the sysctl() binary interface. However this interface + ** is unstable and deprecated and will be removed in the future. + ** For a stable interface use /proc/sys. ** **************************************************************** **************************************************************** @@ -130,6 +124,8 @@ KERN_PIDMAX=55, /* int: PID # limit */ KERN_CORE_PATTERN=56, /* string: pattern for core-file names */ KERN_PANIC_ON_OOPS=57, /* int: whether we will panic on an oops */ + KERN_HPPA_PWRSW=58, /* int: hppa soft-power enable */ + KERN_HPPA_UNALIGNED=59, /* int: hppa unaligned-trap enable */ }; diff -Nru a/include/linux/time.h b/include/linux/time.h --- a/include/linux/time.h Fri Jun 20 13:16:13 2003 +++ b/include/linux/time.h Thu Jul 10 22:22:57 2003 @@ -217,6 +217,21 @@ extern int do_posix_clock_monotonic_gettime(struct timespec *tp); extern long do_nanosleep(struct timespec *t); extern long do_utimes(char __user * filename, struct timeval * times); + +static inline void +set_normalized_timespec (struct timespec *ts, time_t sec, long nsec) +{ + while (nsec > NSEC_PER_SEC) { + nsec -= NSEC_PER_SEC; + ++sec; + } + while (nsec < 0) { + nsec += NSEC_PER_SEC; + --sec; + } + ts->tv_sec = sec; + ts->tv_nsec = nsec; +} #endif #define FD_SETSIZE __FD_SETSIZE diff -Nru a/include/net/irda/irda_device.h b/include/net/irda/irda_device.h --- a/include/net/irda/irda_device.h Mon Feb 24 12:51:23 2003 +++ b/include/net/irda/irda_device.h Wed Jul 9 04:58:28 2003 @@ -230,7 +230,9 @@ dongle_t *irda_device_dongle_init(struct net_device *dev, int type); int irda_device_dongle_cleanup(dongle_t *dongle); +#ifdef CONFIG_ISA void setup_dma(int channel, char *buffer, int count, int mode); +#endif void irda_task_delete(struct irda_task *task); int irda_task_kick(struct irda_task *task); diff -Nru a/include/net/irda/nsc-ircc.h b/include/net/irda/nsc-ircc.h --- a/include/net/irda/nsc-ircc.h Tue Feb 5 09:39:49 2002 +++ b/include/net/irda/nsc-ircc.h Wed Jul 9 08:47:24 2003 @@ -39,17 +39,28 @@ #define DMA_RX_MODE 0x04 /* I/O to mem, ++, demand. */ /* Config registers for the '108 */ -#define CFG_BAIC 0x00 -#define CFG_CSRT 0x01 -#define CFG_MCTL 0x02 +#define CFG_108_BAIC 0x00 +#define CFG_108_CSRT 0x01 +#define CFG_108_MCTL 0x02 /* Config registers for the '338 */ -#define CFG_FER 0x00 -#define CFG_FAR 0x01 -#define CFG_PTR 0x02 -#define CFG_PNP0 0x1b -#define CFG_PNP1 0x1c -#define CFG_PNP3 0x4f +#define CFG_338_FER 0x00 +#define CFG_338_FAR 0x01 +#define CFG_338_PTR 0x02 +#define CFG_338_PNP0 0x1b +#define CFG_338_PNP1 0x1c +#define CFG_338_PNP3 0x4f + +/* Config registers for the '39x (in the logical device bank) */ +#define CFG_39X_LDN 0x07 /* Logical device number (Super I/O bank) */ +#define CFG_39X_ACT 0x30 /* Device activation */ +#define CFG_39X_BASEH 0x60 /* Device base address (high bits) */ +#define CFG_39X_BASEL 0x61 /* Device base address (low bits) */ +#define CFG_39X_IRQNUM 0x70 /* Interrupt number & wake up enable */ +#define CFG_39X_IRQSEL 0x71 /* Interrupt select (edge/level + polarity) */ +#define CFG_39X_DMA0 0x74 /* DMA 0 configuration */ +#define CFG_39X_DMA1 0x75 /* DMA 1 configuration */ +#define CFG_39X_SPC 0xF0 /* Serial port configuration register */ /* Flags for configuration register CRF0 */ #define APEDCRC 0x02 diff -Nru a/init/Kconfig b/init/Kconfig --- a/init/Kconfig Wed Jul 2 21:23:11 2003 +++ b/init/Kconfig Thu Jul 10 22:23:14 2003 @@ -118,6 +118,14 @@ a "non-standard" kernel. Only use this if you really know what you are doing. +config KALLSYMS + bool "Load all symbols for debugging/kksymoops" if EMBEDDED + default y + help + Say Y here to let the kernel print out symbolic crash information and + symbolic stack backtraces. This increases the size of the kernel + somewhat, as all symbols have to be loaded into the kernel image. + config FUTEX bool "Enable futex support" if EMBEDDED default y diff -Nru a/init/version.c b/init/version.c --- a/init/version.c Wed May 22 10:13:45 2002 +++ b/init/version.c Fri Jul 11 17:30:42 2003 @@ -17,8 +17,12 @@ int version_string(LINUX_VERSION_CODE); struct new_utsname system_utsname = { - UTS_SYSNAME, UTS_NODENAME, UTS_RELEASE, UTS_VERSION, - UTS_MACHINE, UTS_DOMAINNAME + .sysname = UTS_SYSNAME, + .nodename = UTS_NODENAME, + .release = UTS_RELEASE, + .version = UTS_VERSION, + .machine = UTS_MACHINE, + .domainname = UTS_DOMAINNAME, }; const char *linux_banner = diff -Nru a/ipc/shm.c b/ipc/shm.c --- a/ipc/shm.c Fri May 9 14:23:37 2003 +++ b/ipc/shm.c Thu Jul 10 22:22:59 2003 @@ -703,7 +703,7 @@ } file = shp->shm_file; - size = file->f_dentry->d_inode->i_size; + size = i_size_read(file->f_dentry->d_inode); shp->shm_nattch++; shm_unlock(shp); diff -Nru a/kernel/compat.c b/kernel/compat.c --- a/kernel/compat.c Tue Jun 17 05:37:03 2003 +++ b/kernel/compat.c Thu Jul 10 22:22:52 2003 @@ -425,11 +425,9 @@ &kernel_mask); set_fs(old_fs); - if (ret > 0) { + if (ret > 0) if (put_user(kernel_mask, user_mask_ptr)) - ret = -EFAULT; - ret = sizeof(compat_ulong_t); - } + return -EFAULT; return ret; } diff -Nru a/kernel/fork.c b/kernel/fork.c --- a/kernel/fork.c Fri Jul 4 23:52:49 2003 +++ b/kernel/fork.c Thu Jul 10 22:23:43 2003 @@ -375,6 +375,7 @@ mm->core_waiters = 0; mm->page_table_lock = SPIN_LOCK_UNLOCKED; mm->ioctx_list_lock = RW_LOCK_UNLOCKED; + mm->ioctx_list = NULL; mm->default_kioctx = (struct kioctx)INIT_KIOCTX(mm->default_kioctx, *mm); mm->free_area_cache = TASK_UNMAPPED_BASE; diff -Nru a/kernel/ksyms.c b/kernel/ksyms.c --- a/kernel/ksyms.c Thu Jul 3 00:32:44 2003 +++ b/kernel/ksyms.c Thu Jul 10 22:23:39 2003 @@ -41,6 +41,7 @@ #include #include #include +#include #include #include #include @@ -76,6 +77,8 @@ EXPORT_SYMBOL(do_munmap); EXPORT_SYMBOL(do_brk); EXPORT_SYMBOL(exit_mm); +EXPORT_SYMBOL_GPL(exit_fs); +EXPORT_SYMBOL_GPL(copy_fs_struct); /* internal kernel memory management */ EXPORT_SYMBOL(__alloc_pages); diff -Nru a/kernel/sched.c b/kernel/sched.c --- a/kernel/sched.c Mon Jul 7 13:01:46 2003 +++ b/kernel/sched.c Thu Jul 10 22:23:46 2003 @@ -1047,7 +1047,7 @@ */ #define CAN_MIGRATE_TASK(p,rq,this_cpu) \ - ((jiffies - (p)->last_run > cache_decay_ticks) && \ + ((!idle || (jiffies - (p)->last_run > cache_decay_ticks)) && \ !task_running(rq, p) && \ ((p)->cpus_allowed & (1UL << (this_cpu)))) diff -Nru a/kernel/sysctl.c b/kernel/sysctl.c --- a/kernel/sysctl.c Sun Jun 29 23:49:26 2003 +++ b/kernel/sysctl.c Thu Jul 10 18:25:18 2003 @@ -87,6 +87,11 @@ extern int stop_a_enabled; #endif +#ifdef __hppa__ +extern int pwrsw_enabled; +extern int unaligned_enabled; +#endif + #ifdef CONFIG_ARCH_S390 #ifdef CONFIG_MATHEMU extern int sysctl_ieee_emulation_warnings; @@ -313,6 +318,30 @@ .proc_handler = &proc_dointvec, }, #endif +#ifdef __hppa__ + { + .ctl_name = KERN_HPPA_PWRSW, + .procname = "soft-power", + .data = &pwrsw_enabled, + .maxlen = sizeof (int), + .mode = 0644, + .proc_handler = &proc_dointvec, + }, + { + .ctl_name = KERN_HPPA_UNALIGNED, + .procname = "unaligned-trap", + .data = &unaligned_enabled, + .maxlen = sizeof (int), + .mode = 0644, + .proc_handler = &proc_dointvec, + }, +#endif +#ifdef __hppa__ + {KERN_HPPA_PWRSW, "soft-power", &pwrsw_enabled, sizeof (int), + 0644, NULL, &proc_dointvec}, + {KERN_HPPA_UNALIGNED, "unaligned-trap", &unaligned_enabled, sizeof (int), + 0644, NULL, &proc_dointvec}, +#endif #if defined(CONFIG_PPC32) && defined(CONFIG_6xx) { .ctl_name = KERN_PPC_POWERSAVE_NAP, @@ -823,7 +852,16 @@ if (copy_from_user(&tmp, args, sizeof(tmp))) return -EFAULT; - + + if (tmp.nlen != 2 || tmp.name[0] != CTL_KERN || + tmp.name[1] != KERN_VERSION) { + int i; + printk(KERN_INFO "%s: numerical sysctl ", current->comm); + for (i = 0; i < tmp.nlen; i++) + printk("%d ", tmp.name[i]); + printk("is obsolete.\n"); + } + lock_kernel(); error = do_sysctl(tmp.name, tmp.nlen, tmp.oldval, tmp.oldlenp, tmp.newval, tmp.newlen); diff -Nru a/lib/vsprintf.c b/lib/vsprintf.c --- a/lib/vsprintf.c Wed Feb 12 05:29:38 2003 +++ b/lib/vsprintf.c Sat Jul 12 12:49:57 2003 @@ -127,7 +127,7 @@ #define SPECIAL 32 /* 0x */ #define LARGE 64 /* use 'ABCDEF' instead of 'abcdef' */ -static char * number(char * buf, char * end, long long num, int base, int size, int precision, int type) +static char * number(char * buf, char * end, unsigned long long num, int base, int size, int precision, int type) { char c,sign,tmp[66]; const char *digits; @@ -143,9 +143,9 @@ c = (type & ZEROPAD) ? '0' : ' '; sign = 0; if (type & SIGN) { - if (num < 0) { + if ((signed long long) num < 0) { sign = '-'; - num = -num; + num = - (signed long long) num; size--; } else if (type & PLUS) { sign = '+'; diff -Nru a/mm/filemap.c b/mm/filemap.c --- a/mm/filemap.c Sun Jul 6 13:23:51 2003 +++ b/mm/filemap.c Thu Jul 10 22:22:59 2003 @@ -555,14 +555,15 @@ for (;;) { struct page *page; unsigned long end_index, nr, ret; + loff_t isize = i_size_read(inode); - end_index = inode->i_size >> PAGE_CACHE_SHIFT; + end_index = isize >> PAGE_CACHE_SHIFT; if (index > end_index) break; nr = PAGE_CACHE_SIZE; if (index == end_index) { - nr = inode->i_size & ~PAGE_CACHE_MASK; + nr = isize & ~PAGE_CACHE_MASK; if (nr <= offset) break; } @@ -763,7 +764,7 @@ retval = 0; if (!count) goto out; /* skip atime */ - size = inode->i_size; + size = i_size_read(inode); if (pos < size) { retval = generic_file_direct_IO(READ, iocb, iov, pos, nr_segs); @@ -951,7 +952,7 @@ endoff = ((area->vm_end - area->vm_start) >> PAGE_CACHE_SHIFT) + area->vm_pgoff; retry_all: - size = (inode->i_size + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; + size = (i_size_read(inode) + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; if (pgoff >= size) goto outside_data_content; @@ -1233,7 +1234,7 @@ pgoff, len >> PAGE_CACHE_SHIFT); repeat: - size = (inode->i_size + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; + size = (i_size_read(inode) + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; if (pgoff + (len >> PAGE_CACHE_SHIFT) > size) return -EINVAL; @@ -1544,7 +1545,7 @@ if (!isblk) { /* FIXME: this is for backwards compatibility with 2.4 */ if (file->f_flags & O_APPEND) - *pos = inode->i_size; + *pos = i_size_read(inode); if (limit != RLIM_INFINITY) { if (*pos >= limit) { @@ -1590,15 +1591,17 @@ if (unlikely(*pos + *count > inode->i_sb->s_maxbytes)) *count = inode->i_sb->s_maxbytes - *pos; } else { + loff_t isize; if (bdev_read_only(inode->i_bdev)) return -EPERM; - if (*pos >= inode->i_size) { - if (*count || *pos > inode->i_size) + isize = i_size_read(inode); + if (*pos >= isize) { + if (*count || *pos > isize) return -ENOSPC; } - if (*pos + *count > inode->i_size) - *count = inode->i_size - *pos; + if (*pos + *count > isize) + *count = isize - *pos; } return 0; } @@ -1685,8 +1688,8 @@ iov, pos, nr_segs); if (written > 0) { loff_t end = pos + written; - if (end > inode->i_size && !isblk) { - inode->i_size = end; + if (end > i_size_read(inode) && !isblk) { + i_size_write(inode, end); mark_inode_dirty(inode); } *ppos = end; @@ -1730,14 +1733,15 @@ status = a_ops->prepare_write(file, page, offset, offset+bytes); if (unlikely(status)) { + loff_t isize = i_size_read(inode); /* * prepare_write() may have instantiated a few blocks * outside i_size. Trim these off again. */ unlock_page(page); page_cache_release(page); - if (pos + bytes > inode->i_size) - vmtruncate(inode, inode->i_size); + if (pos + bytes > isize) + vmtruncate(inode, isize); break; } if (likely(nr_segs == 1)) diff -Nru a/mm/memory.c b/mm/memory.c --- a/mm/memory.c Thu Jun 12 16:41:04 2003 +++ b/mm/memory.c Thu Jul 10 22:22:59 2003 @@ -1109,7 +1109,7 @@ if (inode->i_size < offset) goto do_expand; - inode->i_size = offset; + i_size_write(inode, offset); pgoff = (offset + PAGE_SIZE - 1) >> PAGE_SHIFT; down(&mapping->i_shared_sem); if (unlikely(!list_empty(&mapping->i_mmap))) @@ -1126,7 +1126,7 @@ goto out_sig; if (offset > inode->i_sb->s_maxbytes) goto out; - inode->i_size = offset; + i_size_write(inode, offset); out_truncate: if (inode->i_op && inode->i_op->truncate) diff -Nru a/mm/mmap.c b/mm/mmap.c --- a/mm/mmap.c Wed Jul 2 21:22:38 2003 +++ b/mm/mmap.c Thu Jul 10 19:46:52 2003 @@ -476,10 +476,10 @@ if (!len) return addr; - if (len > TASK_SIZE) - return -EINVAL; - + /* Careful about overflows.. */ len = PAGE_ALIGN(len); + if (!len || len > TASK_SIZE) + return -EINVAL; /* offset overflow? */ if ((pgoff + (len >> PAGE_SHIFT)) < pgoff) diff -Nru a/mm/nommu.c b/mm/nommu.c --- a/mm/nommu.c Wed Jul 2 18:18:55 2003 +++ b/mm/nommu.c Thu Jul 10 22:22:59 2003 @@ -48,7 +48,7 @@ if (inode->i_size < offset) goto do_expand; - inode->i_size = offset; + i_size_write(inode, offset); truncate_inode_pages(mapping, offset); goto out_truncate; @@ -59,7 +59,7 @@ goto out_sig; if (offset > inode->i_sb->s_maxbytes) goto out; - inode->i_size = offset; + i_size_write(inode, offset); out_truncate: if (inode->i_op && inode->i_op->truncate) diff -Nru a/mm/oom_kill.c b/mm/oom_kill.c --- a/mm/oom_kill.c Wed Apr 23 03:15:53 2003 +++ b/mm/oom_kill.c Thu Jul 10 22:23:56 2003 @@ -141,8 +141,16 @@ * CAP_SYS_RAW_IO set, send SIGTERM instead (but it's unlikely that * we select a process with CAP_SYS_RAW_IO set). */ -void oom_kill_task(struct task_struct *p) +static void __oom_kill_task(task_t *p) { + task_lock(p); + if (!p->mm || p->mm == &init_mm) { + WARN_ON(1); + printk(KERN_WARNING "tried to kill an mm-less task!\n"); + task_unlock(p); + return; + } + task_unlock(p); printk(KERN_ERR "Out of Memory: Killed process %d (%s).\n", p->pid, p->comm); /* @@ -161,6 +169,16 @@ } } +static struct mm_struct *oom_kill_task(task_t *p) +{ + struct mm_struct *mm = get_task_mm(p); + if (!mm || mm == &init_mm) + return NULL; + __oom_kill_task(p); + return mm; +} + + /** * oom_kill - kill the "best" process when we run out of memory * @@ -171,9 +189,11 @@ */ static void oom_kill(void) { + struct mm_struct *mm; struct task_struct *g, *p, *q; read_lock(&tasklist_lock); +retry: p = select_bad_process(); /* Found nothing?!?! Either we hang forever, or we panic. */ @@ -182,17 +202,21 @@ panic("Out of memory and no killable processes...\n"); } - oom_kill_task(p); + mm = oom_kill_task(p); + if (!mm) + goto retry; /* * kill all processes that share the ->mm (i.e. all threads), * but are in a different thread group */ do_each_thread(g, q) - if (q->mm == p->mm && q->tgid != p->tgid) - oom_kill_task(q); + if (q->mm == mm && q->tgid != p->tgid) + __oom_kill_task(q); while_each_thread(g, q); - + if (!p->mm) + printk(KERN_INFO "Fixed up OOM kill of mm-less task\n"); read_unlock(&tasklist_lock); + mmput(mm); /* * Make kswapd go out of the way, so "p" has a good chance of diff -Nru a/mm/readahead.c b/mm/readahead.c --- a/mm/readahead.c Sun Jul 6 13:23:51 2003 +++ b/mm/readahead.c Thu Jul 10 22:22:59 2003 @@ -208,11 +208,12 @@ LIST_HEAD(page_pool); int page_idx; int ret = 0; + loff_t isize = i_size_read(inode); - if (inode->i_size == 0) + if (isize == 0) goto out; - end_index = ((inode->i_size - 1) >> PAGE_CACHE_SHIFT); + end_index = ((isize - 1) >> PAGE_CACHE_SHIFT); /* * Preallocate as many pages as we will need. diff -Nru a/mm/shmem.c b/mm/shmem.c --- a/mm/shmem.c Sun Jun 29 23:49:04 2003 +++ b/mm/shmem.c Thu Jul 10 22:22:59 2003 @@ -299,7 +299,7 @@ static const swp_entry_t unswapped = {0}; if (sgp != SGP_WRITE && - ((loff_t) index << PAGE_CACHE_SHIFT) >= inode->i_size) + ((loff_t) index << PAGE_CACHE_SHIFT) >= i_size_read(inode)) return ERR_PTR(-EINVAL); while (!(entry = shmem_swp_entry(info, index, &page))) { @@ -332,7 +332,7 @@ return ERR_PTR(-ENOMEM); } if (sgp != SGP_WRITE && - ((loff_t) index << PAGE_CACHE_SHIFT) >= inode->i_size) { + ((loff_t) index << PAGE_CACHE_SHIFT) >= i_size_read(inode)) { entry = ERR_PTR(-EINVAL); break; } @@ -641,7 +641,7 @@ /* Racing against delete or truncate? Must leave out of page cache */ limit = (inode->i_state & I_FREEING)? 0: - (inode->i_size + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; + (i_size_read(inode) + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; if (idx >= limit || move_from_swap_cache(page, idx, inode->i_mapping) == 0) @@ -964,7 +964,7 @@ enum sgp_type sgp = nonblock? SGP_QUICK: SGP_CACHE; unsigned long size; - size = (inode->i_size + PAGE_SIZE - 1) >> PAGE_SHIFT; + size = (i_size_read(inode) + PAGE_SIZE - 1) >> PAGE_SHIFT; if (pgoff >= size || pgoff + (len >> PAGE_SHIFT) > size) return -EINVAL; @@ -1239,12 +1239,13 @@ for (;;) { struct page *page = NULL; unsigned long end_index, nr, ret; + loff_t i_size = i_size_read(inode); - end_index = inode->i_size >> PAGE_CACHE_SHIFT; + end_index = i_size >> PAGE_CACHE_SHIFT; if (index > end_index) break; if (index == end_index) { - nr = inode->i_size & ~PAGE_CACHE_MASK; + nr = i_size & ~PAGE_CACHE_MASK; if (nr <= offset) break; } @@ -1261,9 +1262,10 @@ * are called without i_sem protection against truncate */ nr = PAGE_CACHE_SIZE; - end_index = inode->i_size >> PAGE_CACHE_SHIFT; + i_size = i_size_read(inode); + end_index = i_size >> PAGE_CACHE_SHIFT; if (index == end_index) { - nr = inode->i_size & ~PAGE_CACHE_MASK; + nr = i_size & ~PAGE_CACHE_MASK; if (nr <= offset) { page_cache_release(page); break; diff -Nru a/mm/swapfile.c b/mm/swapfile.c --- a/mm/swapfile.c Wed Jul 2 21:22:38 2003 +++ b/mm/swapfile.c Thu Jul 10 22:22:59 2003 @@ -926,7 +926,7 @@ */ probe_block = 0; page_no = 0; - last_block = inode->i_size >> blkbits; + last_block = i_size_read(inode) >> blkbits; while ((probe_block + blocks_per_page) <= last_block && page_no < sis->max) { unsigned block_in_page; @@ -1312,7 +1312,7 @@ goto bad_swap; } - swapfilesize = mapping->host->i_size >> PAGE_SHIFT; + swapfilesize = i_size_read(mapping->host) >> PAGE_SHIFT; /* * Read the swap header. diff -Nru a/mm/vmscan.c b/mm/vmscan.c --- a/mm/vmscan.c Sat Jun 14 16:15:50 2003 +++ b/mm/vmscan.c Sat Jul 12 04:20:29 2003 @@ -147,7 +147,7 @@ pages = nr_used_zone_pages(); list_for_each_entry(shrinker, &shrinker_list, list) { - long long delta; + unsigned long long delta; delta = scanned * shrinker->seeks; delta *= (*shrinker->shrinker)(0, gfp_mask); diff -Nru a/net/ipv4/igmp.c b/net/ipv4/igmp.c --- a/net/ipv4/igmp.c Tue Jul 8 17:51:54 2003 +++ b/net/ipv4/igmp.c Thu Jul 10 19:32:15 2003 @@ -1060,17 +1060,19 @@ reporter = im->reporter; igmp_stop_timer(im); - if (IGMP_V1_SEEN(in_dev)) - goto done; - if (IGMP_V2_SEEN(in_dev)) { - if (reporter) - igmp_send_report(in_dev, im, IGMP_HOST_LEAVE_MESSAGE); - goto done; - } - /* IGMPv3 */ - igmpv3_add_delrec(in_dev, im); + if (in_dev->dev->flags & IFF_UP) { + if (IGMP_V1_SEEN(in_dev)) + goto done; + if (IGMP_V2_SEEN(in_dev)) { + if (reporter) + igmp_send_report(in_dev, im, IGMP_HOST_LEAVE_MESSAGE); + goto done; + } + /* IGMPv3 */ + igmpv3_add_delrec(in_dev, im); - igmp_ifc_event(in_dev); + igmp_ifc_event(in_dev); + } done: #endif ip_mc_clear_src(im); diff -Nru a/net/ipv4/raw.c b/net/ipv4/raw.c --- a/net/ipv4/raw.c Tue Jul 8 17:51:54 2003 +++ b/net/ipv4/raw.c Thu Jul 10 17:34:53 2003 @@ -383,7 +383,7 @@ * IP_HDRINCL is much more convenient. */ } else { - err = -EINVAL; + err = -EDESTADDRREQ; if (sk->sk_state != TCP_ESTABLISHED) goto out; daddr = inet->daddr; diff -Nru a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c --- a/net/ipv4/tcp_input.c Wed Jul 9 20:22:27 2003 +++ b/net/ipv4/tcp_input.c Thu Jul 10 17:16:22 2003 @@ -3694,6 +3694,13 @@ tcp_sync_mss(sk, tp->pmtu_cookie); tcp_initialize_rcv_mss(sk); + /* Remember, tcp_poll() does not lock socket! + * Change state from SYN-SENT only after copied_seq + * is initialized. */ + tp->copied_seq = tp->rcv_nxt; + mb(); + tcp_set_state(sk, TCP_ESTABLISHED); + /* Make sure socket is routed, for correct metrics. */ tp->af_specific->rebuild_header(sk); @@ -3713,13 +3720,6 @@ __tcp_fast_path_on(tp, tp->snd_wnd); else tp->pred_flags = 0; - - /* Remember, tcp_poll() does not lock socket! - * Change state from SYN-SENT only after copied_seq - * is initialized. */ - tp->copied_seq = tp->rcv_nxt; - mb(); - tcp_set_state(sk, TCP_ESTABLISHED); if (!sock_flag(sk, SOCK_DEAD)) { sk->sk_state_change(sk); diff -Nru a/net/ipv4/udp.c b/net/ipv4/udp.c --- a/net/ipv4/udp.c Wed Jun 18 13:59:01 2003 +++ b/net/ipv4/udp.c Thu Jul 10 17:34:53 2003 @@ -540,7 +540,7 @@ return -EINVAL; } else { if (sk->sk_state != TCP_ESTABLISHED) - return -ENOTCONN; + return -EDESTADDRREQ; daddr = inet->daddr; dport = inet->dport; /* Open fast path for connected socket. diff -Nru a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c --- a/net/ipv6/ip6_output.c Tue Jul 8 08:22:38 2003 +++ b/net/ipv6/ip6_output.c Thu Jul 10 17:24:53 2003 @@ -1247,11 +1247,9 @@ inet->cork.length = 0; inet->sndmsg_page = NULL; inet->sndmsg_off = 0; - if ((exthdrlen = rt->u.dst.header_len) != 0) { - length += exthdrlen; - transhdrlen += exthdrlen; - } - exthdrlen += opt ? opt->opt_flen : 0; + exthdrlen = rt->u.dst.header_len + (opt ? opt->opt_flen : 0); + length += exthdrlen; + transhdrlen += exthdrlen; } else { rt = np->cork.rt; if (inet->cork.flags & IPCORK_OPT) diff -Nru a/net/ipv6/raw.c b/net/ipv6/raw.c --- a/net/ipv6/raw.c Tue Jul 8 19:09:59 2003 +++ b/net/ipv6/raw.c Thu Jul 10 17:34:53 2003 @@ -602,7 +602,7 @@ fl.oif = sin6->sin6_scope_id; } else { if (sk->sk_state != TCP_ESTABLISHED) - return(-EINVAL); + return -EDESTADDRREQ; proto = inet->num; daddr = &np->daddr; diff -Nru a/net/ipv6/udp.c b/net/ipv6/udp.c --- a/net/ipv6/udp.c Mon Jun 30 08:59:07 2003 +++ b/net/ipv6/udp.c Thu Jul 10 17:34:53 2003 @@ -862,7 +862,7 @@ fl.oif = sin6->sin6_scope_id; } else { if (sk->sk_state != TCP_ESTABLISHED) - return -ENOTCONN; + return -EDESTADDRREQ; up->dport = inet->dport; daddr = &np->daddr; diff -Nru a/net/irda/ircomm/ircomm_tty.c b/net/irda/ircomm/ircomm_tty.c --- a/net/irda/ircomm/ircomm_tty.c Wed Jun 11 12:32:33 2003 +++ b/net/irda/ircomm/ircomm_tty.c Wed Jul 9 04:54:24 2003 @@ -119,6 +119,7 @@ driver->driver_name = "ircomm"; driver->name = "ircomm"; + driver->devfs_name = "ircomm"; driver->major = IRCOMM_TTY_MAJOR; driver->minor_start = IRCOMM_TTY_MINOR; driver->type = TTY_DRIVER_TYPE_SERIAL; diff -Nru a/net/irda/irlap.c b/net/irda/irlap.c --- a/net/irda/irlap.c Sun May 25 19:36:24 2003 +++ b/net/irda/irlap.c Wed Jun 4 08:21:29 2003 @@ -79,6 +79,13 @@ int __init irlap_init(void) { + /* Check if the compiler did its job properly. + * May happen on some ARM configuration, check with Russell King. */ + ASSERT(sizeof(struct xid_frame) == 14, ;); + ASSERT(sizeof(struct test_frame) == 10, ;); + ASSERT(sizeof(struct ua_frame) == 10, ;); + ASSERT(sizeof(struct snrm_frame) == 11, ;); + /* Allocate master array */ irlap = hashbin_new(HB_LOCK); if (irlap == NULL) { diff -Nru a/net/irda/irnet/irnet_irda.c b/net/irda/irnet/irnet_irda.c --- a/net/irda/irnet/irnet_irda.c Thu Jun 19 17:28:31 2003 +++ b/net/irda/irnet/irnet_irda.c Wed Jul 9 04:50:34 2003 @@ -953,7 +953,7 @@ (void *) &irnet_server.s); #endif - DEXIT(IRDA_SERV_TRACE, " - self=0x%X\n", (unsigned int) &irnet_server.s); + DEXIT(IRDA_SERV_TRACE, " - self=0x%p\n", &irnet_server.s); return 0; } diff -Nru a/net/irda/irttp.c b/net/irda/irttp.c --- a/net/irda/irttp.c Thu Jun 19 17:28:31 2003 +++ b/net/irda/irttp.c Wed Jun 4 06:34:45 2003 @@ -1094,7 +1094,8 @@ * Check that the client has reserved enough space for * headers */ - ASSERT(skb_headroom(userdata) >= TTP_MAX_HEADER, return -1;); + ASSERT(skb_headroom(userdata) >= TTP_MAX_HEADER, + { dev_kfree_skb(tx_skb); return -1; } ); } /* Initialize connection parameters */ @@ -1123,7 +1124,7 @@ /* SAR enabled? */ if (max_sdu_size > 0) { ASSERT(skb_headroom(tx_skb) >= (TTP_MAX_HEADER + TTP_SAR_HEADER), - return -1;); + { dev_kfree_skb(tx_skb); return -1; } ); /* Insert SAR parameters */ frame = skb_push(tx_skb, TTP_HEADER+TTP_SAR_HEADER); diff -Nru a/sound/oss/Kconfig b/sound/oss/Kconfig --- a/sound/oss/Kconfig Sun May 25 23:19:41 2003 +++ b/sound/oss/Kconfig Fri Jul 11 17:40:26 2003 @@ -598,6 +598,13 @@ If you compile the driver into the kernel, you have to add "ad1816=,,," to the kernel command line. +config SOUND_AD1889 + tristate "AD1889 based cards (AD1819 codec) (EXPERIMENTAL)" + depends on EXPERIMENTAL && SOUND_OSS + help + Say M here if you have a sound card based on the Analog Devices + AD1889 chip. + config SOUND_SGALAXY tristate "Aztech Sound Galaxy (non-PnP) cards" depends on SOUND_OSS diff -Nru a/sound/oss/Makefile b/sound/oss/Makefile --- a/sound/oss/Makefile Mon Feb 3 14:19:39 2003 +++ b/sound/oss/Makefile Fri Jul 11 17:40:26 2003 @@ -33,6 +33,7 @@ obj-$(CONFIG_SOUND_WAVEARTIST) += waveartist.o obj-$(CONFIG_SOUND_SGALAXY) += sgalaxy.o ad1848.o obj-$(CONFIG_SOUND_AD1816) += ad1816.o +obj-$(CONFIG_SOUND_AD1889) += ad1889.o ac97_codec.o obj-$(CONFIG_SOUND_ACI_MIXER) += aci.o obj-$(CONFIG_SOUND_AWE32_SYNTH) += awe_wave.o diff -Nru a/sound/oss/ac97_codec.c b/sound/oss/ac97_codec.c --- a/sound/oss/ac97_codec.c Sat May 24 17:00:00 2003 +++ b/sound/oss/ac97_codec.c Fri Jul 11 08:28:47 2003 @@ -1,4 +1,3 @@ - /* * ac97_codec.c: Generic AC97 mixer/modem module * @@ -31,6 +30,10 @@ ************************************************************************** * * History + * May 02, 2003 Liam Girdwood + * Removed non existant WM9700 + * Added support for WM9705, WM9708, WM9709, WM9710, WM9711 + * WM9712 and WM9717 * Mar 28, 2002 Randolph Bentson * corrections to support WM9707 in ViewPad 1000 * v0.4 Mar 15 2000 Ollie Lho @@ -43,7 +46,9 @@ * Isolated from trident.c to support multiple ac97 codec */ #include +#include #include +#include #include #include #include @@ -62,9 +67,10 @@ static int ac97_init_mixer(struct ac97_codec *codec); -static int wolfson_init00(struct ac97_codec * codec); static int wolfson_init03(struct ac97_codec * codec); static int wolfson_init04(struct ac97_codec * codec); +static int wolfson_init05(struct ac97_codec * codec); +static int wolfson_init11(struct ac97_codec * codec); static int tritech_init(struct ac97_codec * codec); static int tritech_maestro_init(struct ac97_codec * codec); static int sigmatel_9708_init(struct ac97_codec *codec); @@ -72,7 +78,10 @@ static int sigmatel_9744_init(struct ac97_codec *codec); static int ad1886_init(struct ac97_codec *codec); static int eapd_control(struct ac97_codec *codec, int); -static int crystal_digital_control(struct ac97_codec *codec, int mode); +static int crystal_digital_control(struct ac97_codec *codec, int slots, int rate, int mode); +static int cmedia_init(struct ac97_codec * codec); +static int cmedia_digital_control(struct ac97_codec *codec, int slots, int rate, int mode); +static int generic_digital_control(struct ac97_codec *codec, int slots, int rate, int mode); /* @@ -93,9 +102,11 @@ static struct ac97_ops null_ops = { NULL, NULL, NULL }; static struct ac97_ops default_ops = { NULL, eapd_control, NULL }; -static struct ac97_ops wolfson_ops00 = { wolfson_init00, NULL, NULL }; +static struct ac97_ops default_digital_ops = { NULL, eapd_control, generic_digital_control}; static struct ac97_ops wolfson_ops03 = { wolfson_init03, NULL, NULL }; static struct ac97_ops wolfson_ops04 = { wolfson_init04, NULL, NULL }; +static struct ac97_ops wolfson_ops05 = { wolfson_init05, NULL, NULL }; +static struct ac97_ops wolfson_ops11 = { wolfson_init11, NULL, NULL }; static struct ac97_ops tritech_ops = { tritech_init, NULL, NULL }; static struct ac97_ops tritech_m_ops = { tritech_maestro_init, NULL, NULL }; static struct ac97_ops sigmatel_9708_ops = { sigmatel_9708_init, NULL, NULL }; @@ -103,12 +114,15 @@ static struct ac97_ops sigmatel_9744_ops = { sigmatel_9744_init, NULL, NULL }; static struct ac97_ops crystal_digital_ops = { NULL, eapd_control, crystal_digital_control }; static struct ac97_ops ad1886_ops = { ad1886_init, eapd_control, NULL }; +static struct ac97_ops cmedia_ops = { NULL, eapd_control, NULL}; +static struct ac97_ops cmedia_digital_ops = { cmedia_init, eapd_control, cmedia_digital_control}; /* sorted by vendor/device id */ static const struct { u32 id; char *name; struct ac97_ops *ops; + int flags; } ac97_codec_ids[] = { {0x41445303, "Analog Devices AD1819", &null_ops}, {0x41445340, "Analog Devices AD1881", &null_ops}, @@ -120,8 +134,12 @@ {0x414B4D00, "Asahi Kasei AK4540", &null_ops}, {0x414B4D01, "Asahi Kasei AK4542", &null_ops}, {0x414B4D02, "Asahi Kasei AK4543", &null_ops}, + {0x414C4326, "ALC100P", &null_ops}, {0x414C4710, "ALC200/200P", &null_ops}, - {0x414C4720, "ALC650", &null_ops}, + {0x414C4720, "ALC650", &default_digital_ops}, + {0x434D4941, "CMedia", &cmedia_ops, AC97_NO_PCM_VOLUME }, + {0x434D4942, "CMedia", &cmedia_ops, AC97_NO_PCM_VOLUME }, + {0x434D4961, "CMedia", &cmedia_digital_ops, AC97_NO_PCM_VOLUME }, {0x43525900, "Cirrus Logic CS4297", &default_ops}, {0x43525903, "Cirrus Logic CS4297", &default_ops}, {0x43525913, "Cirrus Logic CS4297A rev A", &default_ops}, @@ -132,6 +150,8 @@ {0x43525931, "Cirrus Logic CS4299 rev A", &crystal_digital_ops}, {0x43525933, "Cirrus Logic CS4299 rev C", &crystal_digital_ops}, {0x43525934, "Cirrus Logic CS4299 rev D", &crystal_digital_ops}, + {0x43585442, "CXT66", &default_ops, AC97_DELUDED_MODEM }, + {0x44543031, "Diamond Technology DT0893", &default_ops}, {0x45838308, "ESS Allegro ES1988", &null_ops}, {0x49434511, "ICE1232", &null_ops}, /* I hope --jk */ {0x4e534331, "National Semiconductor LM4549", &null_ops}, @@ -143,9 +163,11 @@ {0x54524106, "TriTech TR28026", &null_ops}, {0x54524108, "TriTech TR28028", &tritech_ops}, {0x54524123, "TriTech TR A5", &null_ops}, - {0x574D4C00, "Wolfson WM9700A", &wolfson_ops00}, - {0x574D4C03, "Wolfson WM9703/WM9707", &wolfson_ops03}, + {0x574D4C03, "Wolfson WM9703/07/08/17", &wolfson_ops03}, {0x574D4C04, "Wolfson WM9704M/WM9704Q", &wolfson_ops04}, + {0x574D4C05, "Wolfson WM9705/WM9710", &wolfson_ops05}, + {0x574D4C09, "Wolfson WM9709", &null_ops}, + {0x574D4C12, "Wolfson WM9711/9712", &wolfson_ops11}, {0x83847600, "SigmaTel STAC????", &null_ops}, {0x83847604, "SigmaTel STAC9701/3/4/5", &null_ops}, {0x83847605, "SigmaTel STAC9704", &null_ops}, @@ -271,6 +293,10 @@ [SOUND_MIXER_PHONEIN] = AC97_REC_PHONE }; +static LIST_HEAD(codecs); +static LIST_HEAD(codec_drivers); +static DECLARE_MUTEX(codec_sem); + /* reads the given OSS mixer from the ac97 the caller must have insured that the ac97 knows about that given mixer, and should be holding a spinlock for the card */ static int ac97_read_mixer(struct ac97_codec *codec, int oss_channel) @@ -445,7 +471,7 @@ } /* read or write the recmask, the ac97 can really have left and right recording - inputs independently set, but OSS doesn't seem to want us to express that to + inputs independantly set, but OSS doesn't seem to want us to express that to the user. the caller guarantees that we have a supported bit set, and they must be holding the card's spinlock */ static int ac97_recmask_io(struct ac97_codec *codec, int rw, int mask) @@ -487,8 +513,8 @@ if (cmd == SOUND_MIXER_INFO) { mixer_info info; - strlcpy(info.id, codec->name, sizeof(info.id)); - strlcpy(info.name, codec->name, sizeof(info.name)); + strncpy(info.id, codec->name, sizeof(info.id)); + strncpy(info.name, codec->name, sizeof(info.name)); info.modify_counter = codec->modcnt; if (copy_to_user((void *)arg, &info, sizeof(info))) return -EFAULT; @@ -496,8 +522,8 @@ } if (cmd == SOUND_OLD_MIXER_INFO) { _old_mixer_info info; - strlcpy(info.id, codec->name, sizeof(info.id)); - strlcpy(info.name, codec->name, sizeof(info.name)); + strncpy(info.id, codec->name, sizeof(info.id)); + strncpy(info.name, codec->name, sizeof(info.name)); if (copy_to_user((void *)arg, &info, sizeof(info))) return -EFAULT; return 0; @@ -678,6 +704,75 @@ } /** + * ac97_check_modem - Check if the Codec is a modem + * @codec: codec to check + * + * Return true if the device is an AC97 1.0 or AC97 2.0 modem + */ + +static int ac97_check_modem(struct ac97_codec *codec) +{ + /* Check for an AC97 1.0 soft modem (ID1) */ + if(codec->codec_read(codec, AC97_RESET) & 2) + return 1; + /* Check for an AC97 2.x soft modem */ + codec->codec_write(codec, AC97_EXTENDED_MODEM_ID, 0L); + if(codec->codec_read(codec, AC97_EXTENDED_MODEM_ID) & 1) + return 1; + return 0; +} + + +/** + * ac97_alloc_codec - Allocate an AC97 codec + * + * Returns a new AC97 codec structure. AC97 codecs may become + * refcounted soon so this interface is needed. Returns with + * one reference taken. + */ + +struct ac97_codec *ac97_alloc_codec(void) +{ + struct ac97_codec *codec = kmalloc(sizeof(struct ac97_codec), GFP_KERNEL); + if(!codec) + return NULL; + + memset(codec, 0, sizeof(*codec)); + spin_lock_init(&codec->lock); + INIT_LIST_HEAD(&codec->list); + return codec; +} + +EXPORT_SYMBOL(ac97_alloc_codec); + +/** + * ac97_release_codec - Release an AC97 codec + * @codec: codec to release + * + * Release an allocated AC97 codec. This will be refcounted in + * time but for the moment is trivial. Calls the unregister + * handler if the codec is now defunct. + */ + +void ac97_release_codec(struct ac97_codec *codec) +{ + /* Remove from the list first, we don't want to be + "rediscovered" */ + down(&codec_sem); + list_del(&codec->list); + up(&codec_sem); + /* + * The driver needs to deal with internal + * locking to avoid accidents here. + */ + if(codec->driver) + codec->driver->remove(codec, codec->driver); + kfree(codec); +} + +EXPORT_SYMBOL(ac97_release_codec); + +/** * ac97_probe_codec - Initialize and setup AC97-compatible codec * @codec: (in/out) Kernel info for a single AC97 codec * @@ -703,10 +798,13 @@ int ac97_probe_codec(struct ac97_codec *codec) { u16 id1, id2; - u16 audio, modem; + u16 audio; int i; char cidbuf[CODEC_ID_BUFSZ]; - + u16 f; + struct list_head *l; + struct ac97_driver *d; + /* probing AC97 codec, AC97 2.0 says that bit 15 of register 0x00 (reset) should * be read zero. * @@ -729,13 +827,9 @@ } /* probe for Modem Codec */ - codec->codec_write(codec, AC97_EXTENDED_MODEM_ID, 0L); - modem = codec->codec_read(codec, AC97_EXTENDED_MODEM_ID) & 1; - modem |= (audio&2); - audio &= ~2; - + codec->modem = ac97_check_modem(codec); codec->name = NULL; - codec->codec_ops = &null_ops; + codec->codec_ops = &default_ops; id1 = codec->codec_read(codec, AC97_VENDOR_ID1); id2 = codec->codec_read(codec, AC97_VENDOR_ID2); @@ -744,16 +838,51 @@ codec->type = ac97_codec_ids[i].id; codec->name = ac97_codec_ids[i].name; codec->codec_ops = ac97_codec_ids[i].ops; + codec->flags = ac97_codec_ids[i].flags; break; } } + + codec->model = (id1 << 16) | id2; + + f = codec->codec_read(codec, AC97_EXTENDED_STATUS); + if(f & 4) + codec->codec_ops = &default_digital_ops; + + /* A device which thinks its a modem but isnt */ + if(codec->flags & AC97_DELUDED_MODEM) + codec->modem = 0; + if (codec->name == NULL) codec->name = "Unknown"; printk(KERN_INFO "ac97_codec: AC97 %s codec, id: %s (%s)\n", - modem ? "Modem" : (audio ? "Audio" : ""), + codec->modem ? "Modem" : (audio ? "Audio" : ""), codec_id(id1, id2, cidbuf), codec->name); - return ac97_init_mixer(codec); + if(!ac97_init_mixer(codec)) + return 0; + + /* + * Attach last so the caller can override the mixer + * callbacks. + */ + + down(&codec_sem); + list_add(&codec->list, &codecs); + + list_for_each(l, &codec_drivers) { + d = list_entry(l, struct ac97_driver, list); + if ((codec->model ^ d->codec_id) & d->codec_mask) + continue; + if(d->probe(codec, d) == 0) + { + codec->driver = d; + break; + } + } + + up(&codec_sem); + return 1; } static int ac97_init_mixer(struct ac97_codec *codec) @@ -772,6 +901,7 @@ if (!(cap & 0x10)) codec->supported_mixers &= ~SOUND_MASK_ALTPCM; + /* detect bit resolution */ codec->codec_write(codec, AC97_MASTER_VOL_STEREO, 0x2020); if(codec->codec_read(codec, AC97_MASTER_VOL_STEREO) == 0x2020) @@ -800,6 +930,15 @@ ac97_set_mixer(codec, md->mixer, md->value); } + /* + * Volume is MUTE only on this device. We have to initialise + * it but its useless beyond that. + */ + if(codec->flags & AC97_NO_PCM_VOLUME) + { + codec->supported_mixers &= ~SOUND_MASK_PCM; + printk(KERN_WARNING "AC97 codec does not have proper volume support.\n"); + } return 1; } @@ -872,54 +1011,75 @@ return 0; } - -static int wolfson_init00(struct ac97_codec * codec) +static int cmedia_init(struct ac97_codec *codec) { - /* This initialization is suspect, but not known to be wrong. - It was copied from the initialization for the WM9704Q, but - that same sequence is known to fail for the WM9707. Thus - this warning may help someone with hardware to test - this code. */ - codec->codec_write(codec, 0x72, 0x0808); - codec->codec_write(codec, 0x74, 0x0808); - - // patch for DVD noise - codec->codec_write(codec, 0x5a, 0x0200); - - // init vol as PCM vol - codec->codec_write(codec, 0x70, - codec->codec_read(codec, AC97_PCMOUT_VOL)); - - codec->codec_write(codec, AC97_SURROUND_MASTER, 0x0000); + /* Initialise the CMedia 9739 */ + /* + We could set various options here + Register 0x20 bit 0x100 sets mic as center bass + Also do multi_channel_ctrl &=~0x3000 |=0x1000 + + For now we set up the GPIO and PC beep + */ + + u16 v; + + /* MIC */ + codec->codec_write(codec, 0x64, 0x3000); + v = codec->codec_read(codec, 0x64); + v &= ~0x8000; + codec->codec_write(codec, 0x64, v); + codec->codec_write(codec, 0x70, 0x0100); + codec->codec_write(codec, 0x72, 0x0020); return 0; } - + +#define AC97_WM97XX_FMIXER_VOL 0x72 +#define AC97_WM97XX_RMIXER_VOL 0x74 +#define AC97_WM97XX_TEST 0x5a +#define AC97_WM9704_RPCM_VOL 0x70 +#define AC97_WM9711_OUT3VOL 0x16 static int wolfson_init03(struct ac97_codec * codec) { /* this is known to work for the ViewSonic ViewPad 1000 */ - codec->codec_write(codec, 0x72, 0x0808); - codec->codec_write(codec, 0x20, 0x8000); + codec->codec_write(codec, AC97_WM97XX_FMIXER_VOL, 0x0808); + codec->codec_write(codec, AC97_GENERAL_PURPOSE, 0x8000); return 0; } - static int wolfson_init04(struct ac97_codec * codec) { - codec->codec_write(codec, 0x72, 0x0808); - codec->codec_write(codec, 0x74, 0x0808); + codec->codec_write(codec, AC97_WM97XX_FMIXER_VOL, 0x0808); + codec->codec_write(codec, AC97_WM97XX_RMIXER_VOL, 0x0808); // patch for DVD noise - codec->codec_write(codec, 0x5a, 0x0200); + codec->codec_write(codec, AC97_WM97XX_TEST, 0x0200); // init vol as PCM vol - codec->codec_write(codec, 0x70, + codec->codec_write(codec, AC97_WM9704_RPCM_VOL, codec->codec_read(codec, AC97_PCMOUT_VOL)); + /* set rear surround volume */ codec->codec_write(codec, AC97_SURROUND_MASTER, 0x0000); return 0; } +/* WM9705, WM9710 */ +static int wolfson_init05(struct ac97_codec * codec) +{ + /* set front mixer volume */ + codec->codec_write(codec, AC97_WM97XX_FMIXER_VOL, 0x0808); + return 0; +} + +/* WM9711, WM9712 */ +static int wolfson_init11(struct ac97_codec * codec) +{ + /* set out3 volume */ + codec->codec_write(codec, AC97_WM9711_OUT3VOL, 0x0808); + return 0; +} static int tritech_init(struct ac97_codec * codec) { @@ -980,26 +1140,115 @@ return 0; } +static int generic_digital_control(struct ac97_codec *codec, int slots, int rate, int mode) +{ + u16 reg; + + reg = codec->codec_read(codec, AC97_SPDIF_CONTROL); + + switch(rate) + { + /* Off by default */ + default: + case 0: + reg = codec->codec_read(codec, AC97_EXTENDED_STATUS); + codec->codec_write(codec, AC97_EXTENDED_STATUS, (reg & ~AC97_EA_SPDIF)); + if(rate == 0) + return 0; + return -EINVAL; + case 1: + reg = (reg & AC97_SC_SPSR_MASK) | AC97_SC_SPSR_48K; + break; + case 2: + reg = (reg & AC97_SC_SPSR_MASK) | AC97_SC_SPSR_44K; + break; + case 3: + reg = (reg & AC97_SC_SPSR_MASK) | AC97_SC_SPSR_32K; + break; + } + + reg &= ~AC97_SC_CC_MASK; + reg |= (mode & AUDIO_CCMASK) << 6; + + if(mode & AUDIO_DIGITAL) + reg |= 2; + if(mode & AUDIO_PRO) + reg |= 1; + if(mode & AUDIO_DRS) + reg |= 0x4000; + + codec->codec_write(codec, AC97_SPDIF_CONTROL, reg); + + reg = codec->codec_read(codec, AC97_EXTENDED_STATUS); + reg &= (AC97_EA_SLOT_MASK); + reg |= AC97_EA_VRA | AC97_EA_SPDIF | slots; + codec->codec_write(codec, AC97_EXTENDED_STATUS, reg); + + reg = codec->codec_read(codec, AC97_EXTENDED_STATUS); + if(!(reg & 0x0400)) + { + codec->codec_write(codec, AC97_EXTENDED_STATUS, reg & ~ AC97_EA_SPDIF); + return -EINVAL; + } + return 0; +} + /* - * Crystal digital audio control (CS4299 + * Crystal digital audio control (CS4299) */ -static int crystal_digital_control(struct ac97_codec *codec, int mode) +static int crystal_digital_control(struct ac97_codec *codec, int slots, int rate, int mode) { u16 cv; - switch(mode) + if(mode & AUDIO_DIGITAL) + return -EINVAL; + + switch(rate) { case 0: cv = 0x0; break; /* SPEN off */ - case 1: cv = 0x8004; break; /* 48KHz digital */ - case 2: cv = 0x8104; break; /* 44.1KHz digital */ + case 48000: cv = 0x8004; break; /* 48KHz digital */ + case 44100: cv = 0x8104; break; /* 44.1KHz digital */ + case 32768: /* 32Khz */ default: - return -1; /* Not supported yet(eg AC3) */ + return -EINVAL; } codec->codec_write(codec, 0x68, cv); return 0; } +/* + * CMedia digital audio control + * Needs more work. + */ + +static int cmedia_digital_control(struct ac97_codec *codec, int slots, int rate, int mode) +{ + u16 cv; + + if(mode & AUDIO_DIGITAL) + return -EINVAL; + + switch(rate) + { + case 0: cv = 0x0001; break; /* SPEN off */ + case 48000: cv = 0x0009; break; /* 48KHz digital */ + default: + return -EINVAL; + } + codec->codec_write(codec, 0x2A, 0x05c4); + codec->codec_write(codec, 0x6C, cv); + + /* Switch on mix to surround */ + cv = codec->codec_read(codec, 0x64); + cv &= ~0x0200; + if(mode) + cv |= 0x0200; + codec->codec_write(codec, 0x64, cv); + return 0; +} + + /* copied from drivers/sound/maestro.c */ #if 0 /* there has been 1 person on the planet with a pt101 that we know of. If they care, they can put this back in :) */ @@ -1137,4 +1386,67 @@ EXPORT_SYMBOL(ac97_restore_state); +/** + * ac97_register_driver - register a codec helper + * @driver: Driver handler + * + * Register a handler for codecs matching the codec id. The handler + * attach function is called for all present codecs and will be + * called when new codecs are discovered. + */ + +int ac97_register_driver(struct ac97_driver *driver) +{ + struct list_head *l; + struct ac97_codec *c; + + down(&codec_sem); + INIT_LIST_HEAD(&driver->list); + list_add(&driver->list, &codec_drivers); + + list_for_each(l, &codecs) + { + c = list_entry(l, struct ac97_codec, list); + if(c->driver != NULL || ((c->model ^ driver->codec_id) & driver->codec_mask)) + continue; + if(driver->probe(c, driver)) + continue; + c->driver = driver; + } + up(&codec_sem); + return 0; +} + +EXPORT_SYMBOL_GPL(ac97_register_driver); + +/** + * ac97_unregister_driver - unregister a codec helper + * @driver: Driver handler + * + * Register a handler for codecs matching the codec id. The handler + * attach function is called for all present codecs and will be + * called when new codecs are discovered. + */ + +void ac97_unregister_driver(struct ac97_driver *driver) +{ + struct list_head *l; + struct ac97_codec *c; + + down(&codec_sem); + list_del_init(&driver->list); + + list_for_each(l, &codecs) + { + c = list_entry(l, struct ac97_codec, list); + if(c->driver == driver) + driver->remove(c, driver); + c->driver = NULL; + } + + up(&codec_sem); +} + +EXPORT_SYMBOL_GPL(ac97_unregister_driver); + MODULE_LICENSE("GPL"); diff -Nru a/sound/oss/ac97_plugin_ad1980.c b/sound/oss/ac97_plugin_ad1980.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/ac97_plugin_ad1980.c Fri Jul 11 08:21:30 2003 @@ -0,0 +1,124 @@ +/* + ac97_plugin_ad1980.c Copyright (C) 2003 Red Hat, Inc. All rights reserved. + + The contents of this file are subject to the Open Software License version 1.1 + that can be found at http://www.opensource.org/licenses/osl-1.1.txt and is + included herein by reference. + + Alternatively, the contents of this file may be used under the + terms of the GNU General Public License version 2 (the "GPL") as + distributed in the kernel source COPYING file, in which + case the provisions of the GPL are applicable instead of the + above. If you wish to allow the use of your version of this file + only under the terms of the GPL and not to allow others to use + your version of this file under the OSL, indicate your decision + by deleting the provisions above and replace them with the notice + and other provisions required by the GPL. If you do not delete + the provisions above, a recipient may use your version of this + file under either the OSL or the GPL. + + Authors: Arjan van de Ven + + This is an example codec plugin. This one switches the connections + around to match the setups some vendors use with audio switched to + non standard front connectors not the normal rear ones + + This code primarily exists to demonstrate how to use the codec + interface + +*/ + +#include +#include +#include +#include + +/** + * ad1980_remove - codec remove callback + * @codec: The codec that is being removed + * + * This callback occurs when an AC97 codec is being removed. A + * codec remove call will not occur for a codec during that codec + * probe callback. + * + * Most drivers will need to lock their remove versus their + * use of the codec after the probe function. + */ + +static void ad1980_remove(struct ac97_codec *codec) +{ + /* Nothing to do in the simple example */ +} + + +/** + * ad1980_probe - codec found callback + * @codec: ac97 codec matching the idents + * @driver: ac97_driver it matched + * + * This entry point is called when a codec is found which matches + * the driver. At the point it is called the codec is basically + * operational, mixer operations have been initialised and can + * be overriden. Called in process context. The field driver_private + * is available for the driver to use to store stuff. + * + * The caller can claim the device by returning zero, or return + * a negative error code. + */ + +static int ad1980_probe(struct ac97_codec *codec, struct ac97_driver *driver) +{ + u16 control; + +#define AC97_AD_MISC 0x76 + + /* Switch the inputs/outputs over (from Dell code) */ + control = codec->codec_read(codec, AC97_AD_MISC); + codec->codec_write(codec, AC97_AD_MISC, control | 0x0420); + + /* We could refuse the device since we dont need to hang around, + but we will claim it */ + return 0; +} + + +static struct ac97_driver ad1980_driver = { + codec_id: 0x41445370, + codec_mask: 0xFFFFFFFF, + name: "AD1980 example", + probe: ad1980_probe, + remove: __devexit_p(ad1980_remove), +}; + +/** + * ad1980_exit - module exit path + * + * Our module is being unloaded. At this point unregister_driver + * will call back our remove handler for any existing codecs. You + * may not unregister_driver from interrupt context or from a + * probe/remove callback. + */ + +static void ad1980_exit(void) +{ + ac97_unregister_driver(&ad1980_driver); +} + +/** + * ad1980_init - set up ad1980 handlers + * + * After we call the register function it will call our probe + * function for each existing matching device before returning to us. + * Any devices appearing afterwards whose id's match the codec_id + * will also cause the probe function to be called. + * You may not register_driver from interrupt context or from a + * probe/remove callback. + */ + +static int ad1980_init(void) +{ + return ac97_register_driver(&ad1980_driver); +} + +module_init(ad1980_init); +module_exit(ad1980_exit); diff -Nru a/sound/oss/ad1889.c b/sound/oss/ad1889.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/ad1889.c Fri Jul 11 17:40:29 2003 @@ -0,0 +1,1086 @@ +/* + * Copyright 2001 Randolph Chung + * + * Analog Devices 1889 PCI audio driver (AD1819 AC97-compatible codec) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Notes: + * 1. Only flat DMA is supported; s-g is not supported right now + * + * + tausq: Anyway, to set up sample rates for D to A, you just use the sample rate on the codec. For A to D, you need to set the codec always to 48K (using the split sample rate feature on the codec) and then set the resampler on the AD1889 to the sample rate you want. + Also, when changing the sample rate on the codec you need to power it down and re power it up for the change to take effect! + * + * $Id: ad1889.c,v 1.3 2002/10/19 21:31:44 grundler Exp $ + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "ad1889.h" + +#define DBG(fmt, arg...) printk(fmt, ##arg) +#define DEVNAME "ad1889" + +#define NR_HW_CH 4 +#define DAC_RUNNING 1 +#define ADC_RUNNING 2 + +#define UNDERRUN(dev) (0) + +#define AD1889_READW(dev,reg) readw(dev->regbase + reg) +#define AD1889_WRITEW(dev,reg,val) writew((val), dev->regbase + reg) +#define AD1889_READL(dev,reg) readl(dev->regbase + reg) +#define AD1889_WRITEL(dev,reg,val) writel((val), dev->regbase + reg) + +//now 100ms +#define WAIT_10MS() do { int __i; for (__i = 0; __i < 100; __i++) udelay(1000); } while(0) + +/* currently only support a single device */ +static ad1889_dev_t *ad1889_dev = NULL; + +/************************* helper routines ***************************** */ +static inline void ad1889_set_wav_rate(ad1889_dev_t *dev, int rate) +{ + dev->state[AD_WAV_STATE].dmabuf.rate = rate; + AD1889_WRITEW(dev, AD_DSWAS, rate); +} + +static inline void ad1889_set_adc_rate(ad1889_dev_t *dev, int rate) +{ + dev->state[AD_ADC_STATE].dmabuf.rate = rate; + AD1889_WRITEW(dev, AD_DSRES, rate); +} + +static inline void ad1889_set_wav_fmt(ad1889_dev_t *dev, int fmt) +{ + u16 tmp; + + tmp = AD1889_READW(ad1889_dev, AD_DSWSMC); + if (fmt == AFMT_S16_LE) { + //tmp |= 0x0100; /* set WA16 */ + tmp |= 0x0300; /* set WA16 stereo */ + } else if (fmt == AFMT_U8) { + tmp &= ~0x0100; /* clear WA16 */ + } + AD1889_WRITEW(ad1889_dev, AD_DSWSMC, tmp); +} + +static inline void ad1889_set_adc_fmt(ad1889_dev_t *dev, int fmt) +{ + u16 tmp; + + tmp = AD1889_READW(ad1889_dev, AD_DSRAMC); + if (fmt == AFMT_S16_LE) { + tmp |= 0x0100; /* set WA16 */ + } else if (fmt == AFMT_U8) { + tmp &= ~0x0100; /* clear WA16 */ + } + AD1889_WRITEW(ad1889_dev, AD_DSRAMC, tmp); +} + +static void ad1889_start_wav(ad1889_state_t *state) +{ + unsigned long flags; + struct dmabuf *dmabuf = &state->dmabuf; + int cnt; + u16 tmp; + + spin_lock_irqsave(&state->card->lock, flags); + + if (dmabuf->dma_len) /* DMA already in flight */ + goto skip_dma; + + /* setup dma */ + cnt = dmabuf->wr_ptr - dmabuf->rd_ptr; + if (cnt == 0) /* done - don't need to do anything */ + goto skip_dma; + + /* If the wr_ptr has wrapped, only map to the end */ + if (cnt < 0) + cnt = DMA_SIZE - dmabuf->rd_ptr; + + dmabuf->dma_handle = pci_map_single(ad1889_dev->pci, + dmabuf->rawbuf + dmabuf->rd_ptr, + cnt, PCI_DMA_TODEVICE); + dmabuf->dma_len = cnt; + dmabuf->ready = 1; + + /* load up the current register set */ + AD1889_WRITEL(ad1889_dev, AD_DMAWAVCC, cnt); + AD1889_WRITEL(ad1889_dev, AD_DMAWAVICC, cnt); + AD1889_WRITEL(ad1889_dev, AD_DMAWAVCA, dmabuf->dma_handle); + + /* TODO: for now we load the base registers with the same thing */ + AD1889_WRITEL(ad1889_dev, AD_DMAWAVBC, cnt); + AD1889_WRITEL(ad1889_dev, AD_DMAWAVIBC, cnt); + AD1889_WRITEL(ad1889_dev, AD_DMAWAVBA, dmabuf->dma_handle); + + /* and we're off to the races... */ + AD1889_WRITEL(ad1889_dev, AD_DMACHSS, 0x8); + tmp = AD1889_READW(ad1889_dev, AD_DSWSMC); + tmp |= 0x0400; /* set WAEN */ + AD1889_WRITEW(ad1889_dev, AD_DSWSMC, tmp); + (void) AD1889_READW(ad1889_dev, AD_DSWSMC); /* flush posted PCI write */ + + dmabuf->enable |= DAC_RUNNING; + +skip_dma: + spin_unlock_irqrestore(&state->card->lock, flags); +} + + +static void ad1889_stop_wav(ad1889_state_t *state) +{ + unsigned long flags; + struct dmabuf *dmabuf = &state->dmabuf; + + spin_lock_irqsave(&state->card->lock, flags); + + if (dmabuf->enable & DAC_RUNNING) { + u16 tmp; + unsigned long cnt = dmabuf->dma_len; + + tmp = AD1889_READW(ad1889_dev, AD_DSWSMC); + tmp &= ~0x0400; /* clear WAEN */ + AD1889_WRITEW(ad1889_dev, AD_DSWSMC, tmp); + (void) AD1889_READW(ad1889_dev, AD_DSWSMC); /* flush posted PCI write */ + pci_unmap_single(ad1889_dev->pci, dmabuf->dma_handle, + cnt, PCI_DMA_TODEVICE); + + dmabuf->enable &= ~DAC_RUNNING; + + /* update dma pointers */ + dmabuf->rd_ptr += cnt; + dmabuf->rd_ptr &= (DMA_SIZE - 1); + + dmabuf->dma_handle = 0; + dmabuf->dma_len = 0; + dmabuf->ready = 0; + + wake_up(&dmabuf->wait); + } + + spin_unlock_irqrestore(&state->card->lock, flags); +} + + +#if 0 +static void ad1889_startstop_adc(ad1889_state_t *state, int start) +{ + u16 tmp; + unsigned long flags; + + spin_lock_irqsave(&state->card->lock, flags); + + tmp = AD1889_READW(ad1889_dev, AD_DSRAMC); + if (start) { + state->dmabuf.enable |= ADC_RUNNING; + tmp |= 0x0004; /* set ADEN */ + } else { + state->dmabuf.enable &= ~ADC_RUNNING; + tmp &= ~0x0004; /* clear ADEN */ + } + AD1889_WRITEW(ad1889_dev, AD_DSRAMC, tmp); + + spin_unlock_irqrestore(&state->card->lock, flags); +} +#endif + +static ad1889_dev_t *ad1889_alloc_dev(struct pci_dev *pci) +{ + ad1889_dev_t *dev; + struct dmabuf *dmabuf; + int i; + + if ((dev = kmalloc(sizeof(ad1889_dev_t), GFP_KERNEL)) == NULL) + return NULL; + memset(dev, 0, sizeof(ad1889_dev_t)); + spin_lock_init(&dev->lock); + dev->pci = pci; + + for (i = 0; i < AD_MAX_STATES; i++) { + dev->state[i].card = dev; + init_MUTEX(&dev->state[i].sem); + init_waitqueue_head(&dev->state[i].dmabuf.wait); + } + + /* allocate dma buffer */ + + for (i = 0; i < AD_MAX_STATES; i++) { + dmabuf = &dev->state[i].dmabuf; + dmabuf->rawbuf = kmalloc(DMA_SIZE, GFP_KERNEL|GFP_DMA); + if (!dmabuf->rawbuf) + goto err_free_dmabuf; + dmabuf->rawbuf_size = DMA_SIZE; + dmabuf->dma_handle = 0; + dmabuf->rd_ptr = dmabuf->wr_ptr = dmabuf->dma_len = 0UL; + dmabuf->ready = 0; + dmabuf->rate = 44100; + } +out: + return dev; + +err_free_dmabuf: + while (--i >= 0) + kfree(dev->state[i].dmabuf.rawbuf); + kfree(dev); + return NULL; +} + +static void ad1889_free_dev(ad1889_dev_t *dev) +{ + int j; + struct dmabuf *dmabuf; + + if (dev == NULL) + return; + + if (dev->ac97_codec) + ac97_release_codec(dev->ac97_codec); + + for (j = 0; j < AD_MAX_STATES; j++) { + dmabuf = &dev->state[j].dmabuf; + if (dmabuf->rawbuf != NULL) + kfree(dmabuf->rawbuf); + } + + kfree(dev); +} + +static inline void ad1889_trigger_playback(ad1889_dev_t *dev) +{ +#if 0 + u32 val; + struct dmabuf *dmabuf = &dev->state[AD_WAV_STATE].dmabuf; +#endif + + ad1889_start_wav(&dev->state[AD_WAV_STATE]); +} + +int ad1889_read_proc (char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + char *out = page; + int len, i; + ad1889_dev_t *dev = data; + ad1889_reg_t regs[] = { + { "WSMC", AD_DSWSMC, 16 }, + { "RAMC", AD_DSRAMC, 16 }, + { "WADA", AD_DSWADA, 16 }, + { "SYDA", AD_DSSYDA, 16 }, + { "WAS", AD_DSWAS, 16 }, + { "RES", AD_DSRES, 16 }, + { "CCS", AD_DSCCS, 16 }, + { "ADCBA", AD_DMAADCBA, 32 }, + { "ADCCA", AD_DMAADCCA, 32 }, + { "ADCBC", AD_DMAADCBC, 32 }, + { "ADCCC", AD_DMAADCCC, 32 }, + { "ADCIBC", AD_DMAADCIBC, 32 }, + { "ADCICC", AD_DMAADCICC, 32 }, + { "ADCCTRL", AD_DMAADCCTRL, 16 }, + { "WAVBA", AD_DMAWAVBA, 32 }, + { "WAVCA", AD_DMAWAVCA, 32 }, + { "WAVBC", AD_DMAWAVBC, 32 }, + { "WAVCC", AD_DMAWAVCC, 32 }, + { "WAVIBC", AD_DMAWAVIBC, 32 }, + { "WAVICC", AD_DMAWAVICC, 32 }, + { "WAVCTRL", AD_DMAWAVCTRL, 16 }, + { "DISR", AD_DMADISR, 32 }, + { "CHSS", AD_DMACHSS, 32 }, + { "IPC", AD_GPIOIPC, 16 }, + { "OP", AD_GPIOOP, 16 }, + { "IP", AD_GPIOIP, 16 }, + { "ACIC", AD_ACIC, 16 }, + { "AC97_RESET", 0x100 + AC97_RESET, 16 }, + { "AC97_MASTER_VOL_STEREO", 0x100 + AC97_MASTER_VOL_STEREO, 16 }, + { "AC97_HEADPHONE_VOL", 0x100 + AC97_HEADPHONE_VOL, 16 }, + { "AC97_MASTER_VOL_MONO", 0x100 + AC97_MASTER_VOL_MONO, 16 }, + { "AC97_MASTER_TONE", 0x100 + AC97_MASTER_TONE, 16 }, + { "AC97_PCBEEP_VOL", 0x100 + AC97_PCBEEP_VOL, 16 }, + { "AC97_PHONE_VOL", 0x100 + AC97_PHONE_VOL, 16 }, + { "AC97_MIC_VOL", 0x100 + AC97_MIC_VOL, 16 }, + { "AC97_LINEIN_VOL", 0x100 + AC97_LINEIN_VOL, 16 }, + { "AC97_CD_VOL", 0x100 + AC97_CD_VOL, 16 }, + { "AC97_VIDEO_VOL", 0x100 + AC97_VIDEO_VOL, 16 }, + { "AC97_AUX_VOL", 0x100 + AC97_AUX_VOL, 16 }, + { "AC97_PCMOUT_VOL", 0x100 + AC97_PCMOUT_VOL, 16 }, + { "AC97_RECORD_SELECT", 0x100 + AC97_RECORD_SELECT, 16 }, + { "AC97_RECORD_GAIN", 0x100 + AC97_RECORD_GAIN, 16 }, + { "AC97_RECORD_GAIN_MIC", 0x100 + AC97_RECORD_GAIN_MIC, 16 }, + { "AC97_GENERAL_PURPOSE", 0x100 + AC97_GENERAL_PURPOSE, 16 }, + { "AC97_3D_CONTROL", 0x100 + AC97_3D_CONTROL, 16 }, + { "AC97_MODEM_RATE", 0x100 + AC97_MODEM_RATE, 16 }, + { "AC97_POWER_CONTROL", 0x100 + AC97_POWER_CONTROL, 16 }, + { 0 } + }; + + if (dev == NULL) + return -ENODEV; + + for (i = 0; regs[i].name != 0; i++) + out += sprintf(out, "%s: 0x%0*x\n", regs[i].name, + regs[i].width >> 2, + (regs[i].width == 16 + ? AD1889_READW(dev, regs[i].offset) + : AD1889_READL(dev, regs[i].offset))); + + for (i = 0; i < AD_MAX_STATES; i++) { + out += sprintf(out, "DMA status for %s:\n", + (i == AD_WAV_STATE ? "WAV" : "ADC")); + out += sprintf(out, "\t\t0x%p (IOVA: 0x%u)\n", + dev->state[i].dmabuf.rawbuf, + dev->state[i].dmabuf.dma_handle); + + out += sprintf(out, "\tread ptr: offset %u\n", + (unsigned int)dev->state[i].dmabuf.rd_ptr); + out += sprintf(out, "\twrite ptr: offset %u\n", + (unsigned int)dev->state[i].dmabuf.wr_ptr); + out += sprintf(out, "\tdma len: offset %u\n", + (unsigned int)dev->state[i].dmabuf.dma_len); + } + + len = out - page - off; + if (len < count) { + *eof = 1; + if (len <= 0) return 0; + } else { + len = count; + } + *start = page + off; + return len; +} + +/***************************** DMA interfaces ************************** */ +#if 0 +static inline unsigned long ad1889_get_dma_addr(ad1889_state_t *state) +{ + struct dmabuf *dmabuf = &state->dmabuf; + u32 offset; + + if (!(dmabuf->enable & (DAC_RUNNING | ADC_RUNNING))) { + printk(KERN_ERR DEVNAME ": get_dma_addr called without dma enabled\n"); + return 0; + } + + if (dmabuf->enable & DAC_RUNNING) + offset = le32_to_cpu(AD1889_READL(state->card, AD_DMAWAVBA)); + else + offset = le32_to_cpu(AD1889_READL(state->card, AD_DMAADCBA)); + + return (unsigned long)bus_to_virt((unsigned long)offset) - (unsigned long)dmabuf->rawbuf; +} + +static void ad1889_update_ptr(ad1889_dev_t *dev, int wake) +{ + ad1889_state_t *state; + struct dmabuf *dmabuf; + unsigned long hwptr; + int diff; + + /* check ADC first */ + state = &dev->adc_state; + dmabuf = &state->dmabuf; + if (dmabuf->enable & ADC_RUNNING) { + hwptr = ad1889_get_dma_addr(state); + diff = (dmabuf->dmasize + hwptr - dmabuf->hwptr) % dmabuf->dmasize; + + dmabuf->hwptr = hwptr; + dmabuf->total_bytes += diff; + dmabuf->count += diff; + if (dmabuf->count > dmabuf->dmasize) + dmabuf->count = dmabuf->dmasize; + + if (dmabuf->mapped) { + if (wake & dmabuf->count >= dmabuf->fragsize) + wake_up(&dmabuf->wait); + } else { + if (wake & dmabuf->count > 0) + wake_up(&dmabuf->wait); + } + } + + /* check DAC */ + state = &dev->wav_state; + dmabuf = &state->dmabuf; + if (dmabuf->enable & DAC_RUNNING) { +XXX + +} +#endif + +/************************* /dev/dsp interfaces ************************* */ + +static ssize_t ad1889_read(struct file *file, char *buffer, size_t count, + loff_t *ppos) +{ + return 0; +} + +static ssize_t ad1889_write(struct file *file, const char *buffer, size_t count, + loff_t *ppos) +{ + ad1889_dev_t *dev = (ad1889_dev_t *)file->private_data; + ad1889_state_t *state = &dev->state[AD_WAV_STATE]; + volatile struct dmabuf *dmabuf = &state->dmabuf; + ssize_t ret = 0; + DECLARE_WAITQUEUE(wait, current); + + if (ppos != &file->f_pos) + return -ESPIPE; + + down(&state->sem); +#if 0 + if (dmabuf->mapped) { + ret = -ENXIO; + goto err1; + } +#endif + if (!access_ok(VERIFY_READ, buffer, count)) { + ret = -EFAULT; + goto err1; + } + + add_wait_queue(&state->dmabuf.wait, &wait); + + /* start filling dma buffer.... */ + while (count > 0) { + long rem; + long cnt = count; + unsigned long flags; + + + for (;;) { + long used_bytes; + long timeout; /* max time for DMA in jiffies */ + + /* buffer is full if wr catches up to rd */ + spin_lock_irqsave(&state->card->lock, flags); + used_bytes = dmabuf->wr_ptr - dmabuf->rd_ptr; + timeout = (dmabuf->dma_len * HZ) / dmabuf->rate; + spin_unlock_irqrestore(&state->card->lock, flags); + + /* adjust for buffer wrap around */ + used_bytes = (used_bytes + DMA_SIZE) & (DMA_SIZE - 1); + + /* If at least one page unused */ + if (used_bytes < (DMA_SIZE - 0x1000)) + break; + + /* dma buffer full */ + + if (file->f_flags & O_NONBLOCK) { + ret = -EAGAIN; + goto err2; + } + + set_current_state(TASK_INTERRUPTIBLE); + if (!schedule_timeout(timeout + 1)) + printk(KERN_WARNING "AD1889 timeout(%ld) r/w %lx/%lx len %lx\n", + timeout+1, + dmabuf->rd_ptr, dmabuf->wr_ptr, + dmabuf->dma_len); + + if (signal_pending(current)) { + ret = -ERESTARTSYS; + goto err2; + } + + } + + /* watch out for wrapping around static buffer */ + spin_lock_irqsave(&state->card->lock, flags); + rem = DMA_SIZE - dmabuf->wr_ptr; + if (cnt > rem) + cnt = rem; + + rem = dmabuf->wr_ptr; + + /* update dma pointers */ + dmabuf->wr_ptr += cnt; + dmabuf->wr_ptr &= DMA_SIZE - 1; /* wrap ptr if necessary */ + spin_unlock_irqrestore(&state->card->lock, flags); + + /* transfer unwrapped chunk */ + if (copy_from_user(dmabuf->rawbuf + rem, buffer, cnt)) { + ret = -EFAULT; + goto err2; + } + + DBG("Writing 0x%lx bytes to +0x%lx\n", cnt, rem); + + /* update counters */ + count -= cnt; + buffer += cnt; + ret += cnt; + + /* we have something to play - go play it! */ + ad1889_trigger_playback(dev); + } + +err2: + remove_wait_queue(&state->dmabuf.wait, &wait); +err1: + up(&state->sem); + return ret; +} + +static unsigned int ad1889_poll(struct file *file, struct poll_table_struct *wait) +{ + unsigned int mask = 0; +#if 0 + ad1889_dev_t *dev = (ad1889_dev_t *)file->private_data; + ad1889_state_t *state = NULL; + struct dmabuf *dmabuf; + unsigned long flags; + + if (!(file->f_mode & (FMODE_READ | FMODE_WRITE))) + return -EINVAL; + + if (file->f_mode & FMODE_WRITE) { + state = &dev->state[AD_WAV_STATE]; + if (!state) return 0; + dmabuf = &state->dmabuf; + poll_wait(file, &dmabuf->wait, wait); + } + + if (file->f_mode & FMODE_READ) { + state = &dev->state[AD_ADC_STATE]; + if (!state) return 0; + dmabuf = &state->dmabuf; + poll_wait(file, &dmabuf->wait, wait); + } + + spin_lock_irqsave(&dev->lock, flags); + ad1889_update_ptr(dev, 0); + + if (file->f_mode & FMODE_WRITE) { + state = &dev->state[WAV_STATE]; + dmabuf = &state->dmabuf; + if (dmabuf->mapped) { + if (dmabuf->count >= (int)dmabuf->fragsize) + mask |= POLLOUT | POLLWRNORM; + } else { + if ((int)dmabuf->dmasize >= dmabuf->count + + (int)dmabuf->fragsize) + mask |= POLLOUT | POLLWRNORM; + } + } + + if (file ->f_mode & FMODE_READ) { + state = &dev->state[AD_ADC_STATE]; + dmabuf = &state->dmabuf; + if (dmabuf->count >= (int)dmabuf->fragsize) + mask |= POLLIN | POLLRDNORM; + } + spin_unlock_irqrestore(&dev->lock, flags); + +#endif + return mask; +} + +static int ad1889_mmap(struct file *file, struct vm_area_struct *vma) +{ + return 0; +} + +static int ad1889_ioctl(struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg) +{ + int val = 0; + ad1889_dev_t *dev = (ad1889_dev_t *)file->private_data; + struct dmabuf *dmabuf; + audio_buf_info abinfo; + + switch (cmd) + { + case OSS_GETVERSION: + return put_user(SOUND_VERSION, (int *)arg); + + case SNDCTL_DSP_RESET: + break; + + case SNDCTL_DSP_SYNC: + break; + + case SNDCTL_DSP_SPEED: + /* set sampling rate */ + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val > 5400 && val < 48000) + { + if (file->f_mode & FMODE_WRITE) + AD1889_WRITEW(ad1889_dev, AD_DSWAS, val); + if (file->f_mode & FMODE_READ) + AD1889_WRITEW(ad1889_dev, AD_DSRES, val); + } + return 0; + + case SNDCTL_DSP_STEREO: /* undocumented? */ + if (get_user(val, (int *)arg)) + return -EFAULT; + if (file->f_mode & FMODE_READ) { + val = AD1889_READW(ad1889_dev, AD_DSWSMC); + if (val) { + val |= 0x0200; /* set WAST */ + } else { + val &= ~0x0200; /* clear WAST */ + } + AD1889_WRITEW(ad1889_dev, AD_DSWSMC, val); + } + if (file->f_mode & FMODE_WRITE) { + val = AD1889_READW(ad1889_dev, AD_DSRAMC); + if (val) { + val |= 0x0002; /* set ADST */ + } else { + val &= ~0x0002; /* clear ADST */ + } + AD1889_WRITEW(ad1889_dev, AD_DSRAMC, val); + } + + return 0; + + case SNDCTL_DSP_GETBLKSIZE: + return put_user(DMA_SIZE, (int *)arg); + + case SNDCTL_DSP_GETFMTS: + return put_user(AFMT_S16_LE|AFMT_U8, (int *)arg); + + case SNDCTL_DSP_SETFMT: + if (get_user(val, (int *)arg)) + return -EFAULT; + + if (file->f_mode & FMODE_READ) + ad1889_set_adc_fmt(dev, val); + + if (file->f_mode & FMODE_WRITE) + ad1889_set_wav_fmt(dev, val); + + return put_user(val, (int *)arg); + + case SNDCTL_DSP_CHANNELS: + break; + + case SNDCTL_DSP_POST: + /* send all data to device */ + break; + + case SNDCTL_DSP_SUBDIVIDE: + break; + + case SNDCTL_DSP_SETFRAGMENT: + /* not supported; uses fixed fragment sizes */ + return put_user(DMA_SIZE, (int *)arg); + + case SNDCTL_DSP_GETOSPACE: + case SNDCTL_DSP_GETISPACE: + /* space left in dma buffers */ + if (cmd == SNDCTL_DSP_GETOSPACE) + dmabuf = &dev->state[AD_WAV_STATE].dmabuf; + else + dmabuf = &dev->state[AD_ADC_STATE].dmabuf; + abinfo.fragments = 1; + abinfo.fragstotal = 1; + abinfo.fragsize = DMA_SIZE; + abinfo.bytes = DMA_SIZE; + return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; + case SNDCTL_DSP_NONBLOCK: + file->f_flags |= O_NONBLOCK; + return 0; + + case SNDCTL_DSP_GETCAPS: + return put_user(0, (int *)arg); + + case SNDCTL_DSP_GETTRIGGER: + case SNDCTL_DSP_SETTRIGGER: + break; + + case SNDCTL_DSP_GETIPTR: + case SNDCTL_DSP_GETOPTR: + break; + + case SNDCTL_DSP_SETDUPLEX: + break; + + case SNDCTL_DSP_GETODELAY: + break; + + case SOUND_PCM_READ_RATE: + return put_user(AD1889_READW(ad1889_dev, AD_DSWAS), (int *)arg); + + case SOUND_PCM_READ_CHANNELS: + case SOUND_PCM_READ_BITS: + break; + + case SNDCTL_DSP_MAPINBUF: + case SNDCTL_DSP_MAPOUTBUF: + case SNDCTL_DSP_SETSYNCRO: + case SOUND_PCM_WRITE_FILTER: + case SOUND_PCM_READ_FILTER: + break; + + default: + break; + } + + return -ENOTTY; +} + +static int ad1889_open(struct inode *inode, struct file *file) +{ + /* check minor; only support /dev/dsp atm */ + if (minor(inode->i_rdev) != 3) + return -ENXIO; + + file->private_data = ad1889_dev; + + ad1889_set_wav_rate(ad1889_dev, 44100); + ad1889_set_wav_fmt(ad1889_dev, AFMT_S16_LE); + AD1889_WRITEW(ad1889_dev, AD_DSWADA, 0x0404); /* attenuation */ + MOD_INC_USE_COUNT; + return 0; +} + +static int ad1889_release(struct inode *inode, struct file *file) +{ + /* if we have state free it here */ + MOD_DEC_USE_COUNT; + return 0; +} + +static struct file_operations ad1889_fops = { + llseek: no_llseek, + read: ad1889_read, + write: ad1889_write, + poll: ad1889_poll, + ioctl: ad1889_ioctl, + mmap: ad1889_mmap, + open: ad1889_open, + release: ad1889_release, +}; + +/************************* /dev/mixer interfaces ************************ */ +static int ad1889_mixer_open(struct inode *inode, struct file *file) +{ + if (ad1889_dev->ac97_codec->dev_mixer != minor(inode->i_rdev)) + return -ENODEV; + + file->private_data = ad1889_dev->ac97_codec; + MOD_INC_USE_COUNT; + return 0; +} + +static int ad1889_mixer_release(struct inode *inode, struct file *file) +{ + MOD_DEC_USE_COUNT; + return 0; +} + +static int ad1889_mixer_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct ac97_codec *codec = (struct ac97_codec *)file->private_data; + return codec->mixer_ioctl(codec, cmd, arg); +} + +static struct file_operations ad1889_mixer_fops = { + llseek: no_llseek, + ioctl: ad1889_mixer_ioctl, + open: ad1889_mixer_open, + release: ad1889_mixer_release, +}; + +/************************* AC97 interfaces ****************************** */ +static void ad1889_codec_write(struct ac97_codec *ac97, u8 reg, u16 val) +{ + ad1889_dev_t *dev = ac97->private_data; + + //DBG("Writing 0x%x to 0x%lx\n", val, dev->regbase + 0x100 + reg); + AD1889_WRITEW(dev, 0x100 + reg, val); +} + +static u16 ad1889_codec_read(struct ac97_codec *ac97, u8 reg) +{ + ad1889_dev_t *dev = ac97->private_data; + //DBG("Reading from 0x%lx\n", dev->regbase + 0x100 + reg); + return AD1889_READW(dev, 0x100 + reg); +} + +static int ad1889_ac97_init(ad1889_dev_t *dev, int id) +{ + struct ac97_codec *ac97; + u16 eid; + + if ((ac97 = ac97_alloc_codec()) == NULL) + return -ENOMEM; + + ac97->private_data = dev; + ac97->id = id; + + ac97->codec_read = ad1889_codec_read; + ac97->codec_write = ad1889_codec_write; + + if (ac97_probe_codec(ac97) == 0) { + printk(DEVNAME ": ac97_probe_codec failed\n"); + goto out_free; + } + + eid = ad1889_codec_read(ac97, AC97_EXTENDED_ID); + if (eid == 0xffffff) { + printk(KERN_WARNING DEVNAME ": no codec attached?\n"); + goto out_free; + } + + dev->ac97_features = eid; + + if ((ac97->dev_mixer = register_sound_mixer(&ad1889_mixer_fops, -1)) < 0) { + printk(KERN_ERR DEVNAME ": cannot register mixer\n"); + goto out_free; + } + + dev->ac97_codec = ac97; + return 0; + +out_free: + ac97_release_codec(ac97); + return -ENODEV; +} + +static int ad1889_aclink_reset(struct pci_dev * pcidev) +{ + u16 stat; + int retry = 200; + ad1889_dev_t *dev = pci_get_drvdata(pcidev); + + AD1889_WRITEW(dev, AD_DSCCS, 0x8000); /* turn on clock */ + AD1889_READW(dev, AD_DSCCS); + + WAIT_10MS(); + + stat = AD1889_READW(dev, AD_ACIC); + stat |= 0x0002; /* Reset Disable */ + AD1889_WRITEW(dev, AD_ACIC, stat); + (void) AD1889_READW(dev, AD_ACIC); /* flush posted write */ + + udelay(10); + + stat = AD1889_READW(dev, AD_ACIC); + stat |= 0x0001; /* Interface Enable */ + AD1889_WRITEW(dev, AD_ACIC, stat); + + do { + if (AD1889_READW(dev, AD_ACIC) & 0x8000) /* Ready */ + break; + WAIT_10MS(); + retry--; + } while (retry > 0); + + if (!retry) { + printk(KERN_ERR "ad1889_aclink_reset: codec is not ready [0x%x]\n", + AD1889_READW(dev, AD_ACIC)); + return -EBUSY; + } + + /* TODO reset AC97 codec */ + /* TODO set wave/adc pci ctrl status */ + + stat = AD1889_READW(dev, AD_ACIC); + stat |= 0x0004; /* Audio Stream Output Enable */ + AD1889_WRITEW(dev, AD_ACIC, stat); + return 0; +} + +/************************* PCI interfaces ****************************** */ +/* PCI device table */ +static struct pci_device_id ad1889_id_tbl[] __devinitdata = { + { PCI_VENDOR_ID_ANALOG_DEVICES, PCI_DEVICE_ID_AD1889JS, PCI_ANY_ID, + PCI_ANY_ID, 0, 0, (unsigned long)DEVNAME }, + { }, +}; +MODULE_DEVICE_TABLE(pci, ad1889_id_tbl); + +static irqreturn_t ad1889_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + u32 stat; + ad1889_dev_t *dev = (ad1889_dev_t *)dev_id; + + stat = AD1889_READL(dev, AD_DMADISR); + + /* clear ISR */ + AD1889_WRITEL(dev, AD_DMADISR, stat); + + if (stat & 0x8) { /* WAVI */ + DBG("WAV interrupt\n"); + dev->stats.wav_intrs++; + if (dev->state[AD_WAV_STATE].dmabuf.ready) { + ad1889_stop_wav(&dev->state[AD_WAV_STATE]); /* clean up */ + ad1889_start_wav(&dev->state[AD_WAV_STATE]); /* start new */ + } + + } + + if ((stat & 0x2) && dev->state[AD_ADC_STATE].dmabuf.ready) { /* ADCI */ + DBG("ADC interrupt\n"); + dev->stats.adc_intrs++; + } + if(stat) + return IRQ_HANDLED; + return IRQ_NONE; +} + +static void ad1889_initcfg(ad1889_dev_t *dev) +{ + u16 tmp; + + /* make sure the interrupt bits are setup the way we want */ + tmp = AD1889_READW(dev, AD_DMAWAVCTRL); + tmp &= ~0x00ff; /* flat dma, no sg, mask out the intr bits */ + tmp |= 0x0004; /* intr on count, loop */ + AD1889_WRITEW(dev, AD_DMAWAVCTRL, tmp); + + /* unmute... */ + tmp = AD1889_READW(dev, AD_DSWADA); + tmp &= ~0x8080; + AD1889_WRITEW(dev, AD_DSWADA, tmp); +} + +static int __devinit ad1889_probe(struct pci_dev *pcidev, const struct pci_device_id *ent) +{ + int err; + ad1889_dev_t *dev; + unsigned long bar; + struct proc_dir_entry *proc_root = NULL; + + if ((err = pci_enable_device(pcidev)) != 0) { + printk(KERN_ERR DEVNAME ": pci_enable_device failed\n"); + return err; + } + + pci_set_master(pcidev); + if ((dev = ad1889_alloc_dev(pcidev)) == NULL) { + printk(KERN_ERR DEVNAME ": cannot allocate memory for device\n"); + return -ENOMEM; + } + pci_set_drvdata(pcidev, dev); + bar = pci_resource_start(pcidev, 0); + + if (!(pci_resource_flags(pcidev, 0) & IORESOURCE_MEM)) { + printk(KERN_ERR DEVNAME ": memory region not assigned\n"); + goto err_free_mem; + } + + if (request_irq(pcidev->irq, ad1889_interrupt, SA_SHIRQ, DEVNAME, dev) != 0) { + printk(KERN_ERR DEVNAME ": unable to request interrupt\n"); + goto err_free_mem; + } + + request_mem_region(bar, AD_DSIOMEMSIZE, DEVNAME); + dev->regbase = (unsigned long)ioremap_nocache(bar, AD_DSIOMEMSIZE); + + printk(KERN_INFO DEVNAME ": %s at 0x%lx IRQ %d\n", + (char *)ent->driver_data, dev->regbase, pcidev->irq); + + if (ad1889_aclink_reset(pcidev) != 0) + goto err_free_mem; + + /* register /dev/dsp */ + if ((dev->dev_audio = register_sound_dsp(&ad1889_fops, -1)) < 0) { + printk(KERN_ERR DEVNAME ": cannot register /dev/dsp\n"); + goto err_free_irq; + } + + if ((err = ad1889_ac97_init(dev, 0)) != 0) + goto err_free_dsp; + + if (((proc_root = proc_mkdir("driver/ad1889", 0)) == NULL) || + create_proc_read_entry("ac97", S_IFREG|S_IRUGO, proc_root, ac97_read_proc, dev->ac97_codec) == NULL || + create_proc_read_entry("info", S_IFREG|S_IRUGO, proc_root, ad1889_read_proc, dev) == NULL) + goto err_free_dsp; + + ad1889_initcfg(dev); + + //DBG(DEVNAME ": Driver initialization done!\n"); + + ad1889_dev = dev; + + return 0; + +err_free_dsp: + unregister_sound_dsp(dev->dev_audio); + +err_free_irq: + free_irq(pcidev->irq, dev); + +err_free_mem: + ad1889_free_dev(dev); + pci_set_drvdata(pcidev, 0); + + return -ENODEV; +} + +static void __devexit ad1889_remove(struct pci_dev *pcidev) +{ + ad1889_dev_t *dev = pci_get_drvdata(pcidev); + + if (dev == NULL) return; + + unregister_sound_mixer(dev->ac97_codec->dev_mixer); + unregister_sound_dsp(dev->dev_audio); + free_irq(pcidev->irq, dev); + release_mem_region(dev->regbase, AD_DSIOMEMSIZE); + + /* any hw programming needed? */ + ad1889_free_dev(dev); +} + +MODULE_AUTHOR("Randolph Chung"); +MODULE_DESCRIPTION("Analog Devices AD1889 PCI Audio"); +MODULE_LICENSE("GPL"); + +static struct pci_driver ad1889_driver = { + name: DEVNAME, + id_table: ad1889_id_tbl, + probe: ad1889_probe, + remove: __devexit_p(ad1889_remove), +}; + +static int __init ad1889_init_module(void) +{ + return pci_module_init(&ad1889_driver); +} + +static void ad1889_exit_module(void) +{ + pci_unregister_driver(&ad1889_driver); + return; +} + +module_init(ad1889_init_module); +module_exit(ad1889_exit_module); diff -Nru a/sound/oss/ad1889.h b/sound/oss/ad1889.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/ad1889.h Fri Jul 11 08:23:38 2003 @@ -0,0 +1,134 @@ +#ifndef _AD1889_H_ +#define _AD1889_H_ + +#define AD_DSWSMC 0x00 /* DMA input wave/syn mixer control */ +#define AD_DSRAMC 0x02 /* DMA output resamp/ADC mixer control */ +#define AD_DSWADA 0x04 /* DMA input wave attenuation */ +#define AD_DSSYDA 0x06 /* DMA input syn attentuation */ +#define AD_DSWAS 0x08 /* wave input sample rate */ +#define AD_DSRES 0x0a /* resampler output sample rate */ +#define AD_DSCCS 0x0c /* chip control/status */ + +#define AD_DMARESBA 0x40 /* RES base addr */ +#define AD_DMARESCA 0x44 /* RES current addr */ +#define AD_DMARESBC 0x48 /* RES base cnt */ +#define AD_DMARESCC 0x4c /* RES current count */ +#define AD_DMAADCBA 0x50 /* ADC */ +#define AD_DMAADCCA 0x54 +#define AD_DMAADCBC 0x58 +#define AD_DMAADCCC 0x5c +#define AD_DMASYNBA 0x60 /* SYN */ +#define AD_DMASYNCA 0x64 +#define AD_DMASYNBC 0x68 +#define AD_DMASYNCC 0x6c +#define AD_DMAWAVBA 0x70 /* WAV */ +#define AD_DMAWAVCA 0x74 +#define AD_DMAWAVBC 0x78 +#define AD_DMAWAVCC 0x7c +#define AD_DMARESICC 0x80 /* RES interrupt current count */ +#define AD_DMARESIBC 0x84 /* RES interrupt base count */ +#define AD_DMAADCICC 0x88 /* ADC interrupt current count */ +#define AD_DMAADCIBC 0x8c /* ADC interrupt base count */ +#define AD_DMASYNICC 0x90 /* SYN interrupt current count */ +#define AD_DMASYNIBC 0x94 /* SYN interrupt base count */ +#define AD_DMAWAVICC 0x98 /* WAV interrupt current count */ +#define AD_DMAWAVIBC 0x9c /* WAV interrupt base count */ +#define AD_DMARESCTRL 0xa0 /* RES PCI control/status */ +#define AD_DMAADCCTRL 0xa8 /* RES PCI control/status */ +#define AD_DMASYNCTRL 0xb0 /* RES PCI control/status */ +#define AD_DMAWAVCTRL 0xb8 /* RES PCI control/status */ +#define AD_DMADISR 0xc0 /* PCI DMA intr status */ +#define AD_DMACHSS 0xc4 /* PCI DMA channel stop status */ + +#define AD_GPIOIPC 0xc8 /* IO port ctrl */ +#define AD_GPIOOP 0xca /* IO output status */ +#define AD_GPIOIP 0xcc /* IO input status */ + +/* AC97 registers, 0x100 - 0x17f; see ac97.h */ +#define AD_ACIC 0x180 /* AC Link interface ctrl */ + +/* OPL3; BAR1 */ +#define AD_OPLM0AS 0x00 /* Music0 address/status */ +#define AD_OPLM0DATA 0x01 /* Music0 data */ +#define AD_OPLM1A 0x02 /* Music1 address */ +#define AD_OPLM1DATA 0x03 /* Music1 data */ +/* 0x04-0x0f reserved */ + +/* MIDI; BAR2 */ +#define AD_MIDA 0x00 /* MIDI data */ +#define AD_MISC 0x01 /* MIDI status/cmd */ +/* 0x02-0xff reserved */ + +#define AD_DSIOMEMSIZE 512 +#define AD_OPLMEMSIZE 16 +#define AD_MIDIMEMSIZE 16 + +#define AD_WAV_STATE 0 +#define AD_ADC_STATE 1 +#define AD_MAX_STATES 2 + +#define DMA_SIZE (128*1024) + +#define DMA_FLAG_MAPPED 1 + +struct ad1889_dev; + +typedef struct ad1889_state { + struct ad1889_dev *card; + + mode_t open_mode; + struct dmabuf { + unsigned int rate; + unsigned char fmt, enable; + + /* buf management */ + size_t rawbuf_size; + void *rawbuf; + dma_addr_t dma_handle; /* mapped address */ + unsigned long dma_len; /* number of bytes mapped */ + + /* indexes into rawbuf for setting up DMA engine */ + volatile unsigned long rd_ptr, wr_ptr; + + wait_queue_head_t wait; /* to wait for buf servicing */ + + /* OSS bits */ + unsigned int mapped:1; + unsigned int ready:1; + unsigned int ossfragshift; + int ossmaxfrags; + unsigned int subdivision; + } dmabuf; + + struct semaphore sem; +} ad1889_state_t; + +typedef struct ad1889_dev { + unsigned long regbase; + struct pci_dev *pci; + + spinlock_t lock; + + int dev_audio; + + /* states; one per channel; right now only WAV and ADC */ + struct ad1889_state state[AD_MAX_STATES]; + + /* AC97 codec */ + struct ac97_codec *ac97_codec; + u16 ac97_features; + + /* debugging stuff */ + struct stats { + unsigned int wav_intrs, adc_intrs; + unsigned int blocks, underrun, error; + } stats; +} ad1889_dev_t; + +typedef struct ad1889_reg { + const char *name; + int offset; + int width; +} ad1889_reg_t; + +#endif diff -Nru a/sound/oss/ali5455.c b/sound/oss/ali5455.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/ali5455.c Fri Jul 11 10:51:36 2003 @@ -0,0 +1,3735 @@ +/* + * ALI ali5455 and friends ICH driver for Linux + * LEI HU + * + * Built from: + * drivers/sound/i810_audio + * + * The ALi 5455 is similar but not quite identical to the Intel ICH + * series of controllers. Its easier to keep the driver separated from + * the i810 driver. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + * ALi 5455 theory of operation + * + * The chipset provides three DMA channels that talk to an AC97 + * CODEC (AC97 is a digital/analog mixer standard). At its simplest + * you get 48Khz audio with basic volume and mixer controls. At the + * best you get rate adaption in the codec. We set the card up so + * that we never take completion interrupts but instead keep the card + * chasing its tail around a ring buffer. This is needed for mmap + * mode audio and happens to work rather well for non-mmap modes too. + * + * The board has one output channel for PCM audio (supported) and + * a stereo line in and mono microphone input. Again these are normally + * locked to 48Khz only. Right now recording is not finished. + * + * There is no midi support, no synth support. Use timidity. To get + * esd working you need to use esd -r 48000 as it won't probe 48KHz + * by default. mpg123 can't handle 48Khz only audio so use xmms. + * + * If you need to force a specific rate set the clocking= option + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef PCI_DEVICE_ID_ALI_5455 +#define PCI_DEVICE_ID_ALI_5455 0x5455 +#endif + +#ifndef PCI_VENDOR_ID_ALI +#define PCI_VENDOR_ID_ALI 0x10b9 +#endif + +static int strict_clocking = 0; +static unsigned int clocking = 0; +static unsigned int codec_pcmout_share_spdif_locked = 0; +static unsigned int codec_independent_spdif_locked = 0; +static unsigned int controller_pcmout_share_spdif_locked = 0; +static unsigned int controller_independent_spdif_locked = 0; +static unsigned int globel = 0; + +#define ADC_RUNNING 1 +#define DAC_RUNNING 2 +#define CODEC_SPDIFOUT_RUNNING 8 +#define CONTROLLER_SPDIFOUT_RUNNING 4 + +#define SPDIF_ENABLE_OUTPUT 4 /* bits 0,1 are PCM */ + +#define ALI5455_FMT_16BIT 1 +#define ALI5455_FMT_STEREO 2 +#define ALI5455_FMT_MASK 3 + +#define SPDIF_ON 0x0004 +#define SURR_ON 0x0010 +#define CENTER_LFE_ON 0x0020 +#define VOL_MUTED 0x8000 + + +#define ALI_SPDIF_OUT_CH_STATUS 0xbf +/* the 810's array of pointers to data buffers */ + +struct sg_item { +#define BUSADDR_MASK 0xFFFFFFFE + u32 busaddr; +#define CON_IOC 0x80000000 /* interrupt on completion */ +#define CON_BUFPAD 0x40000000 /* pad underrun with last sample, else 0 */ +#define CON_BUFLEN_MASK 0x0000ffff /* buffer length in samples */ + u32 control; +}; + +/* an instance of the ali channel */ +#define SG_LEN 32 +struct ali_channel { + /* these sg guys should probably be allocated + separately as nocache. Must be 8 byte aligned */ + struct sg_item sg[SG_LEN]; /* 32*8 */ + u32 offset; /* 4 */ + u32 port; /* 4 */ + u32 used; + u32 num; +}; + +/* + * we have 3 separate dma engines. pcm in, pcm out, and mic. + * each dma engine has controlling registers. These goofy + * names are from the datasheet, but make it easy to write + * code while leafing through it. + */ + +#define ENUM_ENGINE(PRE,DIG) \ +enum { \ + PRE##_BDBAR = 0x##DIG##0, /* Buffer Descriptor list Base Address */ \ + PRE##_CIV = 0x##DIG##4, /* Current Index Value */ \ + PRE##_LVI = 0x##DIG##5, /* Last Valid Index */ \ + PRE##_SR = 0x##DIG##6, /* Status Register */ \ + PRE##_PICB = 0x##DIG##8, /* Position In Current Buffer */ \ + PRE##_CR = 0x##DIG##b /* Control Register */ \ +} + +ENUM_ENGINE(OFF, 0); /* Offsets */ +ENUM_ENGINE(PI, 4); /* PCM In */ +ENUM_ENGINE(PO, 5); /* PCM Out */ +ENUM_ENGINE(MC, 6); /* Mic In */ +ENUM_ENGINE(CODECSPDIFOUT, 7); /* CODEC SPDIF OUT */ +ENUM_ENGINE(CONTROLLERSPDIFIN, A); /* CONTROLLER SPDIF In */ +ENUM_ENGINE(CONTROLLERSPDIFOUT, B); /* CONTROLLER SPDIF OUT */ + + +enum { + ALI_SCR = 0x00, /* System Control Register */ + ALI_SSR = 0x04, /* System Status Register */ + ALI_DMACR = 0x08, /* DMA Control Register */ + ALI_FIFOCR1 = 0x0c, /* FIFO Control Register 1 */ + ALI_INTERFACECR = 0x10, /* Interface Control Register */ + ALI_INTERRUPTCR = 0x14, /* Interrupt control Register */ + ALI_INTERRUPTSR = 0x18, /* Interrupt Status Register */ + ALI_FIFOCR2 = 0x1c, /* FIFO Control Register 2 */ + ALI_CPR = 0x20, /* Command Port Register */ + ALI_SPR = 0x24, /* Status Port Register */ + ALI_FIFOCR3 = 0x2c, /* FIFO Control Register 3 */ + ALI_TTSR = 0x30, /* Transmit Tag Slot Register */ + ALI_RTSR = 0x34, /* Receive Tag Slot Register */ + ALI_CSPSR = 0x38, /* Command/Status Port Status Register */ + ALI_CAS = 0x3c, /* Codec Write Semaphore Register */ + ALI_SPDIFCSR = 0xf8, /* spdif channel status register */ + ALI_SPDIFICS = 0xfc /* spdif interface control/status */ +}; + +// x-status register(x:pcm in ,pcm out, mic in,) +/* interrupts for a dma engine */ +#define DMA_INT_FIFO (1<<4) /* fifo under/over flow */ +#define DMA_INT_COMPLETE (1<<3) /* buffer read/write complete and ioc set */ +#define DMA_INT_LVI (1<<2) /* last valid done */ +#define DMA_INT_CELV (1<<1) /* last valid is current */ +#define DMA_INT_DCH (1) /* DMA Controller Halted (happens on LVI interrupts) */ //not eqult intel +#define DMA_INT_MASK (DMA_INT_FIFO|DMA_INT_COMPLETE|DMA_INT_LVI) + +/* interrupts for the whole chip */// by interrupt status register finish + +#define INT_SPDIFOUT (1<<23) /* controller spdif out INTERRUPT */ +#define INT_SPDIFIN (1<<22) +#define INT_CODECSPDIFOUT (1<<19) +#define INT_MICIN (1<<18) +#define INT_PCMOUT (1<<17) +#define INT_PCMIN (1<<16) +#define INT_CPRAIS (1<<7) +#define INT_SPRAIS (1<<5) +#define INT_GPIO (1<<1) +#define INT_MASK (INT_SPDIFOUT|INT_CODECSPDIFOUT|INT_MICIN|INT_PCMOUT|INT_PCMIN) + +#define DRIVER_VERSION "0.02ac" + +/* magic numbers to protect our data structures */ +#define ALI5455_CARD_MAGIC 0x5072696E /* "Prin" */ +#define ALI5455_STATE_MAGIC 0x63657373 /* "cess" */ +#define ALI5455_DMA_MASK 0xffffffff /* DMA buffer mask for pci_alloc_consist */ +#define NR_HW_CH 5 //I think 5 channel + +/* maxinum number of AC97 codecs connected, AC97 2.0 defined 4 */ +#define NR_AC97 2 + +/* Please note that an 8bit mono stream is not valid on this card, you must have a 16bit */ +/* stream at a minimum for this card to be happy */ +static const unsigned sample_size[] = { 1, 2, 2, 4 }; +/* Samples are 16bit values, so we are shifting to a word, not to a byte, hence shift */ +/* values are one less than might be expected */ +static const unsigned sample_shift[] = { -1, 0, 0, 1 }; + +#define ALI5455 +static char *card_names[] = { + "ALI 5455" +}; + +static struct pci_device_id ali_pci_tbl[] __initdata = { + {PCI_VENDOR_ID_ALI, PCI_DEVICE_ID_ALI_5455, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, ALI5455}, + {0,} +}; + +MODULE_DEVICE_TABLE(pci, ali_pci_tbl); + +#ifdef CONFIG_PM +#define PM_SUSPENDED(card) (card->pm_suspended) +#else +#define PM_SUSPENDED(card) (0) +#endif + +/* "software" or virtual channel, an instance of opened /dev/dsp */ +struct ali_state { + unsigned int magic; + struct ali_card *card; /* Card info */ + + /* single open lock mechanism, only used for recording */ + struct semaphore open_sem; + wait_queue_head_t open_wait; + + /* file mode */ + mode_t open_mode; + + /* virtual channel number */ + int virt; + +#ifdef CONFIG_PM + unsigned int pm_saved_dac_rate, pm_saved_adc_rate; +#endif + struct dmabuf { + /* wave sample stuff */ + unsigned int rate; + unsigned char fmt, enable, trigger; + + /* hardware channel */ + struct ali_channel *read_channel; + struct ali_channel *write_channel; + struct ali_channel *codec_spdifout_channel; + struct ali_channel *controller_spdifout_channel; + + /* OSS buffer management stuff */ + void *rawbuf; + dma_addr_t dma_handle; + unsigned buforder; + unsigned numfrag; + unsigned fragshift; + + /* our buffer acts like a circular ring */ + unsigned hwptr; /* where dma last started, updated by update_ptr */ + unsigned swptr; /* where driver last clear/filled, updated by read/write */ + int count; /* bytes to be consumed or been generated by dma machine */ + unsigned total_bytes; /* total bytes dmaed by hardware */ + + unsigned error; /* number of over/underruns */ + wait_queue_head_t wait; /* put process on wait queue when no more space in buffer */ + + /* redundant, but makes calculations easier */ + /* what the hardware uses */ + unsigned dmasize; + unsigned fragsize; + unsigned fragsamples; + + /* what we tell the user to expect */ + unsigned userfrags; + unsigned userfragsize; + + /* OSS stuff */ + unsigned mapped:1; + unsigned ready:1; + unsigned update_flag; + unsigned ossfragsize; + unsigned ossmaxfrags; + unsigned subdivision; + } dmabuf; +}; + + +struct ali_card { + struct ali_channel channel[5]; + unsigned int magic; + + /* We keep ali5455 cards in a linked list */ + struct ali_card *next; + + /* The ali has a certain amount of cross channel interaction + so we use a single per card lock */ + spinlock_t lock; + spinlock_t ac97_lock; + + /* PCI device stuff */ + struct pci_dev *pci_dev; + u16 pci_id; +#ifdef CONFIG_PM + u16 pm_suspended; + u32 pm_save_state[64 / sizeof(u32)]; + int pm_saved_mixer_settings[SOUND_MIXER_NRDEVICES][NR_AC97]; +#endif + /* soundcore stuff */ + int dev_audio; + + /* structures for abstraction of hardware facilities, codecs, banks and channels */ + struct ac97_codec *ac97_codec[NR_AC97]; + struct ali_state *states[NR_HW_CH]; + + u16 ac97_features; + u16 ac97_status; + u16 channels; + + /* hardware resources */ + unsigned long iobase; + + u32 irq; + + /* Function support */ + struct ali_channel *(*alloc_pcm_channel) (struct ali_card *); + struct ali_channel *(*alloc_rec_pcm_channel) (struct ali_card *); + struct ali_channel *(*alloc_rec_mic_channel) (struct ali_card *); + struct ali_channel *(*alloc_codec_spdifout_channel) (struct ali_card *); + struct ali_channel *(*alloc_controller_spdifout_channel) (struct ali_card *); + void (*free_pcm_channel) (struct ali_card *, int chan); + + /* We have a *very* long init time possibly, so use this to block */ + /* attempts to open our devices before we are ready (stops oops'es) */ + int initializing; +}; + + +static struct ali_card *devs = NULL; + +static int ali_open_mixdev(struct inode *inode, struct file *file); +static int ali_ioctl_mixdev(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg); +static u16 ali_ac97_get(struct ac97_codec *dev, u8 reg); +static void ali_ac97_set(struct ac97_codec *dev, u8 reg, u16 data); + +static struct ali_channel *ali_alloc_pcm_channel(struct ali_card *card) +{ + if (card->channel[1].used == 1) + return NULL; + card->channel[1].used = 1; + return &card->channel[1]; +} + +static struct ali_channel *ali_alloc_rec_pcm_channel(struct ali_card *card) +{ + if (card->channel[0].used == 1) + return NULL; + card->channel[0].used = 1; + return &card->channel[0]; +} + +static struct ali_channel *ali_alloc_rec_mic_channel(struct ali_card *card) +{ + if (card->channel[2].used == 1) + return NULL; + card->channel[2].used = 1; + return &card->channel[2]; +} + +static struct ali_channel *ali_alloc_codec_spdifout_channel(struct ali_card *card) +{ + if (card->channel[3].used == 1) + return NULL; + card->channel[3].used = 1; + return &card->channel[3]; +} + +static struct ali_channel *ali_alloc_controller_spdifout_channel(struct ali_card *card) +{ + if (card->channel[4].used == 1) + return NULL; + card->channel[4].used = 1; + return &card->channel[4]; +} +static void ali_free_pcm_channel(struct ali_card *card, int channel) +{ + card->channel[channel].used = 0; +} + + +//add support codec spdif out +static int ali_valid_spdif_rate(struct ac97_codec *codec, int rate) +{ + unsigned long id = 0L; + + id = (ali_ac97_get(codec, AC97_VENDOR_ID1) << 16); + id |= ali_ac97_get(codec, AC97_VENDOR_ID2) & 0xffff; + switch (id) { + case 0x41445361: /* AD1886 */ + if (rate == 48000) { + return 1; + } + break; + case 0x414c4720: /* ALC650 */ + if (rate == 48000) { + return 1; + } + break; + default: /* all other codecs, until we know otherwiae */ + if (rate == 48000 || rate == 44100 || rate == 32000) { + return 1; + } + break; + } + return (0); +} + +/* ali_set_spdif_output + * + * Configure the S/PDIF output transmitter. When we turn on + * S/PDIF, we turn off the analog output. This may not be + * the right thing to do. + * + * Assumptions: + * The DSP sample rate must already be set to a supported + * S/PDIF rate (32kHz, 44.1kHz, or 48kHz) or we abort. + */ +static void ali_set_spdif_output(struct ali_state *state, int slots, + int rate) +{ + int vol; + int aud_reg; + struct ac97_codec *codec = state->card->ac97_codec[0]; + + if (!(state->card->ac97_features & 4)) { + state->card->ac97_status &= ~SPDIF_ON; + } else { + if (slots == -1) { /* Turn off S/PDIF */ + aud_reg = ali_ac97_get(codec, AC97_EXTENDED_STATUS); + ali_ac97_set(codec, AC97_EXTENDED_STATUS, (aud_reg & ~AC97_EA_SPDIF)); + + /* If the volume wasn't muted before we turned on S/PDIF, unmute it */ + if (!(state->card->ac97_status & VOL_MUTED)) { + aud_reg = ali_ac97_get(codec, AC97_MASTER_VOL_STEREO); + ali_ac97_set(codec, AC97_MASTER_VOL_STEREO, + (aud_reg & ~VOL_MUTED)); + } + state->card->ac97_status &= ~(VOL_MUTED | SPDIF_ON); + return; + } + + vol = ali_ac97_get(codec, AC97_MASTER_VOL_STEREO); + state->card->ac97_status = vol & VOL_MUTED; + + /* Set S/PDIF transmitter sample rate */ + aud_reg = ali_ac97_get(codec, AC97_SPDIF_CONTROL); + switch (rate) { + case 32000: + aud_reg = (aud_reg & AC97_SC_SPSR_MASK) | AC97_SC_SPSR_32K; + break; + case 44100: + aud_reg = (aud_reg & AC97_SC_SPSR_MASK) | AC97_SC_SPSR_44K; + break; + case 48000: + aud_reg = (aud_reg & AC97_SC_SPSR_MASK) | AC97_SC_SPSR_48K; + break; + default: + /* turn off S/PDIF */ + aud_reg = ali_ac97_get(codec, AC97_EXTENDED_STATUS); + ali_ac97_set(codec, AC97_EXTENDED_STATUS, (aud_reg & ~AC97_EA_SPDIF)); + state->card->ac97_status &= ~SPDIF_ON; + return; + } + + ali_ac97_set(codec, AC97_SPDIF_CONTROL, aud_reg); + + aud_reg = ali_ac97_get(codec, AC97_EXTENDED_STATUS); + aud_reg = (aud_reg & AC97_EA_SLOT_MASK) | slots | AC97_EA_SPDIF; + ali_ac97_set(codec, AC97_EXTENDED_STATUS, aud_reg); + + aud_reg = ali_ac97_get(codec, AC97_POWER_CONTROL); + aud_reg |= 0x0002; + ali_ac97_set(codec, AC97_POWER_CONTROL, aud_reg); + udelay(1); + + state->card->ac97_status |= SPDIF_ON; + + /* Check to make sure the configuration is valid */ + aud_reg = ali_ac97_get(codec, AC97_EXTENDED_STATUS); + if (!(aud_reg & 0x0400)) { + /* turn off S/PDIF */ + ali_ac97_set(codec, AC97_EXTENDED_STATUS, (aud_reg & ~AC97_EA_SPDIF)); + state->card->ac97_status &= ~SPDIF_ON; + return; + } + if (codec_independent_spdif_locked > 0) { + aud_reg = ali_ac97_get(codec, 0x6a); + ali_ac97_set(codec, 0x6a, (aud_reg & 0xefff)); + } + /* Mute the analog output */ + /* Should this only mute the PCM volume??? */ + } +} + +/* ali_set_dac_channels + * + * Configure the codec's multi-channel DACs + * + * The logic is backwards. Setting the bit to 1 turns off the DAC. + * + * What about the ICH? We currently configure it using the + * SNDCTL_DSP_CHANNELS ioctl. If we're turnning on the DAC, + * does that imply that we want the ICH set to support + * these channels? + * + * TODO: + * vailidate that the codec really supports these DACs + * before turning them on. + */ +static void ali_set_dac_channels(struct ali_state *state, int channel) +{ + int aud_reg; + struct ac97_codec *codec = state->card->ac97_codec[0]; + + aud_reg = ali_ac97_get(codec, AC97_EXTENDED_STATUS); + aud_reg |= AC97_EA_PRI | AC97_EA_PRJ | AC97_EA_PRK; + state->card->ac97_status &= ~(SURR_ON | CENTER_LFE_ON); + + switch (channel) { + case 2: /* always enabled */ + break; + case 4: + aud_reg &= ~AC97_EA_PRJ; + state->card->ac97_status |= SURR_ON; + break; + case 6: + aud_reg &= ~(AC97_EA_PRJ | AC97_EA_PRI | AC97_EA_PRK); + state->card->ac97_status |= SURR_ON | CENTER_LFE_ON; + break; + default: + break; + } + ali_ac97_set(codec, AC97_EXTENDED_STATUS, aud_reg); + +} + +/* set playback sample rate */ +static unsigned int ali_set_dac_rate(struct ali_state *state, + unsigned int rate) +{ + struct dmabuf *dmabuf = &state->dmabuf; + u32 new_rate; + struct ac97_codec *codec = state->card->ac97_codec[0]; + + if (!(state->card->ac97_features & 0x0001)) { + dmabuf->rate = clocking; + return clocking; + } + + if (rate > 48000) + rate = 48000; + if (rate < 8000) + rate = 8000; + dmabuf->rate = rate; + + /* + * Adjust for misclocked crap + */ + + rate = (rate * clocking) / 48000; + + if (strict_clocking && rate < 8000) { + rate = 8000; + dmabuf->rate = (rate * 48000) / clocking; + } + + new_rate = ac97_set_dac_rate(codec, rate); + if (new_rate != rate) { + dmabuf->rate = (new_rate * 48000) / clocking; + } + rate = new_rate; + return dmabuf->rate; +} + +/* set recording sample rate */ +static unsigned int ali_set_adc_rate(struct ali_state *state, + unsigned int rate) +{ + struct dmabuf *dmabuf = &state->dmabuf; + u32 new_rate; + struct ac97_codec *codec = state->card->ac97_codec[0]; + + if (!(state->card->ac97_features & 0x0001)) { + dmabuf->rate = clocking; + return clocking; + } + + if (rate > 48000) + rate = 48000; + if (rate < 8000) + rate = 8000; + dmabuf->rate = rate; + + /* + * Adjust for misclocked crap + */ + + rate = (rate * clocking) / 48000; + if (strict_clocking && rate < 8000) { + rate = 8000; + dmabuf->rate = (rate * 48000) / clocking; + } + + new_rate = ac97_set_adc_rate(codec, rate); + + if (new_rate != rate) { + dmabuf->rate = (new_rate * 48000) / clocking; + rate = new_rate; + } + return dmabuf->rate; +} + +/* set codec independent spdifout sample rate */ +static unsigned int ali_set_codecspdifout_rate(struct ali_state *state, + unsigned int rate) +{ + struct dmabuf *dmabuf = &state->dmabuf; + + if (!(state->card->ac97_features & 0x0001)) { + dmabuf->rate = clocking; + return clocking; + } + + if (rate > 48000) + rate = 48000; + if (rate < 8000) + rate = 8000; + dmabuf->rate = rate; + + return dmabuf->rate; +} + +/* set controller independent spdif out function sample rate */ +static void ali_set_spdifout_rate(struct ali_state *state, + unsigned int rate) +{ + unsigned char ch_st_sel; + unsigned short status_rate; + + switch (rate) { + case 44100: + status_rate = 0; + break; + case 32000: + status_rate = 0x300; + break; + case 48000: + default: + status_rate = 0x200; + break; + } + + ch_st_sel = inb(state->card->iobase + ALI_SPDIFICS) & ALI_SPDIF_OUT_CH_STATUS; //select spdif_out + + ch_st_sel |= 0x80; //select right + outb(ch_st_sel, (state->card->iobase + ALI_SPDIFICS)); + outb(status_rate | 0x20, (state->card->iobase + ALI_SPDIFCSR + 2)); + + ch_st_sel &= (~0x80); //select left + outb(ch_st_sel, (state->card->iobase + ALI_SPDIFICS)); + outw(status_rate | 0x10, (state->card->iobase + ALI_SPDIFCSR + 2)); +} + +/* get current playback/recording dma buffer pointer (byte offset from LBA), + called with spinlock held! */ + +static inline unsigned ali_get_dma_addr(struct ali_state *state, int rec) +{ + struct dmabuf *dmabuf = &state->dmabuf; + unsigned int civ, offset, port, port_picb; + unsigned int data; + + if (!dmabuf->enable) + return 0; + + if (rec == 1) + port = state->card->iobase + dmabuf->read_channel->port; + else if (rec == 2) + port = state->card->iobase + dmabuf->codec_spdifout_channel->port; + else if (rec == 3) + port = state->card->iobase + dmabuf->controller_spdifout_channel->port; + else + port = state->card->iobase + dmabuf->write_channel->port; + + port_picb = port + OFF_PICB; + + do { + civ = inb(port + OFF_CIV) & 31; + offset = inw(port_picb); + /* Must have a delay here! */ + if (offset == 0) + udelay(1); + + /* Reread both registers and make sure that that total + * offset from the first reading to the second is 0. + * There is an issue with SiS hardware where it will count + * picb down to 0, then update civ to the next value, + * then set the new picb to fragsize bytes. We can catch + * it between the civ update and the picb update, making + * it look as though we are 1 fragsize ahead of where we + * are. The next to we get the address though, it will + * be back in thdelay is more than long enough + * that we won't have to worry about the chip still being + * out of sync with reality ;-) + */ + } while (civ != (inb(port + OFF_CIV) & 31) || offset != inw(port_picb)); + + data = ((civ + 1) * dmabuf->fragsize - (2 * offset)) % dmabuf->dmasize; + if (inw(port_picb) == 0) + data -= 2048; + + return data; +} + +/* Stop recording (lock held) */ +static inline void __stop_adc(struct ali_state *state) +{ + struct dmabuf *dmabuf = &state->dmabuf; + struct ali_card *card = state->card; + + dmabuf->enable &= ~ADC_RUNNING; + + outl((1 << 18) | (1 << 16), card->iobase + ALI_DMACR); + udelay(1); + + outb(0, card->iobase + PI_CR); + while (inb(card->iobase + PI_CR) != 0); + + // now clear any latent interrupt bits (like the halt bit) + outb(inb(card->iobase + PI_SR) | 0x001e, card->iobase + PI_SR); + outl(inl(card->iobase + ALI_INTERRUPTSR) & INT_PCMIN, card->iobase + ALI_INTERRUPTSR); +} + +static void stop_adc(struct ali_state *state) +{ + struct ali_card *card = state->card; + unsigned long flags; + spin_lock_irqsave(&card->lock, flags); + __stop_adc(state); + spin_unlock_irqrestore(&card->lock, flags); +} + +static inline void __start_adc(struct ali_state *state) +{ + struct dmabuf *dmabuf = &state->dmabuf; + + if (dmabuf->count < dmabuf->dmasize && dmabuf->ready + && !dmabuf->enable && (dmabuf->trigger & PCM_ENABLE_INPUT)) { + dmabuf->enable |= ADC_RUNNING; + outb((1 << 4) | (1 << 2), state->card->iobase + PI_CR); + if (state->card->channel[0].used == 1) + outl(1, state->card->iobase + ALI_DMACR); // DMA CONTROL REGISTRER + udelay(100); + if (state->card->channel[2].used == 1) + outl((1 << 2), state->card->iobase + ALI_DMACR); //DMA CONTROL REGISTER + udelay(100); + } +} + +static void start_adc(struct ali_state *state) +{ + struct ali_card *card = state->card; + unsigned long flags; + + spin_lock_irqsave(&card->lock, flags); + __start_adc(state); + spin_unlock_irqrestore(&card->lock, flags); +} + +/* stop playback (lock held) */ +static inline void __stop_dac(struct ali_state *state) +{ + struct dmabuf *dmabuf = &state->dmabuf; + struct ali_card *card = state->card; + + dmabuf->enable &= ~DAC_RUNNING; + outl(0x00020000, card->iobase + 0x08); + outb(0, card->iobase + PO_CR); + while (inb(card->iobase + PO_CR) != 0) + cpu_relax(); + + outb(inb(card->iobase + PO_SR) | 0x001e, card->iobase + PO_SR); + + outl(inl(card->iobase + ALI_INTERRUPTSR) & INT_PCMOUT, card->iobase + ALI_INTERRUPTSR); +} + +static void stop_dac(struct ali_state *state) +{ + struct ali_card *card = state->card; + unsigned long flags; + spin_lock_irqsave(&card->lock, flags); + __stop_dac(state); + spin_unlock_irqrestore(&card->lock, flags); +} + +static inline void __start_dac(struct ali_state *state) +{ + struct dmabuf *dmabuf = &state->dmabuf; + if (dmabuf->count > 0 && dmabuf->ready && !dmabuf->enable && + (dmabuf->trigger & PCM_ENABLE_OUTPUT)) { + dmabuf->enable |= DAC_RUNNING; + outb((1 << 4) | (1 << 2), state->card->iobase + PO_CR); + outl((1 << 1), state->card->iobase + 0x08); //dma control register + } +} + +static void start_dac(struct ali_state *state) +{ + struct ali_card *card = state->card; + unsigned long flags; + spin_lock_irqsave(&card->lock, flags); + __start_dac(state); + spin_unlock_irqrestore(&card->lock, flags); +} + +/* stop codec and controller spdif out (lock held) */ +static inline void __stop_spdifout(struct ali_state *state) +{ + struct dmabuf *dmabuf = &state->dmabuf; + struct ali_card *card = state->card; + + if (codec_independent_spdif_locked > 0) { + dmabuf->enable &= ~CODEC_SPDIFOUT_RUNNING; + outl((1 << 19), card->iobase + 0x08); + outb(0, card->iobase + CODECSPDIFOUT_CR); + + while (inb(card->iobase + CODECSPDIFOUT_CR) != 0) + cpu_relax(); + + outb(inb(card->iobase + CODECSPDIFOUT_SR) | 0x001e, card->iobase + CODECSPDIFOUT_SR); + outl(inl(card->iobase + ALI_INTERRUPTSR) & INT_CODECSPDIFOUT, card->iobase + ALI_INTERRUPTSR); + } else { + if (controller_independent_spdif_locked > 0) { + dmabuf->enable &= ~CONTROLLER_SPDIFOUT_RUNNING; + outl((1 << 23), card->iobase + 0x08); + outb(0, card->iobase + CONTROLLERSPDIFOUT_CR); + while (inb(card->iobase + CONTROLLERSPDIFOUT_CR) != 0) + cpu_relax(); + outb(inb(card->iobase + CONTROLLERSPDIFOUT_SR) | 0x001e, card->iobase + CONTROLLERSPDIFOUT_SR); + outl(inl(card->iobase + ALI_INTERRUPTSR) & INT_SPDIFOUT, card->iobase + ALI_INTERRUPTSR); + } + } +} + +static void stop_spdifout(struct ali_state *state) +{ + struct ali_card *card = state->card; + unsigned long flags; + spin_lock_irqsave(&card->lock, flags); + __stop_spdifout(state); + spin_unlock_irqrestore(&card->lock, flags); +} + +static inline void __start_spdifout(struct ali_state *state) +{ + struct dmabuf *dmabuf = &state->dmabuf; + if (dmabuf->count > 0 && dmabuf->ready && !dmabuf->enable && + (dmabuf->trigger & SPDIF_ENABLE_OUTPUT)) { + if (codec_independent_spdif_locked > 0) { + dmabuf->enable |= CODEC_SPDIFOUT_RUNNING; + outb((1 << 4) | (1 << 2), state->card->iobase + CODECSPDIFOUT_CR); + outl((1 << 3), state->card->iobase + 0x08); //dma control register + } else { + if (controller_independent_spdif_locked > 0) { + dmabuf->enable |= CONTROLLER_SPDIFOUT_RUNNING; + outb((1 << 4) | (1 << 2), state->card->iobase + CONTROLLERSPDIFOUT_CR); + outl((1 << 7), state->card->iobase + 0x08); //dma control register + } + } + } +} + +static void start_spdifout(struct ali_state *state) +{ + struct ali_card *card = state->card; + unsigned long flags; + spin_lock_irqsave(&card->lock, flags); + __start_spdifout(state); + spin_unlock_irqrestore(&card->lock, flags); +} + +#define DMABUF_DEFAULTORDER (16-PAGE_SHIFT) +#define DMABUF_MINORDER 1 + +/* allocate DMA buffer, playback , recording,spdif out buffer should be allocated separately */ +static int alloc_dmabuf(struct ali_state *state) +{ + struct dmabuf *dmabuf = &state->dmabuf; + void *rawbuf = NULL; + int order, size; + struct page *page, *pend; + + /* If we don't have any oss frag params, then use our default ones */ + if (dmabuf->ossmaxfrags == 0) + dmabuf->ossmaxfrags = 4; + if (dmabuf->ossfragsize == 0) + dmabuf->ossfragsize = (PAGE_SIZE << DMABUF_DEFAULTORDER) / dmabuf->ossmaxfrags; + size = dmabuf->ossfragsize * dmabuf->ossmaxfrags; + + if (dmabuf->rawbuf && (PAGE_SIZE << dmabuf->buforder) == size) + return 0; + /* alloc enough to satisfy the oss params */ + for (order = DMABUF_DEFAULTORDER; order >= DMABUF_MINORDER; order--) { + if ((PAGE_SIZE << order) > size) + continue; + if ((rawbuf = pci_alloc_consistent(state->card->pci_dev, + PAGE_SIZE << order, + &dmabuf->dma_handle))) + break; + } + if (!rawbuf) + return -ENOMEM; + + dmabuf->ready = dmabuf->mapped = 0; + dmabuf->rawbuf = rawbuf; + dmabuf->buforder = order; + + /* now mark the pages as reserved; otherwise remap_page_range doesn't do what we want */ + pend = virt_to_page(rawbuf + (PAGE_SIZE << order) - 1); + for (page = virt_to_page(rawbuf); page <= pend; page++) + SetPageReserved(page); + return 0; +} + +/* free DMA buffer */ +static void dealloc_dmabuf(struct ali_state *state) +{ + struct dmabuf *dmabuf = &state->dmabuf; + struct page *page, *pend; + + if (dmabuf->rawbuf) { + /* undo marking the pages as reserved */ + pend = virt_to_page(dmabuf->rawbuf + (PAGE_SIZE << dmabuf->buforder) - 1); + for (page = virt_to_page(dmabuf->rawbuf); page <= pend; page++) + ClearPageReserved(page); + pci_free_consistent(state->card->pci_dev, + PAGE_SIZE << dmabuf->buforder, + dmabuf->rawbuf, dmabuf->dma_handle); + } + dmabuf->rawbuf = NULL; + dmabuf->mapped = dmabuf->ready = 0; +} + +static int prog_dmabuf(struct ali_state *state, unsigned rec) +{ + struct dmabuf *dmabuf = &state->dmabuf; + struct ali_channel *c = NULL; + struct sg_item *sg; + unsigned long flags; + int ret; + unsigned fragint; + int i; + + spin_lock_irqsave(&state->card->lock, flags); + if (dmabuf->enable & DAC_RUNNING) + __stop_dac(state); + if (dmabuf->enable & ADC_RUNNING) + __stop_adc(state); + if (dmabuf->enable & CODEC_SPDIFOUT_RUNNING) + __stop_spdifout(state); + if (dmabuf->enable & CONTROLLER_SPDIFOUT_RUNNING) + __stop_spdifout(state); + + dmabuf->total_bytes = 0; + dmabuf->count = dmabuf->error = 0; + dmabuf->swptr = dmabuf->hwptr = 0; + spin_unlock_irqrestore(&state->card->lock, flags); + + /* allocate DMA buffer, let alloc_dmabuf determine if we are already + * allocated well enough or if we should replace the current buffer + * (assuming one is already allocated, if it isn't, then allocate it). + */ + if ((ret = alloc_dmabuf(state))) + return ret; + + /* FIXME: figure out all this OSS fragment stuff */ + /* I did, it now does what it should according to the OSS API. DL */ + /* We may not have realloced our dmabuf, but the fragment size to + * fragment number ratio may have changed, so go ahead and reprogram + * things + */ + + dmabuf->dmasize = PAGE_SIZE << dmabuf->buforder; + dmabuf->numfrag = SG_LEN; + dmabuf->fragsize = dmabuf->dmasize / dmabuf->numfrag; + dmabuf->fragsamples = dmabuf->fragsize >> 1; + dmabuf->userfragsize = dmabuf->ossfragsize; + dmabuf->userfrags = dmabuf->dmasize / dmabuf->ossfragsize; + + memset(dmabuf->rawbuf, 0, dmabuf->dmasize); + + if (dmabuf->ossmaxfrags == 4) { + fragint = 8; + dmabuf->fragshift = 2; + } else if (dmabuf->ossmaxfrags == 8) { + fragint = 4; + dmabuf->fragshift = 3; + } else if (dmabuf->ossmaxfrags == 16) { + fragint = 2; + dmabuf->fragshift = 4; + } else { + fragint = 1; + dmabuf->fragshift = 5; + } + /* + * Now set up the ring + */ + + if (rec == 1) + c = dmabuf->read_channel; + else if (rec == 2) + c = dmabuf->codec_spdifout_channel; + else if (rec == 3) + c = dmabuf->controller_spdifout_channel; + else if (rec == 0) + c = dmabuf->write_channel; + if (c != NULL) { + sg = &c->sg[0]; + /* + * Load up 32 sg entries and take an interrupt at half + * way (we might want more interrupts later..) + */ + for (i = 0; i < dmabuf->numfrag; i++) { + sg->busaddr = + virt_to_bus(dmabuf->rawbuf + + dmabuf->fragsize * i); + // the card will always be doing 16bit stereo + sg->control = dmabuf->fragsamples; + sg->control |= CON_BUFPAD; //I modify + // set us up to get IOC interrupts as often as needed to + // satisfy numfrag requirements, no more + if (((i + 1) % fragint) == 0) { + sg->control |= CON_IOC; + } + sg++; + } + spin_lock_irqsave(&state->card->lock, flags); + outb(2, state->card->iobase + c->port + OFF_CR); /* reset DMA machine */ + outl(virt_to_bus(&c->sg[0]), state->card->iobase + c->port + OFF_BDBAR); + outb(0, state->card->iobase + c->port + OFF_CIV); + outb(0, state->card->iobase + c->port + OFF_LVI); + spin_unlock_irqrestore(&state->card->lock, flags); + } + /* set the ready flag for the dma buffer */ + dmabuf->ready = 1; + return 0; +} + +static void __ali_update_lvi(struct ali_state *state, int rec) +{ + struct dmabuf *dmabuf = &state->dmabuf; + int x, port; + port = state->card->iobase; + if (rec == 1) + port += dmabuf->read_channel->port; + else if (rec == 2) + port += dmabuf->codec_spdifout_channel->port; + else if (rec == 3) + port += dmabuf->controller_spdifout_channel->port; + else if (rec == 0) + port += dmabuf->write_channel->port; + /* if we are currently stopped, then our CIV is actually set to our + * *last* sg segment and we are ready to wrap to the next. However, + * if we set our LVI to the last sg segment, then it won't wrap to + * the next sg segment, it won't even get a start. So, instead, when + * we are stopped, we set both the LVI value and also we increment + * the CIV value to the next sg segment to be played so that when + * we call start_{dac,adc}, things will operate properly + */ + if (!dmabuf->enable && dmabuf->ready) { + if (rec && dmabuf->count < dmabuf->dmasize && (dmabuf->trigger & PCM_ENABLE_INPUT)) { + outb((inb(port + OFF_CIV) + 1) & 31, port + OFF_LVI); + __start_adc(state); + while (! (inb(port + OFF_CR) & ((1 << 4) | (1 << 2)))) + cpu_relax(); + } else if (!rec && dmabuf->count && (dmabuf->trigger & PCM_ENABLE_OUTPUT)) { + outb((inb(port + OFF_CIV) + 1) & 31, port + OFF_LVI); + __start_dac(state); + while (!(inb(port + OFF_CR) & ((1 << 4) | (1 << 2)))) + cpu_relax(); + } else if (rec && dmabuf->count && (dmabuf->trigger & SPDIF_ENABLE_OUTPUT)) { + if (codec_independent_spdif_locked > 0) { + // outb((inb(port+OFF_CIV))&31, port+OFF_LVI); + outb((inb(port + OFF_CIV) + 1) & 31, port + OFF_LVI); + __start_spdifout(state); + while (!(inb(port + OFF_CR) & ((1 << 4) | (1 << 2)))) + cpu_relax(); + } else { + if (controller_independent_spdif_locked > 0) { + outb((inb(port + OFF_CIV) + 1) & 31, port + OFF_LVI); + __start_spdifout(state); + while (!(inb(port + OFF_CR) & ((1 << 4) | (1 << 2)))) + cpu_relax(); + } + } + } + } + + /* swptr - 1 is the tail of our transfer */ + x = (dmabuf->dmasize + dmabuf->swptr - 1) % dmabuf->dmasize; + x /= dmabuf->fragsize; + outb(x, port + OFF_LVI); +} + +static void ali_update_lvi(struct ali_state *state, int rec) +{ + struct dmabuf *dmabuf = &state->dmabuf; + unsigned long flags; + if (!dmabuf->ready) + return; + spin_lock_irqsave(&state->card->lock, flags); + __ali_update_lvi(state, rec); + spin_unlock_irqrestore(&state->card->lock, flags); +} + +/* update buffer manangement pointers, especially, dmabuf->count and dmabuf->hwptr */ +static void ali_update_ptr(struct ali_state *state) +{ + struct dmabuf *dmabuf = &state->dmabuf; + unsigned hwptr; + int diff; + + /* error handling and process wake up for DAC */ + if (dmabuf->enable == ADC_RUNNING) { + /* update hardware pointer */ + hwptr = ali_get_dma_addr(state, 1); + diff = (dmabuf->dmasize + hwptr - dmabuf->hwptr) % dmabuf->dmasize; + dmabuf->hwptr = hwptr; + dmabuf->total_bytes += diff; + dmabuf->count += diff; + if (dmabuf->count > dmabuf->dmasize) { + /* buffer underrun or buffer overrun */ + /* this is normal for the end of a read */ + /* only give an error if we went past the */ + /* last valid sg entry */ + if ((inb(state->card->iobase + PI_CIV) & 31) != (inb(state->card->iobase + PI_LVI) & 31)) { + printk(KERN_WARNING "ali_audio: DMA overrun on read\n"); + dmabuf->error++; + } + } + if (dmabuf->count > dmabuf->userfragsize) + wake_up(&dmabuf->wait); + } + /* error handling and process wake up for DAC */ + if (dmabuf->enable == DAC_RUNNING) { + /* update hardware pointer */ + hwptr = ali_get_dma_addr(state, 0); + diff = + (dmabuf->dmasize + hwptr - + dmabuf->hwptr) % dmabuf->dmasize; +#if defined(DEBUG_INTERRUPTS) || defined(DEBUG_MMAP) + printk("DAC HWP %d,%d,%d\n", hwptr, dmabuf->hwptr, diff); +#endif + dmabuf->hwptr = hwptr; + dmabuf->total_bytes += diff; + dmabuf->count -= diff; + if (dmabuf->count < 0) { + /* buffer underrun or buffer overrun */ + /* this is normal for the end of a write */ + /* only give an error if we went past the */ + /* last valid sg entry */ + if ((inb(state->card->iobase + PO_CIV) & 31) != (inb(state->card->iobase + PO_LVI) & 31)) { + printk(KERN_WARNING "ali_audio: DMA overrun on write\n"); + printk(KERN_DEBUG "ali_audio: CIV %d, LVI %d, hwptr %x, count %d\n", + inb(state->card->iobase + PO_CIV) & 31, + inb(state->card->iobase + PO_LVI) & 31, + dmabuf->hwptr, + dmabuf->count); + dmabuf->error++; + } + } + if (dmabuf->count < (dmabuf->dmasize - dmabuf->userfragsize)) + wake_up(&dmabuf->wait); + } + + /* error handling and process wake up for CODEC SPDIF OUT */ + if (dmabuf->enable == CODEC_SPDIFOUT_RUNNING) { + /* update hardware pointer */ + hwptr = ali_get_dma_addr(state, 2); + diff = (dmabuf->dmasize + hwptr - dmabuf->hwptr) % dmabuf->dmasize; + dmabuf->hwptr = hwptr; + dmabuf->total_bytes += diff; + dmabuf->count -= diff; + if (dmabuf->count < 0) { + /* buffer underrun or buffer overrun */ + /* this is normal for the end of a write */ + /* only give an error if we went past the */ + /* last valid sg entry */ + if ((inb(state->card->iobase + CODECSPDIFOUT_CIV) & 31) != (inb(state->card->iobase + CODECSPDIFOUT_LVI) & 31)) { + printk(KERN_WARNING "ali_audio: DMA overrun on write\n"); + printk(KERN_DEBUG "ali_audio: CIV %d, LVI %d, hwptr %x, count %d\n", + inb(state->card->iobase + CODECSPDIFOUT_CIV) & 31, + inb(state->card->iobase + CODECSPDIFOUT_LVI) & 31, + dmabuf->hwptr, dmabuf->count); + dmabuf->error++; + } + } + if (dmabuf->count < (dmabuf->dmasize - dmabuf->userfragsize)) + wake_up(&dmabuf->wait); + } + /* error handling and process wake up for CONTROLLER SPDIF OUT */ + if (dmabuf->enable == CONTROLLER_SPDIFOUT_RUNNING) { + /* update hardware pointer */ + hwptr = ali_get_dma_addr(state, 3); + diff = (dmabuf->dmasize + hwptr - dmabuf->hwptr) % dmabuf->dmasize; + dmabuf->hwptr = hwptr; + dmabuf->total_bytes += diff; + dmabuf->count -= diff; + if (dmabuf->count < 0) { + /* buffer underrun or buffer overrun */ + /* this is normal for the end of a write */ + /* only give an error if we went past the */ + /* last valid sg entry */ + if ((inb(state->card->iobase + CONTROLLERSPDIFOUT_CIV) & 31) != (inb(state->card->iobase + CONTROLLERSPDIFOUT_LVI) & 31)) { + printk(KERN_WARNING + "ali_audio: DMA overrun on write\n"); + printk("ali_audio: CIV %d, LVI %d, hwptr %x, " + "count %d\n", + inb(state->card->iobase + CONTROLLERSPDIFOUT_CIV) & 31, + inb(state->card->iobase + CONTROLLERSPDIFOUT_LVI) & 31, + dmabuf->hwptr, dmabuf->count); + dmabuf->error++; + } + } + if (dmabuf->count < (dmabuf->dmasize - dmabuf->userfragsize)) + wake_up(&dmabuf->wait); + } +} + +static inline int ali_get_free_write_space(struct + ali_state + *state) +{ + struct dmabuf *dmabuf = &state->dmabuf; + int free; + ali_update_ptr(state); + // catch underruns during playback + if (dmabuf->count < 0) { + dmabuf->count = 0; + dmabuf->swptr = dmabuf->hwptr; + } + free = dmabuf->dmasize - dmabuf->count; + free -= (dmabuf->hwptr % dmabuf->fragsize); + if (free < 0) + return (0); + return (free); +} + +static inline int ali_get_available_read_data(struct + ali_state + *state) +{ + struct dmabuf *dmabuf = &state->dmabuf; + int avail; + ali_update_ptr(state); + // catch overruns during record + if (dmabuf->count > dmabuf->dmasize) { + dmabuf->count = dmabuf->dmasize; + dmabuf->swptr = dmabuf->hwptr; + } + avail = dmabuf->count; + avail -= (dmabuf->hwptr % dmabuf->fragsize); + if (avail < 0) + return (0); + return (avail); +} + +static int drain_dac(struct ali_state *state, int signals_allowed) +{ + + DECLARE_WAITQUEUE(wait, current); + struct dmabuf *dmabuf = &state->dmabuf; + unsigned long flags; + unsigned long tmo; + int count; + if (!dmabuf->ready) + return 0; + if (dmabuf->mapped) { + stop_dac(state); + return 0; + } + add_wait_queue(&dmabuf->wait, &wait); + for (;;) { + + spin_lock_irqsave(&state->card->lock, flags); + ali_update_ptr(state); + count = dmabuf->count; + spin_unlock_irqrestore(&state->card->lock, flags); + if (count <= 0) + break; + /* + * This will make sure that our LVI is correct, that our + * pointer is updated, and that the DAC is running. We + * have to force the setting of dmabuf->trigger to avoid + * any possible deadlocks. + */ + if (!dmabuf->enable) { + dmabuf->trigger = PCM_ENABLE_OUTPUT; + ali_update_lvi(state, 0); + } + if (signal_pending(current) && signals_allowed) { + break; + } + + /* It seems that we have to set the current state to + * TASK_INTERRUPTIBLE every time to make the process + * really go to sleep. This also has to be *after* the + * update_ptr() call because update_ptr is likely to + * do a wake_up() which will unset this before we ever + * try to sleep, resuling in a tight loop in this code + * instead of actually sleeping and waiting for an + * interrupt to wake us up! + */ + set_current_state(TASK_INTERRUPTIBLE); + /* + * set the timeout to significantly longer than it *should* + * take for the DAC to drain the DMA buffer + */ + tmo = (count * HZ) / (dmabuf->rate); + if (!schedule_timeout(tmo >= 2 ? tmo : 2)) { + printk(KERN_ERR "ali_audio: drain_dac, dma timeout?\n"); + count = 0; + break; + } + } + set_current_state(TASK_RUNNING); + remove_wait_queue(&dmabuf->wait, &wait); + if (count > 0 && signal_pending(current) && signals_allowed) + return -ERESTARTSYS; + stop_dac(state); + return 0; +} + + +static int drain_spdifout(struct ali_state *state, int signals_allowed) +{ + + DECLARE_WAITQUEUE(wait, current); + struct dmabuf *dmabuf = &state->dmabuf; + unsigned long flags; + unsigned long tmo; + int count; + if (!dmabuf->ready) + return 0; + if (dmabuf->mapped) { + stop_spdifout(state); + return 0; + } + add_wait_queue(&dmabuf->wait, &wait); + for (;;) { + + spin_lock_irqsave(&state->card->lock, flags); + ali_update_ptr(state); + count = dmabuf->count; + spin_unlock_irqrestore(&state->card->lock, flags); + if (count <= 0) + break; + /* + * This will make sure that our LVI is correct, that our + * pointer is updated, and that the DAC is running. We + * have to force the setting of dmabuf->trigger to avoid + * any possible deadlocks. + */ + if (!dmabuf->enable) { + if (codec_independent_spdif_locked > 0) { + dmabuf->trigger = SPDIF_ENABLE_OUTPUT; + ali_update_lvi(state, 2); + } else { + if (controller_independent_spdif_locked > 0) { + dmabuf->trigger = SPDIF_ENABLE_OUTPUT; + ali_update_lvi(state, 3); + } + } + } + if (signal_pending(current) && signals_allowed) { + break; + } + + /* It seems that we have to set the current state to + * TASK_INTERRUPTIBLE every time to make the process + * really go to sleep. This also has to be *after* the + * update_ptr() call because update_ptr is likely to + * do a wake_up() which will unset this before we ever + * try to sleep, resuling in a tight loop in this code + * instead of actually sleeping and waiting for an + * interrupt to wake us up! + */ + set_current_state(TASK_INTERRUPTIBLE); + /* + * set the timeout to significantly longer than it *should* + * take for the DAC to drain the DMA buffer + */ + tmo = (count * HZ) / (dmabuf->rate); + if (!schedule_timeout(tmo >= 2 ? tmo : 2)) { + printk(KERN_ERR "ali_audio: drain_spdifout, dma timeout?\n"); + count = 0; + break; + } + } + set_current_state(TASK_RUNNING); + remove_wait_queue(&dmabuf->wait, &wait); + if (count > 0 && signal_pending(current) && signals_allowed) + return -ERESTARTSYS; + stop_spdifout(state); + return 0; +} + +static void ali_channel_interrupt(struct ali_card *card) +{ + int i, count; + + for (i = 0; i < NR_HW_CH; i++) { + struct ali_state *state = card->states[i]; + struct ali_channel *c = NULL; + struct dmabuf *dmabuf; + unsigned long port = card->iobase; + u16 status; + if (!state) + continue; + if (!state->dmabuf.ready) + continue; + dmabuf = &state->dmabuf; + if (codec_independent_spdif_locked > 0) { + if (dmabuf->enable & CODEC_SPDIFOUT_RUNNING) { + c = dmabuf->codec_spdifout_channel; + } + } else { + if (controller_independent_spdif_locked > 0) { + if (dmabuf->enable & CONTROLLER_SPDIFOUT_RUNNING) + c = dmabuf->controller_spdifout_channel; + } else { + if (dmabuf->enable & DAC_RUNNING) { + c = dmabuf->write_channel; + } else if (dmabuf->enable & ADC_RUNNING) { + c = dmabuf->read_channel; + } else + continue; + } + } + port += c->port; + + status = inw(port + OFF_SR); + + if (status & DMA_INT_COMPLETE) { + /* only wake_up() waiters if this interrupt signals + * us being beyond a userfragsize of data open or + * available, and ali_update_ptr() does that for + * us + */ + ali_update_ptr(state); + } + + if (status & DMA_INT_LVI) { + ali_update_ptr(state); + wake_up(&dmabuf->wait); + + if (dmabuf->enable & DAC_RUNNING) + count = dmabuf->count; + else if (dmabuf->enable & ADC_RUNNING) + count = dmabuf->dmasize - dmabuf->count; + else if (dmabuf->enable & CODEC_SPDIFOUT_RUNNING) + count = dmabuf->count; + else if (dmabuf->enable & CONTROLLER_SPDIFOUT_RUNNING) + count = dmabuf->count; + else count = 0; + + if (count > 0) { + if (dmabuf->enable & DAC_RUNNING) + outl((1 << 1), state->card->iobase + ALI_DMACR); + else if (dmabuf->enable & CODEC_SPDIFOUT_RUNNING) + outl((1 << 3), state->card->iobase + ALI_DMACR); + else if (dmabuf->enable & CONTROLLER_SPDIFOUT_RUNNING) + outl((1 << 7), state->card->iobase + ALI_DMACR); + } else { + if (dmabuf->enable & DAC_RUNNING) + __stop_dac(state); + if (dmabuf->enable & ADC_RUNNING) + __stop_adc(state); + if (dmabuf->enable & CODEC_SPDIFOUT_RUNNING) + __stop_spdifout(state); + if (dmabuf->enable & CONTROLLER_SPDIFOUT_RUNNING) + __stop_spdifout(state); + dmabuf->enable = 0; + wake_up(&dmabuf->wait); + } + + } + if (!(status & DMA_INT_DCH)) { + ali_update_ptr(state); + wake_up(&dmabuf->wait); + if (dmabuf->enable & DAC_RUNNING) + count = dmabuf->count; + else if (dmabuf->enable & ADC_RUNNING) + count = dmabuf->dmasize - dmabuf->count; + else if (dmabuf->enable & CODEC_SPDIFOUT_RUNNING) + count = dmabuf->count; + else if (dmabuf->enable & CONTROLLER_SPDIFOUT_RUNNING) + count = dmabuf->count; + else + count = 0; + + if (count > 0) { + if (dmabuf->enable & DAC_RUNNING) + outl((1 << 1), state->card->iobase + ALI_DMACR); + else if (dmabuf->enable & CODEC_SPDIFOUT_RUNNING) + outl((1 << 3), state->card->iobase + ALI_DMACR); + else if (dmabuf->enable & CONTROLLER_SPDIFOUT_RUNNING) + outl((1 << 7), state->card->iobase + ALI_DMACR); + } else { + if (dmabuf->enable & DAC_RUNNING) + __stop_dac(state); + if (dmabuf->enable & ADC_RUNNING) + __stop_adc(state); + if (dmabuf->enable & CODEC_SPDIFOUT_RUNNING) + __stop_spdifout(state); + if (dmabuf->enable & CONTROLLER_SPDIFOUT_RUNNING) + __stop_spdifout(state); + dmabuf->enable = 0; + wake_up(&dmabuf->wait); + } + } + outw(status & DMA_INT_MASK, port + OFF_SR); + } +} + +static irqreturn_t ali_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct ali_card *card = (struct ali_card *) dev_id; + u32 status; + u16 status2; + + spin_lock(&card->lock); + status = inl(card->iobase + ALI_INTERRUPTSR); + if (!(status & INT_MASK)) { + spin_unlock(&card->lock); + return IRQ_NONE; /* not for us */ + } + + if (codec_independent_spdif_locked > 0) { + if (globel == 0) { + globel += 1; + status2 = inw(card->iobase + 0x76); + outw(status2 | 0x000c, card->iobase + 0x76); + } else { + if (status & (INT_PCMOUT | INT_PCMIN | INT_MICIN | INT_SPDIFOUT | INT_CODECSPDIFOUT)) + ali_channel_interrupt(card); + } + } else { + if (status & (INT_PCMOUT | INT_PCMIN | INT_MICIN | INT_SPDIFOUT | INT_CODECSPDIFOUT)) + ali_channel_interrupt(card); + } + + /* clear 'em */ + outl(status & INT_MASK, card->iobase + ALI_INTERRUPTSR); + spin_unlock(&card->lock); + return IRQ_HANDLED; +} + +/* in this loop, dmabuf.count signifies the amount of data that is + waiting to be copied to the user's buffer. It is filled by the dma + machine and drained by this loop. */ + +static ssize_t ali_read(struct file *file, char *buffer, + size_t count, loff_t * ppos) +{ + struct ali_state *state = (struct ali_state *) file->private_data; + struct ali_card *card = state ? state->card : 0; + struct dmabuf *dmabuf = &state->dmabuf; + ssize_t ret; + unsigned long flags; + unsigned int swptr; + int cnt; + DECLARE_WAITQUEUE(waita, current); +#ifdef DEBUG2 + printk("ali_audio: ali_read called, count = %d\n", count); +#endif + if (ppos != &file->f_pos) + return -ESPIPE; + if (dmabuf->mapped) + return -ENXIO; + if (dmabuf->enable & DAC_RUNNING) + return -ENODEV; + if (!dmabuf->read_channel) { + dmabuf->ready = 0; + dmabuf->read_channel = card->alloc_rec_pcm_channel(card); + if (!dmabuf->read_channel) { + return -EBUSY; + } + } + if (!dmabuf->ready && (ret = prog_dmabuf(state, 1))) + return ret; + if (!access_ok(VERIFY_WRITE, buffer, count)) + return -EFAULT; + ret = 0; + add_wait_queue(&dmabuf->wait, &waita); + while (count > 0) { + set_current_state(TASK_INTERRUPTIBLE); + spin_lock_irqsave(&card->lock, flags); + if (PM_SUSPENDED(card)) { + spin_unlock_irqrestore(&card->lock, flags); + schedule(); + if (signal_pending(current)) { + if (!ret) + ret = -EAGAIN; + break; + } + continue; + } + swptr = dmabuf->swptr; + cnt = ali_get_available_read_data(state); + // this is to make the copy_to_user simpler below + if (cnt > (dmabuf->dmasize - swptr)) + cnt = dmabuf->dmasize - swptr; + spin_unlock_irqrestore(&card->lock, flags); + if (cnt > count) + cnt = count; + /* Lop off the last two bits to force the code to always + * write in full samples. This keeps software that sets + * O_NONBLOCK but doesn't check the return value of the + * write call from getting things out of state where they + * think a full 4 byte sample was written when really only + * a portion was, resulting in odd sound and stereo + * hysteresis. + */ + cnt &= ~0x3; + if (cnt <= 0) { + unsigned long tmo; + /* + * Don't let us deadlock. The ADC won't start if + * dmabuf->trigger isn't set. A call to SETTRIGGER + * could have turned it off after we set it to on + * previously. + */ + dmabuf->trigger = PCM_ENABLE_INPUT; + /* + * This does three things. Updates LVI to be correct, + * makes sure the ADC is running, and updates the + * hwptr. + */ + ali_update_lvi(state, 1); + if (file->f_flags & O_NONBLOCK) { + if (!ret) + ret = -EAGAIN; + goto done; + } + /* Set the timeout to how long it would take to fill + * two of our buffers. If we haven't been woke up + * by then, then we know something is wrong. + */ + tmo = (dmabuf->dmasize * HZ * 2) / (dmabuf->rate * 4); + + /* There are two situations when sleep_on_timeout returns, one is when + the interrupt is serviced correctly and the process is waked up by + ISR ON TIME. Another is when timeout is expired, which means that + either interrupt is NOT serviced correctly (pending interrupt) or it + is TOO LATE for the process to be scheduled to run (scheduler latency) + which results in a (potential) buffer overrun. And worse, there is + NOTHING we can do to prevent it. */ + if (!schedule_timeout(tmo >= 2 ? tmo : 2)) { + printk(KERN_ERR + "ali_audio: recording schedule timeout, " + "dmasz %u fragsz %u count %i hwptr %u swptr %u\n", + dmabuf->dmasize, dmabuf->fragsize, + dmabuf->count, dmabuf->hwptr, + dmabuf->swptr); + /* a buffer overrun, we delay the recovery until next time the + while loop begin and we REALLY have space to record */ + } + if (signal_pending(current)) { + ret = ret ? ret : -ERESTARTSYS; + goto done; + } + continue; + } + + if (copy_to_user(buffer, dmabuf->rawbuf + swptr, cnt)) { + if (!ret) + ret = -EFAULT; + goto done; + } + + swptr = (swptr + cnt) % dmabuf->dmasize; + spin_lock_irqsave(&card->lock, flags); + if (PM_SUSPENDED(card)) { + spin_unlock_irqrestore(&card->lock, flags); + continue; + } + dmabuf->swptr = swptr; + dmabuf->count -= cnt; + spin_unlock_irqrestore(&card->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + } +done: + ali_update_lvi(state, 1); + set_current_state(TASK_RUNNING); + remove_wait_queue(&dmabuf->wait, &waita); + return ret; +} + +/* in this loop, dmabuf.count signifies the amount of data that is waiting to be dma to + the soundcard. it is drained by the dma machine and filled by this loop. */ +static ssize_t ali_write(struct file *file, + const char *buffer, size_t count, loff_t * ppos) +{ + struct ali_state *state = (struct ali_state *) file->private_data; + struct ali_card *card = state ? state->card : 0; + struct dmabuf *dmabuf = &state->dmabuf; + ssize_t ret; + unsigned long flags; + unsigned int swptr = 0; + int cnt, x; + DECLARE_WAITQUEUE(waita, current); +#ifdef DEBUG2 + printk("ali_audio: ali_write called, count = %d\n", count); +#endif + if (ppos != &file->f_pos) + return -ESPIPE; + if (dmabuf->mapped) + return -ENXIO; + if (dmabuf->enable & ADC_RUNNING) + return -ENODEV; + if (codec_independent_spdif_locked > 0) { + if (!dmabuf->codec_spdifout_channel) { + dmabuf->ready = 0; + dmabuf->codec_spdifout_channel = card->alloc_codec_spdifout_channel(card); + if (!dmabuf->codec_spdifout_channel) + return -EBUSY; + } + } else { + if (controller_independent_spdif_locked > 0) { + if (!dmabuf->controller_spdifout_channel) { + dmabuf->ready = 0; + dmabuf->controller_spdifout_channel = card->alloc_controller_spdifout_channel(card); + if (!dmabuf->controller_spdifout_channel) + return -EBUSY; + } + } else { + if (!dmabuf->write_channel) { + dmabuf->ready = 0; + dmabuf->write_channel = + card->alloc_pcm_channel(card); + if (!dmabuf->write_channel) + return -EBUSY; + } + } + } + + if (codec_independent_spdif_locked > 0) { + if (!dmabuf->ready && (ret = prog_dmabuf(state, 2))) + return ret; + } else { + if (controller_independent_spdif_locked > 0) { + if (!dmabuf->ready && (ret = prog_dmabuf(state, 3))) + return ret; + } else { + + if (!dmabuf->ready && (ret = prog_dmabuf(state, 0))) + return ret; + } + } + if (!access_ok(VERIFY_READ, buffer, count)) + return -EFAULT; + ret = 0; + add_wait_queue(&dmabuf->wait, &waita); + while (count > 0) { + set_current_state(TASK_INTERRUPTIBLE); + spin_lock_irqsave(&state->card->lock, flags); + if (PM_SUSPENDED(card)) { + spin_unlock_irqrestore(&card->lock, flags); + schedule(); + if (signal_pending(current)) { + if (!ret) + ret = -EAGAIN; + break; + } + continue; + } + + swptr = dmabuf->swptr; + cnt = ali_get_free_write_space(state); + /* Bound the maximum size to how much we can copy to the + * dma buffer before we hit the end. If we have more to + * copy then it will get done in a second pass of this + * loop starting from the beginning of the buffer. + */ + if (cnt > (dmabuf->dmasize - swptr)) + cnt = dmabuf->dmasize - swptr; + spin_unlock_irqrestore(&state->card->lock, flags); +#ifdef DEBUG2 + printk(KERN_INFO + "ali_audio: ali_write: %d bytes available space\n", + cnt); +#endif + if (cnt > count) + cnt = count; + /* Lop off the last two bits to force the code to always + * write in full samples. This keeps software that sets + * O_NONBLOCK but doesn't check the return value of the + * write call from getting things out of state where they + * think a full 4 byte sample was written when really only + * a portion was, resulting in odd sound and stereo + * hysteresis. + */ + cnt &= ~0x3; + if (cnt <= 0) { + unsigned long tmo; + // There is data waiting to be played + /* + * Force the trigger setting since we would + * deadlock with it set any other way + */ + if (codec_independent_spdif_locked > 0) { + dmabuf->trigger = SPDIF_ENABLE_OUTPUT; + ali_update_lvi(state, 2); + } else { + if (controller_independent_spdif_locked > 0) { + dmabuf->trigger = SPDIF_ENABLE_OUTPUT; + ali_update_lvi(state, 3); + } else { + + dmabuf->trigger = PCM_ENABLE_OUTPUT; + ali_update_lvi(state, 0); + } + } + if (file->f_flags & O_NONBLOCK) { + if (!ret) + ret = -EAGAIN; + goto ret; + } + /* Not strictly correct but works */ + tmo = (dmabuf->dmasize * HZ * 2) / (dmabuf->rate * 4); + /* There are two situations when sleep_on_timeout returns, one is when + the interrupt is serviced correctly and the process is waked up by + ISR ON TIME. Another is when timeout is expired, which means that + either interrupt is NOT serviced correctly (pending interrupt) or it + is TOO LATE for the process to be scheduled to run (scheduler latency) + which results in a (potential) buffer underrun. And worse, there is + NOTHING we can do to prevent it. */ + + /* FIXME - do timeout handling here !! */ + + if (signal_pending(current)) { + if (!ret) + ret = -ERESTARTSYS; + goto ret; + } + continue; + } + if (copy_from_user(dmabuf->rawbuf + swptr, buffer, cnt)) { + if (!ret) + ret = -EFAULT; + goto ret; + } + + swptr = (swptr + cnt) % dmabuf->dmasize; + spin_lock_irqsave(&state->card->lock, flags); + if (PM_SUSPENDED(card)) { + spin_unlock_irqrestore(&card->lock, flags); + continue; + } + + dmabuf->swptr = swptr; + dmabuf->count += cnt; + count -= cnt; + buffer += cnt; + ret += cnt; + spin_unlock_irqrestore(&state->card->lock, flags); + } + if (swptr % dmabuf->fragsize) { + x = dmabuf->fragsize - (swptr % dmabuf->fragsize); + memset(dmabuf->rawbuf + swptr, '\0', x); + } +ret: + if (codec_independent_spdif_locked > 0) { + ali_update_lvi(state, 2); + } else { + if (controller_independent_spdif_locked > 0) { + ali_update_lvi(state, 3); + } else { + ali_update_lvi(state, 0); + } + } + set_current_state(TASK_RUNNING); + remove_wait_queue(&dmabuf->wait, &waita); + return ret; +} + +/* No kernel lock - we have our own spinlock */ +static unsigned int ali_poll(struct file *file, struct poll_table_struct + *wait) +{ + struct ali_state *state = (struct ali_state *) file->private_data; + struct dmabuf *dmabuf = &state->dmabuf; + unsigned long flags; + unsigned int mask = 0; + if (!dmabuf->ready) + return 0; + poll_wait(file, &dmabuf->wait, wait); + spin_lock_irqsave(&state->card->lock, flags); + ali_update_ptr(state); + if (file->f_mode & FMODE_READ && dmabuf->enable & ADC_RUNNING) { + if (dmabuf->count >= (signed) dmabuf->fragsize) + mask |= POLLIN | POLLRDNORM; + } + if (file->f_mode & FMODE_WRITE && (dmabuf->enable & (DAC_RUNNING|CODEC_SPDIFOUT_RUNNING|CONTROLLER_SPDIFOUT_RUNNING))) { + if ((signed) dmabuf->dmasize >= dmabuf->count + (signed) dmabuf->fragsize) + mask |= POLLOUT | POLLWRNORM; + } + spin_unlock_irqrestore(&state->card->lock, flags); + return mask; +} + +static int ali_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct ali_state *state = (struct ali_state *) file->private_data; + struct dmabuf *dmabuf = &state->dmabuf; + int ret = -EINVAL; + unsigned long size; + lock_kernel(); + if (vma->vm_flags & VM_WRITE) { + if (!dmabuf->write_channel && (dmabuf->write_channel = state->card->alloc_pcm_channel(state->card)) == NULL) { + ret = -EBUSY; + goto out; + } + } + if (vma->vm_flags & VM_READ) { + if (!dmabuf->read_channel && (dmabuf->read_channel = state->card->alloc_rec_pcm_channel(state->card)) == NULL) { + ret = -EBUSY; + goto out; + } + } + if ((ret = prog_dmabuf(state, 0)) != 0) + goto out; + ret = -EINVAL; + if (vma->vm_pgoff != 0) + goto out; + size = vma->vm_end - vma->vm_start; + if (size > (PAGE_SIZE << dmabuf->buforder)) + goto out; + ret = -EAGAIN; + if (remap_page_range(vma, vma->vm_start, virt_to_phys(dmabuf->rawbuf), size, vma->vm_page_prot)) + goto out; + dmabuf->mapped = 1; + dmabuf->trigger = 0; + ret = 0; +out: + unlock_kernel(); + return ret; +} + +static int ali_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + struct ali_state *state = (struct ali_state *) file->private_data; + struct ali_channel *c = NULL; + struct dmabuf *dmabuf = &state->dmabuf; + unsigned long flags; + audio_buf_info abinfo; + count_info cinfo; + unsigned int i_scr; + int val = 0, ret; + struct ac97_codec *codec = state->card->ac97_codec[0]; +#ifdef DEBUG + printk("ali_audio: ali_ioctl, arg=0x%x, cmd=", + arg ? *(int *) arg : 0); +#endif + switch (cmd) { + case OSS_GETVERSION: +#ifdef DEBUG + printk("OSS_GETVERSION\n"); +#endif + return put_user(SOUND_VERSION, (int *) arg); + case SNDCTL_DSP_RESET: +#ifdef DEBUG + printk("SNDCTL_DSP_RESET\n"); +#endif + spin_lock_irqsave(&state->card->lock, flags); + if (dmabuf->enable == DAC_RUNNING) { + c = dmabuf->write_channel; + __stop_dac(state); + } + if (dmabuf->enable == ADC_RUNNING) { + c = dmabuf->read_channel; + __stop_adc(state); + } + if (dmabuf->enable == CODEC_SPDIFOUT_RUNNING) { + c = dmabuf->codec_spdifout_channel; + __stop_spdifout(state); + } + if (dmabuf->enable == CONTROLLER_SPDIFOUT_RUNNING) { + c = dmabuf->controller_spdifout_channel; + __stop_spdifout(state); + } + if (c != NULL) { + outb(2, state->card->iobase + c->port + OFF_CR); /* reset DMA machine */ + outl(virt_to_bus(&c->sg[0]), + state->card->iobase + c->port + OFF_BDBAR); + outb(0, state->card->iobase + c->port + OFF_CIV); + outb(0, state->card->iobase + c->port + OFF_LVI); + } + + spin_unlock_irqrestore(&state->card->lock, flags); + synchronize_irq(state->card->pci_dev->irq); + dmabuf->ready = 0; + dmabuf->swptr = dmabuf->hwptr = 0; + dmabuf->count = dmabuf->total_bytes = 0; + return 0; + case SNDCTL_DSP_SYNC: +#ifdef DEBUG + printk("SNDCTL_DSP_SYNC\n"); +#endif + if (codec_independent_spdif_locked > 0) { + if (dmabuf->enable != CODEC_SPDIFOUT_RUNNING + || file->f_flags & O_NONBLOCK) + return 0; + if ((val = drain_spdifout(state, 1))) + return val; + } else { + if (controller_independent_spdif_locked > 0) { + if (dmabuf->enable != + CONTROLLER_SPDIFOUT_RUNNING + || file->f_flags & O_NONBLOCK) + return 0; + if ((val = drain_spdifout(state, 1))) + return val; + } else { + if (dmabuf->enable != DAC_RUNNING + || file->f_flags & O_NONBLOCK) + return 0; + if ((val = drain_dac(state, 1))) + return val; + } + } + dmabuf->total_bytes = 0; + return 0; + case SNDCTL_DSP_SPEED: /* set smaple rate */ +#ifdef DEBUG + printk("SNDCTL_DSP_SPEED\n"); +#endif + if (get_user(val, (int *) arg)) + return -EFAULT; + if (val >= 0) { + if (file->f_mode & FMODE_WRITE) { + if ((state->card->ac97_status & SPDIF_ON)) { /* S/PDIF Enabled */ + /* RELTEK ALC650 only support 48000, need to check that */ + if (ali_valid_spdif_rate(codec, val)) { + if (codec_independent_spdif_locked > 0) { + ali_set_spdif_output(state, -1, 0); + stop_spdifout(state); + dmabuf->ready = 0; + /* I add test codec independent spdif out */ + spin_lock_irqsave(&state->card->lock, flags); + ali_set_codecspdifout_rate(state, val); // I modified + spin_unlock_irqrestore(&state->card->lock, flags); + /* Set S/PDIF transmitter rate. */ + i_scr = inl(state->card->iobase + ALI_SCR); + if ((i_scr & 0x00300000) == 0x00100000) { + ali_set_spdif_output(state, AC97_EA_SPSA_7_8, codec_independent_spdif_locked); + } else { + if ((i_scr&0x00300000) == 0x00200000) + { + ali_set_spdif_output(state, AC97_EA_SPSA_6_9, codec_independent_spdif_locked); + } else { + if ((i_scr & 0x00300000) == 0x00300000) { + ali_set_spdif_output(state, AC97_EA_SPSA_10_11, codec_independent_spdif_locked); + } else { + ali_set_spdif_output(state, AC97_EA_SPSA_7_8, codec_independent_spdif_locked); + } + } + } + + if (!(state->card->ac97_status & SPDIF_ON)) { + val = dmabuf->rate; + } + } else { + if (controller_independent_spdif_locked > 0) + { + stop_spdifout(state); + dmabuf->ready = 0; + spin_lock_irqsave(&state->card->lock, flags); + ali_set_spdifout_rate(state, controller_independent_spdif_locked); + spin_unlock_irqrestore(&state->card->lock, flags); + } else { + /* Set DAC rate */ + ali_set_spdif_output(state, -1, 0); + stop_dac(state); + dmabuf->ready = 0; + spin_lock_irqsave(&state->card->lock, flags); + ali_set_dac_rate(state, val); + spin_unlock_irqrestore(&state->card->lock, flags); + /* Set S/PDIF transmitter rate. */ + ali_set_spdif_output(state, AC97_EA_SPSA_3_4, val); + if (!(state->card->ac97_status & SPDIF_ON)) + { + val = dmabuf->rate; + } + } + } + } else { /* Not a valid rate for S/PDIF, ignore it */ + val = dmabuf->rate; + } + } else { + stop_dac(state); + dmabuf->ready = 0; + spin_lock_irqsave(&state->card->lock, flags); + ali_set_dac_rate(state, val); + spin_unlock_irqrestore(&state->card->lock, flags); + } + } + if (file->f_mode & FMODE_READ) { + stop_adc(state); + dmabuf->ready = 0; + spin_lock_irqsave(&state->card->lock, flags); + ali_set_adc_rate(state, val); + spin_unlock_irqrestore(&state->card->lock, flags); + } + } + return put_user(dmabuf->rate, (int *) arg); + case SNDCTL_DSP_STEREO: /* set stereo or mono channel */ +#ifdef DEBUG + printk("SNDCTL_DSP_STEREO\n"); +#endif + if (dmabuf->enable & DAC_RUNNING) { + stop_dac(state); + } + if (dmabuf->enable & ADC_RUNNING) { + stop_adc(state); + } + if (dmabuf->enable & CODEC_SPDIFOUT_RUNNING) { + stop_spdifout(state); + } + if (dmabuf->enable & CONTROLLER_SPDIFOUT_RUNNING) { + stop_spdifout(state); + } + return put_user(1, (int *) arg); + case SNDCTL_DSP_GETBLKSIZE: + if (file->f_mode & FMODE_WRITE) { + if (codec_independent_spdif_locked > 0) { + if (!dmabuf->ready && (val = prog_dmabuf(state, 2))) + return val; + } else { + if (controller_independent_spdif_locked > 0) { + if (!dmabuf->ready && (val = prog_dmabuf(state, 3))) + return val; + } else { + if (!dmabuf->ready && (val = prog_dmabuf(state, 0))) + return val; + } + } + } + + if (file->f_mode & FMODE_READ) { + if (!dmabuf->ready && (val = prog_dmabuf(state, 1))) + return val; + } +#ifdef DEBUG + printk("SNDCTL_DSP_GETBLKSIZE %d\n", dmabuf->userfragsize); +#endif + return put_user(dmabuf->userfragsize, (int *) arg); + case SNDCTL_DSP_GETFMTS: /* Returns a mask of supported sample format */ +#ifdef DEBUG + printk("SNDCTL_DSP_GETFMTS\n"); +#endif + return put_user(AFMT_S16_LE, (int *) arg); + case SNDCTL_DSP_SETFMT: /* Select sample format */ +#ifdef DEBUG + printk("SNDCTL_DSP_SETFMT\n"); +#endif + return put_user(AFMT_S16_LE, (int *) arg); + case SNDCTL_DSP_CHANNELS: // add support 4,6 channel +#ifdef DEBUG + printk("SNDCTL_DSP_CHANNELS\n"); +#endif + if (get_user(val, (int *) arg)) + return -EFAULT; + if (val > 0) { + if (dmabuf->enable & DAC_RUNNING) { + stop_dac(state); + } + if (dmabuf->enable & CODEC_SPDIFOUT_RUNNING) { + stop_spdifout(state); + } + if (dmabuf->enable & CONTROLLER_SPDIFOUT_RUNNING) { + stop_spdifout(state); + } + if (dmabuf->enable & ADC_RUNNING) { + stop_adc(state); + } + } else { + return put_user(state->card->channels, (int *) arg); + } + + i_scr = inl(state->card->iobase + ALI_SCR); + /* Current # of channels enabled */ + if (i_scr & 0x00000100) + ret = 4; + else if (i_scr & 0x00000200) + ret = 6; + else + ret = 2; + switch (val) { + case 2: /* 2 channels is always supported */ + if (codec_independent_spdif_locked > 0) { + outl(((i_scr & 0xfffffcff) | 0x00100000), (state->card->iobase + ALI_SCR)); + } else + outl((i_scr & 0xfffffcff), (state->card->iobase + ALI_SCR)); + /* Do we need to change mixer settings???? */ + break; + case 4: /* Supported on some chipsets, better check first */ + if (codec_independent_spdif_locked > 0) { + outl(((i_scr & 0xfffffcff) | 0x00000100 | 0x00200000), (state->card->iobase + ALI_SCR)); + } else + outl(((i_scr & 0xfffffcff) | 0x00000100), (state->card->iobase + ALI_SCR)); + break; + case 6: /* Supported on some chipsets, better check first */ + if (codec_independent_spdif_locked > 0) { + outl(((i_scr & 0xfffffcff) | 0x00000200 | 0x00008000 | 0x00300000), (state->card->iobase + ALI_SCR)); + } else + outl(((i_scr & 0xfffffcff) | 0x00000200 | 0x00008000), (state->card->iobase + ALI_SCR)); + break; + default: /* nothing else is ever supported by the chipset */ + val = ret; + break; + } + return put_user(val, (int *) arg); + case SNDCTL_DSP_POST: /* the user has sent all data and is notifying us */ + /* we update the swptr to the end of the last sg segment then return */ +#ifdef DEBUG + printk("SNDCTL_DSP_POST\n"); +#endif + if (codec_independent_spdif_locked > 0) { + if (!dmabuf->ready || (dmabuf->enable != CODEC_SPDIFOUT_RUNNING)) + return 0; + } else { + if (controller_independent_spdif_locked > 0) { + if (!dmabuf->ready || (dmabuf->enable != CONTROLLER_SPDIFOUT_RUNNING)) + return 0; + } else { + if (!dmabuf->ready || (dmabuf->enable != DAC_RUNNING)) + return 0; + } + } + if ((dmabuf->swptr % dmabuf->fragsize) != 0) { + val = dmabuf->fragsize - (dmabuf->swptr % dmabuf->fragsize); + dmabuf->swptr += val; + dmabuf->count += val; + } + return 0; + case SNDCTL_DSP_SUBDIVIDE: + if (dmabuf->subdivision) + return -EINVAL; + if (get_user(val, (int *) arg)) + return -EFAULT; + if (val != 1 && val != 2 && val != 4) + return -EINVAL; +#ifdef DEBUG + printk("SNDCTL_DSP_SUBDIVIDE %d\n", val); +#endif + dmabuf->subdivision = val; + dmabuf->ready = 0; + return 0; + case SNDCTL_DSP_SETFRAGMENT: + if (get_user(val, (int *) arg)) + return -EFAULT; + dmabuf->ossfragsize = 1 << (val & 0xffff); + dmabuf->ossmaxfrags = (val >> 16) & 0xffff; + if (!dmabuf->ossfragsize || !dmabuf->ossmaxfrags) + return -EINVAL; + /* + * Bound the frag size into our allowed range of 256 - 4096 + */ + if (dmabuf->ossfragsize < 256) + dmabuf->ossfragsize = 256; + else if (dmabuf->ossfragsize > 4096) + dmabuf->ossfragsize = 4096; + /* + * The numfrags could be something reasonable, or it could + * be 0xffff meaning "Give me as much as possible". So, + * we check the numfrags * fragsize doesn't exceed our + * 64k buffer limit, nor is it less than our 8k minimum. + * If it fails either one of these checks, then adjust the + * number of fragments, not the size of them. It's OK if + * our number of fragments doesn't equal 32 or anything + * like our hardware based number now since we are using + * a different frag count for the hardware. Before we get + * into this though, bound the maxfrags to avoid overflow + * issues. A reasonable bound would be 64k / 256 since our + * maximum buffer size is 64k and our minimum frag size is + * 256. On the other end, our minimum buffer size is 8k and + * our maximum frag size is 4k, so the lower bound should + * be 2. + */ + if (dmabuf->ossmaxfrags > 256) + dmabuf->ossmaxfrags = 256; + else if (dmabuf->ossmaxfrags < 2) + dmabuf->ossmaxfrags = 2; + val = dmabuf->ossfragsize * dmabuf->ossmaxfrags; + while (val < 8192) { + val <<= 1; + dmabuf->ossmaxfrags <<= 1; + } + while (val > 65536) { + val >>= 1; + dmabuf->ossmaxfrags >>= 1; + } + dmabuf->ready = 0; +#ifdef DEBUG + printk("SNDCTL_DSP_SETFRAGMENT 0x%x, %d, %d\n", val, + dmabuf->ossfragsize, dmabuf->ossmaxfrags); +#endif + return 0; + case SNDCTL_DSP_GETOSPACE: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + if (codec_independent_spdif_locked > 0) { + if (!dmabuf->ready && (val = prog_dmabuf(state, 2)) != 0) + return val; + } else { + if (controller_independent_spdif_locked > 0) { + if (!dmabuf->ready && (val = prog_dmabuf(state, 3)) != 0) + return val; + } else { + if (!dmabuf->ready && (val = prog_dmabuf(state, 0)) != 0) + return val; + } + } + spin_lock_irqsave(&state->card->lock, flags); + ali_update_ptr(state); + abinfo.fragsize = dmabuf->userfragsize; + abinfo.fragstotal = dmabuf->userfrags; + if (dmabuf->mapped) + abinfo.bytes = dmabuf->dmasize; + else + abinfo.bytes = ali_get_free_write_space(state); + abinfo.fragments = abinfo.bytes / dmabuf->userfragsize; + spin_unlock_irqrestore(&state->card->lock, flags); +#if defined(DEBUG) || defined(DEBUG_MMAP) + printk("SNDCTL_DSP_GETOSPACE %d, %d, %d, %d\n", + abinfo.bytes, abinfo.fragsize, abinfo.fragments, + abinfo.fragstotal); +#endif + return copy_to_user((void *) arg, &abinfo, + sizeof(abinfo)) ? -EFAULT : 0; + case SNDCTL_DSP_GETOPTR: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + if (codec_independent_spdif_locked > 0) { + if (!dmabuf->ready && (val = prog_dmabuf(state, 2)) != 0) + return val; + } else { + if (controller_independent_spdif_locked > 0) { + if (!dmabuf->ready && (val = prog_dmabuf(state, 3)) != 0) + return val; + } else { + if (!dmabuf->ready && (val = prog_dmabuf(state, 0)) != 0) + return val; + } + } + spin_lock_irqsave(&state->card->lock, flags); + val = ali_get_free_write_space(state); + cinfo.bytes = dmabuf->total_bytes; + cinfo.ptr = dmabuf->hwptr; + cinfo.blocks = val / dmabuf->userfragsize; + if (codec_independent_spdif_locked > 0) { + if (dmabuf->mapped && (dmabuf->trigger & SPDIF_ENABLE_OUTPUT)) { + dmabuf->count += val; + dmabuf->swptr = (dmabuf->swptr + val) % dmabuf->dmasize; + __ali_update_lvi(state, 2); + } + } else { + if (controller_independent_spdif_locked > 0) { + if (dmabuf->mapped && (dmabuf->trigger & SPDIF_ENABLE_OUTPUT)) { + dmabuf->count += val; + dmabuf->swptr = (dmabuf->swptr + val) % dmabuf->dmasize; + __ali_update_lvi(state, 3); + } + } else { + if (dmabuf->mapped && (dmabuf->trigger & PCM_ENABLE_OUTPUT)) { + dmabuf->count += val; + dmabuf->swptr = (dmabuf->swptr + val) % dmabuf->dmasize; + __ali_update_lvi(state, 0); + } + } + } + spin_unlock_irqrestore(&state->card->lock, flags); +#if defined(DEBUG) || defined(DEBUG_MMAP) + printk("SNDCTL_DSP_GETOPTR %d, %d, %d, %d\n", cinfo.bytes, + cinfo.blocks, cinfo.ptr, dmabuf->count); +#endif + return copy_to_user((void *) arg, &cinfo, sizeof(cinfo)); + case SNDCTL_DSP_GETISPACE: + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + if (!dmabuf->ready && (val = prog_dmabuf(state, 1)) != 0) + return val; + spin_lock_irqsave(&state->card->lock, flags); + abinfo.bytes = ali_get_available_read_data(state); + abinfo.fragsize = dmabuf->userfragsize; + abinfo.fragstotal = dmabuf->userfrags; + abinfo.fragments = abinfo.bytes / dmabuf->userfragsize; + spin_unlock_irqrestore(&state->card->lock, flags); +#if defined(DEBUG) || defined(DEBUG_MMAP) + printk("SNDCTL_DSP_GETISPACE %d, %d, %d, %d\n", + abinfo.bytes, abinfo.fragsize, abinfo.fragments, + abinfo.fragstotal); +#endif + return copy_to_user((void *) arg, &abinfo, + sizeof(abinfo)) ? -EFAULT : 0; + case SNDCTL_DSP_GETIPTR: + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + if (!dmabuf->ready && (val = prog_dmabuf(state, 0)) != 0) + return val; + spin_lock_irqsave(&state->card->lock, flags); + val = ali_get_available_read_data(state); + cinfo.bytes = dmabuf->total_bytes; + cinfo.blocks = val / dmabuf->userfragsize; + cinfo.ptr = dmabuf->hwptr; + if (dmabuf->mapped && (dmabuf->trigger & PCM_ENABLE_INPUT)) { + dmabuf->count -= val; + dmabuf->swptr = (dmabuf->swptr + val) % dmabuf->dmasize; + __ali_update_lvi(state, 1); + } + spin_unlock_irqrestore(&state->card->lock, flags); +#if defined(DEBUG) || defined(DEBUG_MMAP) + printk("SNDCTL_DSP_GETIPTR %d, %d, %d, %d\n", cinfo.bytes, + cinfo.blocks, cinfo.ptr, dmabuf->count); +#endif + return copy_to_user((void *) arg, &cinfo, sizeof(cinfo)); + case SNDCTL_DSP_NONBLOCK: +#ifdef DEBUG + printk("SNDCTL_DSP_NONBLOCK\n"); +#endif + file->f_flags |= O_NONBLOCK; + return 0; + case SNDCTL_DSP_GETCAPS: +#ifdef DEBUG + printk("SNDCTL_DSP_GETCAPS\n"); +#endif + return put_user(DSP_CAP_REALTIME | DSP_CAP_TRIGGER | + DSP_CAP_MMAP | DSP_CAP_BIND, (int *) arg); + case SNDCTL_DSP_GETTRIGGER: + val = 0; +#ifdef DEBUG + printk("SNDCTL_DSP_GETTRIGGER 0x%x\n", dmabuf->trigger); +#endif + return put_user(dmabuf->trigger, (int *) arg); + case SNDCTL_DSP_SETTRIGGER: + if (get_user(val, (int *) arg)) + return -EFAULT; +#if defined(DEBUG) || defined(DEBUG_MMAP) + printk("SNDCTL_DSP_SETTRIGGER 0x%x\n", val); +#endif + if (!(val & PCM_ENABLE_INPUT) && dmabuf->enable == ADC_RUNNING) { + stop_adc(state); + } + if (!(val & PCM_ENABLE_OUTPUT) && dmabuf->enable == DAC_RUNNING) { + stop_dac(state); + } + if (!(val & SPDIF_ENABLE_OUTPUT) && dmabuf->enable == CODEC_SPDIFOUT_RUNNING) { + stop_spdifout(state); + } + if (!(val & SPDIF_ENABLE_OUTPUT) && dmabuf->enable == CONTROLLER_SPDIFOUT_RUNNING) { + stop_spdifout(state); + } + dmabuf->trigger = val; + if (val & PCM_ENABLE_OUTPUT && !(dmabuf->enable & DAC_RUNNING)) { + if (!dmabuf->write_channel) { + dmabuf->ready = 0; + dmabuf->write_channel = state->card->alloc_pcm_channel(state->card); + if (!dmabuf->write_channel) + return -EBUSY; + } + if (!dmabuf->ready && (ret = prog_dmabuf(state, 0))) + return ret; + if (dmabuf->mapped) { + spin_lock_irqsave(&state->card->lock, flags); + ali_update_ptr(state); + dmabuf->count = 0; + dmabuf->swptr = dmabuf->hwptr; + dmabuf->count = ali_get_free_write_space(state); + dmabuf->swptr = (dmabuf->swptr + dmabuf->count) % dmabuf->dmasize; + __ali_update_lvi(state, 0); + spin_unlock_irqrestore(&state->card->lock, + flags); + } else + start_dac(state); + } + if (val & SPDIF_ENABLE_OUTPUT && !(dmabuf->enable & CODEC_SPDIFOUT_RUNNING)) { + if (!dmabuf->codec_spdifout_channel) { + dmabuf->ready = 0; + dmabuf->codec_spdifout_channel = state->card->alloc_codec_spdifout_channel(state->card); + if (!dmabuf->codec_spdifout_channel) + return -EBUSY; + } + if (!dmabuf->ready && (ret = prog_dmabuf(state, 2))) + return ret; + if (dmabuf->mapped) { + spin_lock_irqsave(&state->card->lock, flags); + ali_update_ptr(state); + dmabuf->count = 0; + dmabuf->swptr = dmabuf->hwptr; + dmabuf->count = ali_get_free_write_space(state); + dmabuf->swptr = (dmabuf->swptr + dmabuf->count) % dmabuf->dmasize; + __ali_update_lvi(state, 2); + spin_unlock_irqrestore(&state->card->lock, + flags); + } else + start_spdifout(state); + } + if (val & SPDIF_ENABLE_OUTPUT && !(dmabuf->enable & CONTROLLER_SPDIFOUT_RUNNING)) { + if (!dmabuf->controller_spdifout_channel) { + dmabuf->ready = 0; + dmabuf->controller_spdifout_channel = state->card->alloc_controller_spdifout_channel(state->card); + if (!dmabuf->controller_spdifout_channel) + return -EBUSY; + } + if (!dmabuf->ready && (ret = prog_dmabuf(state, 3))) + return ret; + if (dmabuf->mapped) { + spin_lock_irqsave(&state->card->lock, flags); + ali_update_ptr(state); + dmabuf->count = 0; + dmabuf->swptr = dmabuf->hwptr; + dmabuf->count = ali_get_free_write_space(state); + dmabuf->swptr = (dmabuf->swptr + dmabuf->count) % dmabuf->dmasize; + __ali_update_lvi(state, 3); + spin_unlock_irqrestore(&state->card->lock, flags); + } else + start_spdifout(state); + } + if (val & PCM_ENABLE_INPUT && !(dmabuf->enable & ADC_RUNNING)) { + if (!dmabuf->read_channel) { + dmabuf->ready = 0; + dmabuf->read_channel = state->card->alloc_rec_pcm_channel(state->card); + if (!dmabuf->read_channel) + return -EBUSY; + } + if (!dmabuf->ready && (ret = prog_dmabuf(state, 1))) + return ret; + if (dmabuf->mapped) { + spin_lock_irqsave(&state->card->lock, + flags); + ali_update_ptr(state); + dmabuf->swptr = dmabuf->hwptr; + dmabuf->count = 0; + spin_unlock_irqrestore(&state->card->lock, flags); + } + ali_update_lvi(state, 1); + start_adc(state); + } + return 0; + case SNDCTL_DSP_SETDUPLEX: +#ifdef DEBUG + printk("SNDCTL_DSP_SETDUPLEX\n"); +#endif + return -EINVAL; + case SNDCTL_DSP_GETODELAY: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + spin_lock_irqsave(&state->card->lock, flags); + ali_update_ptr(state); + val = dmabuf->count; + spin_unlock_irqrestore(&state->card->lock, flags); +#ifdef DEBUG + printk("SNDCTL_DSP_GETODELAY %d\n", dmabuf->count); +#endif + return put_user(val, (int *) arg); + case SOUND_PCM_READ_RATE: +#ifdef DEBUG + printk("SOUND_PCM_READ_RATE %d\n", dmabuf->rate); +#endif + return put_user(dmabuf->rate, (int *) arg); + case SOUND_PCM_READ_CHANNELS: +#ifdef DEBUG + printk("SOUND_PCM_READ_CHANNELS\n"); +#endif + return put_user(2, (int *) arg); + case SOUND_PCM_READ_BITS: +#ifdef DEBUG + printk("SOUND_PCM_READ_BITS\n"); +#endif + return put_user(AFMT_S16_LE, (int *) arg); + case SNDCTL_DSP_SETSPDIF: /* Set S/PDIF Control register */ +#ifdef DEBUG + printk("SNDCTL_DSP_SETSPDIF\n"); +#endif + if (get_user(val, (int *) arg)) + return -EFAULT; + /* Check to make sure the codec supports S/PDIF transmitter */ + if ((state->card->ac97_features & 4)) { + /* mask out the transmitter speed bits so the user can't set them */ + val &= ~0x3000; + /* Add the current transmitter speed bits to the passed value */ + ret = ali_ac97_get(codec, AC97_SPDIF_CONTROL); + val |= (ret & 0x3000); + ali_ac97_set(codec, AC97_SPDIF_CONTROL, val); + if (ali_ac97_get(codec, AC97_SPDIF_CONTROL) != val) { + printk(KERN_ERR "ali_audio: Unable to set S/PDIF configuration to 0x%04x.\n", val); + return -EFAULT; + } + } +#ifdef DEBUG + else + printk(KERN_WARNING "ali_audio: S/PDIF transmitter not avalible.\n"); +#endif + return put_user(val, (int *) arg); + case SNDCTL_DSP_GETSPDIF: /* Get S/PDIF Control register */ +#ifdef DEBUG + printk("SNDCTL_DSP_GETSPDIF\n"); +#endif + if (get_user(val, (int *) arg)) + return -EFAULT; + /* Check to make sure the codec supports S/PDIF transmitter */ + if (!(state->card->ac97_features & 4)) { +#ifdef DEBUG + printk(KERN_WARNING "ali_audio: S/PDIF transmitter not avalible.\n"); +#endif + val = 0; + } else { + val = ali_ac97_get(codec, AC97_SPDIF_CONTROL); + } + + return put_user(val, (int *) arg); +//end add support spdif out +//add support 4,6 channel + case SNDCTL_DSP_GETCHANNELMASK: +#ifdef DEBUG + printk("SNDCTL_DSP_GETCHANNELMASK\n"); +#endif + if (get_user(val, (int *) arg)) + return -EFAULT; + /* Based on AC'97 DAC support, not ICH hardware */ + val = DSP_BIND_FRONT; + if (state->card->ac97_features & 0x0004) + val |= DSP_BIND_SPDIF; + if (state->card->ac97_features & 0x0080) + val |= DSP_BIND_SURR; + if (state->card->ac97_features & 0x0140) + val |= DSP_BIND_CENTER_LFE; + return put_user(val, (int *) arg); + case SNDCTL_DSP_BIND_CHANNEL: +#ifdef DEBUG + printk("SNDCTL_DSP_BIND_CHANNEL\n"); +#endif + if (get_user(val, (int *) arg)) + return -EFAULT; + if (val == DSP_BIND_QUERY) { + val = DSP_BIND_FRONT; /* Always report this as being enabled */ + if (state->card->ac97_status & SPDIF_ON) + val |= DSP_BIND_SPDIF; + else { + if (state->card->ac97_status & SURR_ON) + val |= DSP_BIND_SURR; + if (state->card-> + ac97_status & CENTER_LFE_ON) + val |= DSP_BIND_CENTER_LFE; + } + } else { /* Not a query, set it */ + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + if (dmabuf->enable == DAC_RUNNING) { + stop_dac(state); + } + if (val & DSP_BIND_SPDIF) { /* Turn on SPDIF */ + /* Ok, this should probably define what slots + * to use. For now, we'll only set it to the + * defaults: + * + * non multichannel codec maps to slots 3&4 + * 2 channel codec maps to slots 7&8 + * 4 channel codec maps to slots 6&9 + * 6 channel codec maps to slots 10&11 + * + * there should be some way for the app to + * select the slot assignment. + */ + i_scr = inl(state->card->iobase + ALI_SCR); + if (codec_independent_spdif_locked > 0) { + + if ((i_scr & 0x00300000) == 0x00100000) { + ali_set_spdif_output(state, AC97_EA_SPSA_7_8, codec_independent_spdif_locked); + } else { + if ((i_scr & 0x00300000) == 0x00200000) { + ali_set_spdif_output(state, AC97_EA_SPSA_6_9, codec_independent_spdif_locked); + } else { + if ((i_scr & 0x00300000) == 0x00300000) { + ali_set_spdif_output(state, AC97_EA_SPSA_10_11, codec_independent_spdif_locked); + } + } + } + } else { /* codec spdif out (pcm out share ) */ + ali_set_spdif_output(state, AC97_EA_SPSA_3_4, dmabuf->rate); //I do not modify + } + + if (!(state->card->ac97_status & SPDIF_ON)) + val &= ~DSP_BIND_SPDIF; + } else { + int mask; + int channels; + /* Turn off S/PDIF if it was on */ + if (state->card->ac97_status & SPDIF_ON) + ali_set_spdif_output(state, -1, 0); + mask = + val & (DSP_BIND_FRONT | DSP_BIND_SURR | + DSP_BIND_CENTER_LFE); + switch (mask) { + case DSP_BIND_FRONT: + channels = 2; + break; + case DSP_BIND_FRONT | DSP_BIND_SURR: + channels = 4; + break; + case DSP_BIND_FRONT | DSP_BIND_SURR | DSP_BIND_CENTER_LFE: + channels = 6; + break; + default: + val = DSP_BIND_FRONT; + channels = 2; + break; + } + ali_set_dac_channels(state, channels); + /* check that they really got turned on */ + if (!state->card->ac97_status & SURR_ON) + val &= ~DSP_BIND_SURR; + if (!state->card-> + ac97_status & CENTER_LFE_ON) + val &= ~DSP_BIND_CENTER_LFE; + } + } + return put_user(val, (int *) arg); + case SNDCTL_DSP_MAPINBUF: + case SNDCTL_DSP_MAPOUTBUF: + case SNDCTL_DSP_SETSYNCRO: + case SOUND_PCM_WRITE_FILTER: + case SOUND_PCM_READ_FILTER: + return -EINVAL; + } + return -EINVAL; +} + +static int ali_open(struct inode *inode, struct file *file) +{ + int i = 0; + struct ali_card *card = devs; + struct ali_state *state = NULL; + struct dmabuf *dmabuf = NULL; + unsigned int i_scr; + + /* find an available virtual channel (instance of /dev/dsp) */ + + while (card != NULL) { + + /* + * If we are initializing and then fail, card could go + * away unuexpectedly while we are in the for() loop. + * So, check for card on each iteration before we check + * for card->initializing to avoid a possible oops. + * This usually only matters for times when the driver is + * autoloaded by kmod. + */ + for (i = 0; i < 50 && card && card->initializing; i++) { + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(HZ / 20); + } + + for (i = 0; i < NR_HW_CH && card && !card->initializing; i++) { + if (card->states[i] == NULL) { + state = card->states[i] = (struct ali_state *) kmalloc(sizeof(struct ali_state), GFP_KERNEL); + if (state == NULL) + return -ENOMEM; + memset(state, 0, sizeof(struct ali_state)); + dmabuf = &state->dmabuf; + goto found_virt; + } + } + card = card->next; + } + + /* no more virtual channel avaiable */ + if (!state) + return -ENODEV; +found_virt: + /* initialize the virtual channel */ + + state->virt = i; + state->card = card; + state->magic = ALI5455_STATE_MAGIC; + init_waitqueue_head(&dmabuf->wait); + init_MUTEX(&state->open_sem); + file->private_data = state; + dmabuf->trigger = 0; + /* allocate hardware channels */ + if (file->f_mode & FMODE_READ) { + if ((dmabuf->read_channel = + card->alloc_rec_pcm_channel(card)) == NULL) { + kfree(card->states[i]); + card->states[i] = NULL; + return -EBUSY; + } + dmabuf->trigger |= PCM_ENABLE_INPUT; + ali_set_adc_rate(state, 8000); + } + if (file->f_mode & FMODE_WRITE) { + if (codec_independent_spdif_locked > 0) { + if ((dmabuf->codec_spdifout_channel = card->alloc_codec_spdifout_channel(card)) == NULL) { + kfree(card->states[i]); + card->states[i] = NULL; + return -EBUSY; + } + dmabuf->trigger |= SPDIF_ENABLE_OUTPUT; + ali_set_codecspdifout_rate(state, codec_independent_spdif_locked); //It must add + i_scr = inl(state->card->iobase + ALI_SCR); + if ((i_scr & 0x00300000) == 0x00100000) { + ali_set_spdif_output(state, AC97_EA_SPSA_7_8, codec_independent_spdif_locked); + } else { + if ((i_scr & 0x00300000) == 0x00200000) { + ali_set_spdif_output(state, AC97_EA_SPSA_6_9, codec_independent_spdif_locked); + } else { + if ((i_scr & 0x00300000) == 0x00300000) { + ali_set_spdif_output(state, AC97_EA_SPSA_10_11, codec_independent_spdif_locked); + } else { + ali_set_spdif_output(state, AC97_EA_SPSA_7_8, codec_independent_spdif_locked); + } + } + + } + } else { + if (controller_independent_spdif_locked > 0) { + if ((dmabuf->controller_spdifout_channel = card->alloc_controller_spdifout_channel(card)) == NULL) { + kfree(card->states[i]); + card->states[i] = NULL; + return -EBUSY; + } + dmabuf->trigger |= SPDIF_ENABLE_OUTPUT; + ali_set_spdifout_rate(state, controller_independent_spdif_locked); + } else { + if ((dmabuf->write_channel = card->alloc_pcm_channel(card)) == NULL) { + kfree(card->states[i]); + card->states[i] = NULL; + return -EBUSY; + } + /* Initialize to 8kHz? What if we don't support 8kHz? */ + /* Let's change this to check for S/PDIF stuff */ + + dmabuf->trigger |= PCM_ENABLE_OUTPUT; + if (codec_pcmout_share_spdif_locked) { + ali_set_dac_rate(state, codec_pcmout_share_spdif_locked); + ali_set_spdif_output(state, AC97_EA_SPSA_3_4, codec_pcmout_share_spdif_locked); + } else { + ali_set_dac_rate(state, 8000); + } + } + + } + } + + /* set default sample format. According to OSS Programmer's Guide /dev/dsp + should be default to unsigned 8-bits, mono, with sample rate 8kHz and + /dev/dspW will accept 16-bits sample, but we don't support those so we + set it immediately to stereo and 16bit, which is all we do support */ + dmabuf->fmt |= ALI5455_FMT_16BIT | ALI5455_FMT_STEREO; + dmabuf->ossfragsize = 0; + dmabuf->ossmaxfrags = 0; + dmabuf->subdivision = 0; + state->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE); + outl(0x00000000, card->iobase + ALI_INTERRUPTCR); + outl(0x00000000, card->iobase + ALI_INTERRUPTSR); + return 0; +} + +static int ali_release(struct inode *inode, struct file *file) +{ + struct ali_state *state = (struct ali_state *) file->private_data; + struct ali_card *card = state->card; + struct dmabuf *dmabuf = &state->dmabuf; + unsigned long flags; + lock_kernel(); + + /* stop DMA state machine and free DMA buffers/channels */ + if (dmabuf->trigger & PCM_ENABLE_OUTPUT) + drain_dac(state, 0); + + if (dmabuf->trigger & SPDIF_ENABLE_OUTPUT) + drain_spdifout(state, 0); + + if (dmabuf->trigger & PCM_ENABLE_INPUT) + stop_adc(state); + + spin_lock_irqsave(&card->lock, flags); + dealloc_dmabuf(state); + if (file->f_mode & FMODE_WRITE) { + if (codec_independent_spdif_locked > 0) { + state->card->free_pcm_channel(state->card, dmabuf->codec_spdifout_channel->num); + } else { + if (controller_independent_spdif_locked > 0) + state->card->free_pcm_channel(state->card, + dmabuf->controller_spdifout_channel->num); + else state->card->free_pcm_channel(state->card, + dmabuf->write_channel->num); + } + } + if (file->f_mode & FMODE_READ) + state->card->free_pcm_channel(state->card, dmabuf->read_channel->num); + + state->card->states[state->virt] = NULL; + kfree(state); + spin_unlock_irqrestore(&card->lock, flags); + unlock_kernel(); + return 0; +} + +static /*const */ struct file_operations ali_audio_fops = { + owner:THIS_MODULE, + llseek:no_llseek, + read:ali_read, + write:ali_write, + poll:ali_poll, + ioctl:ali_ioctl, + mmap:ali_mmap, + open:ali_open, + release:ali_release, +}; + +/* Read AC97 codec registers */ +static u16 ali_ac97_get(struct ac97_codec *dev, u8 reg) +{ + struct ali_card *card = dev->private_data; + int count1 = 100; + char val; + unsigned short int data = 0, count, addr1, addr2 = 0; + + spin_lock(&card->ac97_lock); + while (count1-- && (inl(card->iobase + ALI_CAS) & 0x80000000)) + udelay(1); + + addr1 = reg; + reg |= 0x0080; + for (count = 0; count < 0x7f; count++) { + val = inb(card->iobase + ALI_CSPSR); + if (val & 0x08) + break; + } + if (count == 0x7f) + { + spin_unlock(&card->ac97_lock); + return -1; + } + outw(reg, (card->iobase + ALI_CPR) + 2); + for (count = 0; count < 0x7f; count++) { + val = inb(card->iobase + ALI_CSPSR); + if (val & 0x02) { + data = inw(card->iobase + ALI_SPR); + addr2 = inw((card->iobase + ALI_SPR) + 2); + break; + } + } + spin_unlock(&card->ac97_lock); + if (count == 0x7f) + return -1; + if (addr2 != addr1) + return -1; + return ((u16) data); +} + +/* write ac97 codec register */ + +static void ali_ac97_set(struct ac97_codec *dev, u8 reg, u16 data) +{ + struct ali_card *card = dev->private_data; + int count1 = 100; + char val; + unsigned short int count; + + spin_lock(&card->ac97_lock); + while (count1-- && (inl(card->iobase + ALI_CAS) & 0x80000000)) + udelay(1); + + for (count = 0; count < 0x7f; count++) { + val = inb(card->iobase + ALI_CSPSR); + if (val & 0x08) + break; + } + if (count == 0x7f) { + printk(KERN_WARNING "ali_ac97_set: AC97 codec register access timed out. \n"); + spin_unlock(&card->ac97_lock); + return; + } + outw(data, (card->iobase + ALI_CPR)); + outb(reg, (card->iobase + ALI_CPR) + 2); + for (count = 0; count < 0x7f; count++) { + val = inb(card->iobase + ALI_CSPSR); + if (val & 0x01) + break; + } + spin_unlock(&card->ac97_lock); + if (count == 0x7f) + printk(KERN_WARNING "ali_ac97_set: AC97 codec register access timed out. \n"); + return; +} + +/* OSS /dev/mixer file operation methods */ + +static int ali_open_mixdev(struct inode *inode, struct file *file) +{ + int i; + int minor = minor(inode->i_rdev); + struct ali_card *card = devs; + for (card = devs; card != NULL; card = card->next) { + /* + * If we are initializing and then fail, card could go + * away unuexpectedly while we are in the for() loop. + * So, check for card on each iteration before we check + * for card->initializing to avoid a possible oops. + * This usually only matters for times when the driver is + * autoloaded by kmod. + */ + for (i = 0; i < 50 && card && card->initializing; i++) { + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(HZ / 20); + } + for (i = 0; i < NR_AC97 && card && !card->initializing; i++) + if (card->ac97_codec[i] != NULL + && card->ac97_codec[i]->dev_mixer == minor) { + file->private_data = card->ac97_codec[i]; + return 0; + } + } + return -ENODEV; +} + +static int ali_ioctl_mixdev(struct inode *inode, + struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct ac97_codec *codec = (struct ac97_codec *) file->private_data; + return codec->mixer_ioctl(codec, cmd, arg); +} + +static /*const */ struct file_operations ali_mixer_fops = { + owner:THIS_MODULE, + llseek:no_llseek, + ioctl:ali_ioctl_mixdev, + open:ali_open_mixdev, +}; + +/* AC97 codec initialisation. These small functions exist so we don't + duplicate code between module init and apm resume */ + +static inline int ali_ac97_exists(struct ali_card *card, int ac97_number) +{ + unsigned int i = 1; + u32 reg = inl(card->iobase + ALI_RTSR); + if (ac97_number) { + while (i < 100) { + + reg = inl(card->iobase + ALI_RTSR); + if (reg & 0x40) { + break; + } else { + outl(reg | 0x00000040, + card->iobase + 0x34); + udelay(1); + } + i++; + } + + } else { + while (i < 100) { + reg = inl(card->iobase + ALI_RTSR); + if (reg & 0x80) { + break; + } else { + outl(reg | 0x00000080, + card->iobase + 0x34); + udelay(1); + } + i++; + } + } + + if (ac97_number) + return reg & 0x40; + else + return reg & 0x80; +} + +static inline int ali_ac97_enable_variable_rate(struct ac97_codec *codec) +{ + ali_ac97_set(codec, AC97_EXTENDED_STATUS, 9); + ali_ac97_set(codec, AC97_EXTENDED_STATUS, ali_ac97_get(codec, AC97_EXTENDED_STATUS) | 0xE800); + return (ali_ac97_get(codec, AC97_EXTENDED_STATUS) & 1); +} + + +static int ali_ac97_probe_and_powerup(struct ali_card *card, struct ac97_codec *codec) +{ + /* Returns 0 on failure */ + int i; + u16 addr; + if (ac97_probe_codec(codec) == 0) + return 0; + /* ac97_probe_codec is success ,then begin to init codec */ + ali_ac97_set(codec, AC97_RESET, 0xffff); + if (card->channel[0].used == 1) { + ali_ac97_set(codec, AC97_RECORD_SELECT, 0x0000); + ali_ac97_set(codec, AC97_LINEIN_VOL, 0x0808); + ali_ac97_set(codec, AC97_RECORD_GAIN, 0x0F0F); + } + + if (card->channel[2].used == 1) //if MICin then init codec + { + ali_ac97_set(codec, AC97_RECORD_SELECT, 0x0000); + ali_ac97_set(codec, AC97_MIC_VOL, 0x8808); + ali_ac97_set(codec, AC97_RECORD_GAIN, 0x0F0F); + ali_ac97_set(codec, AC97_RECORD_GAIN_MIC, 0x0000); + } + + ali_ac97_set(codec, AC97_MASTER_VOL_STEREO, 0x0000); + ali_ac97_set(codec, AC97_HEADPHONE_VOL, 0x0000); + ali_ac97_set(codec, AC97_PCMOUT_VOL, 0x0000); + ali_ac97_set(codec, AC97_CD_VOL, 0x0808); + ali_ac97_set(codec, AC97_VIDEO_VOL, 0x0808); + ali_ac97_set(codec, AC97_AUX_VOL, 0x0808); + ali_ac97_set(codec, AC97_PHONE_VOL, 0x8048); + ali_ac97_set(codec, AC97_PCBEEP_VOL, 0x0000); + ali_ac97_set(codec, AC97_GENERAL_PURPOSE, AC97_GP_MIX); + ali_ac97_set(codec, AC97_MASTER_VOL_MONO, 0x0000); + ali_ac97_set(codec, 0x38, 0x0000); + addr = ali_ac97_get(codec, 0x2a); + ali_ac97_set(codec, 0x2a, addr | 0x0001); + addr = ali_ac97_get(codec, 0x2a); + addr = ali_ac97_get(codec, 0x28); + ali_ac97_set(codec, 0x2c, 0xbb80); + addr = ali_ac97_get(codec, 0x2c); + /* power it all up */ + ali_ac97_set(codec, AC97_POWER_CONTROL, + ali_ac97_get(codec, AC97_POWER_CONTROL) & ~0x7f00); + /* wait for analog ready */ + for (i = 10; i && ((ali_ac97_get(codec, AC97_POWER_CONTROL) & 0xf) != 0xf); i--) { + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(HZ / 20); + } + /* FIXME !! */ + i++; + return i; +} + + +/* I clone ali5455(2.4.7 ) not clone i810_audio(2.4.18) */ + +static int ali_reset_5455(struct ali_card *card) +{ + outl(0x80000003, card->iobase + ALI_SCR); + outl(0x83838383, card->iobase + ALI_FIFOCR1); + outl(0x83838383, card->iobase + ALI_FIFOCR2); + if (controller_pcmout_share_spdif_locked > 0) { + outl((inl(card->iobase + ALI_SPDIFICS) | 0x00000001), + card->iobase + ALI_SPDIFICS); + outl(0x0408000a, card->iobase + ALI_INTERFACECR); + } else { + if (codec_independent_spdif_locked > 0) { + outl((inl(card->iobase + ALI_SCR) | 0x00100000), card->iobase + ALI_SCR); // now I select slot 7 & 8 + outl(0x00200000, card->iobase + ALI_INTERFACECR); //enable codec independent spdifout + } else + outl(0x04080002, card->iobase + ALI_INTERFACECR); + } + + outl(0x00000000, card->iobase + ALI_INTERRUPTCR); + outl(0x00000000, card->iobase + ALI_INTERRUPTSR); + if (controller_independent_spdif_locked > 0) + outl((inl(card->iobase + ALI_SPDIFICS) | 0x00000001), + card->iobase + ALI_SPDIFICS); + return 1; +} + + +static int ali_ac97_random_init_stuff(struct ali_card + *card) +{ + u32 reg = inl(card->iobase + ALI_SCR); + int i = 0; + reg = inl(card->iobase + ALI_SCR); + if ((reg & 2) == 0) /* Cold required */ + reg |= 2; + else + reg |= 1; /* Warm */ + reg &= ~0x80000000; /* ACLink on */ + outl(reg, card->iobase + ALI_SCR); + + while (i < 10) { + if ((inl(card->iobase + 0x18) & (1 << 1)) == 0) + break; + current->state = TASK_UNINTERRUPTIBLE; + schedule_timeout(HZ / 20); + i++; + } + if (i == 10) { + printk(KERN_ERR "ali_audio: AC'97 reset failed.\n"); + return 0; + } + + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(HZ / 2); + return 1; +} + +/* AC97 codec initialisation. */ + +static int __init ali_ac97_init(struct ali_card *card) +{ + int num_ac97 = 0; + int total_channels = 0; + struct ac97_codec *codec; + u16 eid; + + if (!ali_ac97_random_init_stuff(card)) + return 0; + + /* Number of channels supported */ + /* What about the codec? Just because the ICH supports */ + /* multiple channels doesn't mean the codec does. */ + /* we'll have to modify this in the codec section below */ + /* to reflect what the codec has. */ + /* ICH and ICH0 only support 2 channels so don't bother */ + /* to check.... */ + inl(card->iobase + ALI_CPR); + card->channels = 2; + + for (num_ac97 = 0; num_ac97 < NR_AC97; num_ac97++) { + + /* Assume codec isn't available until we go through the + * gauntlet below */ + card->ac97_codec[num_ac97] = NULL; + /* The ICH programmer's reference says you should */ + /* check the ready status before probing. So we chk */ + /* What do we do if it's not ready? Wait and try */ + /* again, or abort? */ + if (!ali_ac97_exists(card, num_ac97)) { + if (num_ac97 == 0) + printk(KERN_ERR "ali_audio: Primary codec not ready.\n"); + break; + } + + if ((codec = ac97_alloc_codec()) == NULL) + return -ENOMEM; + /* initialize some basic codec information, other fields will be filled + in ac97_probe_codec */ + codec->private_data = card; + codec->id = num_ac97; + codec->codec_read = ali_ac97_get; + codec->codec_write = ali_ac97_set; + if (!ali_ac97_probe_and_powerup(card, codec)) { + printk(KERN_ERR "ali_audio: timed out waiting for codec %d analog ready", + num_ac97); + kfree(codec); + break; /* it didn't work */ + } + + /* Store state information about S/PDIF transmitter */ + card->ac97_status = 0; + /* Don't attempt to get eid until powerup is complete */ + eid = ali_ac97_get(codec, AC97_EXTENDED_ID); + if (eid == 0xFFFF) { + printk(KERN_ERR "ali_audio: no codec attached ?\n"); + kfree(codec); + break; + } + + card->ac97_features = eid; + /* Now check the codec for useful features to make up for + the dumbness of the ali5455 hardware engine */ + if (!(eid & 0x0001)) + printk(KERN_WARNING + "ali_audio: only 48Khz playback available.\n"); + else { + if (!ali_ac97_enable_variable_rate(codec)) { + printk(KERN_WARNING + "ali_audio: Codec refused to allow VRA, using 48Khz only.\n"); + card->ac97_features &= ~1; + } + } + + /* Determine how many channels the codec(s) support */ + /* - The primary codec always supports 2 */ + /* - If the codec supports AMAP, surround DACs will */ + /* automaticlly get assigned to slots. */ + /* * Check for surround DACs and increment if */ + /* found. */ + /* - Else check if the codec is revision 2.2 */ + /* * If surround DACs exist, assign them to slots */ + /* and increment channel count. */ + + /* All of this only applies to ICH2 and above. ICH */ + /* and ICH0 only support 2 channels. ICH2 will only */ + /* support multiple codecs in a "split audio" config. */ + /* as described above. */ + + /* TODO: Remove all the debugging messages! */ + + if ((eid & 0xc000) == 0) /* primary codec */ + total_channels += 2; + if ((codec->dev_mixer = register_sound_mixer(&ali_mixer_fops, -1)) < 0) { + printk(KERN_ERR "ali_audio: couldn't register mixer!\n"); + kfree(codec); + break; + } + card->ac97_codec[num_ac97] = codec; + } + /* pick the minimum of channels supported by ICHx or codec(s) */ + card->channels = (card->channels > total_channels) ? total_channels : card->channels; + return num_ac97; +} + +static void __init ali_configure_clocking(void) +{ + struct ali_card *card; + struct ali_state *state; + struct dmabuf *dmabuf; + unsigned int i, offset, new_offset; + unsigned long flags; + card = devs; + + /* We could try to set the clocking for multiple cards, but can you even have + * more than one ali in a machine? Besides, clocking is global, so unless + * someone actually thinks more than one ali in a machine is possible and + * decides to rewrite that little bit, setting the rate for more than one card + * is a waste of time. + */ + if (card != NULL) { + state = card->states[0] = (struct ali_state *) + kmalloc(sizeof(struct ali_state), GFP_KERNEL); + if (state == NULL) + return; + memset(state, 0, sizeof(struct ali_state)); + dmabuf = &state->dmabuf; + dmabuf->write_channel = card->alloc_pcm_channel(card); + state->virt = 0; + state->card = card; + state->magic = ALI5455_STATE_MAGIC; + init_waitqueue_head(&dmabuf->wait); + init_MUTEX(&state->open_sem); + dmabuf->fmt = ALI5455_FMT_STEREO | ALI5455_FMT_16BIT; + dmabuf->trigger = PCM_ENABLE_OUTPUT; + ali_set_dac_rate(state, 48000); + if (prog_dmabuf(state, 0) != 0) + goto config_out_nodmabuf; + + if (dmabuf->dmasize < 16384) + goto config_out; + + dmabuf->count = dmabuf->dmasize; + outb(31, card->iobase + dmabuf->write_channel->port + OFF_LVI); + + local_irq_save(flags); + start_dac(state); + offset = ali_get_dma_addr(state, 0); + mdelay(50); + new_offset = ali_get_dma_addr(state, 0); + stop_dac(state); + + outb(2, card->iobase + dmabuf->write_channel->port + OFF_CR); + local_irq_restore(flags); + + i = new_offset - offset; + + if (i == 0) + goto config_out; + i = i / 4 * 20; + if (i > 48500 || i < 47500) { + clocking = clocking * clocking / i; + } +config_out: + dealloc_dmabuf(state); +config_out_nodmabuf: + state->card->free_pcm_channel(state->card, state->dmabuf. write_channel->num); + kfree(state); + card->states[0] = NULL; + } +} + +/* install the driver, we do not allocate hardware channel nor DMA buffer now, they are defered + until "ACCESS" time (in prog_dmabuf called by open/read/write/ioctl/mmap) */ + +static int __init ali_probe(struct pci_dev *pci_dev, const struct pci_device_id + *pci_id) +{ + struct ali_card *card; + if (pci_enable_device(pci_dev)) + return -EIO; + if (pci_set_dma_mask(pci_dev, ALI5455_DMA_MASK)) { + printk(KERN_ERR "ali5455: architecture does not support" + " 32bit PCI busmaster DMA\n"); + return -ENODEV; + } + + if ((card = kmalloc(sizeof(struct ali_card), GFP_KERNEL)) == NULL) { + printk(KERN_ERR "ali_audio: out of memory\n"); + return -ENOMEM; + } + memset(card, 0, sizeof(*card)); + card->initializing = 1; + card->iobase = pci_resource_start(pci_dev, 0); + card->pci_dev = pci_dev; + card->pci_id = pci_id->device; + card->irq = pci_dev->irq; + card->next = devs; + card->magic = ALI5455_CARD_MAGIC; +#ifdef CONFIG_PM + card->pm_suspended = 0; +#endif + spin_lock_init(&card->lock); + spin_lock_init(&card->ac97_lock); + devs = card; + pci_set_master(pci_dev); + printk(KERN_INFO "ali: %s found at IO 0x%04lx, IRQ %d\n", + card_names[pci_id->driver_data], card->iobase, card->irq); + card->alloc_pcm_channel = ali_alloc_pcm_channel; + card->alloc_rec_pcm_channel = ali_alloc_rec_pcm_channel; + card->alloc_rec_mic_channel = ali_alloc_rec_mic_channel; + card->alloc_codec_spdifout_channel = ali_alloc_codec_spdifout_channel; + card->alloc_controller_spdifout_channel = ali_alloc_controller_spdifout_channel; + card->free_pcm_channel = ali_free_pcm_channel; + card->channel[0].offset = 0; + card->channel[0].port = 0x40; + card->channel[0].num = 0; + card->channel[1].offset = 0; + card->channel[1].port = 0x50; + card->channel[1].num = 1; + card->channel[2].offset = 0; + card->channel[2].port = 0x60; + card->channel[2].num = 2; + card->channel[3].offset = 0; + card->channel[3].port = 0x70; + card->channel[3].num = 3; + card->channel[4].offset = 0; + card->channel[4].port = 0xb0; + card->channel[4].num = 4; + /* claim our iospace and irq */ + request_region(card->iobase, 256, card_names[pci_id->driver_data]); + if (request_irq(card->irq, &ali_interrupt, SA_SHIRQ, + card_names[pci_id->driver_data], card)) { + printk(KERN_ERR "ali_audio: unable to allocate irq %d\n", + card->irq); + release_region(card->iobase, 256); + kfree(card); + return -ENODEV; + } + + if (ali_reset_5455(card) <= 0) { + unregister_sound_dsp(card->dev_audio); + release_region(card->iobase, 256); + free_irq(card->irq, card); + kfree(card); + return -ENODEV; + } + + /* initialize AC97 codec and register /dev/mixer */ + if (ali_ac97_init(card) < 0) { + release_region(card->iobase, 256); + free_irq(card->irq, card); + kfree(card); + return -ENODEV; + } + + pci_set_drvdata(pci_dev, card); + + if (clocking == 0) { + clocking = 48000; + ali_configure_clocking(); + } + + /* register /dev/dsp */ + if ((card->dev_audio = register_sound_dsp(&ali_audio_fops, -1)) < 0) { + int i; + printk(KERN_ERR"ali_audio: couldn't register DSP device!\n"); + release_region(card->iobase, 256); + free_irq(card->irq, card); + for (i = 0; i < NR_AC97; i++) + if (card->ac97_codec[i] != NULL) { + unregister_sound_mixer(card->ac97_codec[i]->dev_mixer); + kfree(card->ac97_codec[i]); + } + kfree(card); + return -ENODEV; + } + card->initializing = 0; + return 0; +} + +static void __devexit ali_remove(struct pci_dev *pci_dev) +{ + int i; + struct ali_card *card = pci_get_drvdata(pci_dev); + /* free hardware resources */ + free_irq(card->irq, devs); + release_region(card->iobase, 256); + /* unregister audio devices */ + for (i = 0; i < NR_AC97; i++) + if (card->ac97_codec[i] != NULL) { + unregister_sound_mixer(card->ac97_codec[i]-> + dev_mixer); + ac97_release_codec(card->ac97_codec[i]); + card->ac97_codec[i] = NULL; + } + unregister_sound_dsp(card->dev_audio); + kfree(card); +} + +#ifdef CONFIG_PM +static int ali_pm_suspend(struct pci_dev *dev, u32 pm_state) +{ + struct ali_card *card = pci_get_drvdata(dev); + struct ali_state *state; + unsigned long flags; + struct dmabuf *dmabuf; + int i, num_ac97; + + if (!card) + return 0; + spin_lock_irqsave(&card->lock, flags); + card->pm_suspended = 1; + for (i = 0; i < NR_HW_CH; i++) { + state = card->states[i]; + if (!state) + continue; + /* this happens only if there are open files */ + dmabuf = &state->dmabuf; + if (dmabuf->enable & DAC_RUNNING || + (dmabuf->count + && (dmabuf->trigger & PCM_ENABLE_OUTPUT))) { + state->pm_saved_dac_rate = dmabuf->rate; + stop_dac(state); + } else { + state->pm_saved_dac_rate = 0; + } + if (dmabuf->enable & ADC_RUNNING) { + state->pm_saved_adc_rate = dmabuf->rate; + stop_adc(state); + } else { + state->pm_saved_adc_rate = 0; + } + dmabuf->ready = 0; + dmabuf->swptr = dmabuf->hwptr = 0; + dmabuf->count = dmabuf->total_bytes = 0; + } + + spin_unlock_irqrestore(&card->lock, flags); + /* save mixer settings */ + for (num_ac97 = 0; num_ac97 < NR_AC97; num_ac97++) { + struct ac97_codec *codec = card->ac97_codec[num_ac97]; + if (!codec) + continue; + for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { + if ((supported_mixer(codec, i)) && (codec->read_mixer)) { + card->pm_saved_mixer_settings[i][num_ac97] = codec->read_mixer(codec, i); + } + } + } + pci_save_state(dev, card->pm_save_state); /* XXX do we need this? */ + pci_disable_device(dev); /* disable busmastering */ + pci_set_power_state(dev, 3); /* Zzz. */ + return 0; +} + + +static int ali_pm_resume(struct pci_dev *dev) +{ + int num_ac97, i = 0; + struct ali_card *card = pci_get_drvdata(dev); + pci_enable_device(dev); + pci_restore_state(dev, card->pm_save_state); + /* observation of a toshiba portege 3440ct suggests that the + hardware has to be more or less completely reinitialized from + scratch after an apm suspend. Works For Me. -dan */ + ali_ac97_random_init_stuff(card); + for (num_ac97 = 0; num_ac97 < NR_AC97; num_ac97++) { + struct ac97_codec *codec = card->ac97_codec[num_ac97]; + /* check they haven't stolen the hardware while we were + away */ + if (!codec || !ali_ac97_exists(card, num_ac97)) { + if (num_ac97) + continue; + else + BUG(); + } + if (!ali_ac97_probe_and_powerup(card, codec)) + BUG(); + if ((card->ac97_features & 0x0001)) { + /* at probe time we found we could do variable + rates, but APM suspend has made it forget + its magical powers */ + if (!ali_ac97_enable_variable_rate(codec)) + BUG(); + } + /* we lost our mixer settings, so restore them */ + for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { + if (supported_mixer(codec, i)) { + int val = card->pm_saved_mixer_settings[i][num_ac97]; + codec->mixer_state[i] = val; + codec->write_mixer(codec, i, + (val & 0xff), + ((val >> 8) & 0xff)); + } + } + } + + /* we need to restore the sample rate from whatever it was */ + for (i = 0; i < NR_HW_CH; i++) { + struct ali_state *state = card->states[i]; + if (state) { + if (state->pm_saved_adc_rate) + ali_set_adc_rate(state, state->pm_saved_adc_rate); + if (state->pm_saved_dac_rate) + ali_set_dac_rate(state, state->pm_saved_dac_rate); + } + } + + card->pm_suspended = 0; + /* any processes that were reading/writing during the suspend + probably ended up here */ + for (i = 0; i < NR_HW_CH; i++) { + struct ali_state *state = card->states[i]; + if (state) + wake_up(&state->dmabuf.wait); + } + return 0; +} +#endif /* CONFIG_PM */ + +MODULE_AUTHOR(""); +MODULE_DESCRIPTION("ALI 5455 audio support"); +MODULE_LICENSE("GPL"); +MODULE_PARM(clocking, "i"); +MODULE_PARM(strict_clocking, "i"); +MODULE_PARM(codec_pcmout_share_spdif_locked, "i"); +MODULE_PARM(codec_independent_spdif_locked, "i"); +MODULE_PARM(controller_pcmout_share_spdif_locked, "i"); +MODULE_PARM(controller_independent_spdif_locked, "i"); +#define ALI5455_MODULE_NAME "ali5455" +static struct pci_driver ali_pci_driver = { + name:ALI5455_MODULE_NAME, id_table:ali_pci_tbl, probe:ali_probe, + remove:__devexit_p(ali_remove), +#ifdef CONFIG_PM + suspend:ali_pm_suspend, resume:ali_pm_resume, +#endif /* CONFIG_PM */ +}; + +static int __init ali_init_module(void) +{ + printk(KERN_INFO "ALI 5455 + AC97 Audio, version " + DRIVER_VERSION ", " __TIME__ " " __DATE__ "\n"); + + if (codec_independent_spdif_locked > 0) { + if (codec_independent_spdif_locked == 32000 + || codec_independent_spdif_locked == 44100 + || codec_independent_spdif_locked == 48000) { + printk(KERN_INFO "ali_audio: Enabling S/PDIF at sample rate %dHz.\n", codec_independent_spdif_locked); + } else { + printk(KERN_INFO "ali_audio: S/PDIF can only be locked to 32000, 44100, or 48000Hz.\n"); + codec_independent_spdif_locked = 0; + } + } + if (controller_independent_spdif_locked > 0) { + if (controller_independent_spdif_locked == 32000 + || controller_independent_spdif_locked == 44100 + || controller_independent_spdif_locked == 48000) { + printk(KERN_INFO "ali_audio: Enabling S/PDIF at sample rate %dHz.\n", controller_independent_spdif_locked); + } else { + printk(KERN_INFO "ali_audio: S/PDIF can only be locked to 32000, 44100, or 48000Hz.\n"); + controller_independent_spdif_locked = 0; + } + } + + if (codec_pcmout_share_spdif_locked > 0) { + if (codec_pcmout_share_spdif_locked == 32000 + || codec_pcmout_share_spdif_locked == 44100 + || codec_pcmout_share_spdif_locked == 48000) { + printk(KERN_INFO "ali_audio: Enabling S/PDIF at sample rate %dHz.\n", codec_pcmout_share_spdif_locked); + } else { + printk(KERN_INFO "ali_audio: S/PDIF can only be locked to 32000, 44100, or 48000Hz.\n"); + codec_pcmout_share_spdif_locked = 0; + } + } + if (controller_pcmout_share_spdif_locked > 0) { + if (controller_pcmout_share_spdif_locked == 32000 + || controller_pcmout_share_spdif_locked == 44100 + || controller_pcmout_share_spdif_locked == 48000) { + printk(KERN_INFO "ali_audio: Enabling controller S/PDIF at sample rate %dHz.\n", controller_pcmout_share_spdif_locked); + } else { + printk(KERN_INFO "ali_audio: S/PDIF can only be locked to 32000, 44100, or 48000Hz.\n"); + controller_pcmout_share_spdif_locked = 0; + } + } + if (!pci_register_driver(&ali_pci_driver)) { + pci_unregister_driver(&ali_pci_driver); + return -ENODEV; + } + return 0; +} + +static void __exit ali_cleanup_module(void) +{ + pci_unregister_driver(&ali_pci_driver); +} + +module_init(ali_init_module); +module_exit(ali_cleanup_module); +/* +Local Variables: +c-basic-offset: 8 +End: +*/ diff -Nru a/sound/oss/au1000.c b/sound/oss/au1000.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/au1000.c Fri Jul 11 08:24:23 2003 @@ -0,0 +1,2238 @@ +/* + * au1000.c -- Sound driver for Alchemy Au1000 MIPS Internet Edge + * Processor. + * + * Copyright 2001 MontaVista Software Inc. + * Author: MontaVista Software, Inc. + * stevel@mvista.com or source@mvista.com + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * THIS SOFTWARE IS PROVIDED ``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 AUTHOR 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + * Module command line parameters: + * + * Supported devices: + * /dev/dsp standard OSS /dev/dsp device + * /dev/mixer standard OSS /dev/mixer device + * + * Notes: + * + * 1. Much of the OSS buffer allocation, ioctl's, and mmap'ing are + * taken, slightly modified or not at all, from the ES1371 driver, + * so refer to the credits in es1371.c for those. The rest of the + * code (probe, open, read, write, the ISR, etc.) is new. + * + * Revision history + * 06.27.2001 Initial version + * 03.20.2002 Added mutex locks around read/write methods, to prevent + * simultaneous access on SMP or preemptible kernels. Also + * removed the counter/pointer fragment aligning at the end + * of read/write methods [stevel]. + * 03.21.2002 Add support for coherent DMA on the audio read/write DMA + * channels [stevel]. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* --------------------------------------------------------------------- */ + +#undef OSS_DOCUMENTED_MIXER_SEMANTICS +#define AU1000_DEBUG +#undef AU1000_VERBOSE_DEBUG + +#define USE_COHERENT_DMA + +#define AU1000_MODULE_NAME "Au1000 audio" +#define PFX AU1000_MODULE_NAME + +#ifdef AU1000_DEBUG +#define dbg(format, arg...) printk(KERN_DEBUG PFX ": " format "\n" , ## arg) +#else +#define dbg(format, arg...) do {} while (0) +#endif +#define err(format, arg...) printk(KERN_ERR PFX ": " format "\n" , ## arg) +#define info(format, arg...) printk(KERN_INFO PFX ": " format "\n" , ## arg) +#define warn(format, arg...) printk(KERN_WARNING PFX ": " format "\n" , ## arg) + + +/* misc stuff */ +#define POLL_COUNT 0x5000 +#define AC97_EXT_DACS (AC97_EXTID_SDAC | AC97_EXTID_CDAC | AC97_EXTID_LDAC) + +/* Boot options */ +static int vra = 0; // 0 = no VRA, 1 = use VRA if codec supports it +MODULE_PARM(vra, "i"); +MODULE_PARM_DESC(vra, "if 1 use VRA if codec supports it"); + + +/* --------------------------------------------------------------------- */ + +struct au1000_state { + /* soundcore stuff */ + int dev_audio; + +#ifdef AU1000_DEBUG + /* debug /proc entry */ + struct proc_dir_entry *ps; + struct proc_dir_entry *ac97_ps; +#endif /* AU1000_DEBUG */ + + struct ac97_codec *codec; + unsigned codec_base_caps;// AC'97 reg 00h, "Reset Register" + unsigned codec_ext_caps; // AC'97 reg 28h, "Extended Audio ID" + int no_vra; // do not use VRA + + spinlock_t lock; + struct semaphore open_sem; + struct semaphore sem; + mode_t open_mode; + wait_queue_head_t open_wait; + + struct dmabuf { + unsigned int dmanr; // DMA Channel number + unsigned sample_rate; // Hz + unsigned src_factor; // SRC interp/decimation (no vra) + unsigned sample_size; // 8 or 16 + int num_channels; // 1 = mono, 2 = stereo, 4, 6 + int dma_bytes_per_sample;// DMA bytes per audio sample frame + int user_bytes_per_sample;// User bytes per audio sample frame + int cnt_factor; // user-to-DMA bytes per audio + // sample frame + void *rawbuf; + dma_addr_t dmaaddr; + unsigned buforder; + unsigned numfrag; // # of DMA fragments in DMA buffer + unsigned fragshift; + void *nextIn; // ptr to next-in to DMA buffer + void *nextOut;// ptr to next-out from DMA buffer + int count; // current byte count in DMA buffer + unsigned total_bytes; // total bytes written or read + unsigned error; // over/underrun + wait_queue_head_t wait; + /* redundant, but makes calculations easier */ + unsigned fragsize; // user perception of fragment size + unsigned dma_fragsize; // DMA (real) fragment size + unsigned dmasize; // Total DMA buffer size + // (mult. of DMA fragsize) + /* OSS stuff */ + unsigned mapped:1; + unsigned ready:1; + unsigned stopped:1; + unsigned ossfragshift; + int ossmaxfrags; + unsigned subdivision; + } dma_dac , dma_adc; +} au1000_state; + +/* --------------------------------------------------------------------- */ + + +static inline unsigned ld2(unsigned int x) +{ + unsigned r = 0; + + if (x >= 0x10000) { + x >>= 16; + r += 16; + } + if (x >= 0x100) { + x >>= 8; + r += 8; + } + if (x >= 0x10) { + x >>= 4; + r += 4; + } + if (x >= 4) { + x >>= 2; + r += 2; + } + if (x >= 2) + r++; + return r; +} + + +#ifdef USE_COHERENT_DMA +static inline void * dma_alloc(size_t size, dma_addr_t * dma_handle) +{ + void* ret = (void *)__get_free_pages(GFP_ATOMIC | GFP_DMA, + get_order(size)); + if (ret != NULL) { + memset(ret, 0, size); + *dma_handle = virt_to_phys(ret); + } + return ret; +} + +static inline void dma_free(size_t size, void* va, dma_addr_t dma_handle) +{ + free_pages((unsigned long)va, get_order(size)); +} +#else +static inline void * dma_alloc(size_t size, dma_addr_t * dma_handle) +{ + return pci_alloc_consistent(NULL, size, dma_handle); +} + +static inline void dma_free(size_t size, void* va, dma_addr_t dma_handle) +{ + pci_free_consistent(NULL, size, va, dma_handle); +} +#endif + +/* --------------------------------------------------------------------- */ + +static void au1000_delay(int msec) +{ + unsigned long tmo; + signed long tmo2; + + if (in_interrupt()) + return; + + tmo = jiffies + (msec * HZ) / 1000; + for (;;) { + tmo2 = tmo - jiffies; + if (tmo2 <= 0) + break; + schedule_timeout(tmo2); + } +} + + +/* --------------------------------------------------------------------- */ + +static u16 rdcodec(struct ac97_codec *codec, u8 addr) +{ + struct au1000_state *s = (struct au1000_state *)codec->private_data; + unsigned long flags; + u32 cmd; + u16 data; + int i; + + spin_lock_irqsave(&s->lock, flags); + + for (i = 0; i < POLL_COUNT; i++) + if (!(au_readl(AC97C_STATUS) & AC97C_CP)) + break; + if (i == POLL_COUNT) + err("rdcodec: codec cmd pending expired!"); + + cmd = (u32) addr & AC97C_INDEX_MASK; + cmd |= AC97C_READ; // read command + au_writel(cmd, AC97C_CMD); + + /* now wait for the data */ + for (i = 0; i < POLL_COUNT; i++) + if (!(au_readl(AC97C_STATUS) & AC97C_CP)) + break; + if (i == POLL_COUNT) { + err("rdcodec: read poll expired!"); + return 0; + } + + data = au_readl(AC97C_CMD) & 0xffff; + + spin_unlock_irqrestore(&s->lock, flags); + + return data; +} + + +static void wrcodec(struct ac97_codec *codec, u8 addr, u16 data) +{ + struct au1000_state *s = (struct au1000_state *)codec->private_data; + unsigned long flags; + u32 cmd; + int i; + + spin_lock_irqsave(&s->lock, flags); + + for (i = 0; i < POLL_COUNT; i++) + if (!(au_readl(AC97C_STATUS) & AC97C_CP)) + break; + if (i == POLL_COUNT) + err("wrcodec: codec cmd pending expired!"); + + cmd = (u32) addr & AC97C_INDEX_MASK; + cmd &= ~AC97C_READ; // write command + cmd |= ((u32) data << AC97C_WD_BIT); // OR in the data word + au_writel(cmd, AC97C_CMD); + + spin_unlock_irqrestore(&s->lock, flags); +} + +static void waitcodec(struct ac97_codec *codec) +{ + u16 temp; + int i; + + /* codec_wait is used to wait for a ready state after + an AC97C_RESET. */ + au1000_delay(10); + + // first poll the CODEC_READY tag bit + for (i = 0; i < POLL_COUNT; i++) + if (au_readl(AC97C_STATUS) & AC97C_READY) + break; + if (i == POLL_COUNT) { + err("waitcodec: CODEC_READY poll expired!"); + return; + } + // get AC'97 powerdown control/status register + temp = rdcodec(codec, AC97_POWER_CONTROL); + + // If anything is powered down, power'em up + if (temp & 0x7f00) { + // Power on + wrcodec(codec, AC97_POWER_CONTROL, 0); + au1000_delay(100); + // Reread + temp = rdcodec(codec, AC97_POWER_CONTROL); + } + + // Check if Codec REF,ANL,DAC,ADC ready + if ((temp & 0x7f0f) != 0x000f) + err("codec reg 26 status (0x%x) not ready!!", temp); +} + + +/* --------------------------------------------------------------------- */ + +/* stop the ADC before calling */ +static void set_adc_rate(struct au1000_state *s, unsigned rate) +{ + struct dmabuf *adc = &s->dma_adc; + struct dmabuf *dac = &s->dma_dac; + unsigned adc_rate, dac_rate; + u16 ac97_extstat; + + if (s->no_vra) { + // calc SRC factor + adc->src_factor = ((96000 / rate) + 1) >> 1; + adc->sample_rate = 48000 / adc->src_factor; + return; + } + + adc->src_factor = 1; + + ac97_extstat = rdcodec(s->codec, AC97_EXTENDED_STATUS); + + rate = rate > 48000 ? 48000 : rate; + + // enable VRA + wrcodec(s->codec, AC97_EXTENDED_STATUS, + ac97_extstat | AC97_EXTSTAT_VRA); + // now write the sample rate + wrcodec(s->codec, AC97_PCM_LR_ADC_RATE, (u16) rate); + // read it back for actual supported rate + adc_rate = rdcodec(s->codec, AC97_PCM_LR_ADC_RATE); + +#ifdef AU1000_VERBOSE_DEBUG + dbg(__FUNCTION__ ": set to %d Hz", adc_rate); +#endif + + // some codec's don't allow unequal DAC and ADC rates, in which case + // writing one rate reg actually changes both. + dac_rate = rdcodec(s->codec, AC97_PCM_FRONT_DAC_RATE); + if (dac->num_channels > 2) + wrcodec(s->codec, AC97_PCM_SURR_DAC_RATE, dac_rate); + if (dac->num_channels > 4) + wrcodec(s->codec, AC97_PCM_LFE_DAC_RATE, dac_rate); + + adc->sample_rate = adc_rate; + dac->sample_rate = dac_rate; +} + +/* stop the DAC before calling */ +static void set_dac_rate(struct au1000_state *s, unsigned rate) +{ + struct dmabuf *dac = &s->dma_dac; + struct dmabuf *adc = &s->dma_adc; + unsigned adc_rate, dac_rate; + u16 ac97_extstat; + + if (s->no_vra) { + // calc SRC factor + dac->src_factor = ((96000 / rate) + 1) >> 1; + dac->sample_rate = 48000 / dac->src_factor; + return; + } + + dac->src_factor = 1; + + ac97_extstat = rdcodec(s->codec, AC97_EXTENDED_STATUS); + + rate = rate > 48000 ? 48000 : rate; + + // enable VRA + wrcodec(s->codec, AC97_EXTENDED_STATUS, + ac97_extstat | AC97_EXTSTAT_VRA); + // now write the sample rate + wrcodec(s->codec, AC97_PCM_FRONT_DAC_RATE, (u16) rate); + // I don't support different sample rates for multichannel, + // so make these channels the same. + if (dac->num_channels > 2) + wrcodec(s->codec, AC97_PCM_SURR_DAC_RATE, (u16) rate); + if (dac->num_channels > 4) + wrcodec(s->codec, AC97_PCM_LFE_DAC_RATE, (u16) rate); + // read it back for actual supported rate + dac_rate = rdcodec(s->codec, AC97_PCM_FRONT_DAC_RATE); + +#ifdef AU1000_VERBOSE_DEBUG + dbg(__FUNCTION__ ": set to %d Hz", dac_rate); +#endif + + // some codec's don't allow unequal DAC and ADC rates, in which case + // writing one rate reg actually changes both. + adc_rate = rdcodec(s->codec, AC97_PCM_LR_ADC_RATE); + + dac->sample_rate = dac_rate; + adc->sample_rate = adc_rate; +} + +static void stop_dac(struct au1000_state *s) +{ + struct dmabuf *db = &s->dma_dac; + unsigned long flags; + + if (db->stopped) + return; + + spin_lock_irqsave(&s->lock, flags); + + disable_dma(db->dmanr); + + db->stopped = 1; + + spin_unlock_irqrestore(&s->lock, flags); +} + +static void stop_adc(struct au1000_state *s) +{ + struct dmabuf *db = &s->dma_adc; + unsigned long flags; + + if (db->stopped) + return; + + spin_lock_irqsave(&s->lock, flags); + + disable_dma(db->dmanr); + + db->stopped = 1; + + spin_unlock_irqrestore(&s->lock, flags); +} + + +static void set_xmit_slots(int num_channels) +{ + u32 ac97_config = au_readl(AC97C_CONFIG) & ~AC97C_XMIT_SLOTS_MASK; + + switch (num_channels) { + case 1: // mono + case 2: // stereo, slots 3,4 + ac97_config |= (0x3 << AC97C_XMIT_SLOTS_BIT); + break; + case 4: // stereo with surround, slots 3,4,7,8 + ac97_config |= (0x33 << AC97C_XMIT_SLOTS_BIT); + break; + case 6: // stereo with surround and center/LFE, slots 3,4,6,7,8,9 + ac97_config |= (0x7b << AC97C_XMIT_SLOTS_BIT); + break; + } + + au_writel(ac97_config, AC97C_CONFIG); +} + +static void set_recv_slots(int num_channels) +{ + u32 ac97_config = au_readl(AC97C_CONFIG) & ~AC97C_RECV_SLOTS_MASK; + + /* + * Always enable slots 3 and 4 (stereo). Slot 6 is + * optional Mic ADC, which I don't support yet. + */ + ac97_config |= (0x3 << AC97C_RECV_SLOTS_BIT); + + au_writel(ac97_config, AC97C_CONFIG); +} + +static void start_dac(struct au1000_state *s) +{ + struct dmabuf *db = &s->dma_dac; + unsigned long flags; + unsigned long buf1, buf2; + + if (!db->stopped) + return; + + spin_lock_irqsave(&s->lock, flags); + + au_readl(AC97C_STATUS); // read status to clear sticky bits + + // reset Buffer 1 and 2 pointers to nextOut and nextOut+dma_fragsize + buf1 = virt_to_phys(db->nextOut); + buf2 = buf1 + db->dma_fragsize; + if (buf2 >= db->dmaaddr + db->dmasize) + buf2 -= db->dmasize; + + set_xmit_slots(db->num_channels); + + init_dma(db->dmanr); + if (get_dma_active_buffer(db->dmanr) == 0) { + clear_dma_done0(db->dmanr); // clear DMA done bit + set_dma_addr0(db->dmanr, buf1); + set_dma_addr1(db->dmanr, buf2); + } else { + clear_dma_done1(db->dmanr); // clear DMA done bit + set_dma_addr1(db->dmanr, buf1); + set_dma_addr0(db->dmanr, buf2); + } + set_dma_count(db->dmanr, db->dma_fragsize>>1); + enable_dma_buffers(db->dmanr); + + start_dma(db->dmanr); + +#ifdef AU1000_VERBOSE_DEBUG + dump_au1000_dma_channel(db->dmanr); +#endif + + db->stopped = 0; + + spin_unlock_irqrestore(&s->lock, flags); +} + +static void start_adc(struct au1000_state *s) +{ + struct dmabuf *db = &s->dma_adc; + unsigned long flags; + unsigned long buf1, buf2; + + if (!db->stopped) + return; + + spin_lock_irqsave(&s->lock, flags); + + au_readl(AC97C_STATUS); // read status to clear sticky bits + + // reset Buffer 1 and 2 pointers to nextIn and nextIn+dma_fragsize + buf1 = virt_to_phys(db->nextIn); + buf2 = buf1 + db->dma_fragsize; + if (buf2 >= db->dmaaddr + db->dmasize) + buf2 -= db->dmasize; + + set_recv_slots(db->num_channels); + + init_dma(db->dmanr); + if (get_dma_active_buffer(db->dmanr) == 0) { + clear_dma_done0(db->dmanr); // clear DMA done bit + set_dma_addr0(db->dmanr, buf1); + set_dma_addr1(db->dmanr, buf2); + } else { + clear_dma_done1(db->dmanr); // clear DMA done bit + set_dma_addr1(db->dmanr, buf1); + set_dma_addr0(db->dmanr, buf2); + } + set_dma_count(db->dmanr, db->dma_fragsize>>1); + enable_dma_buffers(db->dmanr); + + start_dma(db->dmanr); + +#ifdef AU1000_VERBOSE_DEBUG + dump_au1000_dma_channel(db->dmanr); +#endif + + db->stopped = 0; + + spin_unlock_irqrestore(&s->lock, flags); +} + +/* --------------------------------------------------------------------- */ + +#define DMABUF_DEFAULTORDER (17-PAGE_SHIFT) +#define DMABUF_MINORDER 1 + +extern inline void dealloc_dmabuf(struct au1000_state *s, struct dmabuf *db) +{ + struct page *page, *pend; + + if (db->rawbuf) { + /* undo marking the pages as reserved */ + pend = virt_to_page(db->rawbuf + + (PAGE_SIZE << db->buforder) - 1); + for (page = virt_to_page(db->rawbuf); page <= pend; page++) + mem_map_unreserve(page); + dma_free(PAGE_SIZE << db->buforder, db->rawbuf, db->dmaaddr); + } + db->rawbuf = db->nextIn = db->nextOut = NULL; + db->mapped = db->ready = 0; +} + +static int prog_dmabuf(struct au1000_state *s, struct dmabuf *db) +{ + int order; + unsigned user_bytes_per_sec; + unsigned bufs; + struct page *page, *pend; + unsigned rate = db->sample_rate; + + if (!db->rawbuf) { + db->ready = db->mapped = 0; + for (order = DMABUF_DEFAULTORDER; + order >= DMABUF_MINORDER; order--) + if ((db->rawbuf = dma_alloc(PAGE_SIZE << order, + &db->dmaaddr))) + break; + if (!db->rawbuf) + return -ENOMEM; + db->buforder = order; + /* now mark the pages as reserved; + otherwise remap_page_range doesn't do what we want */ + pend = virt_to_page(db->rawbuf + + (PAGE_SIZE << db->buforder) - 1); + for (page = virt_to_page(db->rawbuf); page <= pend; page++) + mem_map_reserve(page); + } + + db->cnt_factor = 1; + if (db->sample_size == 8) + db->cnt_factor *= 2; + if (db->num_channels == 1) + db->cnt_factor *= 2; + db->cnt_factor *= db->src_factor; + + db->count = 0; + db->nextIn = db->nextOut = db->rawbuf; + + db->user_bytes_per_sample = (db->sample_size>>3) * db->num_channels; + db->dma_bytes_per_sample = 2 * ((db->num_channels == 1) ? + 2 : db->num_channels); + + user_bytes_per_sec = rate * db->user_bytes_per_sample; + bufs = PAGE_SIZE << db->buforder; + if (db->ossfragshift) { + if ((1000 << db->ossfragshift) < user_bytes_per_sec) + db->fragshift = ld2(user_bytes_per_sec/1000); + else + db->fragshift = db->ossfragshift; + } else { + db->fragshift = ld2(user_bytes_per_sec / 100 / + (db->subdivision ? db->subdivision : 1)); + if (db->fragshift < 3) + db->fragshift = 3; + } + + db->fragsize = 1 << db->fragshift; + db->dma_fragsize = db->fragsize * db->cnt_factor; + db->numfrag = bufs / db->dma_fragsize; + + while (db->numfrag < 4 && db->fragshift > 3) { + db->fragshift--; + db->fragsize = 1 << db->fragshift; + db->dma_fragsize = db->fragsize * db->cnt_factor; + db->numfrag = bufs / db->dma_fragsize; + } + + if (db->ossmaxfrags >= 4 && db->ossmaxfrags < db->numfrag) + db->numfrag = db->ossmaxfrags; + + db->dmasize = db->dma_fragsize * db->numfrag; + memset(db->rawbuf, 0, bufs); + +#ifdef AU1000_VERBOSE_DEBUG + dbg("rate=%d, samplesize=%d, channels=%d", + rate, db->sample_size, db->num_channels); + dbg("fragsize=%d, cnt_factor=%d, dma_fragsize=%d", + db->fragsize, db->cnt_factor, db->dma_fragsize); + dbg("numfrag=%d, dmasize=%d", db->numfrag, db->dmasize); +#endif + + db->ready = 1; + return 0; +} + +extern inline int prog_dmabuf_adc(struct au1000_state *s) +{ + stop_adc(s); + return prog_dmabuf(s, &s->dma_adc); + +} + +extern inline int prog_dmabuf_dac(struct au1000_state *s) +{ + stop_dac(s); + return prog_dmabuf(s, &s->dma_dac); +} + + +/* hold spinlock for the following */ +static void dac_dma_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct au1000_state *s = (struct au1000_state *) dev_id; + struct dmabuf *dac = &s->dma_dac; + unsigned long newptr; + u32 ac97c_stat, buff_done; + + ac97c_stat = au_readl(AC97C_STATUS); +#ifdef AU1000_VERBOSE_DEBUG + if (ac97c_stat & (AC97C_XU | AC97C_XO | AC97C_TE)) + dbg("AC97C status = 0x%08x", ac97c_stat); +#endif + + if ((buff_done = get_dma_buffer_done(dac->dmanr)) == 0) { + /* fastpath out, to ease interrupt sharing */ + return; + } + + spin_lock(&s->lock); + + if (buff_done != (DMA_D0 | DMA_D1)) { + dac->nextOut += dac->dma_fragsize; + if (dac->nextOut >= dac->rawbuf + dac->dmasize) + dac->nextOut -= dac->dmasize; + + /* update playback pointers */ + newptr = virt_to_phys(dac->nextOut) + dac->dma_fragsize; + if (newptr >= dac->dmaaddr + dac->dmasize) + newptr -= dac->dmasize; + + dac->count -= dac->dma_fragsize; + dac->total_bytes += dac->dma_fragsize; + + if (dac->count <= 0) { +#ifdef AU1000_VERBOSE_DEBUG + dbg("dac underrun"); +#endif + spin_unlock(&s->lock); + stop_dac(s); + spin_lock(&s->lock); + dac->count = 0; + dac->nextIn = dac->nextOut; + } else if (buff_done == DMA_D0) { + clear_dma_done0(dac->dmanr); // clear DMA done bit + set_dma_count0(dac->dmanr, dac->dma_fragsize>>1); + set_dma_addr0(dac->dmanr, newptr); + enable_dma_buffer0(dac->dmanr); // reenable + } else { + clear_dma_done1(dac->dmanr); // clear DMA done bit + set_dma_count1(dac->dmanr, dac->dma_fragsize>>1); + set_dma_addr1(dac->dmanr, newptr); + enable_dma_buffer1(dac->dmanr); // reenable + } + } else { + // both done bits set, we missed an interrupt + spin_unlock(&s->lock); + stop_dac(s); + spin_lock(&s->lock); + + dac->nextOut += 2*dac->dma_fragsize; + if (dac->nextOut >= dac->rawbuf + dac->dmasize) + dac->nextOut -= dac->dmasize; + + dac->count -= 2*dac->dma_fragsize; + dac->total_bytes += 2*dac->dma_fragsize; + + if (dac->count > 0) { + spin_unlock(&s->lock); + start_dac(s); + spin_lock(&s->lock); + } + } + + /* wake up anybody listening */ + if (waitqueue_active(&dac->wait)) + wake_up(&dac->wait); + + spin_unlock(&s->lock); +} + + +static void adc_dma_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct au1000_state *s = (struct au1000_state *) dev_id; + struct dmabuf *adc = &s->dma_adc; + unsigned long newptr; + u32 ac97c_stat, buff_done; + + ac97c_stat = au_readl(AC97C_STATUS); +#ifdef AU1000_VERBOSE_DEBUG + if (ac97c_stat & (AC97C_RU | AC97C_RO)) + dbg("AC97C status = 0x%08x", ac97c_stat); +#endif + + if ((buff_done = get_dma_buffer_done(adc->dmanr)) == 0) { + /* fastpath out, to ease interrupt sharing */ + return; + } + + spin_lock(&s->lock); + + if (buff_done != (DMA_D0 | DMA_D1)) { + if (adc->count + adc->dma_fragsize > adc->dmasize) { + // Overrun. Stop ADC and log the error + spin_unlock(&s->lock); + stop_adc(s); + adc->error++; + err("adc overrun"); + return; + } + + adc->nextIn += adc->dma_fragsize; + if (adc->nextIn >= adc->rawbuf + adc->dmasize) + adc->nextIn -= adc->dmasize; + + /* update capture pointers */ + newptr = virt_to_phys(adc->nextIn) + adc->dma_fragsize; + if (newptr >= adc->dmaaddr + adc->dmasize) + newptr -= adc->dmasize; + + adc->count += adc->dma_fragsize; + adc->total_bytes += adc->dma_fragsize; + + if (buff_done == DMA_D0) { + clear_dma_done0(adc->dmanr); // clear DMA done bit + set_dma_count0(adc->dmanr, adc->dma_fragsize>>1); + set_dma_addr0(adc->dmanr, newptr); + enable_dma_buffer0(adc->dmanr); // reenable + } else { + clear_dma_done1(adc->dmanr); // clear DMA done bit + set_dma_count1(adc->dmanr, adc->dma_fragsize>>1); + set_dma_addr1(adc->dmanr, newptr); + enable_dma_buffer1(adc->dmanr); // reenable + } + } else { + // both done bits set, we missed an interrupt + spin_unlock(&s->lock); + stop_adc(s); + spin_lock(&s->lock); + + if (adc->count + 2*adc->dma_fragsize > adc->dmasize) { + // Overrun. Log the error + adc->error++; + err("adc overrun"); + spin_unlock(&s->lock); + return; + } + + adc->nextIn += 2*adc->dma_fragsize; + if (adc->nextIn >= adc->rawbuf + adc->dmasize) + adc->nextIn -= adc->dmasize; + + adc->count += 2*adc->dma_fragsize; + adc->total_bytes += 2*adc->dma_fragsize; + + spin_unlock(&s->lock); + start_adc(s); + spin_lock(&s->lock); + } + + /* wake up anybody listening */ + if (waitqueue_active(&adc->wait)) + wake_up(&adc->wait); + + spin_unlock(&s->lock); +} + +/* --------------------------------------------------------------------- */ + +static loff_t au1000_llseek(struct file *file, loff_t offset, int origin) +{ + return -ESPIPE; +} + + +static int au1000_open_mixdev(struct inode *inode, struct file *file) +{ + file->private_data = &au1000_state; + return 0; +} + +static int au1000_release_mixdev(struct inode *inode, struct file *file) +{ + return 0; +} + +static int mixdev_ioctl(struct ac97_codec *codec, unsigned int cmd, + unsigned long arg) +{ + return codec->mixer_ioctl(codec, cmd, arg); +} + +static int au1000_ioctl_mixdev(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct au1000_state *s = (struct au1000_state *)file->private_data; + struct ac97_codec *codec = s->codec; + + return mixdev_ioctl(codec, cmd, arg); +} + +static /*const */ struct file_operations au1000_mixer_fops = { + owner:THIS_MODULE, + llseek:au1000_llseek, + ioctl:au1000_ioctl_mixdev, + open:au1000_open_mixdev, + release:au1000_release_mixdev, +}; + +/* --------------------------------------------------------------------- */ + +static int drain_dac(struct au1000_state *s, int nonblock) +{ + unsigned long flags; + int count, tmo; + + if (s->dma_dac.mapped || !s->dma_dac.ready || s->dma_dac.stopped) + return 0; + + for (;;) { + spin_lock_irqsave(&s->lock, flags); + count = s->dma_dac.count; + spin_unlock_irqrestore(&s->lock, flags); + if (count <= 0) + break; + if (signal_pending(current)) + break; + if (nonblock) + return -EBUSY; + tmo = 1000 * count / (s->no_vra ? + 48000 : s->dma_dac.sample_rate); + tmo /= s->dma_dac.dma_bytes_per_sample; + au1000_delay(tmo); + } + if (signal_pending(current)) + return -ERESTARTSYS; + return 0; +} + +/* --------------------------------------------------------------------- */ + +static inline u8 S16_TO_U8(s16 ch) +{ + return (u8) (ch >> 8) + 0x80; +} +static inline s16 U8_TO_S16(u8 ch) +{ + return (s16) (ch - 0x80) << 8; +} + +/* + * Translates user samples to dma buffer suitable for AC'97 DAC data: + * If mono, copy left channel to right channel in dma buffer. + * If 8 bit samples, cvt to 16-bit before writing to dma buffer. + * If interpolating (no VRA), duplicate every audio frame src_factor times. + */ +static int translate_from_user(struct dmabuf *db, + char* dmabuf, + char* userbuf, + int dmacount) +{ + int sample, i; + int interp_bytes_per_sample; + int num_samples; + int mono = (db->num_channels == 1); + char usersample[12]; + s16 ch, dmasample[6]; + + if (db->sample_size == 16 && !mono && db->src_factor == 1) { + // no translation necessary, just copy + if (copy_from_user(dmabuf, userbuf, dmacount)) + return -EFAULT; + return dmacount; + } + + interp_bytes_per_sample = db->dma_bytes_per_sample * db->src_factor; + num_samples = dmacount / interp_bytes_per_sample; + + for (sample = 0; sample < num_samples; sample++) { + if (copy_from_user(usersample, userbuf, + db->user_bytes_per_sample)) { + dbg(__FUNCTION__ ": fault"); + return -EFAULT; + } + + for (i = 0; i < db->num_channels; i++) { + if (db->sample_size == 8) + ch = U8_TO_S16(usersample[i]); + else + ch = *((s16 *) (&usersample[i * 2])); + dmasample[i] = ch; + if (mono) + dmasample[i + 1] = ch; // right channel + } + + // duplicate every audio frame src_factor times + for (i = 0; i < db->src_factor; i++) + memcpy(dmabuf, dmasample, db->dma_bytes_per_sample); + + userbuf += db->user_bytes_per_sample; + dmabuf += interp_bytes_per_sample; + } + + return num_samples * interp_bytes_per_sample; +} + +/* + * Translates AC'97 ADC samples to user buffer: + * If mono, send only left channel to user buffer. + * If 8 bit samples, cvt from 16 to 8 bit before writing to user buffer. + * If decimating (no VRA), skip over src_factor audio frames. + */ +static int translate_to_user(struct dmabuf *db, + char* userbuf, + char* dmabuf, + int dmacount) +{ + int sample, i; + int interp_bytes_per_sample; + int num_samples; + int mono = (db->num_channels == 1); + char usersample[12]; + + if (db->sample_size == 16 && !mono && db->src_factor == 1) { + // no translation necessary, just copy + if (copy_to_user(userbuf, dmabuf, dmacount)) + return -EFAULT; + return dmacount; + } + + interp_bytes_per_sample = db->dma_bytes_per_sample * db->src_factor; + num_samples = dmacount / interp_bytes_per_sample; + + for (sample = 0; sample < num_samples; sample++) { + for (i = 0; i < db->num_channels; i++) { + if (db->sample_size == 8) + usersample[i] = + S16_TO_U8(*((s16 *) (&dmabuf[i * 2]))); + else + *((s16 *) (&usersample[i * 2])) = + *((s16 *) (&dmabuf[i * 2])); + } + + if (copy_to_user(userbuf, usersample, + db->user_bytes_per_sample)) { + dbg(__FUNCTION__ ": fault"); + return -EFAULT; + } + + userbuf += db->user_bytes_per_sample; + dmabuf += interp_bytes_per_sample; + } + + return num_samples * interp_bytes_per_sample; +} + +/* + * Copy audio data to/from user buffer from/to dma buffer, taking care + * that we wrap when reading/writing the dma buffer. Returns actual byte + * count written to or read from the dma buffer. + */ +static int copy_dmabuf_user(struct dmabuf *db, char* userbuf, + int count, int to_user) +{ + char *bufptr = to_user ? db->nextOut : db->nextIn; + char *bufend = db->rawbuf + db->dmasize; + int cnt, ret; + + if (bufptr + count > bufend) { + int partial = (int) (bufend - bufptr); + if (to_user) { + if ((cnt = translate_to_user(db, userbuf, + bufptr, partial)) < 0) + return cnt; + ret = cnt; + if ((cnt = translate_to_user(db, userbuf + partial, + db->rawbuf, + count - partial)) < 0) + return cnt; + ret += cnt; + } else { + if ((cnt = translate_from_user(db, bufptr, userbuf, + partial)) < 0) + return cnt; + ret = cnt; + if ((cnt = translate_from_user(db, db->rawbuf, + userbuf + partial, + count - partial)) < 0) + return cnt; + ret += cnt; + } + } else { + if (to_user) + ret = translate_to_user(db, userbuf, bufptr, count); + else + ret = translate_from_user(db, bufptr, userbuf, count); + } + + return ret; +} + + +static ssize_t au1000_read(struct file *file, char *buffer, + size_t count, loff_t *ppos) +{ + struct au1000_state *s = (struct au1000_state *)file->private_data; + struct dmabuf *db = &s->dma_adc; + DECLARE_WAITQUEUE(wait, current); + ssize_t ret; + unsigned long flags; + int cnt, usercnt, avail; + + if (ppos != &file->f_pos) + return -ESPIPE; + if (db->mapped) + return -ENXIO; + if (!access_ok(VERIFY_WRITE, buffer, count)) + return -EFAULT; + ret = 0; + + count *= db->cnt_factor; + + down(&s->sem); + add_wait_queue(&db->wait, &wait); + + while (count > 0) { + // wait for samples in ADC dma buffer + do { + if (db->stopped) + start_adc(s); + spin_lock_irqsave(&s->lock, flags); + avail = db->count; + if (avail <= 0) + __set_current_state(TASK_INTERRUPTIBLE); + spin_unlock_irqrestore(&s->lock, flags); + if (avail <= 0) { + if (file->f_flags & O_NONBLOCK) { + if (!ret) + ret = -EAGAIN; + goto out; + } + up(&s->sem); + schedule(); + if (signal_pending(current)) { + if (!ret) + ret = -ERESTARTSYS; + goto out2; + } + down(&s->sem); + } + } while (avail <= 0); + + // copy from nextOut to user + if ((cnt = copy_dmabuf_user(db, buffer, + count > avail ? + avail : count, 1)) < 0) { + if (!ret) + ret = -EFAULT; + goto out; + } + + spin_lock_irqsave(&s->lock, flags); + db->count -= cnt; + db->nextOut += cnt; + if (db->nextOut >= db->rawbuf + db->dmasize) + db->nextOut -= db->dmasize; + spin_unlock_irqrestore(&s->lock, flags); + + count -= cnt; + usercnt = cnt / db->cnt_factor; + buffer += usercnt; + ret += usercnt; + } // while (count > 0) + +out: + up(&s->sem); +out2: + remove_wait_queue(&db->wait, &wait); + set_current_state(TASK_RUNNING); + return ret; +} + +static ssize_t au1000_write(struct file *file, const char *buffer, + size_t count, loff_t * ppos) +{ + struct au1000_state *s = (struct au1000_state *)file->private_data; + struct dmabuf *db = &s->dma_dac; + DECLARE_WAITQUEUE(wait, current); + ssize_t ret = 0; + unsigned long flags; + int cnt, usercnt, avail; + +#ifdef AU1000_VERBOSE_DEBUG + dbg("write: count=%d", count); +#endif + + if (ppos != &file->f_pos) + return -ESPIPE; + if (db->mapped) + return -ENXIO; + if (!access_ok(VERIFY_READ, buffer, count)) + return -EFAULT; + + count *= db->cnt_factor; + + down(&s->sem); + add_wait_queue(&db->wait, &wait); + + while (count > 0) { + // wait for space in playback buffer + do { + spin_lock_irqsave(&s->lock, flags); + avail = (int) db->dmasize - db->count; + if (avail <= 0) + __set_current_state(TASK_INTERRUPTIBLE); + spin_unlock_irqrestore(&s->lock, flags); + if (avail <= 0) { + if (file->f_flags & O_NONBLOCK) { + if (!ret) + ret = -EAGAIN; + goto out; + } + up(&s->sem); + schedule(); + if (signal_pending(current)) { + if (!ret) + ret = -ERESTARTSYS; + goto out2; + } + down(&s->sem); + } + } while (avail <= 0); + + // copy from user to nextIn + if ((cnt = copy_dmabuf_user(db, (char *) buffer, + count > avail ? + avail : count, 0)) < 0) { + if (!ret) + ret = -EFAULT; + goto out; + } + + spin_lock_irqsave(&s->lock, flags); + db->count += cnt; + db->nextIn += cnt; + if (db->nextIn >= db->rawbuf + db->dmasize) + db->nextIn -= db->dmasize; + spin_unlock_irqrestore(&s->lock, flags); + if (db->stopped) + start_dac(s); + + count -= cnt; + usercnt = cnt / db->cnt_factor; + buffer += usercnt; + ret += usercnt; + } // while (count > 0) + +out: + up(&s->sem); +out2: + remove_wait_queue(&db->wait, &wait); + set_current_state(TASK_RUNNING); + return ret; +} + + +/* No kernel lock - we have our own spinlock */ +static unsigned int au1000_poll(struct file *file, + struct poll_table_struct *wait) +{ + struct au1000_state *s = (struct au1000_state *)file->private_data; + unsigned long flags; + unsigned int mask = 0; + + if (file->f_mode & FMODE_WRITE) { + if (!s->dma_dac.ready) + return 0; + poll_wait(file, &s->dma_dac.wait, wait); + } + if (file->f_mode & FMODE_READ) { + if (!s->dma_adc.ready) + return 0; + poll_wait(file, &s->dma_adc.wait, wait); + } + + spin_lock_irqsave(&s->lock, flags); + + if (file->f_mode & FMODE_READ) { + if (s->dma_adc.count >= (signed)s->dma_adc.dma_fragsize) + mask |= POLLIN | POLLRDNORM; + } + if (file->f_mode & FMODE_WRITE) { + if (s->dma_dac.mapped) { + if (s->dma_dac.count >= + (signed)s->dma_dac.dma_fragsize) + mask |= POLLOUT | POLLWRNORM; + } else { + if ((signed) s->dma_dac.dmasize >= + s->dma_dac.count + (signed)s->dma_dac.dma_fragsize) + mask |= POLLOUT | POLLWRNORM; + } + } + spin_unlock_irqrestore(&s->lock, flags); + return mask; +} + +static int au1000_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct au1000_state *s = (struct au1000_state *)file->private_data; + struct dmabuf *db; + unsigned long size; + int ret = 0; + + dbg(__FUNCTION__); + + lock_kernel(); + down(&s->sem); + if (vma->vm_flags & VM_WRITE) + db = &s->dma_dac; + else if (vma->vm_flags & VM_READ) + db = &s->dma_adc; + else { + ret = -EINVAL; + goto out; + } + if (vma->vm_pgoff != 0) { + ret = -EINVAL; + goto out; + } + size = vma->vm_end - vma->vm_start; + if (size > (PAGE_SIZE << db->buforder)) { + ret = -EINVAL; + goto out; + } + if (remap_page_range(vma->vm_start, virt_to_phys(db->rawbuf), + size, vma->vm_page_prot)) { + ret = -EAGAIN; + goto out; + } + vma->vm_flags &= ~VM_IO; + db->mapped = 1; +out: + up(&s->sem); + unlock_kernel(); + return ret; +} + + +#ifdef AU1000_VERBOSE_DEBUG +static struct ioctl_str_t { + unsigned int cmd; + const char *str; +} ioctl_str[] = { + {SNDCTL_DSP_RESET, "SNDCTL_DSP_RESET"}, + {SNDCTL_DSP_SYNC, "SNDCTL_DSP_SYNC"}, + {SNDCTL_DSP_SPEED, "SNDCTL_DSP_SPEED"}, + {SNDCTL_DSP_STEREO, "SNDCTL_DSP_STEREO"}, + {SNDCTL_DSP_GETBLKSIZE, "SNDCTL_DSP_GETBLKSIZE"}, + {SNDCTL_DSP_SAMPLESIZE, "SNDCTL_DSP_SAMPLESIZE"}, + {SNDCTL_DSP_CHANNELS, "SNDCTL_DSP_CHANNELS"}, + {SOUND_PCM_WRITE_CHANNELS, "SOUND_PCM_WRITE_CHANNELS"}, + {SOUND_PCM_WRITE_FILTER, "SOUND_PCM_WRITE_FILTER"}, + {SNDCTL_DSP_POST, "SNDCTL_DSP_POST"}, + {SNDCTL_DSP_SUBDIVIDE, "SNDCTL_DSP_SUBDIVIDE"}, + {SNDCTL_DSP_SETFRAGMENT, "SNDCTL_DSP_SETFRAGMENT"}, + {SNDCTL_DSP_GETFMTS, "SNDCTL_DSP_GETFMTS"}, + {SNDCTL_DSP_SETFMT, "SNDCTL_DSP_SETFMT"}, + {SNDCTL_DSP_GETOSPACE, "SNDCTL_DSP_GETOSPACE"}, + {SNDCTL_DSP_GETISPACE, "SNDCTL_DSP_GETISPACE"}, + {SNDCTL_DSP_NONBLOCK, "SNDCTL_DSP_NONBLOCK"}, + {SNDCTL_DSP_GETCAPS, "SNDCTL_DSP_GETCAPS"}, + {SNDCTL_DSP_GETTRIGGER, "SNDCTL_DSP_GETTRIGGER"}, + {SNDCTL_DSP_SETTRIGGER, "SNDCTL_DSP_SETTRIGGER"}, + {SNDCTL_DSP_GETIPTR, "SNDCTL_DSP_GETIPTR"}, + {SNDCTL_DSP_GETOPTR, "SNDCTL_DSP_GETOPTR"}, + {SNDCTL_DSP_MAPINBUF, "SNDCTL_DSP_MAPINBUF"}, + {SNDCTL_DSP_MAPOUTBUF, "SNDCTL_DSP_MAPOUTBUF"}, + {SNDCTL_DSP_SETSYNCRO, "SNDCTL_DSP_SETSYNCRO"}, + {SNDCTL_DSP_SETDUPLEX, "SNDCTL_DSP_SETDUPLEX"}, + {SNDCTL_DSP_GETODELAY, "SNDCTL_DSP_GETODELAY"}, + {SNDCTL_DSP_GETCHANNELMASK, "SNDCTL_DSP_GETCHANNELMASK"}, + {SNDCTL_DSP_BIND_CHANNEL, "SNDCTL_DSP_BIND_CHANNEL"}, + {OSS_GETVERSION, "OSS_GETVERSION"}, + {SOUND_PCM_READ_RATE, "SOUND_PCM_READ_RATE"}, + {SOUND_PCM_READ_CHANNELS, "SOUND_PCM_READ_CHANNELS"}, + {SOUND_PCM_READ_BITS, "SOUND_PCM_READ_BITS"}, + {SOUND_PCM_READ_FILTER, "SOUND_PCM_READ_FILTER"} +}; +#endif + +// Need to hold a spin-lock before calling this! +static int dma_count_done(struct dmabuf *db) +{ + if (db->stopped) + return 0; + + return db->dma_fragsize - get_dma_residue(db->dmanr); +} + + +static int au1000_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct au1000_state *s = (struct au1000_state *)file->private_data; + unsigned long flags; + audio_buf_info abinfo; + count_info cinfo; + int count; + int val, mapped, ret, diff; + + mapped = ((file->f_mode & FMODE_WRITE) && s->dma_dac.mapped) || + ((file->f_mode & FMODE_READ) && s->dma_adc.mapped); + +#ifdef AU1000_VERBOSE_DEBUG + for (count=0; countf_mode & FMODE_WRITE) + return drain_dac(s, file->f_flags & O_NONBLOCK); + return 0; + + case SNDCTL_DSP_SETDUPLEX: + return 0; + + case SNDCTL_DSP_GETCAPS: + return put_user(DSP_CAP_DUPLEX | DSP_CAP_REALTIME | + DSP_CAP_TRIGGER | DSP_CAP_MMAP, (int *)arg); + + case SNDCTL_DSP_RESET: + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + synchronize_irq(); + s->dma_dac.count = s->dma_dac.total_bytes = 0; + s->dma_dac.nextIn = s->dma_dac.nextOut = + s->dma_dac.rawbuf; + } + if (file->f_mode & FMODE_READ) { + stop_adc(s); + synchronize_irq(); + s->dma_adc.count = s->dma_adc.total_bytes = 0; + s->dma_adc.nextIn = s->dma_adc.nextOut = + s->dma_adc.rawbuf; + } + return 0; + + case SNDCTL_DSP_SPEED: + if (get_user(val, (int *) arg)) + return -EFAULT; + if (val >= 0) { + if (file->f_mode & FMODE_READ) { + stop_adc(s); + set_adc_rate(s, val); + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + set_dac_rate(s, val); + } + if (s->open_mode & FMODE_READ) + if ((ret = prog_dmabuf_adc(s))) + return ret; + if (s->open_mode & FMODE_WRITE) + if ((ret = prog_dmabuf_dac(s))) + return ret; + } + return put_user((file->f_mode & FMODE_READ) ? + s->dma_adc.sample_rate : + s->dma_dac.sample_rate, + (int *)arg); + + case SNDCTL_DSP_STEREO: + if (get_user(val, (int *) arg)) + return -EFAULT; + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dma_adc.num_channels = val ? 2 : 1; + if ((ret = prog_dmabuf_adc(s))) + return ret; + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + s->dma_dac.num_channels = val ? 2 : 1; + if (s->codec_ext_caps & AC97_EXT_DACS) { + // disable surround and center/lfe in AC'97 + u16 ext_stat = rdcodec(s->codec, + AC97_EXTENDED_STATUS); + wrcodec(s->codec, AC97_EXTENDED_STATUS, + ext_stat | (AC97_EXTSTAT_PRI | + AC97_EXTSTAT_PRJ | + AC97_EXTSTAT_PRK)); + } + if ((ret = prog_dmabuf_dac(s))) + return ret; + } + return 0; + + case SNDCTL_DSP_CHANNELS: + if (get_user(val, (int *) arg)) + return -EFAULT; + if (val != 0) { + if (file->f_mode & FMODE_READ) { + if (val < 0 || val > 2) + return -EINVAL; + stop_adc(s); + s->dma_adc.num_channels = val; + if ((ret = prog_dmabuf_adc(s))) + return ret; + } + if (file->f_mode & FMODE_WRITE) { + switch (val) { + case 1: + case 2: + break; + case 3: + case 5: + return -EINVAL; + case 4: + if (!(s->codec_ext_caps & + AC97_EXTID_SDAC)) + return -EINVAL; + break; + case 6: + if ((s->codec_ext_caps & + AC97_EXT_DACS) != AC97_EXT_DACS) + return -EINVAL; + break; + default: + return -EINVAL; + } + + stop_dac(s); + if (val <= 2 && + (s->codec_ext_caps & AC97_EXT_DACS)) { + // disable surround and center/lfe + // channels in AC'97 + u16 ext_stat = + rdcodec(s->codec, + AC97_EXTENDED_STATUS); + wrcodec(s->codec, + AC97_EXTENDED_STATUS, + ext_stat | (AC97_EXTSTAT_PRI | + AC97_EXTSTAT_PRJ | + AC97_EXTSTAT_PRK)); + } else if (val >= 4) { + // enable surround, center/lfe + // channels in AC'97 + u16 ext_stat = + rdcodec(s->codec, + AC97_EXTENDED_STATUS); + ext_stat &= ~AC97_EXTSTAT_PRJ; + if (val == 6) + ext_stat &= + ~(AC97_EXTSTAT_PRI | + AC97_EXTSTAT_PRK); + wrcodec(s->codec, + AC97_EXTENDED_STATUS, + ext_stat); + } + + s->dma_dac.num_channels = val; + if ((ret = prog_dmabuf_dac(s))) + return ret; + } + } + return put_user(val, (int *) arg); + + case SNDCTL_DSP_GETFMTS: /* Returns a mask */ + return put_user(AFMT_S16_LE | AFMT_U8, (int *) arg); + + case SNDCTL_DSP_SETFMT: /* Selects ONE fmt */ + if (get_user(val, (int *) arg)) + return -EFAULT; + if (val != AFMT_QUERY) { + if (file->f_mode & FMODE_READ) { + stop_adc(s); + if (val == AFMT_S16_LE) + s->dma_adc.sample_size = 16; + else { + val = AFMT_U8; + s->dma_adc.sample_size = 8; + } + if ((ret = prog_dmabuf_adc(s))) + return ret; + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + if (val == AFMT_S16_LE) + s->dma_dac.sample_size = 16; + else { + val = AFMT_U8; + s->dma_dac.sample_size = 8; + } + if ((ret = prog_dmabuf_dac(s))) + return ret; + } + } else { + if (file->f_mode & FMODE_READ) + val = (s->dma_adc.sample_size == 16) ? + AFMT_S16_LE : AFMT_U8; + else + val = (s->dma_dac.sample_size == 16) ? + AFMT_S16_LE : AFMT_U8; + } + return put_user(val, (int *) arg); + + case SNDCTL_DSP_POST: + return 0; + + case SNDCTL_DSP_GETTRIGGER: + val = 0; + spin_lock_irqsave(&s->lock, flags); + if (file->f_mode & FMODE_READ && !s->dma_adc.stopped) + val |= PCM_ENABLE_INPUT; + if (file->f_mode & FMODE_WRITE && !s->dma_dac.stopped) + val |= PCM_ENABLE_OUTPUT; + spin_unlock_irqrestore(&s->lock, flags); + return put_user(val, (int *) arg); + + case SNDCTL_DSP_SETTRIGGER: + if (get_user(val, (int *) arg)) + return -EFAULT; + if (file->f_mode & FMODE_READ) { + if (val & PCM_ENABLE_INPUT) + start_adc(s); + else + stop_adc(s); + } + if (file->f_mode & FMODE_WRITE) { + if (val & PCM_ENABLE_OUTPUT) + start_dac(s); + else + stop_dac(s); + } + return 0; + + case SNDCTL_DSP_GETOSPACE: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + abinfo.fragsize = s->dma_dac.fragsize; + spin_lock_irqsave(&s->lock, flags); + count = s->dma_dac.count; + count -= dma_count_done(&s->dma_dac); + spin_unlock_irqrestore(&s->lock, flags); + if (count < 0) + count = 0; + abinfo.bytes = (s->dma_dac.dmasize - count) / + s->dma_dac.cnt_factor; + abinfo.fragstotal = s->dma_dac.numfrag; + abinfo.fragments = abinfo.bytes >> s->dma_dac.fragshift; +#ifdef AU1000_VERBOSE_DEBUG + dbg("bytes=%d, fragments=%d", abinfo.bytes, abinfo.fragments); +#endif + return copy_to_user((void *) arg, &abinfo, + sizeof(abinfo)) ? -EFAULT : 0; + + case SNDCTL_DSP_GETISPACE: + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + abinfo.fragsize = s->dma_adc.fragsize; + spin_lock_irqsave(&s->lock, flags); + count = s->dma_adc.count; + count += dma_count_done(&s->dma_adc); + spin_unlock_irqrestore(&s->lock, flags); + if (count < 0) + count = 0; + abinfo.bytes = count / s->dma_adc.cnt_factor; + abinfo.fragstotal = s->dma_adc.numfrag; + abinfo.fragments = abinfo.bytes >> s->dma_adc.fragshift; + return copy_to_user((void *) arg, &abinfo, + sizeof(abinfo)) ? -EFAULT : 0; + + case SNDCTL_DSP_NONBLOCK: + file->f_flags |= O_NONBLOCK; + return 0; + + case SNDCTL_DSP_GETODELAY: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + spin_lock_irqsave(&s->lock, flags); + count = s->dma_dac.count; + count -= dma_count_done(&s->dma_dac); + spin_unlock_irqrestore(&s->lock, flags); + if (count < 0) + count = 0; + count /= s->dma_dac.cnt_factor; + return put_user(count, (int *) arg); + + case SNDCTL_DSP_GETIPTR: + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + spin_lock_irqsave(&s->lock, flags); + cinfo.bytes = s->dma_adc.total_bytes; + count = s->dma_adc.count; + if (!s->dma_adc.stopped) { + diff = dma_count_done(&s->dma_adc); + count += diff; + cinfo.bytes += diff; + cinfo.ptr = virt_to_phys(s->dma_adc.nextIn) + diff - + s->dma_adc.dmaaddr; + } else + cinfo.ptr = virt_to_phys(s->dma_adc.nextIn) - + s->dma_adc.dmaaddr; + if (s->dma_adc.mapped) + s->dma_adc.count &= (s->dma_adc.dma_fragsize-1); + spin_unlock_irqrestore(&s->lock, flags); + if (count < 0) + count = 0; + cinfo.blocks = count >> s->dma_adc.fragshift; + return copy_to_user((void *) arg, &cinfo, sizeof(cinfo)); + + case SNDCTL_DSP_GETOPTR: + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + spin_lock_irqsave(&s->lock, flags); + cinfo.bytes = s->dma_dac.total_bytes; + count = s->dma_dac.count; + if (!s->dma_dac.stopped) { + diff = dma_count_done(&s->dma_dac); + count -= diff; + cinfo.bytes += diff; + cinfo.ptr = virt_to_phys(s->dma_dac.nextOut) + diff - + s->dma_dac.dmaaddr; + } else + cinfo.ptr = virt_to_phys(s->dma_dac.nextOut) - + s->dma_dac.dmaaddr; + if (s->dma_dac.mapped) + s->dma_dac.count &= (s->dma_dac.dma_fragsize-1); + spin_unlock_irqrestore(&s->lock, flags); + if (count < 0) + count = 0; + cinfo.blocks = count >> s->dma_dac.fragshift; + return copy_to_user((void *) arg, &cinfo, sizeof(cinfo)); + + case SNDCTL_DSP_GETBLKSIZE: + if (file->f_mode & FMODE_WRITE) + return put_user(s->dma_dac.fragsize, (int *) arg); + else + return put_user(s->dma_adc.fragsize, (int *) arg); + + case SNDCTL_DSP_SETFRAGMENT: + if (get_user(val, (int *) arg)) + return -EFAULT; + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dma_adc.ossfragshift = val & 0xffff; + s->dma_adc.ossmaxfrags = (val >> 16) & 0xffff; + if (s->dma_adc.ossfragshift < 4) + s->dma_adc.ossfragshift = 4; + if (s->dma_adc.ossfragshift > 15) + s->dma_adc.ossfragshift = 15; + if (s->dma_adc.ossmaxfrags < 4) + s->dma_adc.ossmaxfrags = 4; + if ((ret = prog_dmabuf_adc(s))) + return ret; + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + s->dma_dac.ossfragshift = val & 0xffff; + s->dma_dac.ossmaxfrags = (val >> 16) & 0xffff; + if (s->dma_dac.ossfragshift < 4) + s->dma_dac.ossfragshift = 4; + if (s->dma_dac.ossfragshift > 15) + s->dma_dac.ossfragshift = 15; + if (s->dma_dac.ossmaxfrags < 4) + s->dma_dac.ossmaxfrags = 4; + if ((ret = prog_dmabuf_dac(s))) + return ret; + } + return 0; + + case SNDCTL_DSP_SUBDIVIDE: + if ((file->f_mode & FMODE_READ && s->dma_adc.subdivision) || + (file->f_mode & FMODE_WRITE && s->dma_dac.subdivision)) + return -EINVAL; + if (get_user(val, (int *) arg)) + return -EFAULT; + if (val != 1 && val != 2 && val != 4) + return -EINVAL; + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dma_adc.subdivision = val; + if ((ret = prog_dmabuf_adc(s))) + return ret; + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + s->dma_dac.subdivision = val; + if ((ret = prog_dmabuf_dac(s))) + return ret; + } + return 0; + + case SOUND_PCM_READ_RATE: + return put_user((file->f_mode & FMODE_READ) ? + s->dma_adc.sample_rate : + s->dma_dac.sample_rate, + (int *)arg); + + case SOUND_PCM_READ_CHANNELS: + if (file->f_mode & FMODE_READ) + return put_user(s->dma_adc.num_channels, (int *)arg); + else + return put_user(s->dma_dac.num_channels, (int *)arg); + + case SOUND_PCM_READ_BITS: + if (file->f_mode & FMODE_READ) + return put_user(s->dma_adc.sample_size, (int *)arg); + else + return put_user(s->dma_dac.sample_size, (int *)arg); + + case SOUND_PCM_WRITE_FILTER: + case SNDCTL_DSP_SETSYNCRO: + case SOUND_PCM_READ_FILTER: + return -EINVAL; + } + + return mixdev_ioctl(s->codec, cmd, arg); +} + + +static int au1000_open(struct inode *inode, struct file *file) +{ + int minor = MINOR(inode->i_rdev); + DECLARE_WAITQUEUE(wait, current); + struct au1000_state *s = &au1000_state; + int ret; + +#ifdef AU1000_VERBOSE_DEBUG + if (file->f_flags & O_NONBLOCK) + dbg(__FUNCTION__ ": non-blocking"); + else + dbg(__FUNCTION__ ": blocking"); +#endif + + file->private_data = s; + /* wait for device to become free */ + down(&s->open_sem); + while (s->open_mode & file->f_mode) { + if (file->f_flags & O_NONBLOCK) { + up(&s->open_sem); + return -EBUSY; + } + add_wait_queue(&s->open_wait, &wait); + __set_current_state(TASK_INTERRUPTIBLE); + up(&s->open_sem); + schedule(); + remove_wait_queue(&s->open_wait, &wait); + set_current_state(TASK_RUNNING); + if (signal_pending(current)) + return -ERESTARTSYS; + down(&s->open_sem); + } + + stop_dac(s); + stop_adc(s); + + if (file->f_mode & FMODE_READ) { + s->dma_adc.ossfragshift = s->dma_adc.ossmaxfrags = + s->dma_adc.subdivision = s->dma_adc.total_bytes = 0; + s->dma_adc.num_channels = 1; + s->dma_adc.sample_size = 8; + set_adc_rate(s, 8000); + if ((minor & 0xf) == SND_DEV_DSP16) + s->dma_adc.sample_size = 16; + } + + if (file->f_mode & FMODE_WRITE) { + s->dma_dac.ossfragshift = s->dma_dac.ossmaxfrags = + s->dma_dac.subdivision = s->dma_dac.total_bytes = 0; + s->dma_dac.num_channels = 1; + s->dma_dac.sample_size = 8; + set_dac_rate(s, 8000); + if ((minor & 0xf) == SND_DEV_DSP16) + s->dma_dac.sample_size = 16; + } + + if (file->f_mode & FMODE_READ) { + if ((ret = prog_dmabuf_adc(s))) + return ret; + } + if (file->f_mode & FMODE_WRITE) { + if ((ret = prog_dmabuf_dac(s))) + return ret; + } + + s->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE); + up(&s->open_sem); + init_MUTEX(&s->sem); + return 0; +} + +static int au1000_release(struct inode *inode, struct file *file) +{ + struct au1000_state *s = (struct au1000_state *)file->private_data; + + lock_kernel(); + + if (file->f_mode & FMODE_WRITE) { + unlock_kernel(); + drain_dac(s, file->f_flags & O_NONBLOCK); + lock_kernel(); + } + + down(&s->open_sem); + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + dealloc_dmabuf(s, &s->dma_dac); + } + if (file->f_mode & FMODE_READ) { + stop_adc(s); + dealloc_dmabuf(s, &s->dma_adc); + } + s->open_mode &= ((~file->f_mode) & (FMODE_READ|FMODE_WRITE)); + up(&s->open_sem); + wake_up(&s->open_wait); + unlock_kernel(); + return 0; +} + +static /*const */ struct file_operations au1000_audio_fops = { + owner: THIS_MODULE, + llseek: au1000_llseek, + read: au1000_read, + write: au1000_write, + poll: au1000_poll, + ioctl: au1000_ioctl, + mmap: au1000_mmap, + open: au1000_open, + release: au1000_release, +}; + + +/* --------------------------------------------------------------------- */ + + +/* --------------------------------------------------------------------- */ + +/* + * for debugging purposes, we'll create a proc device that dumps the + * CODEC chipstate + */ + +#ifdef AU1000_DEBUG +static int proc_au1000_dump(char *buf, char **start, off_t fpos, + int length, int *eof, void *data) +{ + struct au1000_state *s = &au1000_state; + int cnt, len = 0; + + /* print out header */ + len += sprintf(buf + len, "\n\t\tAU1000 Audio Debug\n\n"); + + // print out digital controller state + len += sprintf(buf + len, "AU1000 Audio Controller registers\n"); + len += sprintf(buf + len, "---------------------------------\n"); + len += sprintf (buf + len, "AC97C_CONFIG = %08x\n", + au_readl(AC97C_CONFIG)); + len += sprintf (buf + len, "AC97C_STATUS = %08x\n", + au_readl(AC97C_STATUS)); + len += sprintf (buf + len, "AC97C_CNTRL = %08x\n", + au_readl(AC97C_CNTRL)); + + /* print out CODEC state */ + len += sprintf(buf + len, "\nAC97 CODEC registers\n"); + len += sprintf(buf + len, "----------------------\n"); + for (cnt = 0; cnt <= 0x7e; cnt += 2) + len += sprintf(buf + len, "reg %02x = %04x\n", + cnt, rdcodec(s->codec, cnt)); + + if (fpos >= len) { + *start = buf; + *eof = 1; + return 0; + } + *start = buf + fpos; + if ((len -= fpos) > length) + return length; + *eof = 1; + return len; + +} +#endif /* AU1000_DEBUG */ + +/* --------------------------------------------------------------------- */ + +MODULE_AUTHOR("Monta Vista Software, stevel@mvista.com"); +MODULE_DESCRIPTION("Au1000 Audio Driver"); + +/* --------------------------------------------------------------------- */ + +static int __devinit au1000_probe(void) +{ + struct au1000_state *s = &au1000_state; + int val; + char proc_str[80]; + + memset(s, 0, sizeof(struct au1000_state)); + + init_waitqueue_head(&s->dma_adc.wait); + init_waitqueue_head(&s->dma_dac.wait); + init_waitqueue_head(&s->open_wait); + init_MUTEX(&s->open_sem); + spin_lock_init(&s->lock); + + s->codec = ac97_alloc_codec(); + if(s->codec == NULL) + { + error("Out of memory"); + return -1; + } + s->codec->private_data = s; + s->codec->id = 0; + s->codec->codec_read = rdcodec; + s->codec->codec_write = wrcodec; + s->codec->codec_wait = waitcodec; + + if (!request_region(virt_to_phys((void *) AC97C_CONFIG), + 0x14, AU1000_MODULE_NAME)) { + err("AC'97 ports in use"); + goto err_codec; + } + // Allocate the DMA Channels + if ((s->dma_dac.dmanr = request_au1000_dma(DMA_ID_AC97C_TX, + "audio DAC", + dac_dma_interrupt, + SA_INTERRUPT, s)) < 0) { + err("Can't get DAC DMA"); + goto err_dma1; + } + if ((s->dma_adc.dmanr = request_au1000_dma(DMA_ID_AC97C_RX, + "audio ADC", + adc_dma_interrupt, + SA_INTERRUPT, s)) < 0) { + err("Can't get ADC DMA"); + goto err_dma2; + } + + info("DAC: DMA%d/IRQ%d, ADC: DMA%d/IRQ%d", + s->dma_dac.dmanr, get_dma_done_irq(s->dma_dac.dmanr), + s->dma_adc.dmanr, get_dma_done_irq(s->dma_adc.dmanr)); + +#ifdef USE_COHERENT_DMA + // enable DMA coherency in read/write DMA channels + set_dma_mode(s->dma_dac.dmanr, + get_dma_mode(s->dma_dac.dmanr) & ~DMA_NC); + set_dma_mode(s->dma_adc.dmanr, + get_dma_mode(s->dma_adc.dmanr) & ~DMA_NC); +#else + // disable DMA coherency in read/write DMA channels + set_dma_mode(s->dma_dac.dmanr, + get_dma_mode(s->dma_dac.dmanr) | DMA_NC); + set_dma_mode(s->dma_adc.dmanr, + get_dma_mode(s->dma_adc.dmanr) | DMA_NC); +#endif + + /* register devices */ + + if ((s->dev_audio = register_sound_dsp(&au1000_audio_fops, -1)) < 0) + goto err_dev1; + if ((s->codec->dev_mixer = + register_sound_mixer(&au1000_mixer_fops, -1)) < 0) + goto err_dev2; + +#ifdef AU1000_DEBUG + /* intialize the debug proc device */ + s->ps = create_proc_read_entry(AU1000_MODULE_NAME, 0, NULL, + proc_au1000_dump, NULL); +#endif /* AU1000_DEBUG */ + + // configure pins for AC'97 + au_writel(au_readl(SYS_PINFUNC) & ~0x02, SYS_PINFUNC); + + // Assert reset for 10msec to the AC'97 controller, and enable clock + au_writel(AC97C_RS | AC97C_CE, AC97C_CNTRL); + au1000_delay(10); + au_writel(AC97C_CE, AC97C_CNTRL); + au1000_delay(10); // wait for clock to stabilize + + /* cold reset the AC'97 */ + au_writel(AC97C_RESET, AC97C_CONFIG); + au1000_delay(10); + au_writel(0, AC97C_CONFIG); + /* need to delay around 500msec(bleech) to give + some CODECs enough time to wakeup */ + au1000_delay(500); + + /* warm reset the AC'97 to start the bitclk */ + au_writel(AC97C_SG | AC97C_SYNC, AC97C_CONFIG); + udelay(100); + au_writel(0, AC97C_CONFIG); + + /* codec init */ + if (!ac97_probe_codec(s->codec)) + goto err_dev3; + + s->codec_base_caps = rdcodec(s->codec, AC97_RESET); + s->codec_ext_caps = rdcodec(s->codec, AC97_EXTENDED_ID); + info("AC'97 Base/Extended ID = %04x/%04x", + s->codec_base_caps, s->codec_ext_caps); + + /* + * On the Pb1000, audio playback is on the AUX_OUT + * channel (which defaults to LNLVL_OUT in AC'97 + * rev 2.2) so make sure this channel is listed + * as supported (soundcard.h calls this channel + * ALTPCM). ac97_codec.c does not handle detection + * of this channel correctly. + */ + s->codec->supported_mixers |= SOUND_MASK_ALTPCM; + /* + * Now set AUX_OUT's default volume. + */ + val = 0x4343; + mixdev_ioctl(s->codec, SOUND_MIXER_WRITE_ALTPCM, + (unsigned long) &val); + + if (!(s->codec_ext_caps & AC97_EXTID_VRA)) { + // codec does not support VRA + s->no_vra = 1; + } else if (!vra) { + // Boot option says disable VRA + u16 ac97_extstat = rdcodec(s->codec, AC97_EXTENDED_STATUS); + wrcodec(s->codec, AC97_EXTENDED_STATUS, + ac97_extstat & ~AC97_EXTSTAT_VRA); + s->no_vra = 1; + } + if (s->no_vra) + info("no VRA, interpolating and decimating"); + + /* set mic to be the recording source */ + val = SOUND_MASK_MIC; + mixdev_ioctl(s->codec, SOUND_MIXER_WRITE_RECSRC, + (unsigned long) &val); + +#ifdef AU1000_DEBUG + sprintf(proc_str, "driver/%s/%d/ac97", AU1000_MODULE_NAME, + s->codec->id); + s->ac97_ps = create_proc_read_entry (proc_str, 0, NULL, + ac97_read_proc, s->codec); +#endif + + return 0; + + err_dev3: + unregister_sound_mixer(s->codec->dev_mixer); + err_dev2: + unregister_sound_dsp(s->dev_audio); + err_dev1: + free_au1000_dma(s->dma_adc.dmanr); + err_dma2: + free_au1000_dma(s->dma_dac.dmanr); + err_dma1: + release_region(virt_to_phys((void *) AC97C_CONFIG), 0x14); + err_codec: + ac97_release_codec(s->codec); + return -1; +} + +static void __devinit au1000_remove(void) +{ + struct au1000_state *s = &au1000_state; + + if (!s) + return; +#ifdef AU1000_DEBUG + if (s->ps) + remove_proc_entry(AU1000_MODULE_NAME, NULL); +#endif /* AU1000_DEBUG */ + synchronize_irq(); + free_au1000_dma(s->dma_adc.dmanr); + free_au1000_dma(s->dma_dac.dmanr); + release_region(virt_to_phys((void *) AC97C_CONFIG), 0x14); + unregister_sound_dsp(s->dev_audio); + unregister_sound_mixer(s->codec->dev_mixer); + ac97_release_codec(s->codec); +} + +static int __init init_au1000(void) +{ + info("stevel@mvista.com, built " __TIME__ " on " __DATE__); + return au1000_probe(); +} + +static void __exit cleanup_au1000(void) +{ + info("unloading"); + au1000_remove(); +} + +module_init(init_au1000); +module_exit(cleanup_au1000); + +/* --------------------------------------------------------------------- */ + +#ifndef MODULE + +static int __init au1000_setup(char *options) +{ + char *this_opt; + + if (!options || !*options) + return 0; + + for(this_opt=strtok(options, ","); + this_opt; this_opt=strtok(NULL, ",")) { + if (!strncmp(this_opt, "vra", 3)) { + vra = 1; + } + } + + return 1; +} + +__setup("au1000_audio=", au1000_setup); + +#endif /* MODULE */ diff -Nru a/sound/oss/btaudio.c b/sound/oss/btaudio.c --- a/sound/oss/btaudio.c Sat May 24 17:00:00 2003 +++ b/sound/oss/btaudio.c Fri Jul 11 08:28:57 2003 @@ -328,8 +328,8 @@ if (cmd == SOUND_MIXER_INFO) { mixer_info info; memset(&info,0,sizeof(info)); - strlcpy(info.id,"bt878",sizeof(info.id)); - strlcpy(info.name,"Brooktree Bt878 audio",sizeof(info.name)); + strncpy(info.id,"bt878",sizeof(info.id)); + strncpy(info.name,"Brooktree Bt878 audio",sizeof(info.name)); info.modify_counter = bta->mixcount; if (copy_to_user((void *)arg, &info, sizeof(info))) return -EFAULT; @@ -338,8 +338,8 @@ if (cmd == SOUND_OLD_MIXER_INFO) { _old_mixer_info info; memset(&info,0,sizeof(info)); - strlcpy(info.id,"bt878",sizeof(info.id)-1); - strlcpy(info.name,"Brooktree Bt878 audio",sizeof(info.name)); + strncpy(info.id,"bt878",sizeof(info.id)-1); + strncpy(info.name,"Brooktree Bt878 audio",sizeof(info.name)); if (copy_to_user((void *)arg, &info, sizeof(info))) return -EFAULT; return 0; diff -Nru a/sound/oss/cmpci.c b/sound/oss/cmpci.c --- a/sound/oss/cmpci.c Mon Jun 9 16:14:23 2003 +++ b/sound/oss/cmpci.c Fri Jul 11 08:27:53 2003 @@ -1272,8 +1272,8 @@ VALIDATE_STATE(s); if (cmd == SOUND_MIXER_INFO) { mixer_info info; - strlcpy(info.id, "cmpci", sizeof(info.id)); - strlcpy(info.name, "C-Media PCI", sizeof(info.name)); + strncpy(info.id, "cmpci", sizeof(info.id)); + strncpy(info.name, "C-Media PCI", sizeof(info.name)); info.modify_counter = s->mix.modcnt; if (copy_to_user((void *)arg, &info, sizeof(info))) return -EFAULT; @@ -1281,8 +1281,8 @@ } if (cmd == SOUND_OLD_MIXER_INFO) { _old_mixer_info info; - strlcpy(info.id, "cmpci", sizeof(info.id)); - strlcpy(info.name, "C-Media cmpci", sizeof(info.name)); + strncpy(info.id, "cmpci", sizeof(info.id)); + strncpy(info.name, "C-Media cmpci", sizeof(info.name)); if (copy_to_user((void *)arg, &info, sizeof(info))) return -EFAULT; return 0; diff -Nru a/sound/oss/cs46xx.c b/sound/oss/cs46xx.c --- a/sound/oss/cs46xx.c Mon Jun 9 16:18:22 2003 +++ b/sound/oss/cs46xx.c Fri Jul 11 09:05:46 2003 @@ -311,6 +311,9 @@ /* The cs461x has a certain amount of cross channel interaction so we use a single per card lock */ spinlock_t lock; + + /* Keep AC97 sane */ + spinlock_t ac97_lock; /* mixer use count */ atomic_t mixer_use_cnt; @@ -1014,7 +1017,7 @@ } /* - * ganularity is byte boundary, good part. + * granularity is byte boundary, good part. */ if(dmabuf->enable & DAC_RUNNING) { @@ -1195,7 +1198,7 @@ // 2. mark each physical page in range as 'reserved'. for (map = virt_to_page(dmabuf->rawbuf); map <= mapend; map++) - SetPageReserved(map); + cs4x_mem_map_reserve(map); CS_DBGOUT(CS_PARMS, 9, printk("cs46xx: alloc_dmabuf(): allocated %ld (order = %d) bytes at %p\n", PAGE_SIZE << order, order, rawbuf) ); @@ -1232,7 +1235,7 @@ // 2. mark each physical page in range as 'reserved'. for (map = virt_to_page(dmabuf->tmpbuff); map <= mapend; map++) - SetPageReserved(map); + cs4x_mem_map_reserve(map); return 0; } @@ -1247,7 +1250,7 @@ mapend = virt_to_page(dmabuf->rawbuf + (PAGE_SIZE << dmabuf->buforder) - 1); for (map = virt_to_page(dmabuf->rawbuf); map <= mapend; map++) - ClearPageReserved(map); + cs4x_mem_map_unreserve(map); free_dmabuf(state->card, dmabuf); } @@ -1256,7 +1259,7 @@ mapend = virt_to_page(dmabuf->tmpbuff + (PAGE_SIZE << dmabuf->buforder_tmpbuff) - 1); for (map = virt_to_page(dmabuf->tmpbuff); map <= mapend; map++) - ClearPageReserved(map); + cs4x_mem_map_unreserve(map); free_dmabuf2(state->card, dmabuf); } @@ -1910,11 +1913,8 @@ break; if (signal_pending(current)) break; - if (file->f_flags & O_NONBLOCK) { - remove_wait_queue(&card->midi.owait, &wait); - current->state = TASK_RUNNING; - return -EBUSY; - } + if (file->f_flags & O_NONBLOCK) + break; tmo = (count * HZ) / 3100; if (!schedule_timeout(tmo ? : 1) && tmo) printk(KERN_DEBUG "cs46xx: midi timed out??\n"); @@ -2117,7 +2117,7 @@ down(&state->sem); if (!dmabuf->ready && (ret = __prog_dmabuf(state))) - goto out; + goto out2; add_wait_queue(&state->dmabuf.wait, &wait); while (count > 0) { @@ -2187,8 +2187,9 @@ start_adc(state); } out: - up(&state->sem); remove_wait_queue(&state->dmabuf.wait, &wait); +out2: + up(&state->sem); set_current_state(TASK_RUNNING); CS_DBGOUT(CS_WAVE_READ | CS_FUNCTION, 4, printk("cs46xx: cs_read()- %d\n",ret) ); @@ -2213,6 +2214,8 @@ state = (struct cs_state *)card->states[1]; if(!state) return -ENODEV; + if (!access_ok(VERIFY_READ, buffer, count)) + return -EFAULT; dmabuf = &state->dmabuf; if (ppos != &file->f_pos) @@ -2227,11 +2230,6 @@ if (!dmabuf->ready && (ret = __prog_dmabuf(state))) goto out; - if (!access_ok(VERIFY_READ, buffer, count)) - { - ret = -EFAULT; - goto out; - } add_wait_queue(&state->dmabuf.wait, &wait); ret = 0; /* @@ -3007,7 +3005,7 @@ return -ENODEV; case SNDCTL_DSP_SETDUPLEX: - return -EINVAL; + return 0; case SNDCTL_DSP_GETODELAY: if (!(file->f_mode & FMODE_WRITE)) @@ -3832,11 +3830,12 @@ /* Write AC97 codec registers */ -static u16 cs_ac97_get(struct ac97_codec *dev, u8 reg) +static u16 _cs_ac97_get(struct ac97_codec *dev, u8 reg) { struct cs_card *card = dev->private_data; int count,loopcnt; unsigned int tmp; + u16 ret; /* * 1. Write ACCAD = Command Address Register = 46Ch for AC97 register address @@ -3847,7 +3846,6 @@ * 6. Read ACSTS = Status Register = 464h, check VSTS bit */ - cs461x_peekBA0(card, BA0_ACSDA); /* @@ -3938,7 +3936,19 @@ "cs46xx: cs_ac97_get() reg = 0x%x, val = 0x%x, BA0_ACCAD = 0x%x\n", reg, cs461x_peekBA0(card, BA0_ACSDA), cs461x_peekBA0(card, BA0_ACCAD))); - return(cs461x_peekBA0(card, BA0_ACSDA)); + ret = cs461x_peekBA0(card, BA0_ACSDA); + return ret; +} + +static u16 cs_ac97_get(struct ac97_codec *dev, u8 reg) +{ + u16 ret; + struct cs_card *card = dev->private_data; + + spin_lock(&card->ac97_lock); + ret = _cs_ac97_get(dev, reg); + spin_unlock(&card->ac97_lock); + return ret; } static void cs_ac97_set(struct ac97_codec *dev, u8 reg, u16 val) @@ -3947,11 +3957,14 @@ int count; int val2 = 0; + spin_lock(&card->ac97_lock); + if(reg == AC97_CD_VOL) { - val2 = cs_ac97_get(dev, AC97_CD_VOL); + val2 = _cs_ac97_get(dev, AC97_CD_VOL); } + /* * 1. Write ACCAD = Command Address Register = 46Ch for AC97 register address * 2. Write ACCDA = Command Data Register = 470h for data to write to AC97 @@ -3999,6 +4012,8 @@ "cs46xx: AC'97 write problem, reg = 0x%x, val = 0x%x\n", reg, val)); } + spin_unlock(&card->ac97_lock); + /* * Adjust power if the mixer is selected/deselected according * to the CD. @@ -4244,9 +4259,8 @@ "cs46xx: cs_ac97_init()+\n") ); for (num_ac97 = 0; num_ac97 < NR_AC97; num_ac97++) { - if ((codec = kmalloc(sizeof(struct ac97_codec), GFP_KERNEL)) == NULL) + if ((codec = ac97_alloc_codec()) == NULL) return -ENOMEM; - memset(codec, 0, sizeof(struct ac97_codec)); /* initialize some basic codec information, other fields will be filled in ac97_probe_codec */ @@ -4269,10 +4283,10 @@ eid = cs_ac97_get(codec, AC97_EXTENDED_ID); - if(eid==0xFFFFFF) + if(eid==0xFFFF) { printk(KERN_WARNING "cs46xx: codec %d not present\n",num_ac97); - kfree(codec); + ac97_release_codec(codec); break; } @@ -4280,7 +4294,7 @@ if ((codec->dev_mixer = register_sound_mixer(&cs_mixer_fops, -1)) < 0) { printk(KERN_ERR "cs46xx: couldn't register mixer!\n"); - kfree(codec); + ac97_release_codec(codec); break; } card->ac97_codec[num_ac97] = codec; @@ -5298,6 +5312,12 @@ .name = "Hercules Game Theatre XP", .amp = amp_hercules, }, + { + .vendor = 0x1681, + .id = 0xa010, + .name = "Hercules Fortissimo II", + .amp = amp_none, + }, /* Not sure if the 570 needs the clkrun hack */ { .vendor = PCI_VENDOR_ID_IBM, @@ -5381,6 +5401,7 @@ card->irq = pci_dev->irq; card->magic = CS_CARD_MAGIC; spin_lock_init(&card->lock); + spin_lock_init(&card->ac97_lock); pci_set_master(pci_dev); @@ -5508,7 +5529,7 @@ for (j = 0; j < NR_AC97; j++) if (card->ac97_codec[j] != NULL) { unregister_sound_mixer(card->ac97_codec[j]->dev_mixer); - kfree (card->ac97_codec[j]); + ac97_release_codec(card->ac97_codec[j]); } mdelay(10 * cs_laptop_wait); continue; @@ -5665,7 +5686,7 @@ for (i = 0; i < NR_AC97; i++) if (card->ac97_codec[i] != NULL) { unregister_sound_mixer(card->ac97_codec[i]->dev_mixer); - kfree (card->ac97_codec[i]); + ac97_release_codec(card->ac97_codec[i]); } unregister_sound_dsp(card->dev_audio); if(card->dev_midi) diff -Nru a/sound/oss/cs46xx_wrapper-24.h b/sound/oss/cs46xx_wrapper-24.h --- a/sound/oss/cs46xx_wrapper-24.h Mon Apr 21 02:02:19 2003 +++ b/sound/oss/cs46xx_wrapper-24.h Fri Jul 11 09:07:37 2003 @@ -31,6 +31,9 @@ #define CS_OWNER owner: #define CS_THIS_MODULE THIS_MODULE, void cs46xx_null(struct pci_dev *pcidev) { return; } +#define cs4x_mem_map_reserve(page) SetPageReserved(page) +#define cs4x_mem_map_unreserve(page) ClearPageReserved(page) + #define free_dmabuf(card, dmabuf) \ pci_free_consistent((card)->pci_dev, \ PAGE_SIZE << (dmabuf)->buforder, \ diff -Nru a/sound/oss/dmasound/dmasound_core.c b/sound/oss/dmasound/dmasound_core.c --- a/sound/oss/dmasound/dmasound_core.c Sat May 24 17:00:00 2003 +++ b/sound/oss/dmasound/dmasound_core.c Fri Jul 11 08:30:15 2003 @@ -351,8 +351,8 @@ case SOUND_MIXER_INFO: { mixer_info info; - strlcpy(info.id, dmasound.mach.name2, sizeof(info.id)); - strlcpy(info.name, dmasound.mach.name2, sizeof(info.name)); + strncpy(info.id, dmasound.mach.name2, sizeof(info.id)); + strncpy(info.name, dmasound.mach.name2, sizeof(info.name)); info.modify_counter = mixer.modify_counter; if (copy_to_user((int *)arg, &info, sizeof(info))) return -EFAULT; diff -Nru a/sound/oss/emu10k1/efxmgr.c b/sound/oss/emu10k1/efxmgr.c --- a/sound/oss/emu10k1/efxmgr.c Sun Feb 16 16:30:06 2003 +++ b/sound/oss/emu10k1/efxmgr.c Fri Jul 11 08:27:04 2003 @@ -101,10 +101,10 @@ { extern char volume_params[SOUND_MIXER_NRDEVICES]; - card->ac97.mixer_state[oss_mixer] = (right << 8) | left; + card->ac97->mixer_state[oss_mixer] = (right << 8) | left; if (!card->is_aps) - card->ac97.write_mixer(&card->ac97, oss_mixer, left, right); + card->ac97->write_mixer(card->ac97, oss_mixer, left, right); emu10k1_set_volume_gpr(card, card->mgr.ctrl_gpr[oss_mixer][0], left, volume_params[oss_mixer]); @@ -125,7 +125,7 @@ right = (val >> 8) & 0xff; val = 0; } else { - val = card->ac97.mixer_state[oss_channel]; + val = card->ac97->mixer_state[oss_channel]; left = 0; right = 0; } @@ -138,8 +138,8 @@ int oss_channel = VOLCTRL_CHANNEL; int left, right; - left = card->ac97.mixer_state[oss_channel] & 0xff; - right = (card->ac97.mixer_state[oss_channel] >> 8) & 0xff; + left = card->ac97->mixer_state[oss_channel] & 0xff; + right = (card->ac97->mixer_state[oss_channel] >> 8) & 0xff; if ((left += VOLCTRL_STEP_SIZE) > 100) left = 100; @@ -155,8 +155,8 @@ int oss_channel = VOLCTRL_CHANNEL; int left, right; - left = card->ac97.mixer_state[oss_channel] & 0xff; - right = (card->ac97.mixer_state[oss_channel] >> 8) & 0xff; + left = card->ac97->mixer_state[oss_channel] & 0xff; + right = (card->ac97->mixer_state[oss_channel] >> 8) & 0xff; if ((left -= VOLCTRL_STEP_SIZE) < 0) left = 0; diff -Nru a/sound/oss/emu10k1/hwaccess.h b/sound/oss/emu10k1/hwaccess.h --- a/sound/oss/emu10k1/hwaccess.h Sun Feb 16 16:30:06 2003 +++ b/sound/oss/emu10k1/hwaccess.h Fri Jul 11 08:30:47 2003 @@ -162,7 +162,7 @@ struct emu10k1_mididevice *seq_mididev; #endif - struct ac97_codec ac97; + struct ac97_codec *ac97; int ac97_supported_mixers; int ac97_stereo_mixers; diff -Nru a/sound/oss/emu10k1/main.c b/sound/oss/emu10k1/main.c --- a/sound/oss/emu10k1/main.c Mon Apr 21 00:32:53 2003 +++ b/sound/oss/emu10k1/main.c Fri Jul 11 08:30:47 2003 @@ -223,21 +223,31 @@ { char s[32]; - struct ac97_codec *codec = &card->ac97; - card->ac97.dev_mixer = register_sound_mixer(&emu10k1_mixer_fops, -1); - if (card->ac97.dev_mixer < 0) { - printk(KERN_ERR "emu10k1: cannot register mixer device\n"); + struct ac97_codec *codec = ac97_alloc_codec(); + + if(codec == NULL) + { + printk(KERN_ERR "emu10k1: cannot allocate mixer\n"); return -EIO; + } + card->ac97 = codec; + +#warning "Initialisation order race. Must register after usable" + + card->ac97->dev_mixer = register_sound_mixer(&emu10k1_mixer_fops, -1); + if (card->ac97->dev_mixer < 0) { + printk(KERN_ERR "emu10k1: cannot register mixer device\n"); + goto err_codec; } - card->ac97.private_data = card; + card->ac97->private_data = card; if (!card->is_aps) { - card->ac97.id = 0; - card->ac97.codec_read = emu10k1_ac97_read; - card->ac97.codec_write = emu10k1_ac97_write; + card->ac97->id = 0; + card->ac97->codec_read = emu10k1_ac97_read; + card->ac97->codec_write = emu10k1_ac97_write; - if (ac97_probe_codec (&card->ac97) == 0) { + if (ac97_probe_codec (card->ac97) == 0) { printk(KERN_ERR "emu10k1: unable to probe AC97 codec\n"); goto err_out; } @@ -249,7 +259,7 @@ } // Force 5bit: - //card->ac97.bit_resolution=5; + //card->ac97->bit_resolution=5; if (!proc_mkdir ("driver/emu10k1", 0)) { printk(KERN_ERR "emu10k1: unable to create proc directory driver/emu10k1\n"); @@ -263,14 +273,14 @@ } sprintf(s, "driver/emu10k1/%s/ac97", card->pci_dev->slot_name); - if (!create_proc_read_entry (s, 0, 0, ac97_read_proc, &card->ac97)) { + if (!create_proc_read_entry (s, 0, 0, ac97_read_proc, card->ac97)) { printk(KERN_ERR "emu10k1: unable to create proc entry %s\n", s); goto err_ac97_proc; } /* these will store the original values and never be modified */ - card->ac97_supported_mixers = card->ac97.supported_mixers; - card->ac97_stereo_mixers = card->ac97.stereo_mixers; + card->ac97_supported_mixers = card->ac97->supported_mixers; + card->ac97_stereo_mixers = card->ac97->stereo_mixers; } return 0; @@ -282,7 +292,9 @@ err_emu10k1_proc: remove_proc_entry("driver/emu10k1", NULL); err_out: - unregister_sound_mixer (card->ac97.dev_mixer); + unregister_sound_mixer (card->ac97->dev_mixer); + err_codec: + ac97_release_codec(card->ac97); return -EIO; } @@ -300,7 +312,8 @@ remove_proc_entry("driver/emu10k1", NULL); } - unregister_sound_mixer (card->ac97.dev_mixer); + unregister_sound_mixer (card->ac97->dev_mixer); + ac97_release_codec(card->ac97); } static int __devinit emu10k1_midi_init(struct emu10k1_card *card) @@ -694,21 +707,21 @@ mgr->ctrl_gpr[SOUND_MIXER_VOLUME][0] = 8; mgr->ctrl_gpr[SOUND_MIXER_VOLUME][1] = 9; - left = card->ac97.mixer_state[SOUND_MIXER_VOLUME] & 0xff; - right = (card->ac97.mixer_state[SOUND_MIXER_VOLUME] >> 8) & 0xff; + left = card->ac97->mixer_state[SOUND_MIXER_VOLUME] & 0xff; + right = (card->ac97->mixer_state[SOUND_MIXER_VOLUME] >> 8) & 0xff; - emu10k1_set_volume_gpr(card, 8, left, 1 << card->ac97.bit_resolution); - emu10k1_set_volume_gpr(card, 9, right, 1 << card->ac97.bit_resolution); + emu10k1_set_volume_gpr(card, 8, left, 1 << card->ac97->bit_resolution); + emu10k1_set_volume_gpr(card, 9, right, 1 << card->ac97->bit_resolution); //Rear volume mgr->ctrl_gpr[ SOUND_MIXER_OGAIN ][0] = 0x19; mgr->ctrl_gpr[ SOUND_MIXER_OGAIN ][1] = 0x1a; left = right = 67; - card->ac97.mixer_state[SOUND_MIXER_OGAIN] = (right << 8) | left; + card->ac97->mixer_state[SOUND_MIXER_OGAIN] = (right << 8) | left; - card->ac97.supported_mixers |= SOUND_MASK_OGAIN; - card->ac97.stereo_mixers |= SOUND_MASK_OGAIN; + card->ac97->supported_mixers |= SOUND_MASK_OGAIN; + card->ac97->stereo_mixers |= SOUND_MASK_OGAIN; emu10k1_set_volume_gpr(card, 0x19, left, VOL_5BIT); emu10k1_set_volume_gpr(card, 0x1a, right, VOL_5BIT); @@ -717,8 +730,8 @@ mgr->ctrl_gpr[SOUND_MIXER_PCM][0] = 6; mgr->ctrl_gpr[SOUND_MIXER_PCM][1] = 7; - left = card->ac97.mixer_state[SOUND_MIXER_PCM] & 0xff; - right = (card->ac97.mixer_state[SOUND_MIXER_PCM] >> 8) & 0xff; + left = card->ac97->mixer_state[SOUND_MIXER_PCM] & 0xff; + right = (card->ac97->mixer_state[SOUND_MIXER_PCM] >> 8) & 0xff; emu10k1_set_volume_gpr(card, 6, left, VOL_5BIT); emu10k1_set_volume_gpr(card, 7, right, VOL_5BIT); @@ -728,22 +741,22 @@ mgr->ctrl_gpr[SOUND_MIXER_DIGITAL1][1] = 0xf; left = right = 67; - card->ac97.mixer_state[SOUND_MIXER_DIGITAL1] = (right << 8) | left; + card->ac97->mixer_state[SOUND_MIXER_DIGITAL1] = (right << 8) | left; - card->ac97.supported_mixers |= SOUND_MASK_DIGITAL1; - card->ac97.stereo_mixers |= SOUND_MASK_DIGITAL1; + card->ac97->supported_mixers |= SOUND_MASK_DIGITAL1; + card->ac97->stereo_mixers |= SOUND_MASK_DIGITAL1; emu10k1_set_volume_gpr(card, 0xd, left, VOL_5BIT); emu10k1_set_volume_gpr(card, 0xf, right, VOL_5BIT); //hard wire the ac97's pcm, we'll do that in dsp code instead. - emu10k1_ac97_write(&card->ac97, 0x18, 0x0); + emu10k1_ac97_write(card->ac97, 0x18, 0x0); card->ac97_supported_mixers &= ~SOUND_MASK_PCM; card->ac97_stereo_mixers &= ~SOUND_MASK_PCM; //set Igain to 0dB by default, maybe consider hardwiring it here. - emu10k1_ac97_write(&card->ac97, AC97_RECORD_GAIN, 0x0000); - card->ac97.mixer_state[SOUND_MIXER_IGAIN] = 0x101; + emu10k1_ac97_write(card->ac97, AC97_RECORD_GAIN, 0x0000); + card->ac97->mixer_state[SOUND_MIXER_IGAIN] = 0x101; return 0; } diff -Nru a/sound/oss/emu10k1/mixer.c b/sound/oss/emu10k1/mixer.c --- a/sound/oss/emu10k1/mixer.c Sat May 24 17:00:00 2003 +++ b/sound/oss/emu10k1/mixer.c Fri Jul 11 10:29:48 2003 @@ -457,29 +457,29 @@ break; if (addr >= 0) { - unsigned int state = card->ac97.mixer_state[id]; + unsigned int state = card->ac97->mixer_state[id]; if (ch == 1) { state >>= 8; - card->ac97.stereo_mixers |= (1 << id); + card->ac97->stereo_mixers |= (1 << id); } - card->ac97.supported_mixers |= (1 << id); + card->ac97->supported_mixers |= (1 << id); if (id == SOUND_MIXER_TREBLE) { - set_treble(card, card->ac97.mixer_state[id] & 0xff, (card->ac97.mixer_state[id] >> 8) & 0xff); + set_treble(card, card->ac97->mixer_state[id] & 0xff, (card->ac97->mixer_state[id] >> 8) & 0xff); } else if (id == SOUND_MIXER_BASS) { - set_bass(card, card->ac97.mixer_state[id] & 0xff, (card->ac97.mixer_state[id] >> 8) & 0xff); + set_bass(card, card->ac97->mixer_state[id] & 0xff, (card->ac97->mixer_state[id] >> 8) & 0xff); } else emu10k1_set_volume_gpr(card, addr, state & 0xff, volume_params[id]); } else { - card->ac97.stereo_mixers &= ~(1 << id); - card->ac97.stereo_mixers |= card->ac97_stereo_mixers; + card->ac97->stereo_mixers &= ~(1 << id); + card->ac97->stereo_mixers |= card->ac97_stereo_mixers; if (ch == 0) { - card->ac97.supported_mixers &= ~(1 << id); - card->ac97.supported_mixers |= card->ac97_supported_mixers; + card->ac97->supported_mixers &= ~(1 << id); + card->ac97->supported_mixers |= card->ac97_supported_mixers; } } break; @@ -500,9 +500,9 @@ case CMD_AC97_BOOST: if(ctl->val[0]) - emu10k1_ac97_write(&card->ac97, 0x18, 0x0); + emu10k1_ac97_write(card->ac97, 0x18, 0x0); else - emu10k1_ac97_write(&card->ac97, 0x18, 0x0808); + emu10k1_ac97_write(card->ac97, 0x18, 0x0808); break; default: ret = -EINVAL; @@ -577,7 +577,7 @@ int val; int scale; - card->ac97.modcnt++; + card->ac97->modcnt++; if (get_user(val, (int *)arg)) return -EFAULT; @@ -589,7 +589,7 @@ if (right > 100) right = 100; if (left > 100) left = 100; - card->ac97.mixer_state[oss_mixer] = (right << 8) | left; + card->ac97->mixer_state[oss_mixer] = (right << 8) | left; if (oss_mixer == SOUND_MIXER_TREBLE) { set_treble(card, left, right); return 0; @@ -599,7 +599,7 @@ } if (oss_mixer == SOUND_MIXER_VOLUME) - scale = 1 << card->ac97.bit_resolution; + scale = 1 << card->ac97->bit_resolution; else scale = volume_params[oss_mixer]; @@ -607,7 +607,7 @@ emu10k1_set_volume_gpr(card, card->mgr.ctrl_gpr[oss_mixer][1], right, scale); if (card->ac97_supported_mixers & (1 << oss_mixer)) - card->ac97.write_mixer(&card->ac97, oss_mixer, left, right); + card->ac97->write_mixer(card->ac97, oss_mixer, left, right); return 0; } @@ -623,9 +623,9 @@ if (cmd == SOUND_MIXER_INFO) { mixer_info info; - strlcpy(info.id, card->ac97.name, sizeof(info.id)); - strlcpy(info.name, "Creative SBLive - Emu10k1", sizeof(info.name)); - info.modify_counter = card->ac97.modcnt; + strncpy(info.id, card->ac97->name, sizeof(info.id)); + strncpy(info.name, "Creative SBLive - Emu10k1", sizeof(info.name)); + info.modify_counter = card->ac97->modcnt; if (copy_to_user((void *)arg, &info, sizeof(info))) return -EFAULT; @@ -636,7 +636,7 @@ if ((_SIOC_DIR(cmd) == (_SIOC_WRITE|_SIOC_READ)) && oss_mixer <= SOUND_MIXER_NRDEVICES) ret = emu10k1_dsp_mixer(card, oss_mixer, arg); else - ret = card->ac97.mixer_ioctl(&card->ac97, cmd, arg); + ret = card->ac97->mixer_ioctl(card->ac97, cmd, arg); } if (ret < 0) @@ -656,7 +656,7 @@ list_for_each(entry, &emu10k1_devs) { card = list_entry(entry, struct emu10k1_card, list); - if (card->ac97.dev_mixer == minor) + if (card->ac97->dev_mixer == minor) goto match; } diff -Nru a/sound/oss/es1370.c b/sound/oss/es1370.c --- a/sound/oss/es1370.c Mon Jun 9 16:18:51 2003 +++ b/sound/oss/es1370.c Fri Jul 11 09:10:35 2003 @@ -889,8 +889,8 @@ } if (cmd == SOUND_MIXER_INFO) { mixer_info info; - strlcpy(info.id, "ES1370", sizeof(info.id)); - strlcpy(info.name, "Ensoniq ES1370", sizeof(info.name)); + strncpy(info.id, "ES1370", sizeof(info.id)); + strncpy(info.name, "Ensoniq ES1370", sizeof(info.name)); info.modify_counter = s->mix.modcnt; if (copy_to_user((void *)arg, &info, sizeof(info))) return -EFAULT; @@ -898,8 +898,8 @@ } if (cmd == SOUND_OLD_MIXER_INFO) { _old_mixer_info info; - strlcpy(info.id, "ES1370", sizeof(info.id)); - strlcpy(info.name, "Ensoniq ES1370", sizeof(info.name)); + strncpy(info.id, "ES1370", sizeof(info.id)); + strncpy(info.name, "Ensoniq ES1370", sizeof(info.name)); if (copy_to_user((void *)arg, &info, sizeof(info))) return -EFAULT; return 0; @@ -2484,12 +2484,8 @@ break; if (signal_pending(current)) break; - if (file->f_flags & O_NONBLOCK) { - remove_wait_queue(&s->midi.owait, &wait); - set_current_state(TASK_RUNNING); - unlock_kernel(); - return -EBUSY; - } + if (file->f_flags & O_NONBLOCK) + break; tmo = (count * HZ) / 3100; if (!schedule_timeout(tmo ? : 1) && tmo) DBG(printk(KERN_DEBUG "es1370: midi timed out??\n");) diff -Nru a/sound/oss/es1371.c b/sound/oss/es1371.c --- a/sound/oss/es1371.c Sat Jun 14 16:16:16 2003 +++ b/sound/oss/es1371.c Fri Jul 11 09:40:25 2003 @@ -406,7 +406,7 @@ struct proc_dir_entry *ps; #endif /* ES1371_DEBUG */ - struct ac97_codec codec; + struct ac97_codec *codec; /* wave stuff */ unsigned ctrl; @@ -682,10 +682,10 @@ unsigned long flags; unsigned t, x; + spin_lock_irqsave(&s->lock, flags); for (t = 0; t < POLL_COUNT; t++) if (!(inl(s->io+ES1371_REG_CODEC) & CODEC_WIP)) break; - spin_lock_irqsave(&s->lock, flags); /* save the current state for later */ x = wait_src_ready(s); @@ -724,11 +724,12 @@ unsigned long flags; unsigned t, x; + spin_lock_irqsave(&s->lock, flags); + /* wait for WIP to go away */ for (t = 0; t < 0x1000; t++) if (!(inl(s->io+ES1371_REG_CODEC) & CODEC_WIP)) break; - spin_lock_irqsave(&s->lock, flags); /* save the current state for later */ x = (wait_src_ready(s) & (SRC_DIS | SRC_DDAC1 | SRC_DDAC2 | SRC_DADC)); @@ -756,7 +757,6 @@ /* restore SRC reg */ wait_src_ready(s); outl(x, s->io+ES1371_REG_SRCONV); - spin_unlock_irqrestore(&s->lock, flags); /* wait for WIP again */ for (t = 0; t < 0x1000; t++) @@ -768,6 +768,7 @@ if ((x = inl(s->io+ES1371_REG_CODEC)) & CODEC_RDY) break; + spin_unlock_irqrestore(&s->lock, flags); return ((x & CODEC_PIDAT_MASK) >> CODEC_PIDAT_SHIFT); } @@ -1217,7 +1218,7 @@ if (list == &devs) return -ENODEV; s = list_entry(list, struct es1371_state, devs); - if (s->codec.dev_mixer == minor) + if (s->codec->dev_mixer == minor) break; } VALIDATE_STATE(s); @@ -1236,7 +1237,7 @@ static int es1371_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { struct es1371_state *s = (struct es1371_state *)file->private_data; - struct ac97_codec *codec = &s->codec; + struct ac97_codec *codec = s->codec; return mixdev_ioctl(codec, cmd, arg); } @@ -1908,7 +1909,7 @@ return -EINVAL; } - return mixdev_ioctl(&s->codec, cmd, arg); + return mixdev_ioctl(s->codec, cmd, arg); } static int es1371_open(struct inode *inode, struct file *file) @@ -2339,7 +2340,7 @@ return -EINVAL; } - return mixdev_ioctl(&s->codec, cmd, arg); + return mixdev_ioctl(s->codec, cmd, arg); } static int es1371_open_dac(struct inode *inode, struct file *file) @@ -2661,12 +2662,8 @@ break; if (signal_pending(current)) break; - if (file->f_flags & O_NONBLOCK) { - remove_wait_queue(&s->midi.owait, &wait); - set_current_state(TASK_RUNNING); - unlock_kernel(); - return -EBUSY; - } + if (file->f_flags & O_NONBLOCK) + break; tmo = (count * HZ) / 3100; if (!schedule_timeout(tmo ? : 1) && tmo) printk(KERN_DEBUG PFX "midi timed out??\n"); @@ -2720,7 +2717,7 @@ /* print out CODEC state */ len += sprintf (buf + len, "AC97 CODEC state\n"); for (cnt=0; cnt <= 0x7e; cnt = cnt +2) - len+= sprintf (buf + len, "reg:0x%02x val:0x%04x\n", cnt, rdcodec(&s->codec, cnt)); + len+= sprintf (buf + len, "reg:0x%02x val:0x%04x\n", cnt, rdcodec(s->codec, cnt)); if (fpos >=len){ *start = buf; @@ -2820,6 +2817,11 @@ return -ENOMEM; } memset(s, 0, sizeof(struct es1371_state)); + + s->codec = ac97_alloc_codec(); + if(s->codec == NULL) + goto err_codec; + init_waitqueue_head(&s->dma_adc.wait); init_waitqueue_head(&s->dma_dac1.wait); init_waitqueue_head(&s->dma_dac2.wait); @@ -2835,10 +2837,10 @@ s->vendor = pcidev->vendor; s->device = pcidev->device; pci_read_config_byte(pcidev, PCI_REVISION_ID, &s->rev); - s->codec.private_data = s; - s->codec.id = 0; - s->codec.codec_read = rdcodec; - s->codec.codec_write = wrcodec; + s->codec->private_data = s; + s->codec->id = 0; + s->codec->codec_read = rdcodec; + s->codec->codec_write = wrcodec; printk(KERN_INFO PFX "found chip, vendor id 0x%04x device id 0x%04x revision 0x%02x\n", s->vendor, s->device, s->rev); if (!request_region(s->io, ES1371_EXTENT, "es1371")) { @@ -2855,7 +2857,7 @@ /* register devices */ if ((res=(s->dev_audio = register_sound_dsp(&es1371_audio_fops,-1)))<0) goto err_dev1; - if ((res=(s->codec.dev_mixer = register_sound_mixer(&es1371_mixer_fops, -1))) < 0) + if ((res=(s->codec->dev_mixer = register_sound_mixer(&es1371_mixer_fops, -1))) < 0) goto err_dev2; if ((res=(s->dev_dac = register_sound_dsp(&es1371_dac_fops, -1))) < 0) goto err_dev3; @@ -2938,7 +2940,7 @@ /* init the sample rate converter */ src_init(s); /* codec init */ - if (!ac97_probe_codec(&s->codec)) { + if (!ac97_probe_codec(s->codec)) { res = -ENODEV; goto err_gp; } @@ -2947,16 +2949,16 @@ fs = get_fs(); set_fs(KERNEL_DS); val = SOUND_MASK_LINE; - mixdev_ioctl(&s->codec, SOUND_MIXER_WRITE_RECSRC, (unsigned long)&val); + mixdev_ioctl(s->codec, SOUND_MIXER_WRITE_RECSRC, (unsigned long)&val); for (i = 0; i < sizeof(initvol)/sizeof(initvol[0]); i++) { val = initvol[i].vol; - mixdev_ioctl(&s->codec, initvol[i].mixch, (unsigned long)&val); + mixdev_ioctl(s->codec, initvol[i].mixch, (unsigned long)&val); } /* mute master and PCM when in S/PDIF mode */ if (s->spdif_volume != -1) { val = 0x0000; - s->codec.mixer_ioctl(&s->codec, SOUND_MIXER_WRITE_VOLUME, (unsigned long)&val); - s->codec.mixer_ioctl(&s->codec, SOUND_MIXER_WRITE_PCM, (unsigned long)&val); + s->codec->mixer_ioctl(s->codec, SOUND_MIXER_WRITE_VOLUME, (unsigned long)&val); + s->codec->mixer_ioctl(s->codec, SOUND_MIXER_WRITE_PCM, (unsigned long)&val); } set_fs(fs); /* turn on S/PDIF output driver if requested */ @@ -2984,7 +2986,7 @@ err_dev4: unregister_sound_dsp(s->dev_dac); err_dev3: - unregister_sound_mixer(s->codec.dev_mixer); + unregister_sound_mixer(s->codec->dev_mixer); err_dev2: unregister_sound_dsp(s->dev_audio); err_dev1: @@ -2993,6 +2995,8 @@ err_irq: release_region(s->io, ES1371_EXTENT); err_region: + err_codec: + ac97_release_codec(s->codec); kfree(s); return res; } @@ -3018,9 +3022,10 @@ } release_region(s->io, ES1371_EXTENT); unregister_sound_dsp(s->dev_audio); - unregister_sound_mixer(s->codec.dev_mixer); + unregister_sound_mixer(s->codec->dev_mixer); unregister_sound_dsp(s->dev_dac); unregister_sound_midi(s->dev_midi); + ac97_release_codec(s->codec); kfree(s); pci_set_drvdata(dev, NULL); } diff -Nru a/sound/oss/esssolo1.c b/sound/oss/esssolo1.c --- a/sound/oss/esssolo1.c Mon Jun 9 16:19:42 2003 +++ b/sound/oss/esssolo1.c Fri Jul 11 08:37:47 2003 @@ -721,8 +721,8 @@ } if (cmd == SOUND_MIXER_INFO) { mixer_info info; - strlcpy(info.id, "Solo1", sizeof(info.id)); - strlcpy(info.name, "ESS Solo1", sizeof(info.name)); + strncpy(info.id, "Solo1", sizeof(info.id)); + strncpy(info.name, "ESS Solo1", sizeof(info.name)); info.modify_counter = s->mix.modcnt; if (copy_to_user((void *)arg, &info, sizeof(info))) return -EFAULT; @@ -730,8 +730,8 @@ } if (cmd == SOUND_OLD_MIXER_INFO) { _old_mixer_info info; - strlcpy(info.id, "Solo1", sizeof(info.id)); - strlcpy(info.name, "ESS Solo1", sizeof(info.name)); + strncpy(info.id, "Solo1", sizeof(info.id)); + strncpy(info.name, "ESS Solo1", sizeof(info.name)); if (copy_to_user((void *)arg, &info, sizeof(info))) return -EFAULT; return 0; @@ -1972,12 +1972,8 @@ break; if (signal_pending(current)) break; - if (file->f_flags & O_NONBLOCK) { - remove_wait_queue(&s->midi.owait, &wait); - set_current_state(TASK_RUNNING); - unlock_kernel(); - return -EBUSY; - } + if (file->f_flags & O_NONBLOCK) + break; tmo = (count * HZ) / 3100; if (!schedule_timeout(tmo ? : 1) && tmo) printk(KERN_DEBUG "solo1: midi timed out??\n"); diff -Nru a/sound/oss/forte.c b/sound/oss/forte.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/forte.c Fri Jul 11 10:53:05 2003 @@ -0,0 +1,2147 @@ +/* + * forte.c - ForteMedia FM801 OSS Driver + * + * Written by Martin K. Petersen + * Copyright (C) 2002 Hewlett-Packard Company + * Portions Copyright (C) 2003 Martin K. Petersen + * + * Latest version: http://mkp.net/forte/ + * + * Based upon the ALSA FM801 driver by Jaroslav Kysela and OSS drivers + * by Thomas Sailer, Alan Cox, Zach Brown, and Jeff Garzik. Thanks + * guys! + * + * 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. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + * + */ + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include + +#define DRIVER_NAME "forte" +#define DRIVER_VERSION "$Id: forte.c,v 1.63 2003/03/01 05:32:42 mkp Exp $" +#define PFX DRIVER_NAME ": " + +#undef M_DEBUG + +#ifdef M_DEBUG +#define DPRINTK(args...) printk(KERN_WARNING args) +#else +#define DPRINTK(args...) +#endif + +/* Card capabilities */ +#define FORTE_CAPS (DSP_CAP_MMAP | DSP_CAP_TRIGGER) + +/* Supported audio formats */ +#define FORTE_FMTS (AFMT_U8 | AFMT_S16_LE) + +/* Buffers */ +#define FORTE_MIN_FRAG_SIZE 256 +#define FORTE_MAX_FRAG_SIZE PAGE_SIZE +#define FORTE_DEF_FRAG_SIZE 256 +#define FORTE_MIN_FRAGMENTS 2 +#define FORTE_MAX_FRAGMENTS 256 +#define FORTE_DEF_FRAGMENTS 2 +#define FORTE_MIN_BUF_MSECS 500 +#define FORTE_MAX_BUF_MSECS 1000 + +/* PCI BARs */ +#define FORTE_PCM_VOL 0x00 /* PCM Output Volume */ +#define FORTE_FM_VOL 0x02 /* FM Output Volume */ +#define FORTE_I2S_VOL 0x04 /* I2S Volume */ +#define FORTE_REC_SRC 0x06 /* Record Source */ +#define FORTE_PLY_CTRL 0x08 /* Playback Control */ +#define FORTE_PLY_COUNT 0x0a /* Playback Count */ +#define FORTE_PLY_BUF1 0x0c /* Playback Buffer I */ +#define FORTE_PLY_BUF2 0x10 /* Playback Buffer II */ +#define FORTE_CAP_CTRL 0x14 /* Capture Control */ +#define FORTE_CAP_COUNT 0x16 /* Capture Count */ +#define FORTE_CAP_BUF1 0x18 /* Capture Buffer I */ +#define FORTE_CAP_BUF2 0x1c /* Capture Buffer II */ +#define FORTE_CODEC_CTRL 0x22 /* Codec Control */ +#define FORTE_I2S_MODE 0x24 /* I2S Mode Control */ +#define FORTE_VOLUME 0x26 /* Volume Up/Down/Mute Status */ +#define FORTE_I2C_CTRL 0x29 /* I2C Control */ +#define FORTE_AC97_CMD 0x2a /* AC'97 Command */ +#define FORTE_AC97_DATA 0x2c /* AC'97 Data */ +#define FORTE_MPU401_DATA 0x30 /* MPU401 Data */ +#define FORTE_MPU401_CMD 0x31 /* MPU401 Command */ +#define FORTE_GPIO_CTRL 0x52 /* General Purpose I/O Control */ +#define FORTE_GEN_CTRL 0x54 /* General Control */ +#define FORTE_IRQ_MASK 0x56 /* Interrupt Mask */ +#define FORTE_IRQ_STATUS 0x5a /* Interrupt Status */ +#define FORTE_OPL3_BANK0 0x68 /* OPL3 Status Read / Bank 0 Write */ +#define FORTE_OPL3_DATA0 0x69 /* OPL3 Data 0 Write */ +#define FORTE_OPL3_BANK1 0x6a /* OPL3 Bank 1 Write */ +#define FORTE_OPL3_DATA1 0x6b /* OPL3 Bank 1 Write */ +#define FORTE_POWERDOWN 0x70 /* Blocks Power Down Control */ + +#define FORTE_CAP_OFFSET FORTE_CAP_CTRL - FORTE_PLY_CTRL + +#define FORTE_AC97_ADDR_SHIFT 10 + +/* Playback and record control register bits */ +#define FORTE_BUF1_LAST (1<<1) +#define FORTE_BUF2_LAST (1<<2) +#define FORTE_START (1<<5) +#define FORTE_PAUSE (1<<6) +#define FORTE_IMMED_STOP (1<<7) +#define FORTE_RATE_SHIFT 8 +#define FORTE_RATE_MASK (15 << FORTE_RATE_SHIFT) +#define FORTE_CHANNELS_4 (1<<12) /* Playback only */ +#define FORTE_CHANNELS_6 (2<<12) /* Playback only */ +#define FORTE_CHANNELS_6MS (3<<12) /* Playback only */ +#define FORTE_CHANNELS_MASK (3<<12) +#define FORTE_16BIT (1<<14) +#define FORTE_STEREO (1<<15) + +/* IRQ status bits */ +#define FORTE_IRQ_PLAYBACK (1<<8) +#define FORTE_IRQ_CAPTURE (1<<9) +#define FORTE_IRQ_VOLUME (1<<14) +#define FORTE_IRQ_MPU (1<<15) + +/* CODEC control */ +#define FORTE_CC_CODEC_RESET (1<<5) +#define FORTE_CC_AC97_RESET (1<<6) + +/* AC97 cmd */ +#define FORTE_AC97_WRITE (0<<7) +#define FORTE_AC97_READ (1<<7) +#define FORTE_AC97_DP_INVALID (0<<8) +#define FORTE_AC97_DP_VALID (1<<8) +#define FORTE_AC97_PORT_RDY (0<<9) +#define FORTE_AC97_PORT_BSY (1<<9) + + +struct forte_channel { + const char *name; + + unsigned short ctrl; /* Ctrl BAR contents */ + unsigned long iobase; /* Ctrl BAR address */ + + wait_queue_head_t wait; + + void *buf; /* Buffer */ + dma_addr_t buf_handle; /* Buffer handle */ + + unsigned int record; + unsigned int format; + unsigned int rate; + unsigned int stereo; + + unsigned int frag_sz; /* Current fragment size */ + unsigned int frag_num; /* Current # of fragments */ + unsigned int frag_msecs; /* Milliseconds per frag */ + unsigned int buf_sz; /* Current buffer size */ + + unsigned int hwptr; /* Tail */ + unsigned int swptr; /* Head */ + unsigned int filled_frags; /* Fragments currently full */ + unsigned int next_buf; /* Index of next buffer */ + + unsigned int active; /* Channel currently in use */ + unsigned int mapped; /* mmap */ + + unsigned int buf_pages; /* Real size of buffer */ + unsigned int nr_irqs; /* Number of interrupts */ + unsigned int bytes; /* Total bytes */ + unsigned int residue; /* Partial fragment */ +}; + + +struct forte_chip { + struct pci_dev *pci_dev; + unsigned long iobase; + int irq; + + struct semaphore open_sem; /* Device access */ + spinlock_t lock; /* State */ + + spinlock_t ac97_lock; + struct ac97_codec *ac97; + + int multichannel; + int dsp; /* OSS handle */ + int trigger; /* mmap I/O trigger */ + + struct forte_channel play; + struct forte_channel rec; +}; + + +static int channels[] = { 2, 4, 6, }; +static int rates[] = { 5500, 8000, 9600, 11025, 16000, 19200, + 22050, 32000, 38400, 44100, 48000, }; + +static struct forte_chip *forte; +static int found; + + +/* AC97 Codec -------------------------------------------------------------- */ + + +/** + * forte_ac97_wait: + * @chip: fm801 instance whose AC97 codec to wait on + * + * FIXME: + * Stop busy-waiting + */ + +static inline int +forte_ac97_wait (struct forte_chip *chip) +{ + int i = 10000; + + while ( (inw (chip->iobase + FORTE_AC97_CMD) & FORTE_AC97_PORT_BSY) + && i-- ) + cpu_relax(); + + return i == 0; +} + + +/** + * forte_ac97_read: + * @codec: AC97 codec to read from + * @reg: register to read + */ + +u16 +forte_ac97_read (struct ac97_codec *codec, u8 reg) +{ + u16 ret = 0; + struct forte_chip *chip = codec->private_data; + + spin_lock (&chip->ac97_lock); + + /* Knock, knock */ + if (forte_ac97_wait (chip)) { + printk (KERN_ERR PFX "ac97_read: Serial bus busy\n"); + goto out; + } + + /* Send read command */ + outw (reg | (1<<7), chip->iobase + FORTE_AC97_CMD); + + if (forte_ac97_wait (chip)) { + printk (KERN_ERR PFX "ac97_read: Bus busy reading reg 0x%x\n", + reg); + goto out; + } + + /* Sanity checking */ + if (inw (chip->iobase + FORTE_AC97_CMD) & FORTE_AC97_DP_INVALID) { + printk (KERN_ERR PFX "ac97_read: Invalid data port"); + goto out; + } + + /* Fetch result */ + ret = inw (chip->iobase + FORTE_AC97_DATA); + + out: + spin_unlock (&chip->ac97_lock); + return ret; +} + + +/** + * forte_ac97_write: + * @codec: AC97 codec to send command to + * @reg: register to write + * @val: value to write + */ + +void +forte_ac97_write (struct ac97_codec *codec, u8 reg, u16 val) +{ + struct forte_chip *chip = codec->private_data; + + spin_lock (&chip->ac97_lock); + + /* Knock, knock */ + if (forte_ac97_wait (chip)) { + printk (KERN_ERR PFX "ac97_write: Serial bus busy\n"); + goto out; + } + + outw (val, chip->iobase + FORTE_AC97_DATA); + outb (reg | FORTE_AC97_WRITE, chip->iobase + FORTE_AC97_CMD); + + /* Wait for completion */ + if (forte_ac97_wait (chip)) { + printk (KERN_ERR PFX "ac97_write: Bus busy after write\n"); + goto out; + } + + out: + spin_unlock (&chip->ac97_lock); +} + + +/* Mixer ------------------------------------------------------------------- */ + + +/** + * forte_mixer_open: + * @inode: + * @file: + */ + +static int +forte_mixer_open (struct inode *inode, struct file *file) +{ + struct forte_chip *chip = forte; + file->private_data = chip->ac97; + return 0; +} + + +/** + * forte_mixer_release: + * @inode: + * @file: + */ + +static int +forte_mixer_release (struct inode *inode, struct file *file) +{ + /* We will welease Wodewick */ + return 0; +} + + +/** + * forte_mixer_ioctl: + * @inode: + * @file: + */ + +static int +forte_mixer_ioctl (struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct ac97_codec *codec = (struct ac97_codec *) file->private_data; + + return codec->mixer_ioctl (codec, cmd, arg); +} + + +static struct file_operations forte_mixer_fops = { + owner: THIS_MODULE, + llseek: no_llseek, + ioctl: forte_mixer_ioctl, + open: forte_mixer_open, + release: forte_mixer_release, +}; + + +/* Channel ----------------------------------------------------------------- */ + +/** + * forte_channel_reset: + * @channel: Channel to reset + * + * Locking: Must be called with lock held. + */ + +static void +forte_channel_reset (struct forte_channel *channel) +{ + if (!channel || !channel->iobase) + return; + + DPRINTK ("%s: channel = %s\n", __FUNCTION__, channel->name); + + channel->ctrl &= ~FORTE_START; + outw (channel->ctrl, channel->iobase + FORTE_PLY_CTRL); + + /* We always play at least two fragments, hence these defaults */ + channel->hwptr = channel->frag_sz; + channel->next_buf = 1; + channel->swptr = 0; + channel->filled_frags = 0; + channel->active = 0; + channel->bytes = 0; + channel->nr_irqs = 0; + channel->mapped = 0; + channel->residue = 0; +} + + +/** + * forte_channel_start: + * @channel: Channel to start (record/playback) + * + * Locking: Must be called with lock held. + */ + +static void inline +forte_channel_start (struct forte_channel *channel) +{ + if (!channel || !channel->iobase || channel->active) + return; + + channel->ctrl &= ~(FORTE_PAUSE | FORTE_BUF1_LAST | FORTE_BUF2_LAST + | FORTE_IMMED_STOP); + channel->ctrl |= FORTE_START; + channel->active = 1; + outw (channel->ctrl, channel->iobase + FORTE_PLY_CTRL); +} + + +/** + * forte_channel_stop: + * @channel: Channel to stop + * + * Locking: Must be called with lock held. + */ + +static void inline +forte_channel_stop (struct forte_channel *channel) +{ + if (!channel || !channel->iobase) + return; + + channel->ctrl &= ~(FORTE_START | FORTE_PAUSE); + channel->ctrl |= FORTE_IMMED_STOP; + + channel->active = 0; + outw (channel->ctrl, channel->iobase + FORTE_PLY_CTRL); +} + + +/** + * forte_channel_pause: + * @channel: Channel to pause + * + * Locking: Must be called with lock held. + */ + +static void inline +forte_channel_pause (struct forte_channel *channel) +{ + if (!channel || !channel->iobase) + return; + + channel->ctrl |= FORTE_PAUSE; + + channel->active = 0; + outw (channel->ctrl, channel->iobase + FORTE_PLY_CTRL); +} + + +/** + * forte_channel_rate: + * @channel: Channel whose rate to set. Playback and record are + * independent. + * @rate: Channel rate in Hz + * + * Locking: Must be called with lock held. + */ + +static int +forte_channel_rate (struct forte_channel *channel, unsigned int rate) +{ + int new_rate; + + if (!channel || !channel->iobase) + return -EINVAL; + + /* The FM801 only supports a handful of fixed frequencies. + * We find the value closest to what userland requested. + */ + if (rate <= 6250) { rate = 5500; new_rate = 0; } + else if (rate <= 8800) { rate = 8000; new_rate = 1; } + else if (rate <= 10312) { rate = 9600; new_rate = 2; } + else if (rate <= 13512) { rate = 11025; new_rate = 3; } + else if (rate <= 17600) { rate = 16000; new_rate = 4; } + else if (rate <= 20625) { rate = 19200; new_rate = 5; } + else if (rate <= 27025) { rate = 22050; new_rate = 6; } + else if (rate <= 35200) { rate = 32000; new_rate = 7; } + else if (rate <= 41250) { rate = 38400; new_rate = 8; } + else if (rate <= 46050) { rate = 44100; new_rate = 9; } + else { rate = 48000; new_rate = 10; } + + channel->ctrl &= ~FORTE_RATE_MASK; + channel->ctrl |= new_rate << FORTE_RATE_SHIFT; + channel->rate = rate; + + DPRINTK ("%s: %s rate = %d\n", __FUNCTION__, channel->name, rate); + + return rate; +} + + +/** + * forte_channel_format: + * @channel: Channel whose audio format to set + * @format: OSS format ID + * + * Locking: Must be called with lock held. + */ + +static int +forte_channel_format (struct forte_channel *channel, int format) +{ + if (!channel || !channel->iobase) + return -EINVAL; + + switch (format) { + + case AFMT_QUERY: + break; + + case AFMT_U8: + channel->ctrl &= ~FORTE_16BIT; + channel->format = AFMT_U8; + break; + + case AFMT_S16_LE: + default: + channel->ctrl |= FORTE_16BIT; + channel->format = AFMT_S16_LE; + break; + } + + DPRINTK ("%s: %s want %d format, got %d\n", __FUNCTION__, channel->name, + format, channel->format); + + return channel->format; +} + + +/** + * forte_channel_stereo: + * @channel: Channel to toggle + * @stereo: 0 for Mono, 1 for Stereo + * + * Locking: Must be called with lock held. + */ + +static int +forte_channel_stereo (struct forte_channel *channel, unsigned int stereo) +{ + int ret; + + if (!channel || !channel->iobase) + return -EINVAL; + + DPRINTK ("%s: %s stereo = %d\n", __FUNCTION__, channel->name, stereo); + + switch (stereo) { + + case 0: + channel->ctrl &= ~(FORTE_STEREO | FORTE_CHANNELS_MASK); + channel-> stereo = stereo; + ret = stereo; + break; + + case 1: + channel->ctrl &= ~FORTE_CHANNELS_MASK; + channel->ctrl |= FORTE_STEREO; + channel-> stereo = stereo; + ret = stereo; + break; + + default: + DPRINTK ("Unsupported channel format"); + ret = -EINVAL; + break; + } + + return ret; +} + + +/** + * forte_channel_buffer: + * @channel: Channel whose buffer to set up + * + * Locking: Must be called with lock held. + */ + +static void +forte_channel_buffer (struct forte_channel *channel, int sz, int num) +{ + unsigned int msecs, shift; + + /* Go away, I'm busy */ + if (channel->filled_frags || channel->bytes) + return; + + /* Fragment size must be a power of 2 */ + shift = 0; sz++; + while (sz >>= 1) + shift++; + channel->frag_sz = 1 << shift; + + /* Round fragment size to something reasonable */ + if (channel->frag_sz < FORTE_MIN_FRAG_SIZE) + channel->frag_sz = FORTE_MIN_FRAG_SIZE; + + if (channel->frag_sz > FORTE_MAX_FRAG_SIZE) + channel->frag_sz = FORTE_MAX_FRAG_SIZE; + + /* Find fragment length in milliseconds */ + msecs = channel->frag_sz / + (channel->format == AFMT_S16_LE ? 2 : 1) / + (channel->stereo ? 2 : 1) / + (channel->rate / 1000); + + channel->frag_msecs = msecs; + + /* Pick a suitable number of fragments */ + if (msecs * num < FORTE_MIN_BUF_MSECS) + num = FORTE_MIN_BUF_MSECS / msecs; + + if (msecs * num > FORTE_MAX_BUF_MSECS) + num = FORTE_MAX_BUF_MSECS / msecs; + + /* Fragment number must be a power of 2 */ + shift = 0; + while (num >>= 1) + shift++; + channel->frag_num = 1 << (shift + 1); + + /* Round fragment number to something reasonable */ + if (channel->frag_num < FORTE_MIN_FRAGMENTS) + channel->frag_num = FORTE_MIN_FRAGMENTS; + + if (channel->frag_num > FORTE_MAX_FRAGMENTS) + channel->frag_num = FORTE_MAX_FRAGMENTS; + + channel->buf_sz = channel->frag_sz * channel->frag_num; + + DPRINTK ("%s: %s frag_sz = %d, frag_num = %d, buf_sz = %d\n", + __FUNCTION__, channel->name, channel->frag_sz, + channel->frag_num, channel->buf_sz); +} + + +/** + * forte_channel_prep: + * @channel: Channel whose buffer to prepare + * + * Locking: Lock held. + */ + +static void +forte_channel_prep (struct forte_channel *channel) +{ + struct page *page; + int i; + + if (channel->buf) + return; + + forte_channel_buffer (channel, channel->frag_sz, channel->frag_num); + channel->buf_pages = channel->buf_sz >> PAGE_SHIFT; + + if (channel->buf_sz % PAGE_SIZE) + channel->buf_pages++; + + DPRINTK ("%s: %s frag_sz = %d, frag_num = %d, buf_sz = %d, pg = %d\n", + __FUNCTION__, channel->name, channel->frag_sz, + channel->frag_num, channel->buf_sz, channel->buf_pages); + + /* DMA buffer */ + channel->buf = pci_alloc_consistent (forte->pci_dev, + channel->buf_pages * PAGE_SIZE, + &channel->buf_handle); + + if (!channel->buf || !channel->buf_handle) + BUG(); + + page = virt_to_page (channel->buf); + + /* FIXME: can this go away ? */ + for (i = 0 ; i < channel->buf_pages ; i++) + SetPageReserved(page++); + + /* Prep buffer registers */ + outw (channel->frag_sz - 1, channel->iobase + FORTE_PLY_COUNT); + outl (channel->buf_handle, channel->iobase + FORTE_PLY_BUF1); + outl (channel->buf_handle + channel->frag_sz, + channel->iobase + FORTE_PLY_BUF2); + + /* Reset hwptr */ + channel->hwptr = channel->frag_sz; + channel->next_buf = 1; + + DPRINTK ("%s: %s buffer @ %p (%p)\n", __FUNCTION__, channel->name, + channel->buf, channel->buf_handle); +} + + +/** + * forte_channel_drain: + * @chip: + * @channel: + * + * Locking: Don't hold the lock. + */ + +static inline int +forte_channel_drain (struct forte_channel *channel) +{ + DECLARE_WAITQUEUE (wait, current); + unsigned long flags; + + DPRINTK ("%s\n", __FUNCTION__); + + if (channel->mapped) { + spin_lock_irqsave (&forte->lock, flags); + forte_channel_stop (channel); + spin_unlock_irqrestore (&forte->lock, flags); + return 0; + } + + spin_lock_irqsave (&forte->lock, flags); + add_wait_queue (&channel->wait, &wait); + + for (;;) { + if (channel->active == 0 || channel->filled_frags == 1) + break; + + spin_unlock_irqrestore (&forte->lock, flags); + + __set_current_state (TASK_INTERRUPTIBLE); + schedule(); + + spin_lock_irqsave (&forte->lock, flags); + } + + forte_channel_stop (channel); + forte_channel_reset (channel); + set_current_state (TASK_RUNNING); + remove_wait_queue (&channel->wait, &wait); + spin_unlock_irqrestore (&forte->lock, flags); + + return 0; +} + + +/** + * forte_channel_init: + * @chip: Forte chip instance the channel hangs off + * @channel: Channel to initialize + * + * Description: + * Initializes a channel, sets defaults, and allocates + * buffers. + * + * Locking: No lock held. + */ + +static int +forte_channel_init (struct forte_chip *chip, struct forte_channel *channel) +{ + DPRINTK ("%s: chip iobase @ %p\n", __FUNCTION__, (void *)chip->iobase); + + spin_lock_irq (&chip->lock); + memset (channel, 0x0, sizeof (*channel)); + + if (channel == &chip->play) { + channel->name = "PCM_OUT"; + channel->iobase = chip->iobase; + DPRINTK ("%s: PCM-OUT iobase @ %p\n", __FUNCTION__, + (void *) channel->iobase); + } + else if (channel == &chip->rec) { + channel->name = "PCM_IN"; + channel->iobase = chip->iobase + FORTE_CAP_OFFSET; + channel->record = 1; + DPRINTK ("%s: PCM-IN iobase @ %p\n", __FUNCTION__, + (void *) channel->iobase); + } + else + BUG(); + + init_waitqueue_head (&channel->wait); + + /* Defaults: 48kHz, 16-bit, stereo */ + channel->ctrl = inw (channel->iobase + FORTE_PLY_CTRL); + forte_channel_reset (channel); + forte_channel_stereo (channel, 1); + forte_channel_format (channel, AFMT_S16_LE); + forte_channel_rate (channel, 48000); + channel->frag_sz = FORTE_DEF_FRAG_SIZE; + channel->frag_num = FORTE_DEF_FRAGMENTS; + + chip->trigger = 0; + spin_unlock_irq (&chip->lock); + + return 0; +} + + +/** + * forte_channel_free: + * @chip: Chip this channel hangs off + * @channel: Channel to nuke + * + * Description: + * Resets channel and frees buffers. + * + * Locking: Hold your horses. + */ + +static void +forte_channel_free (struct forte_chip *chip, struct forte_channel *channel) +{ + DPRINTK ("%s: %s\n", __FUNCTION__, channel->name); + + if (!channel->buf_handle) + return; + + pci_free_consistent (chip->pci_dev, channel->buf_pages * PAGE_SIZE, + channel->buf, channel->buf_handle); + + memset (channel, 0x0, sizeof (*channel)); +} + + +/* DSP --------------------------------------------------------------------- */ + + +/** + * forte_dsp_ioctl: + */ + +static int +forte_dsp_ioctl (struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg) +{ + int ival=0, ret, rval=0, rd, wr, count; + struct forte_chip *chip; + struct audio_buf_info abi; + struct count_info cinfo; + + chip = file->private_data; + + if (file->f_mode & FMODE_WRITE) + wr = 1; + else + wr = 0; + + if (file->f_mode & FMODE_READ) + rd = 1; + else + rd = 0; + + switch (cmd) { + + case OSS_GETVERSION: + return put_user (SOUND_VERSION, (int *) arg); + + case SNDCTL_DSP_GETCAPS: + DPRINTK ("%s: GETCAPS\n", __FUNCTION__); + + ival = FORTE_CAPS; /* DUPLEX */ + return put_user (ival, (int *) arg); + + case SNDCTL_DSP_GETFMTS: + DPRINTK ("%s: GETFMTS\n", __FUNCTION__); + + ival = FORTE_FMTS; /* U8, 16LE */ + return put_user (ival, (int *) arg); + + case SNDCTL_DSP_SETFMT: /* U8, 16LE */ + DPRINTK ("%s: SETFMT\n", __FUNCTION__); + + if (get_user (ival, (int *) arg)) + return -EFAULT; + + spin_lock_irq (&chip->lock); + + if (rd) { + forte_channel_stop (&chip->rec); + rval = forte_channel_format (&chip->rec, ival); + } + + if (wr) { + forte_channel_stop (&chip->rec); + rval = forte_channel_format (&chip->play, ival); + } + + spin_unlock_irq (&chip->lock); + + return put_user (rval, (int *) arg); + + case SNDCTL_DSP_STEREO: /* 0 - mono, 1 - stereo */ + DPRINTK ("%s: STEREO\n", __FUNCTION__); + + if (get_user (ival, (int *) arg)) + return -EFAULT; + + spin_lock_irq (&chip->lock); + + if (rd) { + forte_channel_stop (&chip->rec); + rval = forte_channel_stereo (&chip->rec, ival); + } + + if (wr) { + forte_channel_stop (&chip->rec); + rval = forte_channel_stereo (&chip->play, ival); + } + + spin_unlock_irq (&chip->lock); + + return put_user (rval, (int *) arg); + + case SNDCTL_DSP_CHANNELS: /* 1 - mono, 2 - stereo */ + DPRINTK ("%s: CHANNELS\n", __FUNCTION__); + + if (get_user (ival, (int *) arg)) + return -EFAULT; + + spin_lock_irq (&chip->lock); + + if (rd) { + forte_channel_stop (&chip->rec); + rval = forte_channel_stereo (&chip->rec, ival-1) + 1; + } + + if (wr) { + forte_channel_stop (&chip->play); + rval = forte_channel_stereo (&chip->play, ival-1) + 1; + } + + spin_unlock_irq (&chip->lock); + + return put_user (rval, (int *) arg); + + case SNDCTL_DSP_SPEED: + DPRINTK ("%s: SPEED\n", __FUNCTION__); + + if (get_user (ival, (int *) arg)) + return -EFAULT; + + spin_lock_irq (&chip->lock); + + if (rd) { + forte_channel_stop (&chip->rec); + rval = forte_channel_rate (&chip->rec, ival); + } + + if (wr) { + forte_channel_stop (&chip->play); + rval = forte_channel_rate (&chip->play, ival); + } + + spin_unlock_irq (&chip->lock); + + return put_user(rval, (int*) arg); + + case SNDCTL_DSP_GETBLKSIZE: + DPRINTK ("%s: GETBLKSIZE\n", __FUNCTION__); + + spin_lock_irq (&chip->lock); + + if (rd) + ival = chip->rec.frag_sz; + + if (wr) + ival = chip->play.frag_sz; + + spin_unlock_irq (&chip->lock); + + return put_user (ival, (int *) arg); + + case SNDCTL_DSP_RESET: + DPRINTK ("%s: RESET\n", __FUNCTION__); + + spin_lock_irq (&chip->lock); + + if (rd) + forte_channel_reset (&chip->rec); + + if (wr) + forte_channel_reset (&chip->play); + + spin_unlock_irq (&chip->lock); + + return 0; + + case SNDCTL_DSP_SYNC: + DPRINTK ("%s: SYNC\n", __FUNCTION__); + + if (wr) + ret = forte_channel_drain (&chip->play); + + return 0; + + case SNDCTL_DSP_POST: + DPRINTK ("%s: POST\n", __FUNCTION__); + + if (wr) { + spin_lock_irq (&chip->lock); + + if (chip->play.filled_frags) + forte_channel_start (&chip->play); + + spin_unlock_irq (&chip->lock); + } + + return 0; + + case SNDCTL_DSP_SETFRAGMENT: + DPRINTK ("%s: SETFRAGMENT\n", __FUNCTION__); + + if (get_user (ival, (int *) arg)) + return -EFAULT; + + spin_lock_irq (&chip->lock); + + if (rd) { + forte_channel_buffer (&chip->rec, ival & 0xffff, + (ival >> 16) & 0xffff); + ival = (chip->rec.frag_num << 16) + chip->rec.frag_sz; + } + + if (wr) { + forte_channel_buffer (&chip->play, ival & 0xffff, + (ival >> 16) & 0xffff); + ival = (chip->play.frag_num << 16) +chip->play.frag_sz; + } + + spin_unlock_irq (&chip->lock); + + return put_user (ival, (int *) arg); + + case SNDCTL_DSP_GETISPACE: + DPRINTK ("%s: GETISPACE\n", __FUNCTION__); + + if (!rd) + return -EINVAL; + + spin_lock_irq (&chip->lock); + + abi.fragstotal = chip->rec.frag_num; + abi.fragsize = chip->rec.frag_sz; + + if (chip->rec.mapped) { + abi.fragments = chip->rec.frag_num - 2; + abi.bytes = abi.fragments * abi.fragsize; + } + else { + abi.fragments = chip->rec.filled_frags; + abi.bytes = abi.fragments * abi.fragsize; + } + + spin_unlock_irq (&chip->lock); + + return copy_to_user ((void *) arg, &abi, sizeof (abi)); + + case SNDCTL_DSP_GETIPTR: + DPRINTK ("%s: GETIPTR\n", __FUNCTION__); + + if (!rd) + return -EINVAL; + + spin_lock_irq (&chip->lock); + + if (chip->rec.active) + cinfo.ptr = chip->rec.hwptr; + else + cinfo.ptr = 0; + + cinfo.bytes = chip->rec.bytes; + cinfo.blocks = chip->rec.nr_irqs; + chip->rec.nr_irqs = 0; + + spin_unlock_irq (&chip->lock); + + return copy_to_user ((void *) arg, &cinfo, sizeof (cinfo)); + + case SNDCTL_DSP_GETOSPACE: + if (!wr) + return -EINVAL; + + spin_lock_irq (&chip->lock); + + abi.fragstotal = chip->play.frag_num; + abi.fragsize = chip->play.frag_sz; + + if (chip->play.mapped) { + abi.fragments = chip->play.frag_num - 2; + abi.bytes = chip->play.buf_sz; + } + else { + abi.fragments = chip->play.frag_num - + chip->play.filled_frags; + + if (chip->play.residue) + abi.fragments--; + + abi.bytes = abi.fragments * abi.fragsize + + chip->play.residue; + } + + spin_unlock_irq (&chip->lock); + + return copy_to_user ((void *) arg, &abi, sizeof (abi)); + + case SNDCTL_DSP_GETOPTR: + if (!wr) + return -EINVAL; + + spin_lock_irq (&chip->lock); + + if (chip->play.active) + cinfo.ptr = chip->play.hwptr; + else + cinfo.ptr = 0; + + cinfo.bytes = chip->play.bytes; + cinfo.blocks = chip->play.nr_irqs; + chip->play.nr_irqs = 0; + + spin_unlock_irq (&chip->lock); + + return copy_to_user ((void *) arg, &cinfo, sizeof (cinfo)); + + case SNDCTL_DSP_GETODELAY: + if (!wr) + return -EINVAL; + + spin_lock_irq (&chip->lock); + + if (!chip->play.active) { + ival = 0; + } + else if (chip->play.mapped) { + count = inw (chip->play.iobase + FORTE_PLY_COUNT) + 1; + ival = chip->play.frag_sz - count; + } + else { + ival = chip->play.filled_frags * chip->play.frag_sz; + + if (chip->play.residue) + ival += chip->play.frag_sz - chip->play.residue; + } + + spin_unlock_irq (&chip->lock); + + return put_user (ival, (int *) arg); + + case SNDCTL_DSP_SETDUPLEX: + DPRINTK ("%s: SETDUPLEX\n", __FUNCTION__); + + return -EINVAL; + + case SNDCTL_DSP_GETTRIGGER: + DPRINTK ("%s: GETTRIGGER\n", __FUNCTION__); + + return put_user (chip->trigger, (int *) arg); + + case SNDCTL_DSP_SETTRIGGER: + + if (get_user (ival, (int *) arg)) + return -EFAULT; + + DPRINTK ("%s: SETTRIGGER %d\n", __FUNCTION__, ival); + + if (wr) { + spin_lock_irq (&chip->lock); + + if (ival & PCM_ENABLE_OUTPUT) + forte_channel_start (&chip->play); + else { + chip->trigger = 1; + forte_channel_prep (&chip->play); + forte_channel_stop (&chip->play); + } + + spin_unlock_irq (&chip->lock); + } + else if (rd) { + spin_lock_irq (&chip->lock); + + if (ival & PCM_ENABLE_INPUT) + forte_channel_start (&chip->rec); + else { + chip->trigger = 1; + forte_channel_prep (&chip->rec); + forte_channel_stop (&chip->rec); + } + + spin_unlock_irq (&chip->lock); + } + + return 0; + + case SOUND_PCM_READ_RATE: + DPRINTK ("%s: PCM_READ_RATE\n", __FUNCTION__); + return put_user (chip->play.rate, (int *) arg); + + case SOUND_PCM_READ_CHANNELS: + DPRINTK ("%s: PCM_READ_CHANNELS\n", __FUNCTION__); + return put_user (chip->play.stereo, (int *) arg); + + case SOUND_PCM_READ_BITS: + DPRINTK ("%s: PCM_READ_BITS\n", __FUNCTION__); + return put_user (chip->play.format, (int *) arg); + + case SNDCTL_DSP_NONBLOCK: + DPRINTK ("%s: DSP_NONBLOCK\n", __FUNCTION__); + file->f_flags |= O_NONBLOCK; + return 0; + + default: + DPRINTK ("Unsupported ioctl: %x (%p)\n", cmd, (void *) arg); + break; + } + + return -EINVAL; +} + + +/** + * forte_dsp_open: + */ + +static int +forte_dsp_open (struct inode *inode, struct file *file) +{ + struct forte_chip *chip = forte; /* FIXME: HACK FROM HELL! */ + + if (file->f_flags & O_NONBLOCK) { + if (down_trylock (&chip->open_sem)) { + DPRINTK ("%s: returning -EAGAIN\n", __FUNCTION__); + return -EAGAIN; + } + } + else { + if (down_interruptible (&chip->open_sem)) { + DPRINTK ("%s: returning -ERESTARTSYS\n", __FUNCTION__); + return -ERESTARTSYS; + } + } + + file->private_data = forte; + + DPRINTK ("%s: dsp opened by %d\n", __FUNCTION__, current->pid); + + if (file->f_mode & FMODE_WRITE) + forte_channel_init (forte, &forte->play); + + if (file->f_mode & FMODE_READ) + forte_channel_init (forte, &forte->rec); + + return 0; +} + + +/** + * forte_dsp_release: + */ + +static int +forte_dsp_release (struct inode *inode, struct file *file) +{ + struct forte_chip *chip = file->private_data; + int ret = 0; + + DPRINTK ("%s: chip @ %p\n", __FUNCTION__, chip); + + if (file->f_mode & FMODE_WRITE) { + forte_channel_drain (&chip->play); + + spin_lock_irq (&chip->lock); + + forte_channel_free (chip, &chip->play); + + spin_unlock_irq (&chip->lock); + } + + if (file->f_mode & FMODE_READ) { + while (chip->rec.filled_frags > 0) + interruptible_sleep_on (&chip->rec.wait); + + spin_lock_irq (&chip->lock); + + forte_channel_stop (&chip->rec); + forte_channel_free (chip, &chip->rec); + + spin_unlock_irq (&chip->lock); + } + + up (&chip->open_sem); + + return ret; +} + + +/** + * forte_dsp_poll: + * + */ + +static unsigned int +forte_dsp_poll (struct file *file, struct poll_table_struct *wait) +{ + struct forte_chip *chip; + struct forte_channel *channel; + unsigned int mask = 0; + + chip = file->private_data; + + if (file->f_mode & FMODE_WRITE) { + channel = &chip->play; + + if (channel->active) + poll_wait (file, &channel->wait, wait); + + spin_lock_irq (&chip->lock); + + if (channel->frag_num - channel->filled_frags > 0) + mask |= POLLOUT | POLLWRNORM; + + spin_unlock_irq (&chip->lock); + } + + if (file->f_mode & FMODE_READ) { + channel = &chip->rec; + + if (channel->active) + poll_wait (file, &channel->wait, wait); + + spin_lock_irq (&chip->lock); + + if (channel->filled_frags > 0) + mask |= POLLIN | POLLRDNORM; + + spin_unlock_irq (&chip->lock); + } + + return mask; +} + + +/** + * forte_dsp_mmap: + */ + +static int +forte_dsp_mmap (struct file *file, struct vm_area_struct *vma) +{ + struct forte_chip *chip; + struct forte_channel *channel; + unsigned long size; + int ret; + + chip = file->private_data; + + DPRINTK ("%s: start %lXh, size %ld, pgoff %ld\n", __FUNCTION__, + vma->vm_start, vma->vm_end - vma->vm_start, vma->vm_pgoff); + + spin_lock_irq (&chip->lock); + + if (vma->vm_flags & VM_WRITE && chip->play.active) { + ret = -EBUSY; + goto out; + } + + if (vma->vm_flags & VM_READ && chip->rec.active) { + ret = -EBUSY; + goto out; + } + + if (file->f_mode & FMODE_WRITE) + channel = &chip->play; + else if (file->f_mode & FMODE_READ) + channel = &chip->rec; + else { + ret = -EINVAL; + goto out; + } + + forte_channel_prep (channel); + channel->mapped = 1; + + if (vma->vm_pgoff != 0) { + ret = -EINVAL; + goto out; + } + + size = vma->vm_end - vma->vm_start; + + if (size > channel->buf_pages * PAGE_SIZE) { + DPRINTK ("%s: size (%ld) > buf_sz (%d) \n", __FUNCTION__, + size, channel->buf_sz); + ret = -EINVAL; + goto out; + } + + if (remap_page_range (vma, vma->vm_start, virt_to_phys (channel->buf), + size, vma->vm_page_prot)) { + DPRINTK ("%s: remap el a no worko\n", __FUNCTION__); + ret = -EAGAIN; + goto out; + } + + ret = 0; + + out: + spin_unlock_irq (&chip->lock); + return ret; +} + + +/** + * forte_dsp_write: + */ + +static ssize_t +forte_dsp_write (struct file *file, const char *buffer, size_t bytes, + loff_t *ppos) +{ + struct forte_chip *chip; + struct forte_channel *channel; + unsigned int i = bytes, sz = 0; + unsigned long flags; + + if (ppos != &file->f_pos) + return -ESPIPE; + + if (!access_ok (VERIFY_READ, buffer, bytes)) + return -EFAULT; + + chip = (struct forte_chip *) file->private_data; + + if (!chip) + BUG(); + + channel = &chip->play; + + if (!channel) + BUG(); + + spin_lock_irqsave (&chip->lock, flags); + + /* Set up buffers with the right fragment size */ + forte_channel_prep (channel); + + while (i) { + /* All fragment buffers in use -> wait */ + if (channel->frag_num - channel->filled_frags == 0) { + DECLARE_WAITQUEUE (wait, current); + + /* For trigger or non-blocking operation, get out */ + if (chip->trigger || file->f_flags & O_NONBLOCK) { + spin_unlock_irqrestore (&chip->lock, flags); + return -EAGAIN; + } + + /* Otherwise wait for buffers */ + add_wait_queue (&channel->wait, &wait); + + for (;;) { + spin_unlock_irqrestore (&chip->lock, flags); + + set_current_state (TASK_INTERRUPTIBLE); + schedule(); + + spin_lock_irqsave (&chip->lock, flags); + + if (channel->frag_num - channel->filled_frags) + break; + } + + remove_wait_queue (&channel->wait, &wait); + set_current_state (TASK_RUNNING); + + if (signal_pending (current)) { + spin_unlock_irqrestore (&chip->lock, flags); + return -ERESTARTSYS; + } + } + + if (channel->residue) + sz = channel->residue; + else if (i > channel->frag_sz) + sz = channel->frag_sz; + else + sz = i; + + spin_unlock_irqrestore (&chip->lock, flags); + + if (copy_from_user ((void *) channel->buf + channel->swptr, buffer, sz)) + return -EFAULT; + + spin_lock_irqsave (&chip->lock, flags); + + /* Advance software pointer */ + buffer += sz; + channel->swptr += sz; + channel->swptr %= channel->buf_sz; + i -= sz; + + /* Only bump filled_frags if a full fragment has been written */ + if (channel->swptr % channel->frag_sz == 0) { + channel->filled_frags++; + channel->residue = 0; + } + else + channel->residue = channel->frag_sz - sz; + + /* If playback isn't active, start it */ + if (channel->active == 0 && chip->trigger == 0) + forte_channel_start (channel); + } + + spin_unlock_irqrestore (&chip->lock, flags); + + return bytes - i; +} + + +/** + * forte_dsp_read: + */ + +static ssize_t +forte_dsp_read (struct file *file, char *buffer, size_t bytes, + loff_t *ppos) +{ + struct forte_chip *chip; + struct forte_channel *channel; + unsigned int i = bytes, sz; + unsigned long flags; + + if (ppos != &file->f_pos) + return -ESPIPE; + + if (!access_ok (VERIFY_WRITE, buffer, bytes)) + return -EFAULT; + + chip = (struct forte_chip *) file->private_data; + + if (!chip) + BUG(); + + channel = &chip->rec; + + if (!channel) + BUG(); + + spin_lock_irqsave (&chip->lock, flags); + + /* Set up buffers with the right fragment size */ + forte_channel_prep (channel); + + /* Start recording */ + if (!chip->trigger) + forte_channel_start (channel); + + while (i) { + /* No fragment buffers in use -> wait */ + if (channel->filled_frags == 0) { + DECLARE_WAITQUEUE (wait, current); + + /* For trigger mode operation, get out */ + if (chip->trigger) { + spin_unlock_irqrestore (&chip->lock, flags); + return -EAGAIN; + } + + add_wait_queue (&channel->wait, &wait); + + for (;;) { + if (channel->active == 0) + break; + + if (channel->filled_frags) + break; + + spin_unlock_irqrestore (&chip->lock, flags); + + set_current_state (TASK_INTERRUPTIBLE); + schedule(); + + spin_lock_irqsave (&chip->lock, flags); + } + + set_current_state (TASK_RUNNING); + remove_wait_queue (&channel->wait, &wait); + } + + if (i > channel->frag_sz) + sz = channel->frag_sz; + else + sz = i; + + spin_unlock_irqrestore (&chip->lock, flags); + + if (copy_to_user (buffer, (void *)channel->buf+channel->swptr, sz)) { + DPRINTK ("%s: copy_to_user failed\n", __FUNCTION__); + return -EFAULT; + } + + spin_lock_irqsave (&chip->lock, flags); + + /* Advance software pointer */ + buffer += sz; + if (channel->filled_frags > 0) + channel->filled_frags--; + channel->swptr += channel->frag_sz; + channel->swptr %= channel->buf_sz; + i -= sz; + } + + spin_unlock_irqrestore (&chip->lock, flags); + + return bytes - i; +} + + +static struct file_operations forte_dsp_fops = { + owner: THIS_MODULE, + llseek: &no_llseek, + read: &forte_dsp_read, + write: &forte_dsp_write, + poll: &forte_dsp_poll, + ioctl: &forte_dsp_ioctl, + open: &forte_dsp_open, + release: &forte_dsp_release, + mmap: &forte_dsp_mmap, +}; + + +/* Common ------------------------------------------------------------------ */ + + +/** + * forte_interrupt: + */ + +static irqreturn_t +forte_interrupt (int irq, void *dev_id, struct pt_regs *regs) +{ + struct forte_chip *chip = dev_id; + struct forte_channel *channel = NULL; + u16 status, count; + + status = inw (chip->iobase + FORTE_IRQ_STATUS); + + /* If this is not for us, get outta here ASAP */ + if ((status & (FORTE_IRQ_PLAYBACK | FORTE_IRQ_CAPTURE)) == 0) + return IRQ_NONE; + + if (status & FORTE_IRQ_PLAYBACK) { + channel = &chip->play; + + spin_lock (&chip->lock); + + if (channel->frag_sz == 0) + goto pack; + + /* Declare a fragment done */ + if (channel->filled_frags > 0) + channel->filled_frags--; + channel->bytes += channel->frag_sz; + channel->nr_irqs++; + + /* Flip-flop between buffer I and II */ + channel->next_buf ^= 1; + + /* Advance hardware pointer by fragment size and wrap around */ + channel->hwptr += channel->frag_sz; + channel->hwptr %= channel->buf_sz; + + /* Buffer I or buffer II BAR */ + outl (channel->buf_handle + channel->hwptr, + channel->next_buf == 0 ? + channel->iobase + FORTE_PLY_BUF1 : + channel->iobase + FORTE_PLY_BUF2); + + /* If the currently playing fragment is last, schedule pause */ + if (channel->filled_frags == 1) + forte_channel_pause (channel); + + pack: + /* Acknowledge interrupt */ + outw (FORTE_IRQ_PLAYBACK, chip->iobase + FORTE_IRQ_STATUS); + + if (waitqueue_active (&channel->wait)) + wake_up_all (&channel->wait); + + spin_unlock (&chip->lock); + } + + if (status & FORTE_IRQ_CAPTURE) { + channel = &chip->rec; + spin_lock (&chip->lock); + + /* One fragment filled */ + channel->filled_frags++; + + /* Get # of completed bytes */ + count = inw (channel->iobase + FORTE_PLY_COUNT) + 1; + + if (count == 0) { + DPRINTK ("%s: last, filled_frags = %d\n", __FUNCTION__, + channel->filled_frags); + channel->filled_frags = 0; + goto rack; + } + + /* Buffer I or buffer II BAR */ + outl (channel->buf_handle + channel->hwptr, + channel->next_buf == 0 ? + channel->iobase + FORTE_PLY_BUF1 : + channel->iobase + FORTE_PLY_BUF2); + + /* Flip-flop between buffer I and II */ + channel->next_buf ^= 1; + + /* Advance hardware pointer by fragment size and wrap around */ + channel->hwptr += channel->frag_sz; + channel->hwptr %= channel->buf_sz; + + /* Out of buffers */ + if (channel->filled_frags == channel->frag_num - 1) + forte_channel_stop (channel); + rack: + /* Acknowledge interrupt */ + outw (FORTE_IRQ_CAPTURE, chip->iobase + FORTE_IRQ_STATUS); + + spin_unlock (&chip->lock); + + if (waitqueue_active (&channel->wait)) + wake_up_all (&channel->wait); + } + + return IRQ_HANDLED; +} + + +/** + * forte_proc_read: + */ + +static int +forte_proc_read (char *page, char **start, off_t off, int count, + int *eof, void *data) +{ + int i = 0, p_rate, p_chan, r_rate; + unsigned short p_reg, r_reg; + + i += sprintf (page, "ForteMedia FM801 OSS Lite driver\n%s\n \n", + DRIVER_VERSION); + + if (!forte->iobase) + return i; + + p_rate = p_chan = -1; + p_reg = inw (forte->iobase + FORTE_PLY_CTRL); + p_rate = (p_reg >> 8) & 15; + p_chan = (p_reg >> 12) & 3; + + if (p_rate >= 0 || p_rate <= 10) + p_rate = rates[p_rate]; + + if (p_chan >= 0 || p_chan <= 2) + p_chan = channels[p_chan]; + + r_rate = -1; + r_reg = inw (forte->iobase + FORTE_CAP_CTRL); + r_rate = (r_reg >> 8) & 15; + + if (r_rate >= 0 || r_rate <= 10) + r_rate = rates[r_rate]; + + i += sprintf (page + i, + " Playback Capture\n" + "FIFO empty : %-3s %-3s\n" + "Buf1 Last : %-3s %-3s\n" + "Buf2 Last : %-3s %-3s\n" + "Started : %-3s %-3s\n" + "Paused : %-3s %-3s\n" + "Immed Stop : %-3s %-3s\n" + "Rate : %-5d %-5d\n" + "Channels : %-5d -\n" + "16-bit : %-3s %-3s\n" + "Stereo : %-3s %-3s\n" + " \n" + "Buffer Sz : %-6d %-6d\n" + "Frag Sz : %-6d %-6d\n" + "Frag Num : %-6d %-6d\n" + "Frag msecs : %-6d %-6d\n" + "Used Frags : %-6d %-6d\n" + "Mapped : %-3s %-3s\n", + p_reg & 1<<0 ? "yes" : "no", + r_reg & 1<<0 ? "yes" : "no", + p_reg & 1<<1 ? "yes" : "no", + r_reg & 1<<1 ? "yes" : "no", + p_reg & 1<<2 ? "yes" : "no", + r_reg & 1<<2 ? "yes" : "no", + p_reg & 1<<5 ? "yes" : "no", + r_reg & 1<<5 ? "yes" : "no", + p_reg & 1<<6 ? "yes" : "no", + r_reg & 1<<6 ? "yes" : "no", + p_reg & 1<<7 ? "yes" : "no", + r_reg & 1<<7 ? "yes" : "no", + p_rate, r_rate, + p_chan, + p_reg & 1<<14 ? "yes" : "no", + r_reg & 1<<14 ? "yes" : "no", + p_reg & 1<<15 ? "yes" : "no", + r_reg & 1<<15 ? "yes" : "no", + forte->play.buf_sz, forte->rec.buf_sz, + forte->play.frag_sz, forte->rec.frag_sz, + forte->play.frag_num, forte->rec.frag_num, + forte->play.frag_msecs, forte->rec.frag_msecs, + forte->play.filled_frags, forte->rec.filled_frags, + forte->play.mapped ? "yes" : "no", + forte->rec.mapped ? "yes" : "no" + ); + + return i; +} + + +/** + * forte_proc_init: + * + * Creates driver info entries in /proc + */ + +static int __init +forte_proc_init (void) +{ + if (!proc_mkdir ("driver/forte", 0)) + return -EIO; + + if (!create_proc_read_entry ("driver/forte/chip", 0, 0, forte_proc_read, forte)) { + remove_proc_entry ("driver/forte", NULL); + return -EIO; + } + + if (!create_proc_read_entry("driver/forte/ac97", 0, 0, ac97_read_proc, forte->ac97)) { + remove_proc_entry ("driver/forte/chip", NULL); + remove_proc_entry ("driver/forte", NULL); + return -EIO; + } + + return 0; +} + + +/** + * forte_proc_remove: + * + * Removes driver info entries in /proc + */ + +static void +forte_proc_remove (void) +{ + remove_proc_entry ("driver/forte/ac97", NULL); + remove_proc_entry ("driver/forte/chip", NULL); + remove_proc_entry ("driver/forte", NULL); +} + + +/** + * forte_chip_init: + * @chip: Chip instance to initialize + * + * Description: + * Resets chip, configures codec and registers the driver with + * the sound subsystem. + * + * Press and hold Start for 8 secs, then switch on Run + * and hold for 4 seconds. Let go of Start. Numbers + * assume a properly oiled TWG. + */ + +static int __devinit +forte_chip_init (struct forte_chip *chip) +{ + u8 revision; + u16 cmdw; + struct ac97_codec *codec; + + pci_read_config_byte (chip->pci_dev, PCI_REVISION_ID, &revision); + + if (revision >= 0xB1) { + chip->multichannel = 1; + printk (KERN_INFO PFX "Multi-channel device detected.\n"); + } + + /* Reset chip */ + outw (FORTE_CC_CODEC_RESET | FORTE_CC_AC97_RESET, + chip->iobase + FORTE_CODEC_CTRL); + udelay(100); + outw (0, chip->iobase + FORTE_CODEC_CTRL); + + /* Request read from AC97 */ + outw (FORTE_AC97_READ | (0 << FORTE_AC97_ADDR_SHIFT), + chip->iobase + FORTE_AC97_CMD); + mdelay(750); + + if ((inw (chip->iobase + FORTE_AC97_CMD) & (3<<8)) != (1<<8)) { + printk (KERN_INFO PFX "AC97 codec not responding"); + return -EIO; + } + + /* Init volume */ + outw (0x0808, chip->iobase + FORTE_PCM_VOL); + outw (0x9f1f, chip->iobase + FORTE_FM_VOL); + outw (0x8808, chip->iobase + FORTE_I2S_VOL); + + /* I2S control - I2S mode */ + outw (0x0003, chip->iobase + FORTE_I2S_MODE); + + /* Interrupt setup - unmask PLAYBACK & CAPTURE */ + cmdw = inw (chip->iobase + FORTE_IRQ_MASK); + cmdw &= ~0x0003; + outw (cmdw, chip->iobase + FORTE_IRQ_MASK); + + /* Interrupt clear */ + outw (FORTE_IRQ_PLAYBACK|FORTE_IRQ_CAPTURE, + chip->iobase + FORTE_IRQ_STATUS); + + /* Set up the AC97 codec */ + if ((codec = ac97_alloc_codec()) == NULL) + return -ENOMEM; + codec->private_data = chip; + codec->codec_read = forte_ac97_read; + codec->codec_write = forte_ac97_write; + codec->id = 0; + + if (ac97_probe_codec (codec) == 0) { + printk (KERN_ERR PFX "codec probe failed\n"); + ac97_release_codec(codec); + return -1; + } + + /* Register mixer */ + if ((codec->dev_mixer = + register_sound_mixer (&forte_mixer_fops, -1)) < 0) { + printk (KERN_ERR PFX "couldn't register mixer!\n"); + ac97_release_codec(codec); + return -1; + } + + chip->ac97 = codec; + + /* Register DSP */ + if ((chip->dsp = register_sound_dsp (&forte_dsp_fops, -1) ) < 0) { + printk (KERN_ERR PFX "couldn't register dsp!\n"); + return -1; + } + + /* Register with /proc */ + if (forte_proc_init()) { + printk (KERN_ERR PFX "couldn't add entries to /proc!\n"); + return -1; + } + + return 0; +} + + +/** + * forte_probe: + * @pci_dev: PCI struct for probed device + * @pci_id: + * + * Description: + * Allocates chip instance, I/O region, and IRQ + */ +static int __init +forte_probe (struct pci_dev *pci_dev, const struct pci_device_id *pci_id) +{ + struct forte_chip *chip; + int ret = 0; + + /* FIXME: Support more than one chip */ + if (found++) + return -EIO; + + /* Ignition */ + if (pci_enable_device (pci_dev)) + return -EIO; + + pci_set_master (pci_dev); + + /* Allocate chip instance and configure */ + forte = (struct forte_chip *) + kmalloc (sizeof (struct forte_chip), GFP_KERNEL); + chip = forte; + + if (chip == NULL) { + printk (KERN_WARNING PFX "Out of memory"); + return -ENOMEM; + } + + memset (chip, 0, sizeof (struct forte_chip)); + chip->pci_dev = pci_dev; + + init_MUTEX(&chip->open_sem); + spin_lock_init (&chip->lock); + spin_lock_init (&chip->ac97_lock); + + if (! request_region (pci_resource_start (pci_dev, 0), + pci_resource_len (pci_dev, 0), DRIVER_NAME)) { + printk (KERN_WARNING PFX "Unable to reserve I/O space"); + ret = -ENOMEM; + goto error; + } + + chip->iobase = pci_resource_start (pci_dev, 0); + chip->irq = pci_dev->irq; + + if (request_irq (chip->irq, forte_interrupt, SA_SHIRQ, DRIVER_NAME, + chip)) { + printk (KERN_WARNING PFX "Unable to reserve IRQ"); + ret = -EIO; + goto error; + } + + pci_set_drvdata (pci_dev, chip); + + printk (KERN_INFO PFX "FM801 chip found at 0x%04lX-0x%04lX IRQ %u\n", + chip->iobase, pci_resource_end (pci_dev, 0), chip->irq); + + /* Power it up */ + if ((ret = forte_chip_init (chip)) == 0) + return 0; + + error: + if (chip->irq) + free_irq (chip->irq, chip); + + if (chip->iobase) + release_region (pci_resource_start (pci_dev, 0), + pci_resource_len (pci_dev, 0)); + + kfree (chip); + + return ret; +} + + +/** + * forte_remove: + * @pci_dev: PCI device to unclaim + * + */ + +static void +forte_remove (struct pci_dev *pci_dev) +{ + struct forte_chip *chip = pci_get_drvdata (pci_dev); + + if (chip == NULL) + return; + + /* Turn volume down to avoid popping */ + outw (0x1f1f, chip->iobase + FORTE_PCM_VOL); + outw (0x1f1f, chip->iobase + FORTE_FM_VOL); + outw (0x1f1f, chip->iobase + FORTE_I2S_VOL); + + forte_proc_remove(); + free_irq (chip->irq, chip); + release_region (chip->iobase, pci_resource_len (pci_dev, 0)); + + unregister_sound_dsp (chip->dsp); + unregister_sound_mixer (chip->ac97->dev_mixer); + ac97_release_codec(chip->ac97); + kfree (chip); + + printk (KERN_INFO PFX "driver released\n"); +} + + +static struct pci_device_id forte_pci_ids[] __devinitdata = { + { 0x1319, 0x0801, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, + { 0, } +}; + + +static struct pci_driver forte_pci_driver = { + name: DRIVER_NAME, + id_table: forte_pci_ids, + probe: forte_probe, + remove: forte_remove, + +}; + + +/** + * forte_init_module: + * + */ + +static int __init +forte_init_module (void) +{ + printk (KERN_INFO PFX DRIVER_VERSION "\n"); + + if (!pci_register_driver (&forte_pci_driver)) { + pci_unregister_driver (&forte_pci_driver); + return -ENODEV; + } + + return 0; +} + + +/** + * forte_cleanup_module: + * + */ + +static void __exit +forte_cleanup_module (void) +{ + pci_unregister_driver (&forte_pci_driver); +} + + +module_init(forte_init_module); +module_exit(forte_cleanup_module); + +MODULE_AUTHOR("Martin K. Petersen "); +MODULE_DESCRIPTION("ForteMedia FM801 OSS Driver"); +MODULE_LICENSE("GPL"); +MODULE_DEVICE_TABLE (pci, forte_pci_ids); diff -Nru a/sound/oss/i810_audio.c b/sound/oss/i810_audio.c --- a/sound/oss/i810_audio.c Wed Jun 11 20:04:57 2003 +++ b/sound/oss/i810_audio.c Fri Jul 11 09:34:21 2003 @@ -117,6 +117,9 @@ #ifndef PCI_DEVICE_ID_INTEL_ICH4 #define PCI_DEVICE_ID_INTEL_ICH4 0x24c5 #endif +#ifndef PCI_DEVICE_ID_INTEL_ICH5 +#define PCI_DEVICE_ID_INTEL_ICH5 0x24d5 +#endif #ifndef PCI_DEVICE_ID_INTEL_440MX #define PCI_DEVICE_ID_INTEL_440MX 0x7195 #endif @@ -272,6 +275,7 @@ INTELICH2, INTELICH3, INTELICH4, + INTELICH5, SI7012, NVIDIA_NFORCE, AMD768, @@ -285,6 +289,7 @@ "Intel ICH2", "Intel ICH3", "Intel ICH4", + "Intel ICH5", "SiS 7012", "NVIDIA nForce Audio", "AMD 768", @@ -303,7 +308,8 @@ { 1, 0x0000 }, /* INTEL440MX */ { 1, 0x0000 }, /* INTELICH2 */ { 2, 0x0000 }, /* INTELICH3 */ - { 3, 0x0003 }, /* INTELICH4 */ + { 3, 0x0003 }, /* INTELICH4 */ + { 3, 0x0003 }, /* INTELICH5 */ /*@FIXME to be verified*/ { 2, 0x0000 }, /* SI7012 */ /*@FIXME to be verified*/ { 2, 0x0000 }, /* NVIDIA_NFORCE */ /*@FIXME to be verified*/ { 2, 0x0000 }, /* AMD768 */ @@ -323,6 +329,8 @@ PCI_ANY_ID, PCI_ANY_ID, 0, 0, INTELICH3}, {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH4, PCI_ANY_ID, PCI_ANY_ID, 0, 0, INTELICH4}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH5, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, INTELICH5}, {PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_7012, PCI_ANY_ID, PCI_ANY_ID, 0, 0, SI7012}, {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_MCP1_AUDIO, @@ -419,6 +427,9 @@ /* The i810 has a certain amount of cross channel interaction so we use a single per card lock */ spinlock_t lock; + + /* Control AC97 access serialization */ + spinlock_t ac97_lock; /* PCI device stuff */ struct pci_dev * pci_dev; @@ -547,80 +558,42 @@ * The DSP sample rate must already be set to a supported * S/PDIF rate (32kHz, 44.1kHz, or 48kHz) or we abort. */ -static void i810_set_spdif_output(struct i810_state *state, int slots, int rate) +static int i810_set_spdif_output(struct i810_state *state, int slots, int rate) { int vol; int aud_reg; + int r = 0; struct ac97_codec *codec = state->card->ac97_codec[0]; - if(!(state->card->ac97_features & 4)) { -#ifdef DEBUG - printk(KERN_WARNING "i810_audio: S/PDIF transmitter not available.\n"); -#endif + if(!codec->codec_ops->digital) { state->card->ac97_status &= ~SPDIF_ON; } else { if ( slots == -1 ) { /* Turn off S/PDIF */ - aud_reg = i810_ac97_get(codec, AC97_EXTENDED_STATUS); - i810_ac97_set(codec, AC97_EXTENDED_STATUS, (aud_reg & ~AC97_EA_SPDIF)); - + codec->codec_ops->digital(codec, 0, 0, 0); /* If the volume wasn't muted before we turned on S/PDIF, unmute it */ if ( !(state->card->ac97_status & VOL_MUTED) ) { aud_reg = i810_ac97_get(codec, AC97_MASTER_VOL_STEREO); i810_ac97_set(codec, AC97_MASTER_VOL_STEREO, (aud_reg & ~VOL_MUTED)); } state->card->ac97_status &= ~(VOL_MUTED | SPDIF_ON); - return; + return 0; } vol = i810_ac97_get(codec, AC97_MASTER_VOL_STEREO); state->card->ac97_status = vol & VOL_MUTED; - - /* Set S/PDIF transmitter sample rate */ - aud_reg = i810_ac97_get(codec, AC97_SPDIF_CONTROL); - switch ( rate ) { - case 32000: - aud_reg = (aud_reg & AC97_SC_SPSR_MASK) | AC97_SC_SPSR_32K; - break; - case 44100: - aud_reg = (aud_reg & AC97_SC_SPSR_MASK) | AC97_SC_SPSR_44K; - break; - case 48000: - aud_reg = (aud_reg & AC97_SC_SPSR_MASK) | AC97_SC_SPSR_48K; - break; - default: -#ifdef DEBUG - printk(KERN_WARNING "i810_audio: %d sample rate not supported by S/PDIF.\n", rate); -#endif - /* turn off S/PDIF */ - aud_reg = i810_ac97_get(codec, AC97_EXTENDED_STATUS); - i810_ac97_set(codec, AC97_EXTENDED_STATUS, (aud_reg & ~AC97_EA_SPDIF)); - state->card->ac97_status &= ~SPDIF_ON; - return; - } - - i810_ac97_set(codec, AC97_SPDIF_CONTROL, aud_reg); - aud_reg = i810_ac97_get(codec, AC97_EXTENDED_STATUS); - aud_reg = (aud_reg & AC97_EA_SLOT_MASK) | slots | AC97_EA_VRA | AC97_EA_SPDIF; - i810_ac97_set(codec, AC97_EXTENDED_STATUS, aud_reg); - state->card->ac97_status |= SPDIF_ON; - - /* Check to make sure the configuration is valid */ - aud_reg = i810_ac97_get(codec, AC97_EXTENDED_STATUS); - if ( ! (aud_reg & 0x0400) ) { -#ifdef DEBUG - printk(KERN_WARNING "i810_audio: S/PDIF transmitter configuration not valid (0x%04x).\n", aud_reg); -#endif + r = codec->codec_ops->digital(codec, slots, rate, 0); - /* turn off S/PDIF */ - i810_ac97_set(codec, AC97_EXTENDED_STATUS, (aud_reg & ~AC97_EA_SPDIF)); + if(r) + state->card->ac97_status |= SPDIF_ON; + else state->card->ac97_status &= ~SPDIF_ON; - return; - } + /* Mute the analog output */ /* Should this only mute the PCM volume??? */ i810_ac97_set(codec, AC97_MASTER_VOL_STEREO, (vol | VOL_MUTED)); } + return r; } /* i810_set_dac_channels @@ -642,8 +615,9 @@ { int aud_reg; struct ac97_codec *codec = state->card->ac97_codec[0]; - + /* No codec, no setup */ + if(codec == NULL) return; @@ -1835,6 +1809,7 @@ } spin_unlock_irqrestore(&state->card->lock, flags); + synchronize_irq(state->card->pci_dev->irq); dmabuf->ready = 0; dmabuf->swptr = dmabuf->hwptr = 0; dmabuf->count = dmabuf->total_bytes = 0; @@ -2493,11 +2468,9 @@ } if(file->f_mode & FMODE_WRITE) { if((dmabuf->write_channel = card->alloc_pcm_channel(card)) == NULL) { - /* free any read channel allocated earlier */ + /* make sure we free the record channel allocated above */ if(file->f_mode & FMODE_READ) - card->free_pcm_channel(card, - dmabuf->read_channel->num); - + card->free_pcm_channel(card,dmabuf->read_channel->num); kfree (card->states[i]); card->states[i] = NULL;; return -EBUSY; @@ -2641,23 +2614,32 @@ static u16 i810_ac97_get(struct ac97_codec *dev, u8 reg) { struct i810_card *card = dev->private_data; + u16 ret; + + spin_lock(&card->ac97_lock); if (card->use_mmio) { - return i810_ac97_get_mmio(dev, reg); + ret = i810_ac97_get_mmio(dev, reg); } else { - return i810_ac97_get_io(dev, reg); + ret = i810_ac97_get_io(dev, reg); } + spin_unlock(&card->ac97_lock); + + return ret; } static void i810_ac97_set(struct ac97_codec *dev, u8 reg, u16 data) { struct i810_card *card = dev->private_data; + + spin_lock(&card->ac97_lock); if (card->use_mmio) { i810_ac97_set_mmio(dev, reg, data); } else { i810_ac97_set_io(dev, reg, data); } + spin_unlock(&card->ac97_lock); } @@ -2803,7 +2785,7 @@ */ /* see i810_ac97_init for the next 7 lines (jsaw) */ inw(card->ac97base); - if ((card->pci_id == PCI_DEVICE_ID_INTEL_ICH4) + if ((card->pci_id == PCI_DEVICE_ID_INTEL_ICH4 || card->pci_id == PCI_DEVICE_ID_INTEL_ICH5) && (card->use_mmio)) { primary_codec_id = (int) readl(card->iobase_mmio + SDM) & 0x3; printk(KERN_INFO "i810_audio: Primary codec has ID %d\n", @@ -2873,7 +2855,7 @@ possible IO channels. Bit 0:1 of SDM then holds the last codec ID spoken to. */ - if ((card->pci_id == PCI_DEVICE_ID_INTEL_ICH4) + if ((card->pci_id == PCI_DEVICE_ID_INTEL_ICH4 || card->pci_id == PCI_DEVICE_ID_INTEL_ICH5) && (card->use_mmio)) { ac97_id = (int) readl(card->iobase_mmio + SDM) & 0x3; printk(KERN_INFO "i810_audio: Connection %d with codec id %d\n", @@ -2892,9 +2874,8 @@ printk(KERN_ERR "i810_audio: Primary codec not ready.\n"); } - if ((codec = kmalloc(sizeof(struct ac97_codec), GFP_KERNEL)) == NULL) + if ((codec = ac97_alloc_codec()) == NULL) return -ENOMEM; - memset(codec, 0, sizeof(struct ac97_codec)); /* initialize some basic codec information, other fields will be filled in ac97_probe_codec */ @@ -2913,7 +2894,7 @@ if(!i810_ac97_probe_and_powerup(card,codec)) { printk(KERN_ERR "i810_audio: timed out waiting for codec %d analog ready.\n", ac97_id); - kfree(codec); + ac97_release_codec(codec); break; /* it didn't work */ } /* Store state information about S/PDIF transmitter */ @@ -2922,32 +2903,22 @@ /* Don't attempt to get eid until powerup is complete */ eid = i810_ac97_get(codec, AC97_EXTENDED_ID); - if(eid==0xFFFFFF) + if(eid==0xFFFF) { printk(KERN_WARNING "i810_audio: no codec attached ?\n"); - kfree(codec); + ac97_release_codec(codec); break; } /* Check for an AC97 1.0 soft modem (ID1) */ - if(codec->codec_read(codec, AC97_RESET) & 2) + if(codec->modem) { - printk(KERN_WARNING "i810_audio: codec %d is an AC97 1.0 softmodem - skipping.\n", ac97_id); - kfree(codec); + printk(KERN_WARNING "i810_audio: codec %d is a softmodem - skipping.\n", ac97_id); + ac97_release_codec(codec); continue; } - /* Check for an AC97 2.x soft modem */ - - codec->codec_write(codec, AC97_EXTENDED_MODEM_ID, 0L); - if(codec->codec_read(codec, AC97_EXTENDED_MODEM_ID) & 1) - { - printk(KERN_WARNING "i810_audio: codec %d is an AC97 2.x softmodem - skipping.\n", ac97_id); - kfree(codec); - continue; - } - card->ac97_features = eid; /* Now check the codec for useful features to make up for @@ -3027,7 +2998,7 @@ if ((codec->dev_mixer = register_sound_mixer(&i810_mixer_fops, -1)) < 0) { printk(KERN_ERR "i810_audio: couldn't register mixer!\n"); - kfree(codec); + ac97_release_codec(codec); break; } @@ -3158,6 +3129,7 @@ card->pm_suspended=0; #endif spin_lock_init(&card->lock); + spin_lock_init(&card->ac97_lock); devs = card; pci_set_master(pci_dev); @@ -3253,7 +3225,7 @@ for (i = 0; i < NR_AC97; i++) if (card->ac97_codec[i] != NULL) { unregister_sound_mixer(card->ac97_codec[i]->dev_mixer); - kfree (card->ac97_codec[i]); + ac97_release_codec(card->ac97_codec[i]); } goto out_iospace; } @@ -3297,7 +3269,7 @@ for (i = 0; i < NR_AC97; i++) if (card->ac97_codec[i] != NULL) { unregister_sound_mixer(card->ac97_codec[i]->dev_mixer); - kfree (card->ac97_codec[i]); + ac97_release_codec(card->ac97_codec[i]); card->ac97_codec[i] = NULL; } unregister_sound_dsp(card->dev_audio); diff -Nru a/sound/oss/ite8172.c b/sound/oss/ite8172.c --- a/sound/oss/ite8172.c Mon Jun 9 16:20:30 2003 +++ b/sound/oss/ite8172.c Fri Jul 11 08:46:58 2003 @@ -42,15 +42,17 @@ * * Memory mapping the audio buffers, and the ioctl controls that go * with it. * * S/PDIF output. + * * I2S support. * 3. The following is not supported: - * * I2S input. * * legacy audio mode. * 4. Support for volume button interrupts is implemented but doesn't * work yet. * * Revision history - * 02.08.2001 0.1 Initial release + * 02.08.2001 Initial release + * 06.22.2001 Added I2S support */ +#include #include #include #include @@ -80,6 +82,19 @@ #undef IT8172_VERBOSE_DEBUG #define DBG(x) {} +#define IT8172_MODULE_NAME "IT8172 audio" +#define PFX IT8172_MODULE_NAME + +#ifdef IT8172_DEBUG +#define dbg(format, arg...) printk(KERN_DEBUG PFX ": " format "\n" , ## arg) +#else +#define dbg(format, arg...) do {} while (0) +#endif +#define err(format, arg...) printk(KERN_ERR PFX ": " format "\n" , ## arg) +#define info(format, arg...) printk(KERN_INFO PFX ": " format "\n" , ## arg) +#define warn(format, arg...) printk(KERN_WARNING PFX ": " format "\n" , ## arg) + + static const unsigned sample_shift[] = { 0, 1, 1, 2 }; @@ -226,75 +241,84 @@ #define POLL_COUNT 0x5000 -#define IT8172_MODULE_NAME "IT8172 audio" -#define PFX IT8172_MODULE_NAME ": " +/* --------------------------------------------------------------------- */ +/* + * Define DIGITAL1 as the I2S channel, since it is not listed in + * soundcard.h. + */ +#define SOUND_MIXER_I2S SOUND_MIXER_DIGITAL1 +#define SOUND_MASK_I2S SOUND_MASK_DIGITAL1 +#define SOUND_MIXER_READ_I2S MIXER_READ(SOUND_MIXER_I2S) +#define SOUND_MIXER_WRITE_I2S MIXER_WRITE(SOUND_MIXER_I2S) /* --------------------------------------------------------------------- */ struct it8172_state { - /* list of it8172 devices */ - struct list_head devs; - - /* the corresponding pci_dev structure */ - struct pci_dev *dev; + /* list of it8172 devices */ + struct list_head devs; - /* soundcore stuff */ - int dev_audio; + /* the corresponding pci_dev structure */ + struct pci_dev *dev; - /* hardware resources */ - unsigned long io; - unsigned int irq; - - /* PCI ID's */ - u16 vendor; - u16 device; - u8 rev; /* the chip revision */ - - /* options */ - int spdif_volume; /* S/PDIF output is enabled if != -1 */ + /* soundcore stuff */ + int dev_audio; + /* hardware resources */ + unsigned long io; + unsigned int irq; + + /* PCI ID's */ + u16 vendor; + u16 device; + u8 rev; /* the chip revision */ + + /* options */ + int spdif_volume; /* S/PDIF output is enabled if != -1 */ + int i2s_volume; /* current I2S out volume, in OSS format */ + int i2s_recording;/* 1 = recording from I2S, 0 = not */ + #ifdef IT8172_DEBUG - /* debug /proc entry */ - struct proc_dir_entry *ps; - struct proc_dir_entry *ac97_ps; + /* debug /proc entry */ + struct proc_dir_entry *ps; + struct proc_dir_entry *ac97_ps; #endif /* IT8172_DEBUG */ - struct ac97_codec codec; + struct ac97_codec *codec; - unsigned short pcc, capcc; - unsigned dacrate, adcrate; + unsigned short pcc, capcc; + unsigned dacrate, adcrate; - spinlock_t lock; - struct semaphore open_sem; - mode_t open_mode; - wait_queue_head_t open_wait; - - struct dmabuf { - void *rawbuf; - dma_addr_t dmaaddr; - unsigned buforder; - unsigned numfrag; - unsigned fragshift; - void* nextIn; - void* nextOut; - int count; - int curBufPtr; - unsigned total_bytes; - unsigned error; /* over/underrun */ - wait_queue_head_t wait; - /* redundant, but makes calculations easier */ - unsigned fragsize; - unsigned dmasize; - unsigned fragsamples; - /* OSS stuff */ - unsigned mapped:1; - unsigned ready:1; - unsigned stopped:1; - unsigned ossfragshift; - int ossmaxfrags; - unsigned subdivision; - } dma_dac, dma_adc; + spinlock_t lock; + struct semaphore open_sem; + mode_t open_mode; + wait_queue_head_t open_wait; + + struct dmabuf { + void *rawbuf; + dma_addr_t dmaaddr; + unsigned buforder; + unsigned numfrag; + unsigned fragshift; + void* nextIn; + void* nextOut; + int count; + int curBufPtr; + unsigned total_bytes; + unsigned error; /* over/underrun */ + wait_queue_head_t wait; + /* redundant, but makes calculations easier */ + unsigned fragsize; + unsigned dmasize; + unsigned fragsamples; + /* OSS stuff */ + unsigned mapped:1; + unsigned ready:1; + unsigned stopped:1; + unsigned ossfragshift; + int ossmaxfrags; + unsigned subdivision; + } dma_dac, dma_adc; }; /* --------------------------------------------------------------------- */ @@ -305,114 +329,114 @@ static inline unsigned ld2(unsigned int x) { - unsigned r = 0; + unsigned r = 0; - if (x >= 0x10000) { - x >>= 16; - r += 16; - } - if (x >= 0x100) { - x >>= 8; - r += 8; - } - if (x >= 0x10) { - x >>= 4; - r += 4; - } - if (x >= 4) { - x >>= 2; - r += 2; - } - if (x >= 2) - r++; - return r; + if (x >= 0x10000) { + x >>= 16; + r += 16; + } + if (x >= 0x100) { + x >>= 8; + r += 8; + } + if (x >= 0x10) { + x >>= 4; + r += 4; + } + if (x >= 4) { + x >>= 2; + r += 2; + } + if (x >= 2) + r++; + return r; } /* --------------------------------------------------------------------- */ static void it8172_delay(int msec) { - unsigned long tmo; - signed long tmo2; + unsigned long tmo; + signed long tmo2; - if (in_interrupt()) - return; + if (in_interrupt()) + return; - tmo = jiffies + (msec*HZ)/1000; - for (;;) { - tmo2 = tmo - jiffies; - if (tmo2 <= 0) - break; - schedule_timeout(tmo2); - } + tmo = jiffies + (msec*HZ)/1000; + for (;;) { + tmo2 = tmo - jiffies; + if (tmo2 <= 0) + break; + schedule_timeout(tmo2); + } } static unsigned short get_compat_rate(unsigned* rate) { - unsigned rate_out = *rate; - unsigned short sr; + unsigned rate_out = *rate; + unsigned short sr; - if (rate_out >= 46050) { - sr = CC_SR_48000; rate_out = 48000; - } else if (rate_out >= 41250) { - sr = CC_SR_44100; rate_out = 44100; - } else if (rate_out >= 35200) { - sr = CC_SR_38400; rate_out = 38400; - } else if (rate_out >= 27025) { - sr = CC_SR_32000; rate_out = 32000; - } else if (rate_out >= 20625) { - sr = CC_SR_22050; rate_out = 22050; - } else if (rate_out >= 17600) { - sr = CC_SR_19200; rate_out = 19200; - } else if (rate_out >= 13513) { - sr = CC_SR_16000; rate_out = 16000; - } else if (rate_out >= 10313) { - sr = CC_SR_11025; rate_out = 11025; - } else if (rate_out >= 8800) { - sr = CC_SR_9600; rate_out = 9600; - } else if (rate_out >= 6750) { - sr = CC_SR_8000; rate_out = 8000; - } else { - sr = CC_SR_5500; rate_out = 5500; - } + if (rate_out >= 46050) { + sr = CC_SR_48000; rate_out = 48000; + } else if (rate_out >= 41250) { + sr = CC_SR_44100; rate_out = 44100; + } else if (rate_out >= 35200) { + sr = CC_SR_38400; rate_out = 38400; + } else if (rate_out >= 27025) { + sr = CC_SR_32000; rate_out = 32000; + } else if (rate_out >= 20625) { + sr = CC_SR_22050; rate_out = 22050; + } else if (rate_out >= 17600) { + sr = CC_SR_19200; rate_out = 19200; + } else if (rate_out >= 13513) { + sr = CC_SR_16000; rate_out = 16000; + } else if (rate_out >= 10313) { + sr = CC_SR_11025; rate_out = 11025; + } else if (rate_out >= 8800) { + sr = CC_SR_9600; rate_out = 9600; + } else if (rate_out >= 6750) { + sr = CC_SR_8000; rate_out = 8000; + } else { + sr = CC_SR_5500; rate_out = 5500; + } - *rate = rate_out; - return sr; + *rate = rate_out; + return sr; } static void set_adc_rate(struct it8172_state *s, unsigned rate) { - unsigned long flags; - unsigned short sr; + unsigned long flags; + unsigned short sr; - sr = get_compat_rate(&rate); + sr = get_compat_rate(&rate); - spin_lock_irqsave(&s->lock, flags); - s->capcc &= ~CC_SR_MASK; - s->capcc |= sr; - outw(s->capcc, s->io+IT_AC_CAPCC); - spin_unlock_irqrestore(&s->lock, flags); + spin_lock_irqsave(&s->lock, flags); + s->capcc &= ~CC_SR_MASK; + s->capcc |= sr; + outw(s->capcc, s->io+IT_AC_CAPCC); + spin_unlock_irqrestore(&s->lock, flags); - s->adcrate = rate; + s->adcrate = rate; } static void set_dac_rate(struct it8172_state *s, unsigned rate) { - unsigned long flags; - unsigned short sr; + unsigned long flags; + unsigned short sr; - sr = get_compat_rate(&rate); + sr = get_compat_rate(&rate); - spin_lock_irqsave(&s->lock, flags); - s->pcc &= ~CC_SR_MASK; - s->pcc |= sr; - outw(s->pcc, s->io+IT_AC_PCC); - spin_unlock_irqrestore(&s->lock, flags); + spin_lock_irqsave(&s->lock, flags); + s->pcc &= ~CC_SR_MASK; + s->pcc |= sr; + outw(s->pcc, s->io+IT_AC_PCC); + spin_unlock_irqrestore(&s->lock, flags); - s->dacrate = rate; + s->dacrate = rate; } @@ -420,89 +444,88 @@ static u16 rdcodec(struct ac97_codec *codec, u8 addr) { - struct it8172_state *s = (struct it8172_state *)codec->private_data; - unsigned long flags; - unsigned short circp, data; - int i; - - spin_lock_irqsave(&s->lock, flags); - - for (i = 0; i < POLL_COUNT; i++) - if (!(inw(s->io+IT_AC_CIRCP) & CIRCP_CPS)) - break; - if (i == POLL_COUNT) - printk(KERN_INFO PFX "rdcodec: codec ready poll expired!\n"); - - circp = addr & CIRCP_CIA_MASK; - circp |= (codec->id << CIRCP_CID_BIT); - circp |= CIRCP_RWC; // read command - outw(circp, s->io+IT_AC_CIRCP); - - /* now wait for the data */ - for (i = 0; i < POLL_COUNT; i++) - if (inw(s->io+IT_AC_CIRCP) & CIRCP_DPVF) - break; - if (i == POLL_COUNT) - printk(KERN_INFO PFX "rdcodec: read poll expired!\n"); + struct it8172_state *s = (struct it8172_state *)codec->private_data; + unsigned long flags; + unsigned short circp, data; + int i; + + spin_lock_irqsave(&s->lock, flags); - data = inw(s->io+IT_AC_CIRDP); - spin_unlock_irqrestore(&s->lock, flags); + for (i = 0; i < POLL_COUNT; i++) + if (!(inw(s->io+IT_AC_CIRCP) & CIRCP_CPS)) + break; + if (i == POLL_COUNT) + err("rdcodec: codec ready poll expired!"); + + circp = addr & CIRCP_CIA_MASK; + circp |= (codec->id << CIRCP_CID_BIT); + circp |= CIRCP_RWC; // read command + outw(circp, s->io+IT_AC_CIRCP); + + /* now wait for the data */ + for (i = 0; i < POLL_COUNT; i++) + if (inw(s->io+IT_AC_CIRCP) & CIRCP_DPVF) + break; + if (i == POLL_COUNT) + err("rdcodec: read poll expired!"); - return data; + data = inw(s->io+IT_AC_CIRDP); + spin_unlock_irqrestore(&s->lock, flags); + + return data; } static void wrcodec(struct ac97_codec *codec, u8 addr, u16 data) { - struct it8172_state *s = (struct it8172_state *)codec->private_data; - unsigned long flags; - unsigned short circp; - int i; - - spin_lock_irqsave(&s->lock, flags); - - for (i = 0; i < POLL_COUNT; i++) - if (!(inw(s->io+IT_AC_CIRCP) & CIRCP_CPS)) - break; - if (i == POLL_COUNT) - printk(KERN_INFO PFX "wrcodec: codec ready poll expired!\n"); - - circp = addr & CIRCP_CIA_MASK; - circp |= (codec->id << CIRCP_CID_BIT); - circp &= ~CIRCP_RWC; // write command + struct it8172_state *s = (struct it8172_state *)codec->private_data; + unsigned long flags; + unsigned short circp; + int i; + + spin_lock_irqsave(&s->lock, flags); + + for (i = 0; i < POLL_COUNT; i++) + if (!(inw(s->io+IT_AC_CIRCP) & CIRCP_CPS)) + break; + if (i == POLL_COUNT) + err("wrcodec: codec ready poll expired!"); + + circp = addr & CIRCP_CIA_MASK; + circp |= (codec->id << CIRCP_CID_BIT); + circp &= ~CIRCP_RWC; // write command - outw(data, s->io+IT_AC_CIRDP); // send data first - outw(circp, s->io+IT_AC_CIRCP); + outw(data, s->io+IT_AC_CIRDP); // send data first + outw(circp, s->io+IT_AC_CIRCP); - spin_unlock_irqrestore(&s->lock, flags); + spin_unlock_irqrestore(&s->lock, flags); } static void waitcodec(struct ac97_codec *codec) { - unsigned short temp; + unsigned short temp; + + /* codec_wait is used to wait for a ready state after + an AC97_RESET. */ + it8172_delay(10); - /* codec_wait is used to wait for a ready state after - an AC97_RESET. */ - it8172_delay(10); - - temp = rdcodec(codec, 0x26); - - // If power down, power up - if (temp & 0x3f00) { - // Power on - wrcodec(codec, 0x26, 0); - it8172_delay(100); - // Reread temp = rdcodec(codec, 0x26); - } + + // If power down, power up + if (temp & 0x3f00) { + // Power on + wrcodec(codec, 0x26, 0); + it8172_delay(100); + // Reread + temp = rdcodec(codec, 0x26); + } - // Check if Codec REF,ANL,DAC,ADC ready***/ - if ((temp & 0x3f0f) != 0x000f) { - printk(KERN_INFO PFX "codec reg 26 status (0x%x) not ready!!\n", - temp); - return; - } + // Check if Codec REF,ANL,DAC,ADC ready***/ + if ((temp & 0x3f0f) != 0x000f) { + err("codec reg 26 status (0x%x) not ready!!", temp); + return; + } } @@ -510,120 +533,120 @@ static inline void stop_adc(struct it8172_state *s) { - struct dmabuf* db = &s->dma_adc; - unsigned long flags; - unsigned char imc; + struct dmabuf* db = &s->dma_adc; + unsigned long flags; + unsigned char imc; - if (db->stopped) - return; + if (db->stopped) + return; - spin_lock_irqsave(&s->lock, flags); + spin_lock_irqsave(&s->lock, flags); - s->capcc &= ~(CC_CA | CC_CP | CC_CB2L | CC_CB1L); - s->capcc |= CC_CSP; - outw(s->capcc, s->io+IT_AC_CAPCC); + s->capcc &= ~(CC_CA | CC_CP | CC_CB2L | CC_CB1L); + s->capcc |= CC_CSP; + outw(s->capcc, s->io+IT_AC_CAPCC); - // disable capture interrupt - imc = inb(s->io+IT_AC_IMC); - outb(imc | IMC_CCIM, s->io+IT_AC_IMC); + // disable capture interrupt + imc = inb(s->io+IT_AC_IMC); + outb(imc | IMC_CCIM, s->io+IT_AC_IMC); - db->stopped = 1; + db->stopped = 1; - spin_unlock_irqrestore(&s->lock, flags); + spin_unlock_irqrestore(&s->lock, flags); } static inline void stop_dac(struct it8172_state *s) { - struct dmabuf* db = &s->dma_dac; - unsigned long flags; - unsigned char imc; + struct dmabuf* db = &s->dma_dac; + unsigned long flags; + unsigned char imc; - if (db->stopped) - return; + if (db->stopped) + return; - spin_lock_irqsave(&s->lock, flags); + spin_lock_irqsave(&s->lock, flags); - s->pcc &= ~(CC_CA | CC_CP | CC_CB2L | CC_CB1L); - s->pcc |= CC_CSP; - outw(s->pcc, s->io+IT_AC_PCC); + s->pcc &= ~(CC_CA | CC_CP | CC_CB2L | CC_CB1L); + s->pcc |= CC_CSP; + outw(s->pcc, s->io+IT_AC_PCC); - // disable playback interrupt - imc = inb(s->io+IT_AC_IMC); - outb(imc | IMC_PCIM, s->io+IT_AC_IMC); + // disable playback interrupt + imc = inb(s->io+IT_AC_IMC); + outb(imc | IMC_PCIM, s->io+IT_AC_IMC); - db->stopped = 1; + db->stopped = 1; - spin_unlock_irqrestore(&s->lock, flags); + spin_unlock_irqrestore(&s->lock, flags); } static void start_dac(struct it8172_state *s) { - struct dmabuf* db = &s->dma_dac; - unsigned long flags; - unsigned char imc; - unsigned long buf1, buf2; + struct dmabuf* db = &s->dma_dac; + unsigned long flags; + unsigned char imc; + unsigned long buf1, buf2; - if (!db->stopped) - return; + if (!db->stopped) + return; - spin_lock_irqsave(&s->lock, flags); + spin_lock_irqsave(&s->lock, flags); - // reset Buffer 1 and 2 pointers to nextOut and nextOut+fragsize - buf1 = virt_to_bus(db->nextOut); - buf2 = buf1 + db->fragsize; - if (buf2 >= db->dmaaddr + db->dmasize) - buf2 -= db->dmasize; - - outl(buf1, s->io+IT_AC_PCB1STA); - outl(buf2, s->io+IT_AC_PCB2STA); - db->curBufPtr = IT_AC_PCB1STA; - - // enable playback interrupt - imc = inb(s->io+IT_AC_IMC); - outb(imc & ~IMC_PCIM, s->io+IT_AC_IMC); + // reset Buffer 1 and 2 pointers to nextOut and nextOut+fragsize + buf1 = virt_to_bus(db->nextOut); + buf2 = buf1 + db->fragsize; + if (buf2 >= db->dmaaddr + db->dmasize) + buf2 -= db->dmasize; + + outl(buf1, s->io+IT_AC_PCB1STA); + outl(buf2, s->io+IT_AC_PCB2STA); + db->curBufPtr = IT_AC_PCB1STA; + + // enable playback interrupt + imc = inb(s->io+IT_AC_IMC); + outb(imc & ~IMC_PCIM, s->io+IT_AC_IMC); - s->pcc &= ~(CC_CSP | CC_CP | CC_CB2L | CC_CB1L); - s->pcc |= CC_CA; - outw(s->pcc, s->io+IT_AC_PCC); + s->pcc &= ~(CC_CSP | CC_CP | CC_CB2L | CC_CB1L); + s->pcc |= CC_CA; + outw(s->pcc, s->io+IT_AC_PCC); - db->stopped = 0; + db->stopped = 0; - spin_unlock_irqrestore(&s->lock, flags); + spin_unlock_irqrestore(&s->lock, flags); } static void start_adc(struct it8172_state *s) { - struct dmabuf* db = &s->dma_adc; - unsigned long flags; - unsigned char imc; - unsigned long buf1, buf2; + struct dmabuf* db = &s->dma_adc; + unsigned long flags; + unsigned char imc; + unsigned long buf1, buf2; - if (!db->stopped) - return; + if (!db->stopped) + return; - spin_lock_irqsave(&s->lock, flags); - - // reset Buffer 1 and 2 pointers to nextIn and nextIn+fragsize - buf1 = virt_to_bus(db->nextIn); - buf2 = buf1 + db->fragsize; - if (buf2 >= db->dmaaddr + db->dmasize) - buf2 -= db->dmasize; - - outl(buf1, s->io+IT_AC_CAPB1STA); - outl(buf2, s->io+IT_AC_CAPB2STA); - db->curBufPtr = IT_AC_CAPB1STA; + spin_lock_irqsave(&s->lock, flags); - // enable capture interrupt - imc = inb(s->io+IT_AC_IMC); - outb(imc & ~IMC_CCIM, s->io+IT_AC_IMC); + // reset Buffer 1 and 2 pointers to nextIn and nextIn+fragsize + buf1 = virt_to_bus(db->nextIn); + buf2 = buf1 + db->fragsize; + if (buf2 >= db->dmaaddr + db->dmasize) + buf2 -= db->dmasize; + + outl(buf1, s->io+IT_AC_CAPB1STA); + outl(buf2, s->io+IT_AC_CAPB2STA); + db->curBufPtr = IT_AC_CAPB1STA; + + // enable capture interrupt + imc = inb(s->io+IT_AC_IMC); + outb(imc & ~IMC_CCIM, s->io+IT_AC_IMC); - s->capcc &= ~(CC_CSP | CC_CP | CC_CB2L | CC_CB1L); - s->capcc |= CC_CA; - outw(s->capcc, s->io+IT_AC_CAPCC); + s->capcc &= ~(CC_CSP | CC_CP | CC_CB2L | CC_CB1L); + s->capcc |= CC_CA; + outw(s->capcc, s->io+IT_AC_CAPCC); - db->stopped = 0; + db->stopped = 0; - spin_unlock_irqrestore(&s->lock, flags); + spin_unlock_irqrestore(&s->lock, flags); } /* --------------------------------------------------------------------- */ @@ -633,94 +656,103 @@ static inline void dealloc_dmabuf(struct it8172_state *s, struct dmabuf *db) { - struct page *page, *pend; + struct page *page, *pend; - if (db->rawbuf) { - /* undo marking the pages as reserved */ - pend = virt_to_page(db->rawbuf + (PAGE_SIZE << db->buforder) - 1); - for (page = virt_to_page(db->rawbuf); page <= pend; page++) - ClearPageReserved(page); - pci_free_consistent(s->dev, PAGE_SIZE << db->buforder, - db->rawbuf, db->dmaaddr); - } - db->rawbuf = db->nextIn = db->nextOut = NULL; - db->mapped = db->ready = 0; + if (db->rawbuf) { + /* undo marking the pages as reserved */ + pend = virt_to_page(db->rawbuf + + (PAGE_SIZE << db->buforder) - 1); + for (page = virt_to_page(db->rawbuf); page <= pend; page++) + mem_map_unreserve(page); + pci_free_consistent(s->dev, PAGE_SIZE << db->buforder, + db->rawbuf, db->dmaaddr); + } + db->rawbuf = db->nextIn = db->nextOut = NULL; + db->mapped = db->ready = 0; } static int prog_dmabuf(struct it8172_state *s, struct dmabuf *db, unsigned rate, unsigned fmt, unsigned reg) { - int order; - unsigned bytepersec; - unsigned bufs; - struct page *page, *pend; - - if (!db->rawbuf) { - db->ready = db->mapped = 0; - for (order = DMABUF_DEFAULTORDER; order >= DMABUF_MINORDER; order--) - if ((db->rawbuf = pci_alloc_consistent(s->dev, - PAGE_SIZE << order, - &db->dmaaddr))) - break; - if (!db->rawbuf) - return -ENOMEM; - db->buforder = order; - /* now mark the pages as reserved; - otherwise remap_page_range doesn't do what we want */ - pend = virt_to_page(db->rawbuf + (PAGE_SIZE << db->buforder) - 1); - for (page = virt_to_page(db->rawbuf); page <= pend; page++) - SetPageReserved(page); - } - - db->count = 0; - db->nextIn = db->nextOut = db->rawbuf; - - bytepersec = rate << sample_shift[fmt]; - bufs = PAGE_SIZE << db->buforder; - if (db->ossfragshift) { - if ((1000 << db->ossfragshift) < bytepersec) - db->fragshift = ld2(bytepersec/1000); - else - db->fragshift = db->ossfragshift; - } else { - db->fragshift = ld2(bytepersec/100/(db->subdivision ? - db->subdivision : 1)); - if (db->fragshift < 3) - db->fragshift = 3; - } - db->numfrag = bufs >> db->fragshift; - while (db->numfrag < 4 && db->fragshift > 3) { - db->fragshift--; + int order; + unsigned bytepersec; + unsigned bufs; + struct page *page, *pend; + + if (!db->rawbuf) { + db->ready = db->mapped = 0; + for (order = DMABUF_DEFAULTORDER; + order >= DMABUF_MINORDER; order--) + if ((db->rawbuf = + pci_alloc_consistent(s->dev, + PAGE_SIZE << order, + &db->dmaaddr))) + break; + if (!db->rawbuf) + return -ENOMEM; + db->buforder = order; + /* now mark the pages as reserved; + otherwise remap_page_range doesn't do what we want */ + pend = virt_to_page(db->rawbuf + + (PAGE_SIZE << db->buforder) - 1); + for (page = virt_to_page(db->rawbuf); page <= pend; page++) + mem_map_reserve(page); + } + + db->count = 0; + db->nextIn = db->nextOut = db->rawbuf; + + bytepersec = rate << sample_shift[fmt]; + bufs = PAGE_SIZE << db->buforder; + if (db->ossfragshift) { + if ((1000 << db->ossfragshift) < bytepersec) + db->fragshift = ld2(bytepersec/1000); + else + db->fragshift = db->ossfragshift; + } else { + db->fragshift = ld2(bytepersec/100/(db->subdivision ? + db->subdivision : 1)); + if (db->fragshift < 3) + db->fragshift = 3; + } db->numfrag = bufs >> db->fragshift; - } - db->fragsize = 1 << db->fragshift; - if (db->ossmaxfrags >= 4 && db->ossmaxfrags < db->numfrag) - db->numfrag = db->ossmaxfrags; - db->fragsamples = db->fragsize >> sample_shift[fmt]; - db->dmasize = db->numfrag << db->fragshift; - memset(db->rawbuf, (fmt & (CC_DF>>CC_FMT_BIT)) ? 0 : 0x80, db->dmasize); - - // set data length register - outw(db->fragsize, s->io+reg+2); - db->ready = 1; + while (db->numfrag < 4 && db->fragshift > 3) { + db->fragshift--; + db->numfrag = bufs >> db->fragshift; + } + db->fragsize = 1 << db->fragshift; + if (db->ossmaxfrags >= 4 && db->ossmaxfrags < db->numfrag) + db->numfrag = db->ossmaxfrags; + db->fragsamples = db->fragsize >> sample_shift[fmt]; + db->dmasize = db->numfrag << db->fragshift; + memset(db->rawbuf, (fmt & (CC_DF>>CC_FMT_BIT)) ? 0 : 0x80, bufs); + +#ifdef IT8172_VERBOSE_DEBUG + dbg("rate=%d, fragsize=%d, numfrag=%d, dmasize=%d", + rate, db->fragsize, db->numfrag, db->dmasize); +#endif - return 0; + // set data length register + outw(db->fragsize, s->io+reg+2); + db->ready = 1; + + return 0; } static inline int prog_dmabuf_adc(struct it8172_state *s) { - stop_adc(s); - return prog_dmabuf(s, &s->dma_adc, s->adcrate, - (s->capcc & CC_FMT_MASK) >> CC_FMT_BIT, - IT_AC_CAPCC); + stop_adc(s); + return prog_dmabuf(s, &s->dma_adc, s->adcrate, + (s->capcc & CC_FMT_MASK) >> CC_FMT_BIT, + IT_AC_CAPCC); } static inline int prog_dmabuf_dac(struct it8172_state *s) { - stop_dac(s); - return prog_dmabuf(s, &s->dma_dac, s->dacrate, - (s->pcc & CC_FMT_MASK) >> CC_FMT_BIT, - IT_AC_PCC); + stop_dac(s); + return prog_dmabuf(s, &s->dma_dac, s->dacrate, + (s->pcc & CC_FMT_MASK) >> CC_FMT_BIT, + IT_AC_PCC); } @@ -728,918 +760,1127 @@ static irqreturn_t it8172_interrupt(int irq, void *dev_id, struct pt_regs *regs) { - struct it8172_state *s = (struct it8172_state *)dev_id; - struct dmabuf* dac = &s->dma_dac; - struct dmabuf* adc = &s->dma_adc; - unsigned char isc, vs; - unsigned short vol, mute; - unsigned long newptr; - - spin_lock(&s->lock); - - isc = inb(s->io+IT_AC_ISC); - - /* fastpath out, to ease interrupt sharing */ - if (!(isc & (ISC_VCI | ISC_CCI | ISC_PCI))) { - spin_unlock(&s->lock); - return IRQ_NONE; - } - - /* clear audio interrupts first */ - outb(isc | ISC_VCI | ISC_CCI | ISC_PCI, s->io+IT_AC_ISC); - - /* handle volume button events */ - if (isc & ISC_VCI) { - vs = inb(s->io+IT_AC_VS); - outb(0, s->io+IT_AC_VS); - vol = inw(s->io+IT_AC_PCMOV); - mute = vol & PCMOV_PCMOM; - vol &= PCMOV_PCMLCG_MASK; - if ((vs & VS_VUP) && vol > 0) - vol--; - if ((vs & VS_VDP) && vol < 0x1f) - vol++; - vol |= (vol << PCMOV_PCMRCG_BIT); - if (vs & VS_VMP) - vol |= (mute ^ PCMOV_PCMOM); - outw(vol, s->io+IT_AC_PCMOV); - } - - /* update capture pointers */ - if (isc & ISC_CCI) { - if (adc->count > adc->dmasize - adc->fragsize) { - // Overrun. Stop ADC and log the error - stop_adc(s); - adc->error++; - printk(KERN_INFO PFX "adc overrun\n"); - } else { - newptr = virt_to_bus(adc->nextIn) + 2*adc->fragsize; - if (newptr >= adc->dmaaddr + adc->dmasize) - newptr -= adc->dmasize; + struct it8172_state *s = (struct it8172_state *)dev_id; + struct dmabuf* dac = &s->dma_dac; + struct dmabuf* adc = &s->dma_adc; + unsigned char isc, vs; + unsigned short vol, mute; + unsigned long newptr; + + spin_lock(&s->lock); + + isc = inb(s->io+IT_AC_ISC); + + /* fastpath out, to ease interrupt sharing */ + if (!(isc & (ISC_VCI | ISC_CCI | ISC_PCI))) { + spin_unlock(&s->lock); + return IRQ_NONE; + } + + /* clear audio interrupts first */ + outb(isc | ISC_VCI | ISC_CCI | ISC_PCI, s->io+IT_AC_ISC); + + /* handle volume button events (ignore if S/PDIF enabled) */ + if ((isc & ISC_VCI) && s->spdif_volume == -1) { + vs = inb(s->io+IT_AC_VS); + outb(0, s->io+IT_AC_VS); + vol = inw(s->io+IT_AC_PCMOV); + mute = vol & PCMOV_PCMOM; + vol &= PCMOV_PCMLCG_MASK; + if ((vs & VS_VUP) && vol > 0) + vol--; + if ((vs & VS_VDP) && vol < 0x1f) + vol++; + vol |= (vol << PCMOV_PCMRCG_BIT); + if (vs & VS_VMP) + vol |= (mute ^ PCMOV_PCMOM); + outw(vol, s->io+IT_AC_PCMOV); + } + + /* update capture pointers */ + if (isc & ISC_CCI) { + if (adc->count > adc->dmasize - adc->fragsize) { + // Overrun. Stop ADC and log the error + stop_adc(s); + adc->error++; + dbg("adc overrun"); + } else { + newptr = virt_to_bus(adc->nextIn) + 2*adc->fragsize; + if (newptr >= adc->dmaaddr + adc->dmasize) + newptr -= adc->dmasize; - outl(newptr, s->io+adc->curBufPtr); - adc->curBufPtr = (adc->curBufPtr == IT_AC_CAPB1STA) ? - IT_AC_CAPB2STA : IT_AC_CAPB1STA; + outl(newptr, s->io+adc->curBufPtr); + adc->curBufPtr = (adc->curBufPtr == IT_AC_CAPB1STA) ? + IT_AC_CAPB2STA : IT_AC_CAPB1STA; - adc->nextIn += adc->fragsize; - if (adc->nextIn >= adc->rawbuf + adc->dmasize) - adc->nextIn -= adc->dmasize; + adc->nextIn += adc->fragsize; + if (adc->nextIn >= adc->rawbuf + adc->dmasize) + adc->nextIn -= adc->dmasize; - adc->count += adc->fragsize; - adc->total_bytes += adc->fragsize; + adc->count += adc->fragsize; + adc->total_bytes += adc->fragsize; - /* wake up anybody listening */ - if (waitqueue_active(&adc->wait)) - wake_up_interruptible(&adc->wait); - } - } - - /* update playback pointers */ - if (isc & ISC_PCI) { - newptr = virt_to_bus(dac->nextOut) + 2*dac->fragsize; - if (newptr >= dac->dmaaddr + dac->dmasize) - newptr -= dac->dmasize; + /* wake up anybody listening */ + if (waitqueue_active(&adc->wait)) + wake_up_interruptible(&adc->wait); + } + } + + /* update playback pointers */ + if (isc & ISC_PCI) { + newptr = virt_to_bus(dac->nextOut) + 2*dac->fragsize; + if (newptr >= dac->dmaaddr + dac->dmasize) + newptr -= dac->dmasize; - outl(newptr, s->io+dac->curBufPtr); - dac->curBufPtr = (dac->curBufPtr == IT_AC_PCB1STA) ? - IT_AC_PCB2STA : IT_AC_PCB1STA; + outl(newptr, s->io+dac->curBufPtr); + dac->curBufPtr = (dac->curBufPtr == IT_AC_PCB1STA) ? + IT_AC_PCB2STA : IT_AC_PCB1STA; - dac->nextOut += dac->fragsize; - if (dac->nextOut >= dac->rawbuf + dac->dmasize) - dac->nextOut -= dac->dmasize; + dac->nextOut += dac->fragsize; + if (dac->nextOut >= dac->rawbuf + dac->dmasize) + dac->nextOut -= dac->dmasize; - dac->count -= dac->fragsize; - dac->total_bytes += dac->fragsize; + dac->count -= dac->fragsize; + dac->total_bytes += dac->fragsize; - /* wake up anybody listening */ - if (waitqueue_active(&dac->wait)) - wake_up_interruptible(&dac->wait); + /* wake up anybody listening */ + if (waitqueue_active(&dac->wait)) + wake_up_interruptible(&dac->wait); - if (dac->count <= 0) - stop_dac(s); - } + if (dac->count <= 0) + stop_dac(s); + } - spin_unlock(&s->lock); - return IRQ_HANDLED; + spin_unlock(&s->lock); + return IRQ_HANDLED; } /* --------------------------------------------------------------------- */ +static loff_t it8172_llseek(struct file *file, loff_t offset, int origin) +{ + return -ESPIPE; +} + + static int it8172_open_mixdev(struct inode *inode, struct file *file) { - int minor = minor(inode->i_rdev); - struct list_head *list; - struct it8172_state *s; - - for (list = devs.next; ; list = list->next) { - if (list == &devs) - return -ENODEV; - s = list_entry(list, struct it8172_state, devs); - if (s->codec.dev_mixer == minor) - break; - } - file->private_data = s; - return 0; + int minor = MINOR(inode->i_rdev); + struct list_head *list; + struct it8172_state *s; + + for (list = devs.next; ; list = list->next) { + if (list == &devs) + return -ENODEV; + s = list_entry(list, struct it8172_state, devs); + if (s->codec->dev_mixer == minor) + break; + } + file->private_data = s; + return 0; } static int it8172_release_mixdev(struct inode *inode, struct file *file) { - return 0; + return 0; +} + + +static u16 +cvt_ossvol(unsigned int gain) +{ + u16 ret; + + if (gain == 0) + return 0; + + if (gain > 100) + gain = 100; + + ret = (100 - gain + 32) / 4; + ret = ret > 31 ? 31 : ret; + return ret; } static int mixdev_ioctl(struct ac97_codec *codec, unsigned int cmd, unsigned long arg) { - return codec->mixer_ioctl(codec, cmd, arg); + struct it8172_state *s = (struct it8172_state *)codec->private_data; + unsigned int left, right; + unsigned long flags; + int val; + u16 vol; + + /* + * When we are in S/PDIF mode, we want to disable any analog output so + * we filter the master/PCM channel volume ioctls. + * + * Also filter I2S channel, which AC'97 knows nothing about. + */ + + switch (cmd) { + case SOUND_MIXER_WRITE_VOLUME: + // if not in S/PDIF mode, pass to AC'97 + if (s->spdif_volume == -1) + break; + return 0; + case SOUND_MIXER_WRITE_PCM: + // if not in S/PDIF mode, pass to AC'97 + if (s->spdif_volume == -1) + break; + if (get_user(val, (int *)arg)) + return -EFAULT; + right = ((val >> 8) & 0xff); + left = (val & 0xff); + if (right > 100) + right = 100; + if (left > 100) + left = 100; + s->spdif_volume = (right << 8) | left; + vol = cvt_ossvol(left); + vol |= (cvt_ossvol(right) << PCMOV_PCMRCG_BIT); + if (vol == 0) + vol = PCMOV_PCMOM; // mute + spin_lock_irqsave(&s->lock, flags); + outw(vol, s->io+IT_AC_PCMOV); + spin_unlock_irqrestore(&s->lock, flags); + return put_user(s->spdif_volume, (int *)arg); + case SOUND_MIXER_READ_PCM: + // if not in S/PDIF mode, pass to AC'97 + if (s->spdif_volume == -1) + break; + return put_user(s->spdif_volume, (int *)arg); + case SOUND_MIXER_WRITE_I2S: + if (get_user(val, (int *)arg)) + return -EFAULT; + right = ((val >> 8) & 0xff); + left = (val & 0xff); + if (right > 100) + right = 100; + if (left > 100) + left = 100; + s->i2s_volume = (right << 8) | left; + vol = cvt_ossvol(left); + vol |= (cvt_ossvol(right) << I2SV_I2SRCG_BIT); + if (vol == 0) + vol = I2SV_I2SOM; // mute + outw(vol, s->io+IT_AC_I2SV); + return put_user(s->i2s_volume, (int *)arg); + case SOUND_MIXER_READ_I2S: + return put_user(s->i2s_volume, (int *)arg); + case SOUND_MIXER_WRITE_RECSRC: + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val & SOUND_MASK_I2S) { + s->i2s_recording = 1; + outb(DRSS_I2S, s->io+IT_AC_DRSS); + return 0; + } else { + s->i2s_recording = 0; + outb(DRSS_AC97_PRIM, s->io+IT_AC_DRSS); + // now let AC'97 select record source + break; + } + case SOUND_MIXER_READ_RECSRC: + if (s->i2s_recording) + return put_user(SOUND_MASK_I2S, (int *)arg); + else + // let AC'97 report recording source + break; + } + + return codec->mixer_ioctl(codec, cmd, arg); } static int it8172_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { - struct it8172_state *s = (struct it8172_state *)file->private_data; - struct ac97_codec *codec = &s->codec; + struct it8172_state *s = (struct it8172_state *)file->private_data; + struct ac97_codec *codec = s->codec; - return mixdev_ioctl(codec, cmd, arg); + return mixdev_ioctl(codec, cmd, arg); } static /*const*/ struct file_operations it8172_mixer_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .ioctl = it8172_ioctl_mixdev, - .open = it8172_open_mixdev, - .release = it8172_release_mixdev, + owner: THIS_MODULE, + llseek: it8172_llseek, + ioctl: it8172_ioctl_mixdev, + open: it8172_open_mixdev, + release: it8172_release_mixdev, }; /* --------------------------------------------------------------------- */ static int drain_dac(struct it8172_state *s, int nonblock) { - unsigned long flags; - int count, tmo; + unsigned long flags; + int count, tmo; - if (s->dma_dac.mapped || !s->dma_dac.ready) - return 0; + if (s->dma_dac.mapped || !s->dma_dac.ready || s->dma_dac.stopped) + return 0; - for (;;) { - spin_lock_irqsave(&s->lock, flags); - count = s->dma_dac.count; - spin_unlock_irqrestore(&s->lock, flags); - if (count <= 0) - break; + for (;;) { + spin_lock_irqsave(&s->lock, flags); + count = s->dma_dac.count; + spin_unlock_irqrestore(&s->lock, flags); + if (count <= 0) + break; + if (signal_pending(current)) + break; + //if (nonblock) + //return -EBUSY; + tmo = 1000 * count / s->dacrate; + tmo >>= sample_shift[(s->pcc & CC_FMT_MASK) >> CC_FMT_BIT]; + it8172_delay(tmo); + } if (signal_pending(current)) - break; - if (nonblock) - return -EBUSY; - tmo = 1000 * count / s->dacrate; - tmo >>= sample_shift[(s->pcc & CC_FMT_MASK) >> CC_FMT_BIT]; - it8172_delay(tmo); - } - if (signal_pending(current)) - return -ERESTARTSYS; - return 0; + return -ERESTARTSYS; + return 0; } /* --------------------------------------------------------------------- */ + +/* + * Copy audio data to/from user buffer from/to dma buffer, taking care + * that we wrap when reading/writing the dma buffer. Returns actual byte + * count written to or read from the dma buffer. + */ +static int copy_dmabuf_user(struct dmabuf *db, char* userbuf, + int count, int to_user) +{ + char* bufptr = to_user ? db->nextOut : db->nextIn; + char* bufend = db->rawbuf + db->dmasize; + + if (bufptr + count > bufend) { + int partial = (int)(bufend - bufptr); + if (to_user) { + if (copy_to_user(userbuf, bufptr, partial)) + return -EFAULT; + if (copy_to_user(userbuf + partial, db->rawbuf, + count - partial)) + return -EFAULT; + } else { + if (copy_from_user(bufptr, userbuf, partial)) + return -EFAULT; + if (copy_from_user(db->rawbuf, + userbuf + partial, + count - partial)) + return -EFAULT; + } + } else { + if (to_user) { + if (copy_to_user(userbuf, bufptr, count)) + return -EFAULT; + } else { + if (copy_from_user(bufptr, userbuf, count)) + return -EFAULT; + } + } + + return count; +} + + static ssize_t it8172_read(struct file *file, char *buffer, size_t count, loff_t *ppos) { - struct it8172_state *s = (struct it8172_state *)file->private_data; - struct dmabuf *db = &s->dma_adc; - ssize_t ret; - unsigned long flags; - int cnt, bufcnt, avail; + struct it8172_state *s = (struct it8172_state *)file->private_data; + struct dmabuf *db = &s->dma_adc; + ssize_t ret; + unsigned long flags; + int cnt, remainder, avail; + + if (ppos != &file->f_pos) + return -ESPIPE; + if (db->mapped) + return -ENXIO; + if (!access_ok(VERIFY_WRITE, buffer, count)) + return -EFAULT; + ret = 0; - if (ppos != &file->f_pos) - return -ESPIPE; - if (db->mapped) - return -ENXIO; - if (!access_ok(VERIFY_WRITE, buffer, count)) - return -EFAULT; - ret = 0; - - while (count > 0) { - // wait for samples in capture buffer - do { - spin_lock_irqsave(&s->lock, flags); - if (db->stopped) - start_adc(s); - avail = db->count; - spin_unlock_irqrestore(&s->lock, flags); - if (avail <= 0) { - if (file->f_flags & O_NONBLOCK) { - if (!ret) - ret = -EAGAIN; - return ret; - } - interruptible_sleep_on(&db->wait); - if (signal_pending(current)) { - if (!ret) - ret = -ERESTARTSYS; - return ret; - } - } - } while (avail <= 0); - - cnt = count > avail ? avail : count; - bufcnt = cnt; - if (cnt % db->fragsize) { - // round count up to nearest fragment - int newcnt = db->fragsize * ((cnt + db->fragsize) / db->fragsize); - cnt = newcnt; - } - - // copy from nextOut to user - if (copy_to_user(buffer, db->nextOut, bufcnt)) { - if (!ret) - ret = -EFAULT; - return ret; - } + while (count > 0) { + // wait for samples in capture buffer + do { + spin_lock_irqsave(&s->lock, flags); + if (db->stopped) + start_adc(s); + avail = db->count; + spin_unlock_irqrestore(&s->lock, flags); + if (avail <= 0) { + if (file->f_flags & O_NONBLOCK) { + if (!ret) + ret = -EAGAIN; + return ret; + } + interruptible_sleep_on(&db->wait); + if (signal_pending(current)) { + if (!ret) + ret = -ERESTARTSYS; + return ret; + } + } + } while (avail <= 0); + + // copy from nextOut to user + if ((cnt = copy_dmabuf_user(db, buffer, count > avail ? + avail : count, 1)) < 0) { + if (!ret) + ret = -EFAULT; + return ret; + } + spin_lock_irqsave(&s->lock, flags); + db->count -= cnt; + spin_unlock_irqrestore(&s->lock, flags); + + db->nextOut += cnt; + if (db->nextOut >= db->rawbuf + db->dmasize) + db->nextOut -= db->dmasize; + + count -= cnt; + buffer += cnt; + ret += cnt; + } // while (count > 0) + + /* + * See if the dma buffer count after this read call is + * aligned on a fragsize boundary. If not, read from + * buffer until we reach a boundary, and let's hope this + * is just the last remainder of an audio record. If not + * it means the user is not reading in fragsize chunks, in + * which case it's his/her fault that there are audio gaps + * in their record. + */ spin_lock_irqsave(&s->lock, flags); - db->count -= cnt; + remainder = db->count % db->fragsize; + if (remainder) { + db->nextOut += remainder; + if (db->nextOut >= db->rawbuf + db->dmasize) + db->nextOut -= db->dmasize; + db->count -= remainder; + } spin_unlock_irqrestore(&s->lock, flags); - db->nextOut += cnt; - if (db->nextOut >= db->rawbuf + db->dmasize) - db->nextOut -= db->dmasize; - - count -= bufcnt; - buffer += bufcnt; - ret += bufcnt; - } // while (count > 0) - - return ret; + return ret; } static ssize_t it8172_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) { - struct it8172_state *s = (struct it8172_state *)file->private_data; - struct dmabuf *db = &s->dma_dac; - ssize_t ret; - unsigned long flags; - int cnt, bufcnt, avail; - - if (ppos != &file->f_pos) - return -ESPIPE; - if (db->mapped) - return -ENXIO; - if (!access_ok(VERIFY_READ, buffer, count)) - return -EFAULT; - ret = 0; - - while (count > 0) { - // wait for space in playback buffer - do { - spin_lock_irqsave(&s->lock, flags); - avail = db->dmasize - db->count; - spin_unlock_irqrestore(&s->lock, flags); - if (avail <= 0) { - if (file->f_flags & O_NONBLOCK) { - if (!ret) - ret = -EAGAIN; - return ret; - } - interruptible_sleep_on(&db->wait); - if (signal_pending(current)) { - if (!ret) - ret = -ERESTARTSYS; - return ret; - } - } - } while (avail <= 0); + struct it8172_state *s = (struct it8172_state *)file->private_data; + struct dmabuf *db = &s->dma_dac; + ssize_t ret; + unsigned long flags; + int cnt, remainder, avail; + + if (ppos != &file->f_pos) + return -ESPIPE; + if (db->mapped) + return -ENXIO; + if (!access_ok(VERIFY_READ, buffer, count)) + return -EFAULT; + ret = 0; + + while (count > 0) { + // wait for space in playback buffer + do { + spin_lock_irqsave(&s->lock, flags); + avail = db->dmasize - db->count; + spin_unlock_irqrestore(&s->lock, flags); + if (avail <= 0) { + if (file->f_flags & O_NONBLOCK) { + if (!ret) + ret = -EAGAIN; + return ret; + } + interruptible_sleep_on(&db->wait); + if (signal_pending(current)) { + if (!ret) + ret = -ERESTARTSYS; + return ret; + } + } + } while (avail <= 0); - cnt = count > avail ? avail : count; - // copy to nextIn - if (copy_from_user(db->nextIn, buffer, cnt)) { - if (!ret) - ret = -EFAULT; - return ret; - } - - bufcnt = cnt; - if (cnt % db->fragsize) { - // round count up to nearest fragment, and fill remainder of - // fragment with silence - int newcnt = db->fragsize * ((cnt + db->fragsize) / db->fragsize); - memset(db->nextIn + cnt, (s->pcc & CC_DF) ? 0 : 0x80, newcnt - cnt); - cnt = newcnt; - } + // copy to nextIn + if ((cnt = copy_dmabuf_user(db, (char*)buffer, + count > avail ? + avail : count, 0)) < 0) { + if (!ret) + ret = -EFAULT; + return ret; + } - spin_lock_irqsave(&s->lock, flags); - db->count += cnt; - if (db->stopped) - start_dac(s); - spin_unlock_irqrestore(&s->lock, flags); + spin_lock_irqsave(&s->lock, flags); + db->count += cnt; + if (db->stopped) + start_dac(s); + spin_unlock_irqrestore(&s->lock, flags); - db->nextIn += cnt; - if (db->nextIn >= db->rawbuf + db->dmasize) - db->nextIn -= db->dmasize; + db->nextIn += cnt; + if (db->nextIn >= db->rawbuf + db->dmasize) + db->nextIn -= db->dmasize; - count -= bufcnt; - buffer += bufcnt; - ret += bufcnt; - } // while (count > 0) + count -= cnt; + buffer += cnt; + ret += cnt; + } // while (count > 0) - return ret; + /* + * See if the dma buffer count after this write call is + * aligned on a fragsize boundary. If not, fill buffer + * with silence to the next boundary, and let's hope this + * is just the last remainder of an audio playback. If not + * it means the user is not sending us fragsize chunks, in + * which case it's his/her fault that there are audio gaps + * in their playback. + */ + spin_lock_irqsave(&s->lock, flags); + remainder = db->count % db->fragsize; + if (remainder) { + int fill_cnt = db->fragsize - remainder; + memset(db->nextIn, 0, fill_cnt); + db->nextIn += fill_cnt; + if (db->nextIn >= db->rawbuf + db->dmasize) + db->nextIn -= db->dmasize; + db->count += fill_cnt; + } + spin_unlock_irqrestore(&s->lock, flags); + + return ret; } /* No kernel lock - we have our own spinlock */ static unsigned int it8172_poll(struct file *file, struct poll_table_struct *wait) { - struct it8172_state *s = (struct it8172_state *)file->private_data; - unsigned long flags; - unsigned int mask = 0; - - if (file->f_mode & FMODE_WRITE) - poll_wait(file, &s->dma_dac.wait, wait); - if (file->f_mode & FMODE_READ) - poll_wait(file, &s->dma_adc.wait, wait); - spin_lock_irqsave(&s->lock, flags); - if (file->f_mode & FMODE_READ) { - if (s->dma_adc.count >= (signed)s->dma_adc.fragsize) - mask |= POLLIN | POLLRDNORM; - } - if (file->f_mode & FMODE_WRITE) { - if (s->dma_dac.mapped) { - if (s->dma_dac.count >= (signed)s->dma_dac.fragsize) - mask |= POLLOUT | POLLWRNORM; - } else { - if ((signed)s->dma_dac.dmasize >= - s->dma_dac.count + (signed)s->dma_dac.fragsize) - mask |= POLLOUT | POLLWRNORM; - } - } - spin_unlock_irqrestore(&s->lock, flags); - return mask; + struct it8172_state *s = (struct it8172_state *)file->private_data; + unsigned long flags; + unsigned int mask = 0; + + if (file->f_mode & FMODE_WRITE) { + if (!s->dma_dac.ready) + return 0; + poll_wait(file, &s->dma_dac.wait, wait); + } + if (file->f_mode & FMODE_READ) { + if (!s->dma_adc.ready) + return 0; + poll_wait(file, &s->dma_adc.wait, wait); + } + + spin_lock_irqsave(&s->lock, flags); + if (file->f_mode & FMODE_READ) { + if (s->dma_adc.count >= (signed)s->dma_adc.fragsize) + mask |= POLLIN | POLLRDNORM; + } + if (file->f_mode & FMODE_WRITE) { + if (s->dma_dac.mapped) { + if (s->dma_dac.count >= (signed)s->dma_dac.fragsize) + mask |= POLLOUT | POLLWRNORM; + } else { + if ((signed)s->dma_dac.dmasize >= + s->dma_dac.count + (signed)s->dma_dac.fragsize) + mask |= POLLOUT | POLLWRNORM; + } + } + spin_unlock_irqrestore(&s->lock, flags); + return mask; } static int it8172_mmap(struct file *file, struct vm_area_struct *vma) { - struct it8172_state *s = (struct it8172_state *)file->private_data; - struct dmabuf *db; - unsigned long size; - - lock_kernel(); - if (vma->vm_flags & VM_WRITE) - db = &s->dma_dac; - else if (vma->vm_flags & VM_READ) - db = &s->dma_adc; - else { - unlock_kernel(); - return -EINVAL; - } - if (vma->vm_pgoff != 0) { - unlock_kernel(); - return -EINVAL; - } - size = vma->vm_end - vma->vm_start; - if (size > (PAGE_SIZE << db->buforder)) { - unlock_kernel(); - return -EINVAL; - } - if (remap_page_range(vma, vma->vm_start, virt_to_phys(db->rawbuf), - size, vma->vm_page_prot)) { + struct it8172_state *s = (struct it8172_state *)file->private_data; + struct dmabuf *db; + unsigned long size; + + lock_kernel(); + if (vma->vm_flags & VM_WRITE) + db = &s->dma_dac; + else if (vma->vm_flags & VM_READ) + db = &s->dma_adc; + else { + unlock_kernel(); + return -EINVAL; + } + if (vma->vm_pgoff != 0) { + unlock_kernel(); + return -EINVAL; + } + size = vma->vm_end - vma->vm_start; + if (size > (PAGE_SIZE << db->buforder)) { + unlock_kernel(); + return -EINVAL; + } + if (remap_page_range(vma, vma->vm_start, virt_to_phys(db->rawbuf), + size, vma->vm_page_prot)) { + unlock_kernel(); + return -EAGAIN; + } + db->mapped = 1; unlock_kernel(); - return -EAGAIN; - } - db->mapped = 1; - unlock_kernel(); - return 0; + return 0; } #ifdef IT8172_VERBOSE_DEBUG static struct ioctl_str_t { - unsigned int cmd; - const char* str; + unsigned int cmd; + const char* str; } ioctl_str[] = { - {SNDCTL_DSP_RESET, "SNDCTL_DSP_RESET"}, - {SNDCTL_DSP_SYNC, "SNDCTL_DSP_SYNC"}, - {SNDCTL_DSP_SPEED, "SNDCTL_DSP_SPEED"}, - {SNDCTL_DSP_STEREO, "SNDCTL_DSP_STEREO"}, - {SNDCTL_DSP_GETBLKSIZE, "SNDCTL_DSP_GETBLKSIZE"}, - {SNDCTL_DSP_SAMPLESIZE, "SNDCTL_DSP_SAMPLESIZE"}, - {SNDCTL_DSP_CHANNELS, "SNDCTL_DSP_CHANNELS"}, - {SOUND_PCM_WRITE_CHANNELS, "SOUND_PCM_WRITE_CHANNELS"}, - {SOUND_PCM_WRITE_FILTER, "SOUND_PCM_WRITE_FILTER"}, - {SNDCTL_DSP_POST, "SNDCTL_DSP_POST"}, - {SNDCTL_DSP_SUBDIVIDE, "SNDCTL_DSP_SUBDIVIDE"}, - {SNDCTL_DSP_SETFRAGMENT, "SNDCTL_DSP_SETFRAGMENT"}, - {SNDCTL_DSP_GETFMTS, "SNDCTL_DSP_GETFMTS"}, - {SNDCTL_DSP_SETFMT, "SNDCTL_DSP_SETFMT"}, - {SNDCTL_DSP_GETOSPACE, "SNDCTL_DSP_GETOSPACE"}, - {SNDCTL_DSP_GETISPACE, "SNDCTL_DSP_GETISPACE"}, - {SNDCTL_DSP_NONBLOCK, "SNDCTL_DSP_NONBLOCK"}, - {SNDCTL_DSP_GETCAPS, "SNDCTL_DSP_GETCAPS"}, - {SNDCTL_DSP_GETTRIGGER, "SNDCTL_DSP_GETTRIGGER"}, - {SNDCTL_DSP_SETTRIGGER, "SNDCTL_DSP_SETTRIGGER"}, - {SNDCTL_DSP_GETIPTR, "SNDCTL_DSP_GETIPTR"}, - {SNDCTL_DSP_GETOPTR, "SNDCTL_DSP_GETOPTR"}, - {SNDCTL_DSP_MAPINBUF, "SNDCTL_DSP_MAPINBUF"}, - {SNDCTL_DSP_MAPOUTBUF, "SNDCTL_DSP_MAPOUTBUF"}, - {SNDCTL_DSP_SETSYNCRO, "SNDCTL_DSP_SETSYNCRO"}, - {SNDCTL_DSP_SETDUPLEX, "SNDCTL_DSP_SETDUPLEX"}, - {SNDCTL_DSP_GETODELAY, "SNDCTL_DSP_GETODELAY"}, - {SNDCTL_DSP_GETCHANNELMASK, "SNDCTL_DSP_GETCHANNELMASK"}, - {SNDCTL_DSP_BIND_CHANNEL, "SNDCTL_DSP_BIND_CHANNEL"}, - {OSS_GETVERSION, "OSS_GETVERSION"}, - {SOUND_PCM_READ_RATE, "SOUND_PCM_READ_RATE"}, - {SOUND_PCM_READ_CHANNELS, "SOUND_PCM_READ_CHANNELS"}, - {SOUND_PCM_READ_BITS, "SOUND_PCM_READ_BITS"}, - {SOUND_PCM_READ_FILTER, "SOUND_PCM_READ_FILTER"} + {SNDCTL_DSP_RESET, "SNDCTL_DSP_RESET"}, + {SNDCTL_DSP_SYNC, "SNDCTL_DSP_SYNC"}, + {SNDCTL_DSP_SPEED, "SNDCTL_DSP_SPEED"}, + {SNDCTL_DSP_STEREO, "SNDCTL_DSP_STEREO"}, + {SNDCTL_DSP_GETBLKSIZE, "SNDCTL_DSP_GETBLKSIZE"}, + {SNDCTL_DSP_SAMPLESIZE, "SNDCTL_DSP_SAMPLESIZE"}, + {SNDCTL_DSP_CHANNELS, "SNDCTL_DSP_CHANNELS"}, + {SOUND_PCM_WRITE_CHANNELS, "SOUND_PCM_WRITE_CHANNELS"}, + {SOUND_PCM_WRITE_FILTER, "SOUND_PCM_WRITE_FILTER"}, + {SNDCTL_DSP_POST, "SNDCTL_DSP_POST"}, + {SNDCTL_DSP_SUBDIVIDE, "SNDCTL_DSP_SUBDIVIDE"}, + {SNDCTL_DSP_SETFRAGMENT, "SNDCTL_DSP_SETFRAGMENT"}, + {SNDCTL_DSP_GETFMTS, "SNDCTL_DSP_GETFMTS"}, + {SNDCTL_DSP_SETFMT, "SNDCTL_DSP_SETFMT"}, + {SNDCTL_DSP_GETOSPACE, "SNDCTL_DSP_GETOSPACE"}, + {SNDCTL_DSP_GETISPACE, "SNDCTL_DSP_GETISPACE"}, + {SNDCTL_DSP_NONBLOCK, "SNDCTL_DSP_NONBLOCK"}, + {SNDCTL_DSP_GETCAPS, "SNDCTL_DSP_GETCAPS"}, + {SNDCTL_DSP_GETTRIGGER, "SNDCTL_DSP_GETTRIGGER"}, + {SNDCTL_DSP_SETTRIGGER, "SNDCTL_DSP_SETTRIGGER"}, + {SNDCTL_DSP_GETIPTR, "SNDCTL_DSP_GETIPTR"}, + {SNDCTL_DSP_GETOPTR, "SNDCTL_DSP_GETOPTR"}, + {SNDCTL_DSP_MAPINBUF, "SNDCTL_DSP_MAPINBUF"}, + {SNDCTL_DSP_MAPOUTBUF, "SNDCTL_DSP_MAPOUTBUF"}, + {SNDCTL_DSP_SETSYNCRO, "SNDCTL_DSP_SETSYNCRO"}, + {SNDCTL_DSP_SETDUPLEX, "SNDCTL_DSP_SETDUPLEX"}, + {SNDCTL_DSP_GETODELAY, "SNDCTL_DSP_GETODELAY"}, + {SNDCTL_DSP_GETCHANNELMASK, "SNDCTL_DSP_GETCHANNELMASK"}, + {SNDCTL_DSP_BIND_CHANNEL, "SNDCTL_DSP_BIND_CHANNEL"}, + {OSS_GETVERSION, "OSS_GETVERSION"}, + {SOUND_PCM_READ_RATE, "SOUND_PCM_READ_RATE"}, + {SOUND_PCM_READ_CHANNELS, "SOUND_PCM_READ_CHANNELS"}, + {SOUND_PCM_READ_BITS, "SOUND_PCM_READ_BITS"}, + {SOUND_PCM_READ_FILTER, "SOUND_PCM_READ_FILTER"} }; #endif static int it8172_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { - struct it8172_state *s = (struct it8172_state *)file->private_data; - unsigned long flags; - audio_buf_info abinfo; - count_info cinfo; - int count; - int val, mapped, ret, diff; + struct it8172_state *s = (struct it8172_state *)file->private_data; + unsigned long flags; + audio_buf_info abinfo; + count_info cinfo; + int count; + int val, mapped, ret, diff; - mapped = ((file->f_mode & FMODE_WRITE) && s->dma_dac.mapped) || - ((file->f_mode & FMODE_READ) && s->dma_adc.mapped); + mapped = ((file->f_mode & FMODE_WRITE) && s->dma_dac.mapped) || + ((file->f_mode & FMODE_READ) && s->dma_adc.mapped); #ifdef IT8172_VERBOSE_DEBUG - for (count=0; countf_mode & FMODE_WRITE) - return drain_dac(s, file->f_flags & O_NONBLOCK); - return 0; + switch (cmd) { + case OSS_GETVERSION: + return put_user(SOUND_VERSION, (int *)arg); + + case SNDCTL_DSP_SYNC: + if (file->f_mode & FMODE_WRITE) + return drain_dac(s, file->f_flags & O_NONBLOCK); + return 0; - case SNDCTL_DSP_SETDUPLEX: - return 0; + case SNDCTL_DSP_SETDUPLEX: + return 0; - case SNDCTL_DSP_GETCAPS: - return put_user(DSP_CAP_DUPLEX | DSP_CAP_REALTIME | - DSP_CAP_TRIGGER | DSP_CAP_MMAP, (int *)arg); + case SNDCTL_DSP_GETCAPS: + return put_user(DSP_CAP_DUPLEX | DSP_CAP_REALTIME | + DSP_CAP_TRIGGER | DSP_CAP_MMAP, (int *)arg); - case SNDCTL_DSP_RESET: - if (file->f_mode & FMODE_WRITE) { - stop_dac(s); - synchronize_irq(s->irq); - s->dma_dac.count = s->dma_dac.total_bytes = 0; - s->dma_dac.nextIn = s->dma_dac.nextOut = s->dma_dac.rawbuf; - } - if (file->f_mode & FMODE_READ) { - stop_adc(s); - synchronize_irq(s->irq); - s->dma_adc.count = s->dma_adc.total_bytes = 0; - s->dma_adc.nextIn = s->dma_adc.nextOut = s->dma_adc.rawbuf; - } - return 0; + case SNDCTL_DSP_RESET: + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + synchronize_irq(); + s->dma_dac.count = s->dma_dac.total_bytes = 0; + s->dma_dac.nextIn = s->dma_dac.nextOut = + s->dma_dac.rawbuf; + } + if (file->f_mode & FMODE_READ) { + stop_adc(s); + synchronize_irq(); + s->dma_adc.count = s->dma_adc.total_bytes = 0; + s->dma_adc.nextIn = s->dma_adc.nextOut = + s->dma_adc.rawbuf; + } + return 0; - case SNDCTL_DSP_SPEED: - if (get_user(val, (int *)arg)) - return -EFAULT; - if (val >= 0) { - if (file->f_mode & FMODE_READ) { - stop_adc(s); - set_adc_rate(s, val); - if ((ret = prog_dmabuf_adc(s))) - return ret; - } - if (file->f_mode & FMODE_WRITE) { - stop_dac(s); - set_dac_rate(s, val); - if ((ret = prog_dmabuf_dac(s))) - return ret; - } - } - return put_user((file->f_mode & FMODE_READ) ? - s->adcrate : s->dacrate, (int *)arg); - - case SNDCTL_DSP_STEREO: - if (get_user(val, (int *)arg)) - return -EFAULT; - if (file->f_mode & FMODE_READ) { - stop_adc(s); - if (val) - s->capcc |= CC_SM; - else - s->capcc &= ~CC_SM; - outw(s->capcc, s->io+IT_AC_CAPCC); - if ((ret = prog_dmabuf_adc(s))) - return ret; - } - if (file->f_mode & FMODE_WRITE) { - stop_dac(s); - if (val) - s->pcc |= CC_SM; - else - s->pcc &= ~CC_SM; - outw(s->pcc, s->io+IT_AC_PCC); - if ((ret = prog_dmabuf_dac(s))) - return ret; - } - return 0; + case SNDCTL_DSP_SPEED: + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val >= 0) { + if (file->f_mode & FMODE_READ) { + stop_adc(s); + set_adc_rate(s, val); + if ((ret = prog_dmabuf_adc(s))) + return ret; + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + set_dac_rate(s, val); + if ((ret = prog_dmabuf_dac(s))) + return ret; + } + } + return put_user((file->f_mode & FMODE_READ) ? + s->adcrate : s->dacrate, (int *)arg); - case SNDCTL_DSP_CHANNELS: - if (get_user(val, (int *)arg)) - return -EFAULT; - if (val != 0) { - if (file->f_mode & FMODE_READ) { - stop_adc(s); - if (val >= 2) { - val = 2; - s->capcc |= CC_SM; + case SNDCTL_DSP_STEREO: + if (get_user(val, (int *)arg)) + return -EFAULT; + if (file->f_mode & FMODE_READ) { + stop_adc(s); + if (val) + s->capcc |= CC_SM; + else + s->capcc &= ~CC_SM; + outw(s->capcc, s->io+IT_AC_CAPCC); + if ((ret = prog_dmabuf_adc(s))) + return ret; } - else - s->capcc &= ~CC_SM; - outw(s->capcc, s->io+IT_AC_CAPCC); - if ((ret = prog_dmabuf_adc(s))) - return ret; - } - if (file->f_mode & FMODE_WRITE) { - stop_dac(s); - switch (val) { - case 1: - s->pcc &= ~CC_SM; - break; - case 2: - s->pcc |= CC_SM; - break; - default: - // FIX! support multichannel??? - val = 2; - s->pcc |= CC_SM; - break; + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + if (val) + s->pcc |= CC_SM; + else + s->pcc &= ~CC_SM; + outw(s->pcc, s->io+IT_AC_PCC); + if ((ret = prog_dmabuf_dac(s))) + return ret; } - outw(s->pcc, s->io+IT_AC_PCC); - if ((ret = prog_dmabuf_dac(s))) - return ret; - } - } - return put_user(val, (int *)arg); + return 0; + + case SNDCTL_DSP_CHANNELS: + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val != 0) { + if (file->f_mode & FMODE_READ) { + stop_adc(s); + if (val >= 2) { + val = 2; + s->capcc |= CC_SM; + } + else + s->capcc &= ~CC_SM; + outw(s->capcc, s->io+IT_AC_CAPCC); + if ((ret = prog_dmabuf_adc(s))) + return ret; + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + switch (val) { + case 1: + s->pcc &= ~CC_SM; + break; + case 2: + s->pcc |= CC_SM; + break; + default: + // FIX! support multichannel??? + val = 2; + s->pcc |= CC_SM; + break; + } + outw(s->pcc, s->io+IT_AC_PCC); + if ((ret = prog_dmabuf_dac(s))) + return ret; + } + } + return put_user(val, (int *)arg); - case SNDCTL_DSP_GETFMTS: /* Returns a mask */ - return put_user(AFMT_S16_LE|AFMT_U8, (int *)arg); + case SNDCTL_DSP_GETFMTS: /* Returns a mask */ + return put_user(AFMT_S16_LE|AFMT_U8, (int *)arg); - case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/ - if (get_user(val, (int *)arg)) - return -EFAULT; - if (val != AFMT_QUERY) { - if (file->f_mode & FMODE_READ) { - stop_adc(s); - if (val == AFMT_S16_LE) - s->capcc |= CC_DF; - else { - val = AFMT_U8; - s->capcc &= ~CC_DF; - } - outw(s->capcc, s->io+IT_AC_CAPCC); - if ((ret = prog_dmabuf_adc(s))) - return ret; - } - if (file->f_mode & FMODE_WRITE) { - stop_dac(s); - if (val == AFMT_S16_LE) - s->pcc |= CC_DF; - else { - val = AFMT_U8; - s->pcc &= ~CC_DF; + case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/ + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val != AFMT_QUERY) { + if (file->f_mode & FMODE_READ) { + stop_adc(s); + if (val == AFMT_S16_LE) + s->capcc |= CC_DF; + else { + val = AFMT_U8; + s->capcc &= ~CC_DF; + } + outw(s->capcc, s->io+IT_AC_CAPCC); + if ((ret = prog_dmabuf_adc(s))) + return ret; + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + if (val == AFMT_S16_LE) + s->pcc |= CC_DF; + else { + val = AFMT_U8; + s->pcc &= ~CC_DF; + } + outw(s->pcc, s->io+IT_AC_PCC); + if ((ret = prog_dmabuf_dac(s))) + return ret; + } + } else { + if (file->f_mode & FMODE_READ) + val = (s->capcc & CC_DF) ? + AFMT_S16_LE : AFMT_U8; + else + val = (s->pcc & CC_DF) ? + AFMT_S16_LE : AFMT_U8; } - outw(s->pcc, s->io+IT_AC_PCC); - if ((ret = prog_dmabuf_dac(s))) - return ret; - } - } else { - if (file->f_mode & FMODE_READ) - val = (s->capcc & CC_DF) ? AFMT_S16_LE : AFMT_U8; - else - val = (s->pcc & CC_DF) ? AFMT_S16_LE : AFMT_U8; - } - return put_user(val, (int *)arg); + return put_user(val, (int *)arg); - case SNDCTL_DSP_POST: - return 0; + case SNDCTL_DSP_POST: + return 0; - case SNDCTL_DSP_GETTRIGGER: - val = 0; - spin_lock_irqsave(&s->lock, flags); - if (file->f_mode & FMODE_READ && !s->dma_adc.stopped) - val |= PCM_ENABLE_INPUT; - if (file->f_mode & FMODE_WRITE && !s->dma_dac.stopped) - val |= PCM_ENABLE_OUTPUT; - spin_unlock_irqrestore(&s->lock, flags); - return put_user(val, (int *)arg); + case SNDCTL_DSP_GETTRIGGER: + val = 0; + spin_lock_irqsave(&s->lock, flags); + if (file->f_mode & FMODE_READ && !s->dma_adc.stopped) + val |= PCM_ENABLE_INPUT; + if (file->f_mode & FMODE_WRITE && !s->dma_dac.stopped) + val |= PCM_ENABLE_OUTPUT; + spin_unlock_irqrestore(&s->lock, flags); + return put_user(val, (int *)arg); - case SNDCTL_DSP_SETTRIGGER: - if (get_user(val, (int *)arg)) - return -EFAULT; - if (file->f_mode & FMODE_READ) { - if (val & PCM_ENABLE_INPUT) - start_adc(s); - else - stop_adc(s); - } - if (file->f_mode & FMODE_WRITE) { - if (val & PCM_ENABLE_OUTPUT) - start_dac(s); - else - stop_dac(s); - } - return 0; + case SNDCTL_DSP_SETTRIGGER: + if (get_user(val, (int *)arg)) + return -EFAULT; + if (file->f_mode & FMODE_READ) { + if (val & PCM_ENABLE_INPUT) + start_adc(s); + else + stop_adc(s); + } + if (file->f_mode & FMODE_WRITE) { + if (val & PCM_ENABLE_OUTPUT) + start_dac(s); + else + stop_dac(s); + } + return 0; - case SNDCTL_DSP_GETOSPACE: - if (!(file->f_mode & FMODE_WRITE)) - return -EINVAL; - abinfo.fragsize = s->dma_dac.fragsize; - spin_lock_irqsave(&s->lock, flags); - count = s->dma_dac.count; - if (!s->dma_dac.stopped) - count -= (s->dma_dac.fragsize - inw(s->io+IT_AC_PCDL)); - spin_unlock_irqrestore(&s->lock, flags); - if (count < 0) - count = 0; - abinfo.bytes = s->dma_dac.dmasize - count; - abinfo.fragstotal = s->dma_dac.numfrag; - abinfo.fragments = abinfo.bytes >> s->dma_dac.fragshift; - return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; - - case SNDCTL_DSP_GETISPACE: - if (!(file->f_mode & FMODE_READ)) - return -EINVAL; - abinfo.fragsize = s->dma_adc.fragsize; - spin_lock_irqsave(&s->lock, flags); - count = s->dma_adc.count; - if (!s->dma_adc.stopped) - count += (s->dma_adc.fragsize - inw(s->io+IT_AC_CAPCDL)); - spin_unlock_irqrestore(&s->lock, flags); - if (count < 0) - count = 0; - abinfo.bytes = count; - abinfo.fragstotal = s->dma_adc.numfrag; - abinfo.fragments = abinfo.bytes >> s->dma_adc.fragshift; - return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; + case SNDCTL_DSP_GETOSPACE: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + abinfo.fragsize = s->dma_dac.fragsize; + spin_lock_irqsave(&s->lock, flags); + count = s->dma_dac.count; + if (!s->dma_dac.stopped) + count -= (s->dma_dac.fragsize - + inw(s->io+IT_AC_PCDL)); + spin_unlock_irqrestore(&s->lock, flags); + if (count < 0) + count = 0; + abinfo.bytes = s->dma_dac.dmasize - count; + abinfo.fragstotal = s->dma_dac.numfrag; + abinfo.fragments = abinfo.bytes >> s->dma_dac.fragshift; + return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? + -EFAULT : 0; + + case SNDCTL_DSP_GETISPACE: + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + abinfo.fragsize = s->dma_adc.fragsize; + spin_lock_irqsave(&s->lock, flags); + count = s->dma_adc.count; + if (!s->dma_adc.stopped) + count += (s->dma_adc.fragsize - + inw(s->io+IT_AC_CAPCDL)); + spin_unlock_irqrestore(&s->lock, flags); + if (count < 0) + count = 0; + abinfo.bytes = count; + abinfo.fragstotal = s->dma_adc.numfrag; + abinfo.fragments = abinfo.bytes >> s->dma_adc.fragshift; + return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? + -EFAULT : 0; - case SNDCTL_DSP_NONBLOCK: - file->f_flags |= O_NONBLOCK; - return 0; - - case SNDCTL_DSP_GETODELAY: - if (!(file->f_mode & FMODE_WRITE)) - return -EINVAL; - spin_lock_irqsave(&s->lock, flags); - count = s->dma_dac.count; - if (!s->dma_dac.stopped) - count -= (s->dma_dac.fragsize - inw(s->io+IT_AC_PCDL)); - spin_unlock_irqrestore(&s->lock, flags); - if (count < 0) - count = 0; - return put_user(count, (int *)arg); - - case SNDCTL_DSP_GETIPTR: - if (!(file->f_mode & FMODE_READ)) - return -EINVAL; - spin_lock_irqsave(&s->lock, flags); - cinfo.bytes = s->dma_adc.total_bytes; - count = s->dma_adc.count; - if (!s->dma_adc.stopped) { - diff = s->dma_adc.fragsize - inw(s->io+IT_AC_CAPCDL); - count += diff; - cinfo.bytes += diff; - cinfo.ptr = inl(s->io+s->dma_adc.curBufPtr) - s->dma_adc.dmaaddr; - } else - cinfo.ptr = virt_to_bus(s->dma_adc.nextIn) - s->dma_adc.dmaaddr; - if (s->dma_adc.mapped) - s->dma_adc.count &= s->dma_adc.fragsize-1; - spin_unlock_irqrestore(&s->lock, flags); - if (count < 0) - count = 0; - cinfo.blocks = count >> s->dma_adc.fragshift; - if (copy_to_user((void *)arg, &cinfo, sizeof(cinfo))) - return -EFAULT; - return 0; - - case SNDCTL_DSP_GETOPTR: - if (!(file->f_mode & FMODE_READ)) - return -EINVAL; - spin_lock_irqsave(&s->lock, flags); - cinfo.bytes = s->dma_dac.total_bytes; - count = s->dma_dac.count; - if (!s->dma_dac.stopped) { - diff = s->dma_dac.fragsize - inw(s->io+IT_AC_CAPCDL); - count -= diff; - cinfo.bytes += diff; - cinfo.ptr = inl(s->io+s->dma_dac.curBufPtr) - s->dma_dac.dmaaddr; - } else - cinfo.ptr = virt_to_bus(s->dma_dac.nextOut) - s->dma_dac.dmaaddr; - if (s->dma_dac.mapped) - s->dma_dac.count &= s->dma_dac.fragsize-1; - spin_unlock_irqrestore(&s->lock, flags); - if (count < 0) - count = 0; - cinfo.blocks = count >> s->dma_dac.fragshift; - if (copy_to_user((void *)arg, &cinfo, sizeof(cinfo))) - return -EFAULT; - return 0; - - case SNDCTL_DSP_GETBLKSIZE: - if (file->f_mode & FMODE_WRITE) - return put_user(s->dma_dac.fragsize, (int *)arg); - else - return put_user(s->dma_adc.fragsize, (int *)arg); + case SNDCTL_DSP_NONBLOCK: + file->f_flags |= O_NONBLOCK; + return 0; + + case SNDCTL_DSP_GETODELAY: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + spin_lock_irqsave(&s->lock, flags); + count = s->dma_dac.count; + if (!s->dma_dac.stopped) + count -= (s->dma_dac.fragsize - + inw(s->io+IT_AC_PCDL)); + spin_unlock_irqrestore(&s->lock, flags); + if (count < 0) + count = 0; + return put_user(count, (int *)arg); + + case SNDCTL_DSP_GETIPTR: + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + spin_lock_irqsave(&s->lock, flags); + cinfo.bytes = s->dma_adc.total_bytes; + count = s->dma_adc.count; + if (!s->dma_adc.stopped) { + diff = s->dma_adc.fragsize - inw(s->io+IT_AC_CAPCDL); + count += diff; + cinfo.bytes += diff; + cinfo.ptr = inl(s->io+s->dma_adc.curBufPtr) - + s->dma_adc.dmaaddr; + } else + cinfo.ptr = virt_to_bus(s->dma_adc.nextIn) - + s->dma_adc.dmaaddr; + if (s->dma_adc.mapped) + s->dma_adc.count &= s->dma_adc.fragsize-1; + spin_unlock_irqrestore(&s->lock, flags); + if (count < 0) + count = 0; + cinfo.blocks = count >> s->dma_adc.fragshift; + return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)); + + case SNDCTL_DSP_GETOPTR: + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + spin_lock_irqsave(&s->lock, flags); + cinfo.bytes = s->dma_dac.total_bytes; + count = s->dma_dac.count; + if (!s->dma_dac.stopped) { + diff = s->dma_dac.fragsize - inw(s->io+IT_AC_CAPCDL); + count -= diff; + cinfo.bytes += diff; + cinfo.ptr = inl(s->io+s->dma_dac.curBufPtr) - + s->dma_dac.dmaaddr; + } else + cinfo.ptr = virt_to_bus(s->dma_dac.nextOut) - + s->dma_dac.dmaaddr; + if (s->dma_dac.mapped) + s->dma_dac.count &= s->dma_dac.fragsize-1; + spin_unlock_irqrestore(&s->lock, flags); + if (count < 0) + count = 0; + cinfo.blocks = count >> s->dma_dac.fragshift; + return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)); + + case SNDCTL_DSP_GETBLKSIZE: + if (file->f_mode & FMODE_WRITE) + return put_user(s->dma_dac.fragsize, (int *)arg); + else + return put_user(s->dma_adc.fragsize, (int *)arg); - case SNDCTL_DSP_SETFRAGMENT: - if (get_user(val, (int *)arg)) - return -EFAULT; - if (file->f_mode & FMODE_READ) { - stop_adc(s); - s->dma_adc.ossfragshift = val & 0xffff; - s->dma_adc.ossmaxfrags = (val >> 16) & 0xffff; - if (s->dma_adc.ossfragshift < 4) - s->dma_adc.ossfragshift = 4; - if (s->dma_adc.ossfragshift > 15) - s->dma_adc.ossfragshift = 15; - if (s->dma_adc.ossmaxfrags < 4) - s->dma_adc.ossmaxfrags = 4; - if ((ret = prog_dmabuf_adc(s))) - return ret; - } - if (file->f_mode & FMODE_WRITE) { - stop_dac(s); - s->dma_dac.ossfragshift = val & 0xffff; - s->dma_dac.ossmaxfrags = (val >> 16) & 0xffff; - if (s->dma_dac.ossfragshift < 4) - s->dma_dac.ossfragshift = 4; - if (s->dma_dac.ossfragshift > 15) - s->dma_dac.ossfragshift = 15; - if (s->dma_dac.ossmaxfrags < 4) - s->dma_dac.ossmaxfrags = 4; - if ((ret = prog_dmabuf_dac(s))) - return ret; - } - return 0; + case SNDCTL_DSP_SETFRAGMENT: + if (get_user(val, (int *)arg)) + return -EFAULT; + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dma_adc.ossfragshift = val & 0xffff; + s->dma_adc.ossmaxfrags = (val >> 16) & 0xffff; + if (s->dma_adc.ossfragshift < 4) + s->dma_adc.ossfragshift = 4; + if (s->dma_adc.ossfragshift > 15) + s->dma_adc.ossfragshift = 15; + if (s->dma_adc.ossmaxfrags < 4) + s->dma_adc.ossmaxfrags = 4; + if ((ret = prog_dmabuf_adc(s))) + return ret; + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + s->dma_dac.ossfragshift = val & 0xffff; + s->dma_dac.ossmaxfrags = (val >> 16) & 0xffff; + if (s->dma_dac.ossfragshift < 4) + s->dma_dac.ossfragshift = 4; + if (s->dma_dac.ossfragshift > 15) + s->dma_dac.ossfragshift = 15; + if (s->dma_dac.ossmaxfrags < 4) + s->dma_dac.ossmaxfrags = 4; + if ((ret = prog_dmabuf_dac(s))) + return ret; + } + return 0; - case SNDCTL_DSP_SUBDIVIDE: - if ((file->f_mode & FMODE_READ && s->dma_adc.subdivision) || - (file->f_mode & FMODE_WRITE && s->dma_dac.subdivision)) - return -EINVAL; - if (get_user(val, (int *)arg)) - return -EFAULT; - if (val != 1 && val != 2 && val != 4) - return -EINVAL; - if (file->f_mode & FMODE_READ) { - stop_adc(s); - s->dma_adc.subdivision = val; - if ((ret = prog_dmabuf_adc(s))) - return ret; - } - if (file->f_mode & FMODE_WRITE) { - stop_dac(s); - s->dma_dac.subdivision = val; - if ((ret = prog_dmabuf_dac(s))) - return ret; - } - return 0; + case SNDCTL_DSP_SUBDIVIDE: + if ((file->f_mode & FMODE_READ && s->dma_adc.subdivision) || + (file->f_mode & FMODE_WRITE && s->dma_dac.subdivision)) + return -EINVAL; + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val != 1 && val != 2 && val != 4) + return -EINVAL; + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dma_adc.subdivision = val; + if ((ret = prog_dmabuf_adc(s))) + return ret; + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + s->dma_dac.subdivision = val; + if ((ret = prog_dmabuf_dac(s))) + return ret; + } + return 0; - case SOUND_PCM_READ_RATE: - return put_user((file->f_mode & FMODE_READ) ? - s->adcrate : s->dacrate, (int *)arg); - - case SOUND_PCM_READ_CHANNELS: - if (file->f_mode & FMODE_READ) - return put_user((s->capcc & CC_SM) ? 2 : 1, (int *)arg); - else - return put_user((s->pcc & CC_SM) ? 2 : 1, (int *)arg); + case SOUND_PCM_READ_RATE: + return put_user((file->f_mode & FMODE_READ) ? + s->adcrate : s->dacrate, (int *)arg); + + case SOUND_PCM_READ_CHANNELS: + if (file->f_mode & FMODE_READ) + return put_user((s->capcc & CC_SM) ? 2 : 1, + (int *)arg); + else + return put_user((s->pcc & CC_SM) ? 2 : 1, + (int *)arg); - case SOUND_PCM_READ_BITS: - if (file->f_mode & FMODE_READ) - return put_user((s->capcc & CC_DF) ? 16 : 8, (int *)arg); - else - return put_user((s->pcc & CC_DF) ? 16 : 8, (int *)arg); + case SOUND_PCM_READ_BITS: + if (file->f_mode & FMODE_READ) + return put_user((s->capcc & CC_DF) ? 16 : 8, + (int *)arg); + else + return put_user((s->pcc & CC_DF) ? 16 : 8, + (int *)arg); - case SOUND_PCM_WRITE_FILTER: - case SNDCTL_DSP_SETSYNCRO: - case SOUND_PCM_READ_FILTER: - return -EINVAL; - } + case SOUND_PCM_WRITE_FILTER: + case SNDCTL_DSP_SETSYNCRO: + case SOUND_PCM_READ_FILTER: + return -EINVAL; + } - return mixdev_ioctl(&s->codec, cmd, arg); + return mixdev_ioctl(s->codec, cmd, arg); } static int it8172_open(struct inode *inode, struct file *file) { - int minor = minor(inode->i_rdev); - DECLARE_WAITQUEUE(wait, current); - unsigned long flags; - struct list_head *list; - struct it8172_state *s; - int ret; - - for (list = devs.next; ; list = list->next) { - if (list == &devs) - return -ENODEV; - s = list_entry(list, struct it8172_state, devs); - if (!((s->dev_audio ^ minor) & ~0xf)) - break; - } - file->private_data = s; - /* wait for device to become free */ - down(&s->open_sem); - while (s->open_mode & file->f_mode) { - if (file->f_flags & O_NONBLOCK) { - up(&s->open_sem); - return -EBUSY; + int minor = MINOR(inode->i_rdev); + DECLARE_WAITQUEUE(wait, current); + unsigned long flags; + struct list_head *list; + struct it8172_state *s; + int ret; + +#ifdef IT8172_VERBOSE_DEBUG + if (file->f_flags & O_NONBLOCK) + dbg(__FUNCTION__ ": non-blocking"); + else + dbg(__FUNCTION__ ": blocking"); +#endif + + for (list = devs.next; ; list = list->next) { + if (list == &devs) + return -ENODEV; + s = list_entry(list, struct it8172_state, devs); + if (!((s->dev_audio ^ minor) & ~0xf)) + break; } - add_wait_queue(&s->open_wait, &wait); - __set_current_state(TASK_INTERRUPTIBLE); - up(&s->open_sem); - schedule(); - remove_wait_queue(&s->open_wait, &wait); - set_current_state(TASK_RUNNING); - if (signal_pending(current)) - return -ERESTARTSYS; + file->private_data = s; + /* wait for device to become free */ down(&s->open_sem); - } + while (s->open_mode & file->f_mode) { + if (file->f_flags & O_NONBLOCK) { + up(&s->open_sem); + return -EBUSY; + } + add_wait_queue(&s->open_wait, &wait); + __set_current_state(TASK_INTERRUPTIBLE); + up(&s->open_sem); + schedule(); + remove_wait_queue(&s->open_wait, &wait); + set_current_state(TASK_RUNNING); + if (signal_pending(current)) + return -ERESTARTSYS; + down(&s->open_sem); + } - spin_lock_irqsave(&s->lock, flags); + spin_lock_irqsave(&s->lock, flags); - if (file->f_mode & FMODE_READ) { - s->dma_adc.ossfragshift = s->dma_adc.ossmaxfrags = - s->dma_adc.subdivision = s->dma_adc.total_bytes = 0; - s->capcc &= ~(CC_SM | CC_DF); - set_adc_rate(s, 8000); - if ((minor & 0xf) == SND_DEV_DSP16) - s->capcc |= CC_DF; - outw(s->capcc, s->io+IT_AC_CAPCC); - if ((ret = prog_dmabuf_adc(s))) - return ret; - } - if (file->f_mode & FMODE_WRITE) { - s->dma_dac.ossfragshift = s->dma_dac.ossmaxfrags = - s->dma_dac.subdivision = s->dma_dac.total_bytes = 0; - s->pcc &= ~(CC_SM | CC_DF); - set_dac_rate(s, 8000); - if ((minor & 0xf) == SND_DEV_DSP16) - s->pcc |= CC_DF; - outw(s->pcc, s->io+IT_AC_PCC); - if ((ret = prog_dmabuf_dac(s))) - return ret; - } - - spin_unlock_irqrestore(&s->lock, flags); - - s->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE); - up(&s->open_sem); - return 0; + if (file->f_mode & FMODE_READ) { + s->dma_adc.ossfragshift = s->dma_adc.ossmaxfrags = + s->dma_adc.subdivision = s->dma_adc.total_bytes = 0; + s->capcc &= ~(CC_SM | CC_DF); + set_adc_rate(s, 8000); + if ((minor & 0xf) == SND_DEV_DSP16) + s->capcc |= CC_DF; + outw(s->capcc, s->io+IT_AC_CAPCC); + if ((ret = prog_dmabuf_adc(s))) { + spin_unlock_irqrestore(&s->lock, flags); + return ret; + } + } + if (file->f_mode & FMODE_WRITE) { + s->dma_dac.ossfragshift = s->dma_dac.ossmaxfrags = + s->dma_dac.subdivision = s->dma_dac.total_bytes = 0; + s->pcc &= ~(CC_SM | CC_DF); + set_dac_rate(s, 8000); + if ((minor & 0xf) == SND_DEV_DSP16) + s->pcc |= CC_DF; + outw(s->pcc, s->io+IT_AC_PCC); + if ((ret = prog_dmabuf_dac(s))) { + spin_unlock_irqrestore(&s->lock, flags); + return ret; + } + } + + spin_unlock_irqrestore(&s->lock, flags); + + s->open_mode |= (file->f_mode & (FMODE_READ | FMODE_WRITE)); + up(&s->open_sem); + return 0; } static int it8172_release(struct inode *inode, struct file *file) { - struct it8172_state *s = (struct it8172_state *)file->private_data; + struct it8172_state *s = (struct it8172_state *)file->private_data; - lock_kernel(); - if (file->f_mode & FMODE_WRITE) - drain_dac(s, file->f_flags & O_NONBLOCK); - down(&s->open_sem); - if (file->f_mode & FMODE_WRITE) { - stop_dac(s); - dealloc_dmabuf(s, &s->dma_dac); - } - if (file->f_mode & FMODE_READ) { - stop_adc(s); - dealloc_dmabuf(s, &s->dma_adc); - } - s->open_mode &= (~file->f_mode) & (FMODE_READ|FMODE_WRITE); - up(&s->open_sem); - wake_up(&s->open_wait); - unlock_kernel(); - return 0; +#ifdef IT8172_VERBOSE_DEBUG + dbg(__FUNCTION__); +#endif + lock_kernel(); + if (file->f_mode & FMODE_WRITE) + drain_dac(s, file->f_flags & O_NONBLOCK); + down(&s->open_sem); + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + dealloc_dmabuf(s, &s->dma_dac); + } + if (file->f_mode & FMODE_READ) { + stop_adc(s); + dealloc_dmabuf(s, &s->dma_adc); + } + s->open_mode &= ((~file->f_mode) & (FMODE_READ|FMODE_WRITE)); + up(&s->open_sem); + wake_up(&s->open_wait); + unlock_kernel(); + return 0; } static /*const*/ struct file_operations it8172_audio_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .read = it8172_read, - .write = it8172_write, - .poll = it8172_poll, - .ioctl = it8172_ioctl, - .mmap = it8172_mmap, - .open = it8172_open, - .release = it8172_release, + owner: THIS_MODULE, + llseek: it8172_llseek, + read: it8172_read, + write: it8172_write, + poll: it8172_poll, + ioctl: it8172_ioctl, + mmap: it8172_mmap, + open: it8172_open, + release: it8172_release, }; @@ -1657,51 +1898,51 @@ static int proc_it8172_dump (char *buf, char **start, off_t fpos, int length, int *eof, void *data) { - struct it8172_state *s; - int cnt, len = 0; - - if (list_empty(&devs)) - return 0; - s = list_entry(devs.next, struct it8172_state, devs); - - /* print out header */ - len += sprintf(buf + len, "\n\t\tIT8172 Audio Debug\n\n"); - - // print out digital controller state - len += sprintf (buf + len, "IT8172 Audio Controller registers\n"); - len += sprintf (buf + len, "---------------------------------\n"); - cnt=0; - while (cnt < 0x72) { - if (cnt == IT_AC_PCB1STA || cnt == IT_AC_PCB2STA || - cnt == IT_AC_CAPB1STA || cnt == IT_AC_CAPB2STA || - cnt == IT_AC_PFDP) { - len+= sprintf (buf + len, "reg %02x = %08x\n", - cnt, inl(s->io+cnt)); - cnt += 4; - } else { - len+= sprintf (buf + len, "reg %02x = %04x\n", - cnt, inw(s->io+cnt)); - cnt += 2; - } - } - - /* print out CODEC state */ - len += sprintf (buf + len, "\nAC97 CODEC registers\n"); - len += sprintf (buf + len, "----------------------\n"); - for (cnt=0; cnt <= 0x7e; cnt = cnt +2) - len+= sprintf (buf + len, "reg %02x = %04x\n", - cnt, rdcodec(&s->codec, cnt)); + struct it8172_state *s; + int cnt, len = 0; - if (fpos >=len){ - *start = buf; + if (list_empty(&devs)) + return 0; + s = list_entry(devs.next, struct it8172_state, devs); + + /* print out header */ + len += sprintf(buf + len, "\n\t\tIT8172 Audio Debug\n\n"); + + // print out digital controller state + len += sprintf (buf + len, "IT8172 Audio Controller registers\n"); + len += sprintf (buf + len, "---------------------------------\n"); + cnt=0; + while (cnt < 0x72) { + if (cnt == IT_AC_PCB1STA || cnt == IT_AC_PCB2STA || + cnt == IT_AC_CAPB1STA || cnt == IT_AC_CAPB2STA || + cnt == IT_AC_PFDP) { + len+= sprintf (buf + len, "reg %02x = %08x\n", + cnt, inl(s->io+cnt)); + cnt += 4; + } else { + len+= sprintf (buf + len, "reg %02x = %04x\n", + cnt, inw(s->io+cnt)); + cnt += 2; + } + } + + /* print out CODEC state */ + len += sprintf (buf + len, "\nAC97 CODEC registers\n"); + len += sprintf (buf + len, "----------------------\n"); + for (cnt=0; cnt <= 0x7e; cnt = cnt +2) + len+= sprintf (buf + len, "reg %02x = %04x\n", + cnt, rdcodec(s->codec, cnt)); + + if (fpos >=len){ + *start = buf; + *eof =1; + return 0; + } + *start = buf + fpos; + if ((len -= fpos) > length) + return length; *eof =1; - return 0; - } - *start = buf + fpos; - if ((len -= fpos) > length) - return length; - *eof =1; - return len; + return len; } #endif /* IT8172_DEBUG */ @@ -1712,244 +1953,307 @@ #define NR_DEVICE 5 static int spdif[NR_DEVICE] = { 0, }; +static int i2s_fmt[NR_DEVICE] = { 0, }; static unsigned int devindex = 0; MODULE_PARM(spdif, "1-" __MODULE_STRING(NR_DEVICE) "i"); MODULE_PARM_DESC(spdif, "if 1 the S/PDIF digital output is enabled"); +MODULE_PARM(i2s_fmt, "1-" __MODULE_STRING(NR_DEVICE) "i"); +MODULE_PARM_DESC(i2s_fmt, "the format of I2S"); MODULE_AUTHOR("Monta Vista Software, stevel@mvista.com"); -MODULE_DESCRIPTION("IT8172 AudioPCI97 Driver"); -MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("IT8172 Audio Driver"); /* --------------------------------------------------------------------- */ static int __devinit it8172_probe(struct pci_dev *pcidev, const struct pci_device_id *pciid) { - struct it8172_state *s; - int i, val; - unsigned short pcisr, vol; - unsigned char legacy, imc; - char proc_str[80]; - - if (pcidev->irq == 0) - return -1; - - if (!(s = kmalloc(sizeof(struct it8172_state), GFP_KERNEL))) { - printk(KERN_ERR PFX "alloc of device struct failed\n"); - return -1; - } + struct it8172_state *s; + int i, val; + unsigned short pcisr, vol; + unsigned char legacy, imc; + char proc_str[80]; + + if (pcidev->irq == 0) + return -1; + + if (!(s = kmalloc(sizeof(struct it8172_state), GFP_KERNEL))) { + err("alloc of device struct failed"); + return -1; + } - memset(s, 0, sizeof(struct it8172_state)); - init_waitqueue_head(&s->dma_adc.wait); - init_waitqueue_head(&s->dma_dac.wait); - init_waitqueue_head(&s->open_wait); - init_MUTEX(&s->open_sem); - spin_lock_init(&s->lock); - s->dev = pcidev; - s->io = pci_resource_start(pcidev, 0); - s->irq = pcidev->irq; - s->vendor = pcidev->vendor; - s->device = pcidev->device; - pci_read_config_byte(pcidev, PCI_REVISION_ID, &s->rev); - s->codec.private_data = s; - s->codec.id = 0; - s->codec.codec_read = rdcodec; - s->codec.codec_write = wrcodec; - s->codec.codec_wait = waitcodec; - - if (!request_region(s->io, pci_resource_len(pcidev,0), - IT8172_MODULE_NAME)) { - printk(KERN_ERR PFX "io ports %#lx->%#lx in use\n", - s->io, s->io + pci_resource_len(pcidev,0)-1); - goto err_region; - } - if (request_irq(s->irq, it8172_interrupt, SA_INTERRUPT, - IT8172_MODULE_NAME, s)) { - printk(KERN_ERR PFX "irq %u in use\n", s->irq); - goto err_irq; - } - - printk(KERN_INFO PFX "IO at %#lx, IRQ %d\n", s->io, s->irq); - - /* register devices */ - if ((s->dev_audio = register_sound_dsp(&it8172_audio_fops, -1)) < 0) - goto err_dev1; - if ((s->codec.dev_mixer = - register_sound_mixer(&it8172_mixer_fops, -1)) < 0) - goto err_dev2; + memset(s, 0, sizeof(struct it8172_state)); + init_waitqueue_head(&s->dma_adc.wait); + init_waitqueue_head(&s->dma_dac.wait); + init_waitqueue_head(&s->open_wait); + init_MUTEX(&s->open_sem); + spin_lock_init(&s->lock); + s->dev = pcidev; + s->io = pci_resource_start(pcidev, 0); + s->irq = pcidev->irq; + s->vendor = pcidev->vendor; + s->device = pcidev->device; + pci_read_config_byte(pcidev, PCI_REVISION_ID, &s->rev); + + s->codec = ac97_alloc_codec(); + if(s->codec == NULL) + goto err_codec; + + s->codec->private_data = s; + s->codec->id = 0; + s->codec->codec_read = rdcodec; + s->codec->codec_write = wrcodec; + s->codec->codec_wait = waitcodec; + + if (!request_region(s->io, pci_resource_len(pcidev,0), + IT8172_MODULE_NAME)) { + err("io ports %#lx->%#lx in use", + s->io, s->io + pci_resource_len(pcidev,0)-1); + goto err_region; + } + if (request_irq(s->irq, it8172_interrupt, SA_INTERRUPT, + IT8172_MODULE_NAME, s)) { + err("irq %u in use", s->irq); + goto err_irq; + } + + info("IO at %#lx, IRQ %d", s->io, s->irq); + + /* register devices */ + if ((s->dev_audio = register_sound_dsp(&it8172_audio_fops, -1)) < 0) + goto err_dev1; + if ((s->codec->dev_mixer = + register_sound_mixer(&it8172_mixer_fops, -1)) < 0) + goto err_dev2; #ifdef IT8172_DEBUG - /* initialize the debug proc device */ - s->ps = create_proc_read_entry(IT8172_MODULE_NAME, 0, NULL, - proc_it8172_dump, NULL); + /* intialize the debug proc device */ + s->ps = create_proc_read_entry(IT8172_MODULE_NAME, 0, NULL, + proc_it8172_dump, NULL); #endif /* IT8172_DEBUG */ - /* - * Reset the Audio device using the IT8172 PCI Reset register. This - * creates an audible double click on a speaker connected to Line-out. - */ - IT_IO_READ16(IT_PM_PCISR, pcisr); - pcisr |= IT_PM_PCISR_ACSR; - IT_IO_WRITE16(IT_PM_PCISR, pcisr); - /* wait up to 100msec for reset to complete */ - for (i=0; pcisr & IT_PM_PCISR_ACSR; i++) { - it8172_delay(10); - if (i == 10) - break; + /* + * Reset the Audio device using the IT8172 PCI Reset register. This + * creates an audible double click on a speaker connected to Line-out. + */ IT_IO_READ16(IT_PM_PCISR, pcisr); - } - if (i == 10) { - printk(KERN_ERR PFX "chip reset timeout!\n"); - goto err_dev3; - } - - /* enable pci io and bus mastering */ - if (pci_enable_device(pcidev)) - goto err_dev3; - pci_set_master(pcidev); - - /* get out of legacy mode */ - pci_read_config_byte (pcidev, 0x40, &legacy); - pci_write_config_byte (pcidev, 0x40, legacy & ~1); - - s->spdif_volume = -1; - /* check to see if s/pdif mode is being requested */ - if (spdif[devindex]) { - printk(KERN_INFO PFX "enabling S/PDIF output\n"); - s->spdif_volume = 0; - outb(GC_SOE, s->io+IT_AC_GC); - } else { - printk(KERN_INFO PFX "disabling S/PDIF output\n"); - outb(0, s->io+IT_AC_GC); - } - - /* cold reset the AC97 */ - outw(CODECC_CR, s->io+IT_AC_CODECC); - udelay(1000); - outw(0, s->io+IT_AC_CODECC); - /* need to delay around 500msec(bleech) to give - some CODECs enough time to wakeup */ - it8172_delay(500); - - /* AC97 warm reset to start the bitclk */ - outw(CODECC_WR, s->io+IT_AC_CODECC); - udelay(1000); - outw(0, s->io+IT_AC_CODECC); - - /* codec init */ - if (!ac97_probe_codec(&s->codec)) - goto err_dev3; - - /* Enable Volume button interrupts */ - imc = inb(s->io+IT_AC_IMC); - outb(imc & ~IMC_VCIM, s->io+IT_AC_IMC); - - /* Un-mute PCM and FM out on the controller */ - vol = inw(s->io+IT_AC_PCMOV); - outw(vol & ~PCMOV_PCMOM, s->io+IT_AC_PCMOV); - vol = inw(s->io+IT_AC_FMOV); - outw(vol & ~FMOV_FMOM, s->io+IT_AC_FMOV); - - /* set channel defaults to 8-bit, mono, 8 Khz */ - s->pcc = 0; - s->capcc = 0; - set_dac_rate(s, 8000); - set_adc_rate(s, 8000); - - /* set mic to be the recording source */ - val = SOUND_MASK_MIC; - mixdev_ioctl(&s->codec, SOUND_MIXER_WRITE_RECSRC, (unsigned long)&val); - - /* mute master and PCM when in S/PDIF mode */ - if (s->spdif_volume != -1) { - val = 0x0000; - mixdev_ioctl(&s->codec, SOUND_MIXER_WRITE_VOLUME, - (unsigned long)&val); - mixdev_ioctl(&s->codec, SOUND_MIXER_WRITE_PCM, + pcisr |= IT_PM_PCISR_ACSR; + IT_IO_WRITE16(IT_PM_PCISR, pcisr); + /* wait up to 100msec for reset to complete */ + for (i=0; pcisr & IT_PM_PCISR_ACSR; i++) { + it8172_delay(10); + if (i == 10) + break; + IT_IO_READ16(IT_PM_PCISR, pcisr); + } + if (i == 10) { + err("chip reset timeout!"); + goto err_dev3; + } + + /* enable pci io and bus mastering */ + if (pci_enable_device(pcidev)) + goto err_dev3; + pci_set_master(pcidev); + + /* get out of legacy mode */ + pci_read_config_byte (pcidev, 0x40, &legacy); + pci_write_config_byte (pcidev, 0x40, legacy & ~1); + + s->spdif_volume = -1; + /* check to see if s/pdif mode is being requested */ + if (spdif[devindex]) { + info("enabling S/PDIF output"); + s->spdif_volume = 0; + outb(GC_SOE, s->io+IT_AC_GC); + } else { + info("disabling S/PDIF output"); + outb(0, s->io+IT_AC_GC); + } + + /* check to see if I2S format requested */ + if (i2s_fmt[devindex]) { + info("setting I2S format to 0x%02x", i2s_fmt[devindex]); + outb(i2s_fmt[devindex], s->io+IT_AC_I2SMC); + } else { + outb(I2SMC_I2SF_I2S, s->io+IT_AC_I2SMC); + } + + /* cold reset the AC97 */ + outw(CODECC_CR, s->io+IT_AC_CODECC); + udelay(1000); + outw(0, s->io+IT_AC_CODECC); + /* need to delay around 500msec(bleech) to give + some CODECs enough time to wakeup */ + it8172_delay(500); + + /* AC97 warm reset to start the bitclk */ + outw(CODECC_WR, s->io+IT_AC_CODECC); + udelay(1000); + outw(0, s->io+IT_AC_CODECC); + + /* codec init */ + if (!ac97_probe_codec(s->codec)) + goto err_dev3; + + /* add I2S as allowable recording source */ + s->codec->record_sources |= SOUND_MASK_I2S; + + /* Enable Volume button interrupts */ + imc = inb(s->io+IT_AC_IMC); + outb(imc & ~IMC_VCIM, s->io+IT_AC_IMC); + + /* Un-mute PCM and FM out on the controller */ + vol = inw(s->io+IT_AC_PCMOV); + outw(vol & ~PCMOV_PCMOM, s->io+IT_AC_PCMOV); + vol = inw(s->io+IT_AC_FMOV); + outw(vol & ~FMOV_FMOM, s->io+IT_AC_FMOV); + + /* set channel defaults to 8-bit, mono, 8 Khz */ + s->pcc = 0; + s->capcc = 0; + set_dac_rate(s, 8000); + set_adc_rate(s, 8000); + + /* set mic to be the recording source */ + val = SOUND_MASK_MIC; + mixdev_ioctl(s->codec, SOUND_MIXER_WRITE_RECSRC, (unsigned long)&val); - } + + /* mute AC'97 master and PCM when in S/PDIF mode */ + if (s->spdif_volume != -1) { + val = 0x0000; + s->codec->mixer_ioctl(s->codec, SOUND_MIXER_WRITE_VOLUME, + (unsigned long)&val); + s->codec->mixer_ioctl(s->codec, SOUND_MIXER_WRITE_PCM, + (unsigned long)&val); + } #ifdef IT8172_DEBUG - sprintf(proc_str, "driver/%s/%d/ac97", IT8172_MODULE_NAME, s->codec.id); - s->ac97_ps = create_proc_read_entry (proc_str, 0, NULL, - ac97_read_proc, &s->codec); + sprintf(proc_str, "driver/%s/%d/ac97", IT8172_MODULE_NAME, + s->codec->id); + s->ac97_ps = create_proc_read_entry (proc_str, 0, NULL, + ac97_read_proc, s->codec); #endif - /* store it in the driver field */ - pci_set_drvdata(pcidev, s); - pcidev->dma_mask = 0xffffffff; - /* put it into driver list */ - list_add_tail(&s->devs, &devs); - /* increment devindex */ - if (devindex < NR_DEVICE-1) - devindex++; - return 0; + /* store it in the driver field */ + pci_set_drvdata(pcidev, s); + pcidev->dma_mask = 0xffffffff; + /* put it into driver list */ + list_add_tail(&s->devs, &devs); + /* increment devindex */ + if (devindex < NR_DEVICE-1) + devindex++; + return 0; err_dev3: - unregister_sound_mixer(s->codec.dev_mixer); + unregister_sound_mixer(s->codec->dev_mixer); err_dev2: - unregister_sound_dsp(s->dev_audio); + unregister_sound_dsp(s->dev_audio); err_dev1: - printk(KERN_ERR PFX "cannot register misc device\n"); - free_irq(s->irq, s); + err("cannot register misc device"); + free_irq(s->irq, s); err_irq: - release_region(s->io, pci_resource_len(pcidev,0)); + release_region(s->io, pci_resource_len(pcidev,0)); err_region: - kfree(s); - return -1; + ac97_release_codec(s->codec); + err_codec: + kfree(s); + return -1; } static void __devinit it8172_remove(struct pci_dev *dev) { - struct it8172_state *s = pci_get_drvdata(dev); + struct it8172_state *s = pci_get_drvdata(dev); - if (!s) - return; - list_del(&s->devs); + if (!s) + return; + list_del(&s->devs); #ifdef IT8172_DEBUG - if (s->ps) - remove_proc_entry(IT8172_MODULE_NAME, NULL); + if (s->ps) + remove_proc_entry(IT8172_MODULE_NAME, NULL); #endif /* IT8172_DEBUG */ - synchronize_irq(s->irq); - free_irq(s->irq, s); - release_region(s->io, pci_resource_len(dev,0)); - unregister_sound_dsp(s->dev_audio); - unregister_sound_mixer(s->codec.dev_mixer); - kfree(s); - pci_set_drvdata(dev, NULL); + synchronize_irq(); + free_irq(s->irq, s); + release_region(s->io, pci_resource_len(dev,0)); + unregister_sound_dsp(s->dev_audio); + unregister_sound_mixer(s->codec->dev_mixer); + ac97_codec_release(s->codec); + kfree(s); + pci_set_drvdata(dev, NULL); } static struct pci_device_id id_table[] __devinitdata = { - { PCI_VENDOR_ID_ITE, PCI_DEVICE_ID_ITE_IT8172G_AUDIO, PCI_ANY_ID, - PCI_ANY_ID, 0, 0 }, - { 0, } + { PCI_VENDOR_ID_ITE, PCI_DEVICE_ID_ITE_IT8172G_AUDIO, PCI_ANY_ID, + PCI_ANY_ID, 0, 0 }, + { 0, } }; MODULE_DEVICE_TABLE(pci, id_table); static struct pci_driver it8172_driver = { - .name = IT8172_MODULE_NAME, - .id_table = id_table, - .probe = it8172_probe, - .remove = it8172_remove, + name: IT8172_MODULE_NAME, + id_table: id_table, + probe: it8172_probe, + remove: it8172_remove }; static int __init init_it8172(void) { - printk("version v0.26 time " __TIME__ " " __DATE__ "\n"); - return pci_module_init(&it8172_driver); + if (!pci_present()) /* No PCI bus in this machine! */ + return -ENODEV; + info("version v0.5 time " __TIME__ " " __DATE__); + return pci_module_init(&it8172_driver); } static void __exit cleanup_it8172(void) { - printk(KERN_INFO PFX "unloading\n"); - pci_unregister_driver(&it8172_driver); + info("unloading"); + pci_unregister_driver(&it8172_driver); } module_init(init_it8172); module_exit(cleanup_it8172); +/* --------------------------------------------------------------------- */ + +#ifndef MODULE + +/* format is: it8172=[spdif],[i2s:] */ + +static int __init it8172_setup(char *options) +{ + char* this_opt; + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= NR_DEVICE) + return 0; + + if (!options || !*options) + return 0; + + for(this_opt=strtok(options, ","); + this_opt; this_opt=strtok(NULL, ",")) { + if (!strncmp(this_opt, "spdif", 5)) { + spdif[nr_dev] = 1; + } else if (!strncmp(this_opt, "i2s:", 4)) { + if (!strncmp(this_opt+4, "dac", 3)) + i2s_fmt[nr_dev] = I2SMC_I2SF_DAC; + else if (!strncmp(this_opt+4, "adc", 3)) + i2s_fmt[nr_dev] = I2SMC_I2SF_ADC; + else if (!strncmp(this_opt+4, "i2s", 3)) + i2s_fmt[nr_dev] = I2SMC_I2SF_I2S; + } + } + + nr_dev++; + return 1; +} + +__setup("it8172=", it8172_setup); + +#endif /* MODULE */ diff -Nru a/sound/oss/maestro.c b/sound/oss/maestro.c --- a/sound/oss/maestro.c Wed Jun 4 05:28:02 2003 +++ b/sound/oss/maestro.c Sun Jul 6 10:51:23 2003 @@ -726,6 +726,12 @@ left = (left * mh->scale) / 100; if ((left == 0) && (right == 0)) val |= 0x8000; + } else if (mixer == SOUND_MIXER_PCM || mixer == SOUND_MIXER_CD) { + /* log conversion seems bad for them */ + if ((left == 0) && (right == 0)) + val = 0x8000; + right = ((100 - right) * mh->scale) / 100; + left = ((100 - left) * mh->scale) / 100; } else { /* log conversion for the stereo controls */ if((left == 0) && (right == 0)) @@ -1937,12 +1943,12 @@ manner by adjusting the master mixer volume. */ volume = c->mix.mixer_state[0] & 0xff; if (vol_evt == UP_EVT) { - volume += 10; + volume += 5; if (volume > 100) volume = 100; } else if (vol_evt == DOWN_EVT) { - volume -= 10; + volume -= 5; if (volume < 0) volume = 0; } else { @@ -2024,8 +2030,8 @@ VALIDATE_CARD(card); if (cmd == SOUND_MIXER_INFO) { mixer_info info; - strlcpy(info.id, card_names[card->card_type], sizeof(info.id)); - strlcpy(info.name, card_names[card->card_type], sizeof(info.name)); + strncpy(info.id, card_names[card->card_type], sizeof(info.id)); + strncpy(info.name, card_names[card->card_type], sizeof(info.name)); info.modify_counter = card->mix.modcnt; if (copy_to_user((void *)arg, &info, sizeof(info))) return -EFAULT; @@ -2033,8 +2039,8 @@ } if (cmd == SOUND_OLD_MIXER_INFO) { _old_mixer_info info; - strlcpy(info.id, card_names[card->card_type], sizeof(info.id)); - strlcpy(info.name, card_names[card->card_type], sizeof(info.name)); + strncpy(info.id, card_names[card->card_type], sizeof(info.id)); + strncpy(info.name, card_names[card->card_type], sizeof(info.name)); if (copy_to_user((void *)arg, &info, sizeof(info))) return -EFAULT; return 0; diff -Nru a/sound/oss/maestro3.c b/sound/oss/maestro3.c --- a/sound/oss/maestro3.c Mon Jun 9 16:20:56 2003 +++ b/sound/oss/maestro3.c Fri Jul 11 08:47:14 2003 @@ -2301,9 +2301,8 @@ { struct ac97_codec *codec; - if ((codec = kmalloc(sizeof(struct ac97_codec), GFP_KERNEL)) == NULL) + if ((codec = ac97_alloc_codec()) == NULL) return -ENOMEM; - memset(codec, 0, sizeof(struct ac97_codec)); codec->private_data = card; codec->codec_read = m3_ac97_read; @@ -2313,13 +2312,13 @@ if (ac97_probe_codec(codec) == 0) { printk(KERN_ERR PFX "codec probe failed\n"); - kfree(codec); + ac97_release_codec(codec); return -1; } if ((codec->dev_mixer = register_sound_mixer(&m3_mixer_fops, -1)) < 0) { printk(KERN_ERR PFX "couldn't register mixer!\n"); - kfree(codec); + ac97_release_codec(codec); return -1; } diff -Nru a/sound/oss/msnd_pinnacle.c b/sound/oss/msnd_pinnacle.c --- a/sound/oss/msnd_pinnacle.c Sat May 24 17:00:00 2003 +++ b/sound/oss/msnd_pinnacle.c Fri Jul 11 08:28:08 2003 @@ -555,8 +555,8 @@ } #define set_mixer_info() \ - strlcpy(info.id, "MSNDMIXER", sizeof(info.id)); \ - strlcpy(info.name, "MultiSound Mixer", sizeof(info.name)); + strncpy(info.id, "MSNDMIXER", sizeof(info.id)); \ + strncpy(info.name, "MultiSound Mixer", sizeof(info.name)); static int mixer_ioctl(unsigned int cmd, unsigned long arg) { diff -Nru a/sound/oss/nec_vrc5477.c b/sound/oss/nec_vrc5477.c --- a/sound/oss/nec_vrc5477.c Mon Jun 9 16:21:28 2003 +++ b/sound/oss/nec_vrc5477.c Fri Jul 11 08:49:34 2003 @@ -194,7 +194,7 @@ struct proc_dir_entry *ac97_ps; #endif /* VRC5477_AC97_DEBUG */ - struct ac97_codec codec; + struct ac97_codec *codec; unsigned dacChannels, adcChannels; unsigned short dacRate, adcRate; @@ -385,7 +385,7 @@ static void set_adc_rate(struct vrc5477_ac97_state *s, unsigned rate) { - wrcodec(&s->codec, AC97_PCM_LR_ADC_RATE, rate); + wrcodec(s->codec, AC97_PCM_LR_ADC_RATE, rate); s->adcRate = rate; } @@ -393,8 +393,8 @@ static void set_dac_rate(struct vrc5477_ac97_state *s, unsigned rate) { if(s->extended_status & AC97_EXTSTAT_VRA) { - wrcodec(&s->codec, AC97_PCM_FRONT_DAC_RATE, rate); - s->dacRate = rdcodec(&s->codec, AC97_PCM_FRONT_DAC_RATE); + wrcodec(s->codec, AC97_PCM_FRONT_DAC_RATE, rate); + s->dacRate = rdcodec(s->codec, AC97_PCM_FRONT_DAC_RATE); } } @@ -865,7 +865,7 @@ if (list == &devs) return -ENODEV; s = list_entry(list, struct vrc5477_ac97_state, devs); - if (s->codec.dev_mixer == minor) + if (s->codec->dev_mixer == minor) break; } file->private_data = s; @@ -889,7 +889,7 @@ { struct vrc5477_ac97_state *s = (struct vrc5477_ac97_state *)file->private_data; - struct ac97_codec *codec = &s->codec; + struct ac97_codec *codec = s->codec; return mixdev_ioctl(codec, cmd, arg); } @@ -1187,7 +1187,7 @@ #endif count -= copyCount; - totalCopyCount =+ copyCount; + totalCopyCount += copyCount; avail -= copyFragCount; totalCopyFragCount += copyFragCount; @@ -1563,7 +1563,7 @@ return -EINVAL; } - return mixdev_ioctl(&s->codec, cmd, arg); + return mixdev_ioctl(s->codec, cmd, arg); } @@ -1789,7 +1789,7 @@ len += sprintf (buf + len, "----------------------\n"); for (cnt=0; cnt <= 0x7e; cnt = cnt +2) len+= sprintf (buf + len, "reg %02x = %04x\n", - cnt, rdcodec(&s->codec, cnt)); + cnt, rdcodec(s->codec, cnt)); if (fpos >=len){ *start = buf; @@ -1842,12 +1842,14 @@ s->dev = pcidev; s->io = pci_resource_start(pcidev, 0); s->irq = pcidev->irq; + + s->codec = ac97_alloc_codec(); - s->codec.private_data = s; - s->codec.id = 0; - s->codec.codec_read = rdcodec; - s->codec.codec_write = wrcodec; - s->codec.codec_wait = waitcodec; + s->codec->private_data = s; + s->codec->id = 0; + s->codec->codec_read = rdcodec; + s->codec->codec_write = wrcodec; + s->codec->codec_wait = waitcodec; /* setting some other default values such as * adcChannels, adcRate is done in open() so that @@ -1855,7 +1857,7 @@ */ /* test if get response from ac97, if not return */ - if (ac97_codec_not_present(&(s->codec))) { + if (ac97_codec_not_present(s->codec)) { printk(KERN_ERR PFX "no ac97 codec\n"); goto err_region; @@ -1878,7 +1880,7 @@ /* register devices */ if ((s->dev_audio = register_sound_dsp(&vrc5477_ac97_audio_fops, -1)) < 0) goto err_dev1; - if ((s->codec.dev_mixer = + if ((s->codec->dev_mixer = register_sound_mixer(&vrc5477_ac97_mixer_fops, -1)) < 0) goto err_dev2; @@ -1899,22 +1901,22 @@ while (inl(s->io + VRC5477_ACLINK_CTRL) & VRC5477_ACLINK_CTRL_RST_ON); /* codec init */ - if (!ac97_probe_codec(&s->codec)) + if (!ac97_probe_codec(s->codec)) goto err_dev3; #ifdef VRC5477_AC97_DEBUG sprintf(proc_str, "driver/%s/%d/ac97", - VRC5477_AC97_MODULE_NAME, s->codec.id); + VRC5477_AC97_MODULE_NAME, s->codec->id); s->ac97_ps = create_proc_read_entry (proc_str, 0, NULL, - ac97_read_proc, &s->codec); + ac97_read_proc, s->codec); /* TODO : why this proc file does not show up? */ #endif /* Try to enable variable rate audio mode. */ - wrcodec(&s->codec, AC97_EXTENDED_STATUS, - rdcodec(&s->codec, AC97_EXTENDED_STATUS) | AC97_EXTSTAT_VRA); + wrcodec(s->codec, AC97_EXTENDED_STATUS, + rdcodec(s->codec, AC97_EXTENDED_STATUS) | AC97_EXTSTAT_VRA); /* Did we enable it? */ - if(rdcodec(&s->codec, AC97_EXTENDED_STATUS) & AC97_EXTSTAT_VRA) + if(rdcodec(s->codec, AC97_EXTENDED_STATUS) & AC97_EXTSTAT_VRA) s->extended_status |= AC97_EXTSTAT_VRA; else { s->dacRate = 48000; @@ -1923,17 +1925,17 @@ } /* let us get the default volumne louder */ - wrcodec(&s->codec, 0x2, 0x1010); /* master volume, middle */ - wrcodec(&s->codec, 0xc, 0x10); /* phone volume, middle */ - // wrcodec(&s->codec, 0xe, 0x10); /* misc volume, middle */ - wrcodec(&s->codec, 0x10, 0x8000); /* line-in 2 line-out disable */ - wrcodec(&s->codec, 0x18, 0x0707); /* PCM out (line out) middle */ + wrcodec(s->codec, 0x2, 0x1010); /* master volume, middle */ + wrcodec(s->codec, 0xc, 0x10); /* phone volume, middle */ + // wrcodec(s->codec, 0xe, 0x10); /* misc volume, middle */ + wrcodec(s->codec, 0x10, 0x8000); /* line-in 2 line-out disable */ + wrcodec(s->codec, 0x18, 0x0707); /* PCM out (line out) middle */ /* by default we select line in the input */ - wrcodec(&s->codec, 0x1a, 0x0404); - wrcodec(&s->codec, 0x1c, 0x0f0f); - wrcodec(&s->codec, 0x1e, 0x07); + wrcodec(s->codec, 0x1a, 0x0404); + wrcodec(s->codec, 0x1c, 0x0f0f); + wrcodec(s->codec, 0x1e, 0x07); /* enable the master interrupt but disable all others */ outl(VRC5477_INT_MASK_NMASK, s->io + VRC5477_INT_MASK); @@ -1949,7 +1951,7 @@ return 0; err_dev3: - unregister_sound_mixer(s->codec.dev_mixer); + unregister_sound_mixer(s->codec->dev_mixer); err_dev2: unregister_sound_dsp(s->dev_audio); err_dev1: @@ -1958,6 +1960,7 @@ err_irq: release_region(s->io, pci_resource_len(pcidev,0)); err_region: + ac97_release_codec(codec); kfree(s); return -1; } @@ -1979,7 +1982,8 @@ free_irq(s->irq, s); release_region(s->io, pci_resource_len(dev,0)); unregister_sound_dsp(s->dev_audio); - unregister_sound_mixer(s->codec.dev_mixer); + unregister_sound_mixer(s->codec->dev_mixer); + ac97_release_codec(s->codec); kfree(s); pci_set_drvdata(dev, NULL); } diff -Nru a/sound/oss/nm256_audio.c b/sound/oss/nm256_audio.c --- a/sound/oss/nm256_audio.c Mon Apr 21 00:32:53 2003 +++ b/sound/oss/nm256_audio.c Fri Jul 11 08:20:34 2003 @@ -1105,7 +1105,7 @@ printk (KERN_ERR "NM256: This doesn't look to me like the AC97-compatible version.\n"); printk (KERN_ERR " You can force the driver to load by passing in the module\n"); printk (KERN_ERR " parameter:\n"); - printk (KERN_ERR " force_ac97 = 1\n"); + printk (KERN_ERR " force_load = 1\n"); printk (KERN_ERR "\n"); printk (KERN_ERR " More likely, you should be using the appropriate SB-16 or\n"); printk (KERN_ERR " CS4232 driver instead. (If your BIOS has settings for\n"); diff -Nru a/sound/oss/sonicvibes.c b/sound/oss/sonicvibes.c --- a/sound/oss/sonicvibes.c Mon Jun 9 16:22:39 2003 +++ b/sound/oss/sonicvibes.c Fri Jul 11 08:27:40 2003 @@ -1046,8 +1046,8 @@ VALIDATE_STATE(s); if (cmd == SOUND_MIXER_INFO) { mixer_info info; - strlcpy(info.id, "SonicVibes", sizeof(info.id)); - strlcpy(info.name, "S3 SonicVibes", sizeof(info.name)); + strncpy(info.id, "SonicVibes", sizeof(info.id)); + strncpy(info.name, "S3 SonicVibes", sizeof(info.name)); info.modify_counter = s->mix.modcnt; if (copy_to_user((void *)arg, &info, sizeof(info))) return -EFAULT; @@ -1055,8 +1055,8 @@ } if (cmd == SOUND_OLD_MIXER_INFO) { _old_mixer_info info; - strlcpy(info.id, "SonicVibes", sizeof(info.id)); - strlcpy(info.name, "S3 SonicVibes", sizeof(info.name)); + strncpy(info.id, "SonicVibes", sizeof(info.id)); + strncpy(info.name, "S3 SonicVibes", sizeof(info.name)); if (copy_to_user((void *)arg, &info, sizeof(info))) return -EFAULT; return 0; diff -Nru a/sound/oss/soundcard.c b/sound/oss/soundcard.c --- a/sound/oss/soundcard.c Sat May 24 17:00:00 2003 +++ b/sound/oss/soundcard.c Fri Jul 11 08:27:26 2003 @@ -289,8 +289,8 @@ { mixer_info info; - strlcpy(info.id, mixer_devs[dev]->id, sizeof(info.id)); - strlcpy(info.name, mixer_devs[dev]->name, sizeof(info.name)); + strncpy(info.id, mixer_devs[dev]->id, sizeof(info.id)); + strncpy(info.name, mixer_devs[dev]->name, sizeof(info.name)); info.modify_counter = mixer_devs[dev]->modify_counter; if (__copy_to_user(arg, &info, sizeof(info))) return -EFAULT; @@ -301,8 +301,8 @@ { _old_mixer_info info; - strlcpy(info.id, mixer_devs[dev]->id, sizeof(info.id)); - strlcpy(info.name, mixer_devs[dev]->name, sizeof(info.name)); + strncpy(info.id, mixer_devs[dev]->id, sizeof(info.id)); + strncpy(info.name, mixer_devs[dev]->name, sizeof(info.name)); if (copy_to_user(arg, &info, sizeof(info))) return -EFAULT; return 0; diff -Nru a/sound/oss/swarm_cs4297a.c b/sound/oss/swarm_cs4297a.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/swarm_cs4297a.c Fri Jul 11 08:51:31 2003 @@ -0,0 +1,2745 @@ +/******************************************************************************* +* +* "swarm_cs4297a.c" -- Cirrus Logic-Crystal CS4297a linux audio driver. +* +* Copyright (C) 2001 Broadcom Corporation. +* Copyright (C) 2000,2001 Cirrus Logic Corp. +* -- adapted from drivers by Thomas Sailer, +* -- but don't bug him; Problems should go to: +* -- tom woller (twoller@crystal.cirrus.com) or +* (audio@crystal.cirrus.com). +* -- adapted from cs4281 PCI driver for cs4297a on +* BCM1250 Synchronous Serial interface +* (kwalker@broadcom.com) +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +* +* Module command line parameters: +* none +* +* Supported devices: +* /dev/dsp standard /dev/dsp device, (mostly) OSS compatible +* /dev/mixer standard /dev/mixer device, (mostly) OSS compatible +* /dev/midi simple MIDI UART interface, no ioctl +* +* Modification History +* 08/20/00 trw - silence and no stopping DAC until release +* 08/23/00 trw - added CS_DBG statements, fix interrupt hang issue on DAC stop. +* 09/18/00 trw - added 16bit only record with conversion +* 09/24/00 trw - added Enhanced Full duplex (separate simultaneous +* capture/playback rates) +* 10/03/00 trw - fixed mmap (fixed GRECORD and the XMMS mmap test plugin +* libOSSm.so) +* 10/11/00 trw - modified for 2.4.0-test9 kernel enhancements (NR_MAP removal) +* 11/03/00 trw - fixed interrupt loss/stutter, added debug. +* 11/10/00 bkz - added __devinit to cs4297a_hw_init() +* 11/10/00 trw - fixed SMP and capture spinlock hang. +* 12/04/00 trw - cleaned up CSDEBUG flags and added "defaultorder" moduleparm. +* 12/05/00 trw - fixed polling (myth2), and added underrun swptr fix. +* 12/08/00 trw - added PM support. +* 12/14/00 trw - added wrapper code, builds under 2.4.0, 2.2.17-20, 2.2.17-8 +* (RH/Dell base), 2.2.18, 2.2.12. cleaned up code mods by ident. +* 12/19/00 trw - added PM support for 2.2 base (apm_callback). other PM cleanup. +* 12/21/00 trw - added fractional "defaultorder" inputs. if >100 then use +* defaultorder-100 as power of 2 for the buffer size. example: +* 106 = 2^(106-100) = 2^6 = 64 bytes for the buffer size. +* +*******************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +struct cs4297a_state; +EXPORT_NO_SYMBOLS; + +static void stop_dac(struct cs4297a_state *s); +static void stop_adc(struct cs4297a_state *s); +static void start_dac(struct cs4297a_state *s); +static void start_adc(struct cs4297a_state *s); +#undef OSS_DOCUMENTED_MIXER_SEMANTICS + +// --------------------------------------------------------------------- + +#define CS4297a_MAGIC 0xf00beef1 + +// buffer order determines the size of the dma buffer for the driver. +// under Linux, a smaller buffer allows more responsiveness from many of the +// applications (e.g. games). A larger buffer allows some of the apps (esound) +// to not underrun the dma buffer as easily. As default, use 32k (order=3) +// rather than 64k as some of the games work more responsively. +// log base 2( buff sz = 32k). + +//static unsigned long defaultorder = 3; +//MODULE_PARM(defaultorder, "i"); + +// +// Turn on/off debugging compilation by commenting out "#define CSDEBUG" +// +#define CSDEBUG 0 +#if CSDEBUG +#define CSDEBUG_INTERFACE 1 +#else +#undef CSDEBUG_INTERFACE +#endif +// +// cs_debugmask areas +// +#define CS_INIT 0x00000001 // initialization and probe functions +#define CS_ERROR 0x00000002 // tmp debugging bit placeholder +#define CS_INTERRUPT 0x00000004 // interrupt handler (separate from all other) +#define CS_FUNCTION 0x00000008 // enter/leave functions +#define CS_WAVE_WRITE 0x00000010 // write information for wave +#define CS_WAVE_READ 0x00000020 // read information for wave +#define CS_AC97 0x00000040 // AC97 register access +#define CS_DESCR 0x00000080 // descriptor management +#define CS_OPEN 0x00000400 // all open functions in the driver +#define CS_RELEASE 0x00000800 // all release functions in the driver +#define CS_PARMS 0x00001000 // functional and operational parameters +#define CS_IOCTL 0x00002000 // ioctl (non-mixer) +#define CS_TMP 0x10000000 // tmp debug mask bit + +// +// CSDEBUG is usual mode is set to 1, then use the +// cs_debuglevel and cs_debugmask to turn on or off debugging. +// Debug level of 1 has been defined to be kernel errors and info +// that should be printed on any released driver. +// +#if CSDEBUG +#define CS_DBGOUT(mask,level,x) if((cs_debuglevel >= (level)) && ((mask) & cs_debugmask) ) {x;} +#else +#define CS_DBGOUT(mask,level,x) +#endif + +#if CSDEBUG +static unsigned long cs_debuglevel = 4; // levels range from 1-9 +static unsigned long cs_debugmask = CS_INIT /*| CS_IOCTL*/; +MODULE_PARM(cs_debuglevel, "i"); +MODULE_PARM(cs_debugmask, "i"); +#endif +#define CS_TRUE 1 +#define CS_FALSE 0 + +#define CS_TYPE_ADC 0 +#define CS_TYPE_DAC 1 + +#define SER_BASE (A_SER_BASE_1 + KSEG1) +#define SS_CSR(t) (SER_BASE+t) +#define SS_TXTBL(t) (SER_BASE+R_SER_TX_TABLE_BASE+(t*8)) +#define SS_RXTBL(t) (SER_BASE+R_SER_RX_TABLE_BASE+(t*8)) + +#define FRAME_BYTES 32 +#define FRAME_SAMPLE_BYTES 4 + +/* Should this be variable? */ +#define SAMPLE_BUF_SIZE (16*1024) +#define SAMPLE_FRAME_COUNT (SAMPLE_BUF_SIZE / FRAME_SAMPLE_BYTES) +/* The driver can explode/shrink the frames to/from a smaller sample + buffer */ +#define DMA_BLOAT_FACTOR 1 +#define DMA_DESCR (SAMPLE_FRAME_COUNT / DMA_BLOAT_FACTOR) +#define DMA_BUF_SIZE (DMA_DESCR * FRAME_BYTES) + +/* Use the maxmium count (255 == 5.1 ms between interrupts) */ +#define DMA_INT_CNT ((1 << S_DMA_INT_PKTCNT) - 1) + +/* Figure this out: how many TX DMAs ahead to schedule a reg access */ +#define REG_LATENCY 150 + +#define FRAME_TX_US 20 + +#define SERDMA_NEXTBUF(d,f) (((d)->f+1) % (d)->ringsz) + +static const char invalid_magic[] = + KERN_CRIT "cs4297a: invalid magic value\n"; + +#define VALIDATE_STATE(s) \ +({ \ + if (!(s) || (s)->magic != CS4297a_MAGIC) { \ + printk(invalid_magic); \ + return -ENXIO; \ + } \ +}) + +struct list_head cs4297a_devs = { &cs4297a_devs, &cs4297a_devs }; + +typedef struct serdma_descr_s { + u64 descr_a; + u64 descr_b; +} serdma_descr_t; + +typedef unsigned long paddr_t; + +typedef struct serdma_s { + unsigned ringsz; + serdma_descr_t *descrtab; + serdma_descr_t *descrtab_end; + paddr_t descrtab_phys; + + serdma_descr_t *descr_add; + serdma_descr_t *descr_rem; + + u64 *dma_buf; // buffer for DMA contents (frames) + paddr_t dma_buf_phys; + u16 *sample_buf; // tmp buffer for sample conversions + u16 *sb_swptr; + u16 *sb_hwptr; + u16 *sb_end; + + dma_addr_t dmaaddr; +// unsigned buforder; // Log base 2 of 'dma_buf' size in bytes.. + unsigned numfrag; // # of 'fragments' in the buffer. + unsigned fragshift; // Log base 2 of fragment size. + unsigned hwptr, swptr; + unsigned total_bytes; // # bytes process since open. + unsigned blocks; // last returned blocks value GETOPTR + unsigned wakeup; // interrupt occurred on block + int count; + unsigned underrun; // underrun flag + unsigned error; // over/underrun + wait_queue_head_t wait; + wait_queue_head_t reg_wait; + // redundant, but makes calculations easier + unsigned fragsize; // 2**fragshift.. + unsigned sbufsz; // 2**buforder. + unsigned fragsamples; + // OSS stuff + unsigned mapped:1; // Buffer mapped in cs4297a_mmap()? + unsigned ready:1; // prog_dmabuf_dac()/adc() successful? + unsigned endcleared:1; + unsigned type:1; // adc or dac buffer (CS_TYPE_XXX) + unsigned ossfragshift; + int ossmaxfrags; + unsigned subdivision; +} serdma_t; + +struct cs4297a_state { + // magic + unsigned int magic; + + struct list_head list; + + // soundcore stuff + int dev_audio; + int dev_mixer; + + // hardware resources + unsigned int irq; + + struct { + unsigned int rx_ovrrn; /* FIFO */ + unsigned int rx_overflow; /* staging buffer */ + unsigned int tx_underrun; + unsigned int rx_bad; + unsigned int rx_good; + } stats; + + // mixer registers + struct { + unsigned short vol[10]; + unsigned int recsrc; + unsigned int modcnt; + unsigned short micpreamp; + } mix; + + // wave stuff + struct properties { + unsigned fmt; + unsigned fmt_original; // original requested format + unsigned channels; + unsigned rate; + } prop_dac, prop_adc; + unsigned conversion:1; // conversion from 16 to 8 bit in progress + unsigned ena; + spinlock_t lock; + struct semaphore open_sem; + struct semaphore open_sem_adc; + struct semaphore open_sem_dac; + mode_t open_mode; + wait_queue_head_t open_wait; + wait_queue_head_t open_wait_adc; + wait_queue_head_t open_wait_dac; + + dma_addr_t dmaaddr_sample_buf; + unsigned buforder_sample_buf; // Log base 2 of 'dma_buf' size in bytes.. + + serdma_t dma_dac, dma_adc; + + volatile u16 read_value; + volatile u16 read_reg; + volatile u64 reg_request; +}; + +#if 1 +#define prog_codec(a,b) +#define dealloc_dmabuf(a,b); +#endif + +static int prog_dmabuf_adc(struct cs4297a_state *s) +{ + s->dma_adc.ready = 1; + return 0; +} + + +static int prog_dmabuf_dac(struct cs4297a_state *s) +{ + s->dma_dac.ready = 1; + return 0; +} + +static void clear_advance(void *buf, unsigned bsize, unsigned bptr, + unsigned len, unsigned char c) +{ + if (bptr + len > bsize) { + unsigned x = bsize - bptr; + memset(((char *) buf) + bptr, c, x); + bptr = 0; + len -= x; + } + CS_DBGOUT(CS_WAVE_WRITE, 4, printk(KERN_INFO + "cs4297a: clear_advance(): memset %d at 0x%.8x for %d size \n", + (unsigned)c, (unsigned)((char *) buf) + bptr, len)); + memset(((char *) buf) + bptr, c, len); +} + +#if CSDEBUG + +// DEBUG ROUTINES + +#define SOUND_MIXER_CS_GETDBGLEVEL _SIOWR('M',120, int) +#define SOUND_MIXER_CS_SETDBGLEVEL _SIOWR('M',121, int) +#define SOUND_MIXER_CS_GETDBGMASK _SIOWR('M',122, int) +#define SOUND_MIXER_CS_SETDBGMASK _SIOWR('M',123, int) + +static void cs_printioctl(unsigned int x) +{ + unsigned int i; + unsigned char vidx; + // Index of mixtable1[] member is Device ID + // and must be <= SOUND_MIXER_NRDEVICES. + // Value of array member is index into s->mix.vol[] + static const unsigned char mixtable1[SOUND_MIXER_NRDEVICES] = { + [SOUND_MIXER_PCM] = 1, // voice + [SOUND_MIXER_LINE1] = 2, // AUX + [SOUND_MIXER_CD] = 3, // CD + [SOUND_MIXER_LINE] = 4, // Line + [SOUND_MIXER_SYNTH] = 5, // FM + [SOUND_MIXER_MIC] = 6, // Mic + [SOUND_MIXER_SPEAKER] = 7, // Speaker + [SOUND_MIXER_RECLEV] = 8, // Recording level + [SOUND_MIXER_VOLUME] = 9 // Master Volume + }; + + switch (x) { + case SOUND_MIXER_CS_GETDBGMASK: + CS_DBGOUT(CS_IOCTL, 4, + printk("SOUND_MIXER_CS_GETDBGMASK:\n")); + break; + case SOUND_MIXER_CS_GETDBGLEVEL: + CS_DBGOUT(CS_IOCTL, 4, + printk("SOUND_MIXER_CS_GETDBGLEVEL:\n")); + break; + case SOUND_MIXER_CS_SETDBGMASK: + CS_DBGOUT(CS_IOCTL, 4, + printk("SOUND_MIXER_CS_SETDBGMASK:\n")); + break; + case SOUND_MIXER_CS_SETDBGLEVEL: + CS_DBGOUT(CS_IOCTL, 4, + printk("SOUND_MIXER_CS_SETDBGLEVEL:\n")); + break; + case OSS_GETVERSION: + CS_DBGOUT(CS_IOCTL, 4, printk("OSS_GETVERSION:\n")); + break; + case SNDCTL_DSP_SYNC: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SYNC:\n")); + break; + case SNDCTL_DSP_SETDUPLEX: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SETDUPLEX:\n")); + break; + case SNDCTL_DSP_GETCAPS: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETCAPS:\n")); + break; + case SNDCTL_DSP_RESET: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_RESET:\n")); + break; + case SNDCTL_DSP_SPEED: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SPEED:\n")); + break; + case SNDCTL_DSP_STEREO: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_STEREO:\n")); + break; + case SNDCTL_DSP_CHANNELS: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_CHANNELS:\n")); + break; + case SNDCTL_DSP_GETFMTS: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETFMTS:\n")); + break; + case SNDCTL_DSP_SETFMT: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SETFMT:\n")); + break; + case SNDCTL_DSP_POST: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_POST:\n")); + break; + case SNDCTL_DSP_GETTRIGGER: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETTRIGGER:\n")); + break; + case SNDCTL_DSP_SETTRIGGER: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SETTRIGGER:\n")); + break; + case SNDCTL_DSP_GETOSPACE: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETOSPACE:\n")); + break; + case SNDCTL_DSP_GETISPACE: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETISPACE:\n")); + break; + case SNDCTL_DSP_NONBLOCK: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_NONBLOCK:\n")); + break; + case SNDCTL_DSP_GETODELAY: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETODELAY:\n")); + break; + case SNDCTL_DSP_GETIPTR: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETIPTR:\n")); + break; + case SNDCTL_DSP_GETOPTR: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETOPTR:\n")); + break; + case SNDCTL_DSP_GETBLKSIZE: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETBLKSIZE:\n")); + break; + case SNDCTL_DSP_SETFRAGMENT: + CS_DBGOUT(CS_IOCTL, 4, + printk("SNDCTL_DSP_SETFRAGMENT:\n")); + break; + case SNDCTL_DSP_SUBDIVIDE: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SUBDIVIDE:\n")); + break; + case SOUND_PCM_READ_RATE: + CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_PCM_READ_RATE:\n")); + break; + case SOUND_PCM_READ_CHANNELS: + CS_DBGOUT(CS_IOCTL, 4, + printk("SOUND_PCM_READ_CHANNELS:\n")); + break; + case SOUND_PCM_READ_BITS: + CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_PCM_READ_BITS:\n")); + break; + case SOUND_PCM_WRITE_FILTER: + CS_DBGOUT(CS_IOCTL, 4, + printk("SOUND_PCM_WRITE_FILTER:\n")); + break; + case SNDCTL_DSP_SETSYNCRO: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SETSYNCRO:\n")); + break; + case SOUND_PCM_READ_FILTER: + CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_PCM_READ_FILTER:\n")); + break; + case SOUND_MIXER_PRIVATE1: + CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_PRIVATE1:\n")); + break; + case SOUND_MIXER_PRIVATE2: + CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_PRIVATE2:\n")); + break; + case SOUND_MIXER_PRIVATE3: + CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_PRIVATE3:\n")); + break; + case SOUND_MIXER_PRIVATE4: + CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_PRIVATE4:\n")); + break; + case SOUND_MIXER_PRIVATE5: + CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_PRIVATE5:\n")); + break; + case SOUND_MIXER_INFO: + CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_INFO:\n")); + break; + case SOUND_OLD_MIXER_INFO: + CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_OLD_MIXER_INFO:\n")); + break; + + default: + switch (_IOC_NR(x)) { + case SOUND_MIXER_VOLUME: + CS_DBGOUT(CS_IOCTL, 4, + printk("SOUND_MIXER_VOLUME:\n")); + break; + case SOUND_MIXER_SPEAKER: + CS_DBGOUT(CS_IOCTL, 4, + printk("SOUND_MIXER_SPEAKER:\n")); + break; + case SOUND_MIXER_RECLEV: + CS_DBGOUT(CS_IOCTL, 4, + printk("SOUND_MIXER_RECLEV:\n")); + break; + case SOUND_MIXER_MIC: + CS_DBGOUT(CS_IOCTL, 4, + printk("SOUND_MIXER_MIC:\n")); + break; + case SOUND_MIXER_SYNTH: + CS_DBGOUT(CS_IOCTL, 4, + printk("SOUND_MIXER_SYNTH:\n")); + break; + case SOUND_MIXER_RECSRC: + CS_DBGOUT(CS_IOCTL, 4, + printk("SOUND_MIXER_RECSRC:\n")); + break; + case SOUND_MIXER_DEVMASK: + CS_DBGOUT(CS_IOCTL, 4, + printk("SOUND_MIXER_DEVMASK:\n")); + break; + case SOUND_MIXER_RECMASK: + CS_DBGOUT(CS_IOCTL, 4, + printk("SOUND_MIXER_RECMASK:\n")); + break; + case SOUND_MIXER_STEREODEVS: + CS_DBGOUT(CS_IOCTL, 4, + printk("SOUND_MIXER_STEREODEVS:\n")); + break; + case SOUND_MIXER_CAPS: + CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_CAPS:\n")); + break; + default: + i = _IOC_NR(x); + if (i >= SOUND_MIXER_NRDEVICES + || !(vidx = mixtable1[i])) { + CS_DBGOUT(CS_IOCTL, 4, printk + ("UNKNOWN IOCTL: 0x%.8x NR=%d\n", + x, i)); + } else { + CS_DBGOUT(CS_IOCTL, 4, printk + ("SOUND_MIXER_IOCTL AC9x: 0x%.8x NR=%d\n", + x, i)); + } + break; + } + } +} +#endif + + +static int ser_init(struct cs4297a_state *s) +{ + int i; + + CS_DBGOUT(CS_INIT, 2, + printk(KERN_INFO "cs4297a: Setting up serial parameters\n")); + + out64(M_SYNCSER_CMD_RX_RESET | M_SYNCSER_CMD_TX_RESET, SS_CSR(R_SER_CMD)); + + out64(M_SYNCSER_MSB_FIRST, SS_CSR(R_SER_MODE)); + out64(32, SS_CSR(R_SER_MINFRM_SZ)); + out64(32, SS_CSR(R_SER_MAXFRM_SZ)); + + out64(1, SS_CSR(R_SER_TX_RD_THRSH)); + out64(4, SS_CSR(R_SER_TX_WR_THRSH)); + out64(8, SS_CSR(R_SER_RX_RD_THRSH)); + + /* This looks good from experimentation */ + out64((M_SYNCSER_TXSYNC_INT | V_SYNCSER_TXSYNC_DLY(0) | M_SYNCSER_TXCLK_EXT | + M_SYNCSER_RXSYNC_INT | V_SYNCSER_RXSYNC_DLY(1) | M_SYNCSER_RXCLK_EXT | M_SYNCSER_RXSYNC_EDGE), + SS_CSR(R_SER_LINE_MODE)); + + /* This looks good from experimentation */ + out64(V_SYNCSER_SEQ_COUNT(14) | M_SYNCSER_SEQ_ENABLE | M_SYNCSER_SEQ_STROBE, + SS_TXTBL(0)); + out64(V_SYNCSER_SEQ_COUNT(15) | M_SYNCSER_SEQ_ENABLE | M_SYNCSER_SEQ_BYTE, + SS_TXTBL(1)); + out64(V_SYNCSER_SEQ_COUNT(13) | M_SYNCSER_SEQ_ENABLE | M_SYNCSER_SEQ_BYTE, + SS_TXTBL(2)); + out64(V_SYNCSER_SEQ_COUNT( 0) | M_SYNCSER_SEQ_ENABLE | + M_SYNCSER_SEQ_STROBE | M_SYNCSER_SEQ_LAST, SS_TXTBL(3)); + + out64(V_SYNCSER_SEQ_COUNT(14) | M_SYNCSER_SEQ_ENABLE | M_SYNCSER_SEQ_STROBE, + SS_RXTBL(0)); + out64(V_SYNCSER_SEQ_COUNT(15) | M_SYNCSER_SEQ_ENABLE | M_SYNCSER_SEQ_BYTE, + SS_RXTBL(1)); + out64(V_SYNCSER_SEQ_COUNT(13) | M_SYNCSER_SEQ_ENABLE | M_SYNCSER_SEQ_BYTE, + SS_RXTBL(2)); + out64(V_SYNCSER_SEQ_COUNT( 0) | M_SYNCSER_SEQ_ENABLE | M_SYNCSER_SEQ_STROBE | + M_SYNCSER_SEQ_LAST, SS_RXTBL(3)); + + for (i=4; i<16; i++) { + /* Just in case... */ + out64(M_SYNCSER_SEQ_LAST, SS_TXTBL(i)); + out64(M_SYNCSER_SEQ_LAST, SS_RXTBL(i)); + } + + return 0; +} + +static int init_serdma(serdma_t *dma) +{ + CS_DBGOUT(CS_INIT, 2, + printk(KERN_ERR "cs4297a: desc - %d sbufsize - %d dbufsize - %d\n", + DMA_DESCR, SAMPLE_BUF_SIZE, DMA_BUF_SIZE)); + + /* Descriptors */ + dma->ringsz = DMA_DESCR; + dma->descrtab = kmalloc(dma->ringsz * sizeof(serdma_descr_t), GFP_KERNEL); + if (!dma->descrtab) { + printk(KERN_ERR "cs4297a: kmalloc descrtab failed\n"); + return -1; + } + memset(dma->descrtab, 0, dma->ringsz * sizeof(serdma_descr_t)); + dma->descrtab_end = dma->descrtab + dma->ringsz; + /* XXX bloddy mess, use proper DMA API here ... */ + dma->descrtab_phys = PHYSADDR((int)dma->descrtab); + dma->descr_add = dma->descr_rem = dma->descrtab; + + /* Frame buffer area */ + dma->dma_buf = kmalloc(DMA_BUF_SIZE, GFP_KERNEL); + if (!dma->dma_buf) { + printk(KERN_ERR "cs4297a: kmalloc dma_buf failed\n"); + kfree(dma->descrtab); + return -1; + } + memset(dma->dma_buf, 0, DMA_BUF_SIZE); + dma->dma_buf_phys = PHYSADDR((int)dma->dma_buf); + + /* Samples buffer area */ + dma->sbufsz = SAMPLE_BUF_SIZE; + dma->sample_buf = kmalloc(dma->sbufsz, GFP_KERNEL); + if (!dma->sample_buf) { + printk(KERN_ERR "cs4297a: kmalloc sample_buf failed\n"); + kfree(dma->descrtab); + kfree(dma->dma_buf); + return -1; + } + dma->sb_swptr = dma->sb_hwptr = dma->sample_buf; + dma->sb_end = (u16 *)((void *)dma->sample_buf + dma->sbufsz); + dma->fragsize = dma->sbufsz >> 1; + + CS_DBGOUT(CS_INIT, 4, + printk(KERN_ERR "cs4297a: descrtab - %08x dma_buf - %x sample_buf - %x\n", + (int)dma->descrtab, (int)dma->dma_buf, + (int)dma->sample_buf)); + + return 0; +} + +static int dma_init(struct cs4297a_state *s) +{ + int i; + + CS_DBGOUT(CS_INIT, 2, + printk(KERN_INFO "cs4297a: Setting up DMA\n")); + + if (init_serdma(&s->dma_adc) || + init_serdma(&s->dma_dac)) + return -1; + + if (in64(SS_CSR(R_SER_DMA_DSCR_COUNT_RX))|| + in64(SS_CSR(R_SER_DMA_DSCR_COUNT_TX))) { + panic("DMA state corrupted?!"); + } + + /* Initialize now - the descr/buffer pairings will never + change... */ + for (i=0; idma_dac.descrtab[i].descr_a = M_DMA_SERRX_SOP | V_DMA_DSCRA_A_SIZE(1) | + (s->dma_dac.dma_buf_phys + i*FRAME_BYTES); + s->dma_dac.descrtab[i].descr_b = V_DMA_DSCRB_PKT_SIZE(FRAME_BYTES); + s->dma_adc.descrtab[i].descr_a = V_DMA_DSCRA_A_SIZE(1) | + (s->dma_adc.dma_buf_phys + i*FRAME_BYTES); + s->dma_adc.descrtab[i].descr_b = 0; + } + + out64((M_DMA_EOP_INT_EN | V_DMA_INT_PKTCNT(DMA_INT_CNT) | + V_DMA_RINGSZ(DMA_DESCR) | M_DMA_TDX_EN), + SS_CSR(R_SER_DMA_CONFIG0_RX)); + out64(M_DMA_L2CA, SS_CSR(R_SER_DMA_CONFIG1_RX)); + out64(s->dma_adc.descrtab_phys, SS_CSR(R_SER_DMA_DSCR_BASE_RX)); + + out64(V_DMA_RINGSZ(DMA_DESCR), SS_CSR(R_SER_DMA_CONFIG0_TX)); + out64(M_DMA_L2CA | M_DMA_NO_DSCR_UPDT, SS_CSR(R_SER_DMA_CONFIG1_TX)); + out64(s->dma_dac.descrtab_phys, SS_CSR(R_SER_DMA_DSCR_BASE_TX)); + + /* Prep the receive DMA descriptor ring */ + out64(DMA_DESCR, SS_CSR(R_SER_DMA_DSCR_COUNT_RX)); + + out64(M_SYNCSER_DMA_RX_EN | M_SYNCSER_DMA_TX_EN, SS_CSR(R_SER_DMA_ENABLE)); + + out64((M_SYNCSER_RX_SYNC_ERR | M_SYNCSER_RX_OVERRUN | M_SYNCSER_RX_EOP_COUNT), + SS_CSR(R_SER_INT_MASK)); + + /* Enable the rx/tx; let the codec warm up to the sync and + start sending good frames before the receive FIFO is + enabled */ + out64(M_SYNCSER_CMD_TX_EN, SS_CSR(R_SER_CMD)); + udelay(1000); + out64(M_SYNCSER_CMD_RX_EN | M_SYNCSER_CMD_TX_EN, SS_CSR(R_SER_CMD)); + + /* XXXKW is this magic? (the "1" part) */ + while ((in64(SS_CSR(R_SER_STATUS)) & 0xf1) != 1) + ; + + CS_DBGOUT(CS_INIT, 4, + printk(KERN_INFO "cs4297a: status: %08x\n", + (unsigned int)(in64(SS_CSR(R_SER_STATUS)) & 0xffffffff))); + + return 0; +} + +static int serdma_reg_access(struct cs4297a_state *s, u64 data) +{ + serdma_t *d = &s->dma_dac; + u64 *data_p; + unsigned swptr; + int flags; + serdma_descr_t *descr; + + if (s->reg_request) { + printk(KERN_ERR "cs4297a: attempt to issue multiple reg_access\n"); + return -1; + } + + if (s->ena & FMODE_WRITE) { + /* Since a writer has the DSP open, we have to mux the + request in */ + s->reg_request = data; + interruptible_sleep_on(&s->dma_dac.reg_wait); + /* XXXKW how can I deal with the starvation case where + the opener isn't writing? */ + } else { + /* Be safe when changing ring pointers */ + spin_lock_irqsave(&s->lock, flags); + if (d->hwptr != d->swptr) { + printk(KERN_ERR "cs4297a: reg access found bookkeeping error (hw/sw = %d/%d\n", + d->hwptr, d->swptr); + spin_unlock_irqrestore(&s->lock, flags); + return -1; + } + swptr = d->swptr; + d->hwptr = d->swptr = (d->swptr + 1) % d->ringsz; + spin_unlock_irqrestore(&s->lock, flags); + + descr = &d->descrtab[swptr]; + data_p = &d->dma_buf[swptr * 4]; + *data_p = data; + out64(1, SS_CSR(R_SER_DMA_DSCR_COUNT_TX)); + CS_DBGOUT(CS_DESCR, 4, + printk(KERN_INFO "cs4297a: add_tx %p (%x -> %x)\n", + data_p, swptr, d->hwptr)); + } + + CS_DBGOUT(CS_FUNCTION, 6, + printk(KERN_INFO "cs4297a: serdma_reg_access()-\n")); + + return 0; +} + +//**************************************************************************** +// "cs4297a_read_ac97" -- Reads an AC97 register +//**************************************************************************** +static int cs4297a_read_ac97(struct cs4297a_state *s, u32 offset, + u32 * value) +{ + CS_DBGOUT(CS_AC97, 1, + printk(KERN_INFO "cs4297a: read reg %2x\n", offset)); + if (serdma_reg_access(s, (0xCLL << 60) | (1LL << 47) | ((u64)(offset & 0x7F) << 40))) + return -1; + + interruptible_sleep_on(&s->dma_adc.reg_wait); + *value = s->read_value; + CS_DBGOUT(CS_AC97, 2, + printk(KERN_INFO "cs4297a: rdr reg %x -> %x\n", s->read_reg, s->read_value)); + + return 0; +} + + +//**************************************************************************** +// "cs4297a_write_ac97()"-- writes an AC97 register +//**************************************************************************** +static int cs4297a_write_ac97(struct cs4297a_state *s, u32 offset, + u32 value) +{ + CS_DBGOUT(CS_AC97, 1, + printk(KERN_INFO "cs4297a: write reg %2x -> %04x\n", offset, value)); + return (serdma_reg_access(s, (0xELL << 60) | ((u64)(offset & 0x7F) << 40) | ((value & 0xffff) << 12))); +} + +static void stop_dac(struct cs4297a_state *s) +{ + unsigned long flags; + + CS_DBGOUT(CS_WAVE_WRITE, 3, printk(KERN_INFO "cs4297a: stop_dac():\n")); + spin_lock_irqsave(&s->lock, flags); + s->ena &= ~FMODE_WRITE; +#if 0 + /* XXXKW what do I really want here? My theory for now is + that I just flip the "ena" bit, and the interrupt handler + will stop processing the xmit channel */ + out64((s->ena & FMODE_READ) ? M_SYNCSER_DMA_RX_EN : 0, + SS_CSR(R_SER_DMA_ENABLE)); +#endif + + spin_unlock_irqrestore(&s->lock, flags); +} + + +static void start_dac(struct cs4297a_state *s) +{ + unsigned long flags; + + CS_DBGOUT(CS_FUNCTION, 3, printk(KERN_INFO "cs4297a: start_dac()+\n")); + spin_lock_irqsave(&s->lock, flags); + if (!(s->ena & FMODE_WRITE) && (s->dma_dac.mapped || + (s->dma_dac.count > 0 + && s->dma_dac.ready))) { + s->ena |= FMODE_WRITE; + /* XXXKW what do I really want here? My theory for + now is that I just flip the "ena" bit, and the + interrupt handler will start processing the xmit + channel */ + + CS_DBGOUT(CS_WAVE_WRITE | CS_PARMS, 8, printk(KERN_INFO + "cs4297a: start_dac(): start dma\n")); + + } + spin_unlock_irqrestore(&s->lock, flags); + CS_DBGOUT(CS_FUNCTION, 3, + printk(KERN_INFO "cs4297a: start_dac()-\n")); +} + + +static void stop_adc(struct cs4297a_state *s) +{ + unsigned long flags; + + CS_DBGOUT(CS_FUNCTION, 3, + printk(KERN_INFO "cs4297a: stop_adc()+\n")); + + spin_lock_irqsave(&s->lock, flags); + s->ena &= ~FMODE_READ; + + if (s->conversion == 1) { + s->conversion = 0; + s->prop_adc.fmt = s->prop_adc.fmt_original; + } + /* Nothing to do really, I need to keep the DMA going + XXXKW when do I get here, and is there more I should do? */ + spin_unlock_irqrestore(&s->lock, flags); + CS_DBGOUT(CS_FUNCTION, 3, + printk(KERN_INFO "cs4297a: stop_adc()-\n")); +} + + +static void start_adc(struct cs4297a_state *s) +{ + unsigned long flags; + + CS_DBGOUT(CS_FUNCTION, 2, + printk(KERN_INFO "cs4297a: start_adc()+\n")); + + if (!(s->ena & FMODE_READ) && + (s->dma_adc.mapped || s->dma_adc.count <= + (signed) (s->dma_adc.sbufsz - 2 * s->dma_adc.fragsize)) + && s->dma_adc.ready) { + if (s->prop_adc.fmt & AFMT_S8 || s->prop_adc.fmt & AFMT_U8) { + // + // now only use 16 bit capture, due to truncation issue + // in the chip, noticable distortion occurs. + // allocate buffer and then convert from 16 bit to + // 8 bit for the user buffer. + // + s->prop_adc.fmt_original = s->prop_adc.fmt; + if (s->prop_adc.fmt & AFMT_S8) { + s->prop_adc.fmt &= ~AFMT_S8; + s->prop_adc.fmt |= AFMT_S16_LE; + } + if (s->prop_adc.fmt & AFMT_U8) { + s->prop_adc.fmt &= ~AFMT_U8; + s->prop_adc.fmt |= AFMT_U16_LE; + } + // + // prog_dmabuf_adc performs a stop_adc() but that is + // ok since we really haven't started the DMA yet. + // + prog_codec(s, CS_TYPE_ADC); + + prog_dmabuf_adc(s); + s->conversion = 1; + } + spin_lock_irqsave(&s->lock, flags); + s->ena |= FMODE_READ; + /* Nothing to do really, I am probably already + DMAing... XXXKW when do I get here, and is there + more I should do? */ + spin_unlock_irqrestore(&s->lock, flags); + + CS_DBGOUT(CS_PARMS, 6, printk(KERN_INFO + "cs4297a: start_adc(): start adc\n")); + } + CS_DBGOUT(CS_FUNCTION, 2, + printk(KERN_INFO "cs4297a: start_adc()-\n")); + +} + + +// call with spinlock held! +static void cs4297a_update_ptr(struct cs4297a_state *s, int intflag) +{ + int good_diff, diff, diff2; + u64 *data_p, data; + u32 *s_ptr; + unsigned hwptr; + u32 status; + serdma_t *d; + serdma_descr_t *descr; + + // update ADC pointer + status = intflag ? in64(SS_CSR(R_SER_STATUS)) : 0; + + if ((s->ena & FMODE_READ) || (status & (M_SYNCSER_RX_EOP_COUNT))) { + d = &s->dma_adc; + hwptr = (unsigned) (((in64(SS_CSR(R_SER_DMA_CUR_DSCR_ADDR_RX)) & M_DMA_CURDSCR_ADDR) - + d->descrtab_phys) / sizeof(serdma_descr_t)); + + if (s->ena & FMODE_READ) { + CS_DBGOUT(CS_FUNCTION, 2, + printk(KERN_INFO "cs4297a: upd_rcv sw->hw->hw %x/%x/%x (int-%d)n", + d->swptr, d->hwptr, hwptr, intflag)); + /* Number of DMA buffers available for software: */ + diff2 = diff = (d->ringsz + hwptr - d->hwptr) % d->ringsz; + d->hwptr = hwptr; + good_diff = 0; + s_ptr = (u32 *)&(d->dma_buf[d->swptr*4]); + descr = &d->descrtab[d->swptr]; + while (diff2--) { + u64 data = *(u64 *)s_ptr; + u64 descr_a; + u16 left, right; + descr_a = descr->descr_a; + descr->descr_a &= ~M_DMA_SERRX_SOP; + if ((descr_a & M_DMA_DSCRA_A_ADDR) != PHYSADDR((int)s_ptr)) { + printk(KERN_ERR "cs4297a: RX Bad address (read)\n"); + } + if (((data & 0x9800000000000000) != 0x9800000000000000) || + (!(descr_a & M_DMA_SERRX_SOP)) || + (G_DMA_DSCRB_PKT_SIZE(descr->descr_b) != FRAME_BYTES)) { + s->stats.rx_bad++; + printk(KERN_DEBUG "cs4297a: RX Bad attributes (read)\n"); + continue; + } + s->stats.rx_good++; + if ((data >> 61) == 7) { + s->read_value = (data >> 12) & 0xffff; + s->read_reg = (data >> 40) & 0x7f; + wake_up(&d->reg_wait); + } + if (d->count && (d->sb_hwptr == d->sb_swptr)) { + s->stats.rx_overflow++; + printk(KERN_DEBUG "cs4297a: RX overflow\n"); + continue; + } + good_diff++; + left = ((s_ptr[1] & 0xff) << 8) | ((s_ptr[2] >> 24) & 0xff); + right = (s_ptr[2] >> 4) & 0xffff; + *d->sb_hwptr++ = left; + *d->sb_hwptr++ = right; + if (d->sb_hwptr == d->sb_end) + d->sb_hwptr = d->sample_buf; + descr++; + if (descr == d->descrtab_end) { + descr = d->descrtab; + s_ptr = (u32 *)s->dma_adc.dma_buf; + } else { + s_ptr += 8; + } + } + d->total_bytes += good_diff * FRAME_SAMPLE_BYTES; + d->count += good_diff * FRAME_SAMPLE_BYTES; + if (d->count > d->sbufsz) { + printk(KERN_ERR "cs4297a: bogus receive overflow!!\n"); + } + d->swptr = (d->swptr + diff) % d->ringsz; + out64(diff, SS_CSR(R_SER_DMA_DSCR_COUNT_RX)); + if (d->mapped) { + if (d->count >= (signed) d->fragsize) + wake_up(&d->wait); + } else { + if (d->count > 0) { + CS_DBGOUT(CS_WAVE_READ, 4, + printk(KERN_INFO + "cs4297a: update count -> %d\n", d->count)); + wake_up(&d->wait); + } + } + } else { + /* Receive is going even if no one is + listening (for register accesses and to + avoid FIFO overrun) */ + diff2 = diff = (hwptr + d->ringsz - d->hwptr) % d->ringsz; + if (!diff) { + printk(KERN_ERR "cs4297a: RX full or empty?\n"); + } + + descr = &d->descrtab[d->swptr]; + data_p = &d->dma_buf[d->swptr*4]; + + /* Force this to happen at least once; I got + here because of an interrupt, so there must + be a buffer to process. */ + do { + data = *data_p; + if ((descr->descr_a & M_DMA_DSCRA_A_ADDR) != PHYSADDR((int)data_p)) { + printk(KERN_ERR "cs4297a: RX Bad address %d (%x %x)\n", d->swptr, + (int)(descr->descr_a & M_DMA_DSCRA_A_ADDR), + (int)PHYSADDR((int)data_p)); + } + if (!(data & (1LL << 63)) || + !(descr->descr_a & M_DMA_SERRX_SOP) || + (G_DMA_DSCRB_PKT_SIZE(descr->descr_b) != FRAME_BYTES)) { + s->stats.rx_bad++; + printk(KERN_DEBUG "cs4297a: RX Bad attributes\n"); + } else { + s->stats.rx_good++; + if ((data >> 61) == 7) { + s->read_value = (data >> 12) & 0xffff; + s->read_reg = (data >> 40) & 0x7f; + wake_up(&d->reg_wait); + } + } + descr->descr_a &= ~M_DMA_SERRX_SOP; + descr++; + d->swptr++; + data_p += 4; + if (descr == d->descrtab_end) { + descr = d->descrtab; + d->swptr = 0; + data_p = d->dma_buf; + } + out64(1, SS_CSR(R_SER_DMA_DSCR_COUNT_RX)); + } while (--diff); + d->hwptr = hwptr; + + CS_DBGOUT(CS_DESCR, 6, + printk(KERN_INFO "cs4297a: hw/sw %x/%x\n", d->hwptr, d->swptr)); + } + + CS_DBGOUT(CS_PARMS, 8, printk(KERN_INFO + "cs4297a: cs4297a_update_ptr(): s=0x%.8x hwptr=%d total_bytes=%d count=%d \n", + (unsigned)s, d->hwptr, + d->total_bytes, d->count)); + } + + /* XXXKW worry about s->reg_request -- there is a starvation + case if s->ena has FMODE_WRITE on, but the client isn't + doing writes */ + + // update DAC pointer + // + // check for end of buffer, means that we are going to wait for another interrupt + // to allow silence to fill the fifos on the part, to keep pops down to a minimum. + // + if (s->ena & FMODE_WRITE) { + serdma_t *d = &s->dma_dac; + hwptr = (unsigned) (((in64(SS_CSR(R_SER_DMA_CUR_DSCR_ADDR_TX)) & M_DMA_CURDSCR_ADDR) - + d->descrtab_phys) / sizeof(serdma_descr_t)); + diff = (d->ringsz + hwptr - d->hwptr) % d->ringsz; + CS_DBGOUT(CS_WAVE_WRITE, 4, printk(KERN_INFO + "cs4297a: cs4297a_update_ptr(): hw/hw/sw %x/%x/%x diff %d count %d\n", + d->hwptr, hwptr, d->swptr, diff, d->count)); + d->hwptr = hwptr; + /* XXXKW stereo? conversion? Just assume 2 16-bit samples for now */ + d->total_bytes += diff * FRAME_SAMPLE_BYTES; + if (d->mapped) { + d->count += diff * FRAME_SAMPLE_BYTES; + if (d->count >= d->fragsize) { + d->wakeup = 1; + wake_up(&d->wait); + if (d->count > d->sbufsz) + d->count &= d->sbufsz - 1; + } + } else { + d->count -= diff * FRAME_SAMPLE_BYTES; + if (d->count <= 0) { + // + // fill with silence, and do not shut down the DAC. + // Continue to play silence until the _release. + // + CS_DBGOUT(CS_WAVE_WRITE, 6, printk(KERN_INFO + "cs4297a: cs4297a_update_ptr(): memset %d at 0x%.8x for %d size \n", + (unsigned)(s->prop_dac.fmt & + (AFMT_U8 | AFMT_U16_LE)) ? 0x80 : 0, + (unsigned)d->dma_buf, + d->ringsz)); + memset(d->dma_buf, 0, d->ringsz * FRAME_BYTES); + if (d->count < 0) { + d->underrun = 1; + s->stats.tx_underrun++; + d->count = 0; + CS_DBGOUT(CS_ERROR, 9, printk(KERN_INFO + "cs4297a: cs4297a_update_ptr(): underrun\n")); + } + } else if (d->count <= + (signed) d->fragsize + && !d->endcleared) { + /* XXXKW what is this for? */ + clear_advance(d->dma_buf, + d->sbufsz, + d->swptr, + d->fragsize, + 0); + d->endcleared = 1; + } + if ( (d->count <= (signed) d->sbufsz/2) || intflag) + { + CS_DBGOUT(CS_WAVE_WRITE, 4, + printk(KERN_INFO + "cs4297a: update count -> %d\n", d->count)); + wake_up(&d->wait); + } + } + CS_DBGOUT(CS_PARMS, 8, printk(KERN_INFO + "cs4297a: cs4297a_update_ptr(): s=0x%.8x hwptr=%d total_bytes=%d count=%d \n", + (unsigned) s, d->hwptr, + d->total_bytes, d->count)); + } +} + +static int mixer_ioctl(struct cs4297a_state *s, unsigned int cmd, + unsigned long arg) +{ + // Index to mixer_src[] is value of AC97 Input Mux Select Reg. + // Value of array member is recording source Device ID Mask. + static const unsigned int mixer_src[8] = { + SOUND_MASK_MIC, SOUND_MASK_CD, 0, SOUND_MASK_LINE1, + SOUND_MASK_LINE, SOUND_MASK_VOLUME, 0, 0 + }; + + // Index of mixtable1[] member is Device ID + // and must be <= SOUND_MIXER_NRDEVICES. + // Value of array member is index into s->mix.vol[] + static const unsigned char mixtable1[SOUND_MIXER_NRDEVICES] = { + [SOUND_MIXER_PCM] = 1, // voice + [SOUND_MIXER_LINE1] = 2, // AUX + [SOUND_MIXER_CD] = 3, // CD + [SOUND_MIXER_LINE] = 4, // Line + [SOUND_MIXER_SYNTH] = 5, // FM + [SOUND_MIXER_MIC] = 6, // Mic + [SOUND_MIXER_SPEAKER] = 7, // Speaker + [SOUND_MIXER_RECLEV] = 8, // Recording level + [SOUND_MIXER_VOLUME] = 9 // Master Volume + }; + + static const unsigned mixreg[] = { + AC97_PCMOUT_VOL, + AC97_AUX_VOL, + AC97_CD_VOL, + AC97_LINEIN_VOL + }; + unsigned char l, r, rl, rr, vidx; + unsigned char attentbl[11] = + { 63, 42, 26, 17, 14, 11, 8, 6, 4, 2, 0 }; + unsigned temp1; + int i, val; + + VALIDATE_STATE(s); + CS_DBGOUT(CS_FUNCTION, 4, printk(KERN_INFO + "cs4297a: mixer_ioctl(): s=0x%.8x cmd=0x%.8x\n", + (unsigned) s, cmd)); +#if CSDEBUG + cs_printioctl(cmd); +#endif +#if CSDEBUG_INTERFACE + + if ((cmd == SOUND_MIXER_CS_GETDBGMASK) || + (cmd == SOUND_MIXER_CS_SETDBGMASK) || + (cmd == SOUND_MIXER_CS_GETDBGLEVEL) || + (cmd == SOUND_MIXER_CS_SETDBGLEVEL)) + { + switch (cmd) { + + case SOUND_MIXER_CS_GETDBGMASK: + return put_user(cs_debugmask, + (unsigned long *) arg); + + case SOUND_MIXER_CS_GETDBGLEVEL: + return put_user(cs_debuglevel, + (unsigned long *) arg); + + case SOUND_MIXER_CS_SETDBGMASK: + if (get_user(val, (unsigned long *) arg)) + return -EFAULT; + cs_debugmask = val; + return 0; + + case SOUND_MIXER_CS_SETDBGLEVEL: + if (get_user(val, (unsigned long *) arg)) + return -EFAULT; + cs_debuglevel = val; + return 0; + default: + CS_DBGOUT(CS_ERROR, 1, printk(KERN_INFO + "cs4297a: mixer_ioctl(): ERROR unknown debug cmd\n")); + return 0; + } + } +#endif + + if (cmd == SOUND_MIXER_PRIVATE1) { + return -EINVAL; + } + if (cmd == SOUND_MIXER_PRIVATE2) { + // enable/disable/query spatializer + if (get_user(val, (int *) arg)) + return -EFAULT; + if (val != -1) { + temp1 = (val & 0x3f) >> 2; + cs4297a_write_ac97(s, AC97_3D_CONTROL, temp1); + cs4297a_read_ac97(s, AC97_GENERAL_PURPOSE, + &temp1); + cs4297a_write_ac97(s, AC97_GENERAL_PURPOSE, + temp1 | 0x2000); + } + cs4297a_read_ac97(s, AC97_3D_CONTROL, &temp1); + return put_user((temp1 << 2) | 3, (int *) arg); + } + if (cmd == SOUND_MIXER_INFO) { + mixer_info info; + strncpy(info.id, "CS4297a", sizeof(info.id)); + strncpy(info.name, "Crystal CS4297a", sizeof(info.name)); + info.modify_counter = s->mix.modcnt; + if (copy_to_user((void *) arg, &info, sizeof(info))) + return -EFAULT; + return 0; + } + if (cmd == SOUND_OLD_MIXER_INFO) { + _old_mixer_info info; + strncpy(info.id, "CS4297a", sizeof(info.id)); + strncpy(info.name, "Crystal CS4297a", sizeof(info.name)); + if (copy_to_user((void *) arg, &info, sizeof(info))) + return -EFAULT; + return 0; + } + if (cmd == OSS_GETVERSION) + return put_user(SOUND_VERSION, (int *) arg); + + if (_IOC_TYPE(cmd) != 'M' || _SIOC_SIZE(cmd) != sizeof(int)) + return -EINVAL; + + // If ioctl has only the SIOC_READ bit(bit 31) + // on, process the only-read commands. + if (_SIOC_DIR(cmd) == _SIOC_READ) { + switch (_IOC_NR(cmd)) { + case SOUND_MIXER_RECSRC: // Arg contains a bit for each recording source + cs4297a_read_ac97(s, AC97_RECORD_SELECT, + &temp1); + return put_user(mixer_src[temp1 & 7], (int *) arg); + + case SOUND_MIXER_DEVMASK: // Arg contains a bit for each supported device + return put_user(SOUND_MASK_PCM | SOUND_MASK_LINE | + SOUND_MASK_VOLUME | SOUND_MASK_RECLEV, + (int *) arg); + + case SOUND_MIXER_RECMASK: // Arg contains a bit for each supported recording source + return put_user(SOUND_MASK_LINE | SOUND_MASK_VOLUME, + (int *) arg); + + case SOUND_MIXER_STEREODEVS: // Mixer channels supporting stereo + return put_user(SOUND_MASK_PCM | SOUND_MASK_LINE | + SOUND_MASK_VOLUME | SOUND_MASK_RECLEV, + (int *) arg); + + case SOUND_MIXER_CAPS: + return put_user(SOUND_CAP_EXCL_INPUT, (int *) arg); + + default: + i = _IOC_NR(cmd); + if (i >= SOUND_MIXER_NRDEVICES + || !(vidx = mixtable1[i])) + return -EINVAL; + return put_user(s->mix.vol[vidx - 1], (int *) arg); + } + } + // If ioctl doesn't have both the SIOC_READ and + // the SIOC_WRITE bit set, return invalid. + if (_SIOC_DIR(cmd) != (_SIOC_READ | _SIOC_WRITE)) + return -EINVAL; + + // Increment the count of volume writes. + s->mix.modcnt++; + + // Isolate the command; it must be a write. + switch (_IOC_NR(cmd)) { + + case SOUND_MIXER_RECSRC: // Arg contains a bit for each recording source + if (get_user(val, (int *) arg)) + return -EFAULT; + i = hweight32(val); // i = # bits on in val. + if (i != 1) // One & only 1 bit must be on. + return 0; + for (i = 0; i < sizeof(mixer_src) / sizeof(int); i++) { + if (val == mixer_src[i]) { + temp1 = (i << 8) | i; + cs4297a_write_ac97(s, + AC97_RECORD_SELECT, + temp1); + return 0; + } + } + return 0; + + case SOUND_MIXER_VOLUME: + if (get_user(val, (int *) arg)) + return -EFAULT; + l = val & 0xff; + if (l > 100) + l = 100; // Max soundcard.h vol is 100. + if (l < 6) { + rl = 63; + l = 0; + } else + rl = attentbl[(10 * l) / 100]; // Convert 0-100 vol to 63-0 atten. + + r = (val >> 8) & 0xff; + if (r > 100) + r = 100; // Max right volume is 100, too + if (r < 6) { + rr = 63; + r = 0; + } else + rr = attentbl[(10 * r) / 100]; // Convert volume to attenuation. + + if ((rl > 60) && (rr > 60)) // If both l & r are 'low', + temp1 = 0x8000; // turn on the mute bit. + else + temp1 = 0; + + temp1 |= (rl << 8) | rr; + + cs4297a_write_ac97(s, AC97_MASTER_VOL_STEREO, temp1); + cs4297a_write_ac97(s, AC97_PHONE_VOL, temp1); + +#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS + s->mix.vol[8] = ((unsigned int) r << 8) | l; +#else + s->mix.vol[8] = val; +#endif + return put_user(s->mix.vol[8], (int *) arg); + + case SOUND_MIXER_SPEAKER: + if (get_user(val, (int *) arg)) + return -EFAULT; + l = val & 0xff; + if (l > 100) + l = 100; + if (l < 3) { + rl = 0; + l = 0; + } else { + rl = (l * 2 - 5) / 13; // Convert 0-100 range to 0-15. + l = (rl * 13 + 5) / 2; + } + + if (rl < 3) { + temp1 = 0x8000; + rl = 0; + } else + temp1 = 0; + rl = 15 - rl; // Convert volume to attenuation. + temp1 |= rl << 1; + cs4297a_write_ac97(s, AC97_PCBEEP_VOL, temp1); + +#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS + s->mix.vol[6] = l << 8; +#else + s->mix.vol[6] = val; +#endif + return put_user(s->mix.vol[6], (int *) arg); + + case SOUND_MIXER_RECLEV: + if (get_user(val, (int *) arg)) + return -EFAULT; + l = val & 0xff; + if (l > 100) + l = 100; + r = (val >> 8) & 0xff; + if (r > 100) + r = 100; + rl = (l * 2 - 5) / 13; // Convert 0-100 scale to 0-15. + rr = (r * 2 - 5) / 13; + if (rl < 3 && rr < 3) + temp1 = 0x8000; + else + temp1 = 0; + + temp1 = temp1 | (rl << 8) | rr; + cs4297a_write_ac97(s, AC97_RECORD_GAIN, temp1); + +#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS + s->mix.vol[7] = ((unsigned int) r << 8) | l; +#else + s->mix.vol[7] = val; +#endif + return put_user(s->mix.vol[7], (int *) arg); + + case SOUND_MIXER_MIC: + if (get_user(val, (int *) arg)) + return -EFAULT; + l = val & 0xff; + if (l > 100) + l = 100; + if (l < 1) { + l = 0; + rl = 0; + } else { + rl = ((unsigned) l * 5 - 4) / 16; // Convert 0-100 range to 0-31. + l = (rl * 16 + 4) / 5; + } + cs4297a_read_ac97(s, AC97_MIC_VOL, &temp1); + temp1 &= 0x40; // Isolate 20db gain bit. + if (rl < 3) { + temp1 |= 0x8000; + rl = 0; + } + rl = 31 - rl; // Convert volume to attenuation. + temp1 |= rl; + cs4297a_write_ac97(s, AC97_MIC_VOL, temp1); + +#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS + s->mix.vol[5] = val << 8; +#else + s->mix.vol[5] = val; +#endif + return put_user(s->mix.vol[5], (int *) arg); + + + case SOUND_MIXER_SYNTH: + if (get_user(val, (int *) arg)) + return -EFAULT; + l = val & 0xff; + if (l > 100) + l = 100; + if (get_user(val, (int *) arg)) + return -EFAULT; + r = (val >> 8) & 0xff; + if (r > 100) + r = 100; + rl = (l * 2 - 11) / 3; // Convert 0-100 range to 0-63. + rr = (r * 2 - 11) / 3; + if (rl < 3) // If l is low, turn on + temp1 = 0x0080; // the mute bit. + else + temp1 = 0; + + rl = 63 - rl; // Convert vol to attenuation. +// writel(temp1 | rl, s->pBA0 + FMLVC); + if (rr < 3) // If rr is low, turn on + temp1 = 0x0080; // the mute bit. + else + temp1 = 0; + rr = 63 - rr; // Convert vol to attenuation. +// writel(temp1 | rr, s->pBA0 + FMRVC); + +#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS + s->mix.vol[4] = (r << 8) | l; +#else + s->mix.vol[4] = val; +#endif + return put_user(s->mix.vol[4], (int *) arg); + + + default: + CS_DBGOUT(CS_IOCTL, 4, printk(KERN_INFO + "cs4297a: mixer_ioctl(): default\n")); + + i = _IOC_NR(cmd); + if (i >= SOUND_MIXER_NRDEVICES || !(vidx = mixtable1[i])) + return -EINVAL; + if (get_user(val, (int *) arg)) + return -EFAULT; + l = val & 0xff; + if (l > 100) + l = 100; + if (l < 1) { + l = 0; + rl = 31; + } else + rl = (attentbl[(l * 10) / 100]) >> 1; + + r = (val >> 8) & 0xff; + if (r > 100) + r = 100; + if (r < 1) { + r = 0; + rr = 31; + } else + rr = (attentbl[(r * 10) / 100]) >> 1; + if ((rl > 30) && (rr > 30)) + temp1 = 0x8000; + else + temp1 = 0; + temp1 = temp1 | (rl << 8) | rr; + cs4297a_write_ac97(s, mixreg[vidx - 1], temp1); + +#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS + s->mix.vol[vidx - 1] = ((unsigned int) r << 8) | l; +#else + s->mix.vol[vidx - 1] = val; +#endif + return put_user(s->mix.vol[vidx - 1], (int *) arg); + } +} + + +// --------------------------------------------------------------------- + +static loff_t cs4297a_llseek(struct file *file, loff_t offset, int origin) +{ + return -ESPIPE; +} + + +// --------------------------------------------------------------------- + +static int cs4297a_open_mixdev(struct inode *inode, struct file *file) +{ + int minor = MINOR(inode->i_rdev); + struct cs4297a_state *s=NULL; + struct list_head *entry; + + CS_DBGOUT(CS_FUNCTION | CS_OPEN, 4, + printk(KERN_INFO "cs4297a: cs4297a_open_mixdev()+\n")); + + list_for_each(entry, &cs4297a_devs) + { + s = list_entry(entry, struct cs4297a_state, list); + if(s->dev_mixer == minor) + break; + } + if (!s) + { + CS_DBGOUT(CS_FUNCTION | CS_OPEN | CS_ERROR, 2, + printk(KERN_INFO "cs4297a: cs4297a_open_mixdev()- -ENODEV\n")); + return -ENODEV; + } + VALIDATE_STATE(s); + file->private_data = s; + MOD_INC_USE_COUNT; + + CS_DBGOUT(CS_FUNCTION | CS_OPEN, 4, + printk(KERN_INFO "cs4297a: cs4297a_open_mixdev()- 0\n")); + + return 0; +} + + +static int cs4297a_release_mixdev(struct inode *inode, struct file *file) +{ + struct cs4297a_state *s = + (struct cs4297a_state *) file->private_data; + + VALIDATE_STATE(s); + MOD_DEC_USE_COUNT; + return 0; +} + + +static int cs4297a_ioctl_mixdev(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + return mixer_ioctl((struct cs4297a_state *) file->private_data, cmd, + arg); +} + + +// ****************************************************************************************** +// Mixer file operations struct. +// ****************************************************************************************** +static /*const */ struct file_operations cs4297a_mixer_fops = { + llseek:cs4297a_llseek, + ioctl:cs4297a_ioctl_mixdev, + open:cs4297a_open_mixdev, + release:cs4297a_release_mixdev, +}; + +// --------------------------------------------------------------------- + + +static int drain_adc(struct cs4297a_state *s, int nonblock) +{ + /* This routine serves no purpose currently - any samples + sitting in the receive queue will just be processed by the + background consumer. This would be different if DMA + actually stopped when there were no clients. */ + return 0; +} + +static int drain_dac(struct cs4297a_state *s, int nonblock) +{ + DECLARE_WAITQUEUE(wait, current); + unsigned long flags; + unsigned hwptr; + unsigned tmo; + int count; + + if (s->dma_dac.mapped) + return 0; + if (nonblock) + return -EBUSY; + add_wait_queue(&s->dma_dac.wait, &wait); + while ((count = in64(SS_CSR(R_SER_DMA_DSCR_COUNT_TX))) || + (s->dma_dac.count > 0)) { + if (!signal_pending(current)) { + set_current_state(TASK_INTERRUPTIBLE); + /* XXXKW is this calculation working? */ + tmo = ((count * FRAME_TX_US) * HZ) / 1000000; + schedule_timeout(tmo + 1); + } else { + /* XXXKW do I care if there is a signal pending? */ + } + } + spin_lock_irqsave(&s->lock, flags); + /* Reset the bookkeeping */ + hwptr = (int)(((in64(SS_CSR(R_SER_DMA_CUR_DSCR_ADDR_TX)) & M_DMA_CURDSCR_ADDR) - + s->dma_dac.descrtab_phys) / sizeof(serdma_descr_t)); + s->dma_dac.hwptr = s->dma_dac.swptr = hwptr; + spin_unlock_irqrestore(&s->lock, flags); + remove_wait_queue(&s->dma_dac.wait, &wait); + current->state = TASK_RUNNING; + return 0; +} + + +// --------------------------------------------------------------------- + +static ssize_t cs4297a_read(struct file *file, char *buffer, size_t count, + loff_t * ppos) +{ + struct cs4297a_state *s = + (struct cs4297a_state *) file->private_data; + ssize_t ret; + unsigned long flags; + int cnt, count_fr, cnt_by; + unsigned copied = 0; + + CS_DBGOUT(CS_FUNCTION | CS_WAVE_READ, 2, + printk(KERN_INFO "cs4297a: cs4297a_read()+ %d \n", count)); + + VALIDATE_STATE(s); + if (ppos != &file->f_pos) + return -ESPIPE; + if (s->dma_adc.mapped) + return -ENXIO; + if (!s->dma_adc.ready && (ret = prog_dmabuf_adc(s))) + return ret; + if (!access_ok(VERIFY_WRITE, buffer, count)) + return -EFAULT; + ret = 0; +// +// "count" is the amount of bytes to read (from app), is decremented each loop +// by the amount of bytes that have been returned to the user buffer. +// "cnt" is the running total of each read from the buffer (changes each loop) +// "buffer" points to the app's buffer +// "ret" keeps a running total of the amount of bytes that have been copied +// to the user buffer. +// "copied" is the total bytes copied into the user buffer for each loop. +// + while (count > 0) { + CS_DBGOUT(CS_WAVE_READ, 8, printk(KERN_INFO + "_read() count>0 count=%d .count=%d .swptr=%d .hwptr=%d \n", + count, s->dma_adc.count, + s->dma_adc.swptr, s->dma_adc.hwptr)); + spin_lock_irqsave(&s->lock, flags); + + /* cnt will be the number of available samples (16-bit + stereo); it starts out as the maxmimum consequetive + samples */ + cnt = (s->dma_adc.sb_end - s->dma_adc.sb_swptr) / 2; + count_fr = s->dma_adc.count / FRAME_SAMPLE_BYTES; + + // dma_adc.count is the current total bytes that have not been read. + // if the amount of unread bytes from the current sw pointer to the + // end of the buffer is greater than the current total bytes that + // have not been read, then set the "cnt" (unread bytes) to the + // amount of unread bytes. + + if (count_fr < cnt) + cnt = count_fr; + cnt_by = cnt * FRAME_SAMPLE_BYTES; + spin_unlock_irqrestore(&s->lock, flags); + // + // if we are converting from 8/16 then we need to copy + // twice the number of 16 bit bytes then 8 bit bytes. + // + if (s->conversion) { + if (cnt_by > (count * 2)) { + cnt = (count * 2) / FRAME_SAMPLE_BYTES; + cnt_by = count * 2; + } + } else { + if (cnt_by > count) { + cnt = count / FRAME_SAMPLE_BYTES; + cnt_by = count; + } + } + // + // "cnt" NOW is the smaller of the amount that will be read, + // and the amount that is requested in this read (or partial). + // if there are no bytes in the buffer to read, then start the + // ADC and wait for the interrupt handler to wake us up. + // + if (cnt <= 0) { + + // start up the dma engine and then continue back to the top of + // the loop when wake up occurs. + start_adc(s); + if (file->f_flags & O_NONBLOCK) + return ret ? ret : -EAGAIN; + interruptible_sleep_on(&s->dma_adc.wait); + if (signal_pending(current)) + return ret ? ret : -ERESTARTSYS; + continue; + } + // there are bytes in the buffer to read. + // copy from the hw buffer over to the user buffer. + // user buffer is designated by "buffer" + // virtual address to copy from is dma_buf+swptr + // the "cnt" is the number of bytes to read. + + CS_DBGOUT(CS_WAVE_READ, 2, printk(KERN_INFO + "_read() copy_to cnt=%d count=%d ", cnt_by, count)); + CS_DBGOUT(CS_WAVE_READ, 8, printk(KERN_INFO + " .sbufsz=%d .count=%d buffer=0x%.8x ret=%d\n", + s->dma_adc.sbufsz, s->dma_adc.count, + (unsigned) buffer, ret)); + + if (copy_to_user (buffer, ((void *)s->dma_adc.sb_swptr), cnt_by)) + return ret ? ret : -EFAULT; + copied = cnt_by; + + /* Return the descriptors */ + spin_lock_irqsave(&s->lock, flags); + CS_DBGOUT(CS_FUNCTION, 2, + printk(KERN_INFO "cs4297a: upd_rcv sw->hw %x/%x\n", s->dma_adc.swptr, s->dma_adc.hwptr)); + s->dma_adc.count -= cnt_by; + s->dma_adc.sb_swptr += cnt * 2; + if (s->dma_adc.sb_swptr == s->dma_adc.sb_end) + s->dma_adc.sb_swptr = s->dma_adc.sample_buf; + spin_unlock_irqrestore(&s->lock, flags); + count -= copied; + buffer += copied; + ret += copied; + start_adc(s); + } + CS_DBGOUT(CS_FUNCTION | CS_WAVE_READ, 2, + printk(KERN_INFO "cs4297a: cs4297a_read()- %d\n", ret)); + return ret; +} + + +static ssize_t cs4297a_write(struct file *file, const char *buffer, + size_t count, loff_t * ppos) +{ + struct cs4297a_state *s = + (struct cs4297a_state *) file->private_data; + ssize_t ret; + unsigned long flags; + unsigned swptr, hwptr; + int cnt; + + CS_DBGOUT(CS_FUNCTION | CS_WAVE_WRITE, 2, + printk(KERN_INFO "cs4297a: cs4297a_write()+ count=%d\n", + count)); + VALIDATE_STATE(s); + + if (ppos != &file->f_pos) + return -ESPIPE; + if (s->dma_dac.mapped) + return -ENXIO; + if (!s->dma_dac.ready && (ret = prog_dmabuf_dac(s))) + return ret; + if (!access_ok(VERIFY_READ, buffer, count)) + return -EFAULT; + ret = 0; + while (count > 0) { + serdma_t *d = &s->dma_dac; + int copy_cnt; + u32 *s_tmpl; + u32 *t_tmpl; + u32 left, right; + /* XXXKW check system endian here ... */ + int swap = (s->prop_dac.fmt == AFMT_S16_LE) || (s->prop_dac.fmt == AFMT_U16_LE); + + /* XXXXXX this is broken for BLOAT_FACTOR */ + spin_lock_irqsave(&s->lock, flags); + if (d->count < 0) { + d->count = 0; + d->swptr = d->hwptr; + } + if (d->underrun) { + d->underrun = 0; + hwptr = (unsigned) (((in64(SS_CSR(R_SER_DMA_CUR_DSCR_ADDR_TX)) & M_DMA_CURDSCR_ADDR) - + d->descrtab_phys) / sizeof(serdma_descr_t)); + d->swptr = d->hwptr = hwptr; + } + swptr = d->swptr; + cnt = d->sbufsz - (swptr * FRAME_SAMPLE_BYTES); + /* Will this write fill up the buffer? */ + if (d->count + cnt > d->sbufsz) + cnt = d->sbufsz - d->count; + spin_unlock_irqrestore(&s->lock, flags); + if (cnt > count) + cnt = count; + if (cnt <= 0) { + start_dac(s); + if (file->f_flags & O_NONBLOCK) + return ret ? ret : -EAGAIN; + interruptible_sleep_on(&d->wait); + if (signal_pending(current)) + return ret ? ret : -ERESTARTSYS; + continue; + } + if (copy_from_user(d->sample_buf, buffer, cnt)) + return ret ? ret : -EFAULT; + + copy_cnt = cnt; + s_tmpl = (u32 *)d->sample_buf; + t_tmpl = (u32 *)(d->dma_buf + (swptr * 4)); + + /* XXXKW assuming 16-bit stereo! */ + do { + t_tmpl[0] = 0x98000000; + left = s_tmpl[0] >> 16; + if (left & 0x8000) + left |= 0xf0000; + right = s_tmpl[0] & 0xffff; + if (right & 0x8000) + right |= 0xf0000; + if (swap) { + t_tmpl[1] = left & 0xff; + t_tmpl[2] = ((left & 0xff00) << 16) | ((right & 0xff) << 12) | + ((right & 0xff00) >> 4); + } else { + t_tmpl[1] = left >> 8; + t_tmpl[2] = ((left & 0xff) << 24) | (right << 4); + } + s_tmpl++; + t_tmpl += 8; + copy_cnt -= 4; + } while (copy_cnt); + + /* Mux in any pending read/write accesses */ + if (s->reg_request) { + *(u64 *)(d->dma_buf + (swptr * 4)) |= s->reg_request; + s->reg_request = 0; + wake_up(&s->dma_dac.reg_wait); + } + + CS_DBGOUT(CS_WAVE_WRITE, 4, + printk(KERN_INFO + "cs4297a: copy in %d to swptr %x\n", cnt, swptr)); + + swptr = (swptr + (cnt/FRAME_SAMPLE_BYTES)) % d->ringsz; + out64(cnt/FRAME_SAMPLE_BYTES, SS_CSR(R_SER_DMA_DSCR_COUNT_TX)); + spin_lock_irqsave(&s->lock, flags); + d->swptr = swptr; + d->count += cnt; + d->endcleared = 0; + spin_unlock_irqrestore(&s->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + start_dac(s); + } + CS_DBGOUT(CS_FUNCTION | CS_WAVE_WRITE, 2, + printk(KERN_INFO "cs4297a: cs4297a_write()- %d\n", ret)); + return ret; +} + + +static unsigned int cs4297a_poll(struct file *file, + struct poll_table_struct *wait) +{ + struct cs4297a_state *s = + (struct cs4297a_state *) file->private_data; + unsigned long flags; + unsigned int mask = 0; + + CS_DBGOUT(CS_FUNCTION | CS_WAVE_WRITE | CS_WAVE_READ, 4, + printk(KERN_INFO "cs4297a: cs4297a_poll()+\n")); + VALIDATE_STATE(s); + if (file->f_mode & FMODE_WRITE) { + CS_DBGOUT(CS_FUNCTION | CS_WAVE_WRITE | CS_WAVE_READ, 4, + printk(KERN_INFO + "cs4297a: cs4297a_poll() wait on FMODE_WRITE\n")); + if(!s->dma_dac.ready && prog_dmabuf_dac(s)) + return 0; + poll_wait(file, &s->dma_dac.wait, wait); + } + if (file->f_mode & FMODE_READ) { + CS_DBGOUT(CS_FUNCTION | CS_WAVE_WRITE | CS_WAVE_READ, 4, + printk(KERN_INFO + "cs4297a: cs4297a_poll() wait on FMODE_READ\n")); + if(!s->dma_dac.ready && prog_dmabuf_adc(s)) + return 0; + poll_wait(file, &s->dma_adc.wait, wait); + } + spin_lock_irqsave(&s->lock, flags); + cs4297a_update_ptr(s,CS_FALSE); + if (file->f_mode & FMODE_WRITE) { + if (s->dma_dac.mapped) { + if (s->dma_dac.count >= + (signed) s->dma_dac.fragsize) { + if (s->dma_dac.wakeup) + mask |= POLLOUT | POLLWRNORM; + else + mask = 0; + s->dma_dac.wakeup = 0; + } + } else { + if ((signed) (s->dma_dac.sbufsz/2) >= s->dma_dac.count) + mask |= POLLOUT | POLLWRNORM; + } + } else if (file->f_mode & FMODE_READ) { + if (s->dma_adc.mapped) { + if (s->dma_adc.count >= (signed) s->dma_adc.fragsize) + mask |= POLLIN | POLLRDNORM; + } else { + if (s->dma_adc.count > 0) + mask |= POLLIN | POLLRDNORM; + } + } + spin_unlock_irqrestore(&s->lock, flags); + CS_DBGOUT(CS_FUNCTION | CS_WAVE_WRITE | CS_WAVE_READ, 4, + printk(KERN_INFO "cs4297a: cs4297a_poll()- 0x%.8x\n", + mask)); + return mask; +} + + +static int cs4297a_mmap(struct file *file, struct vm_area_struct *vma) +{ + /* XXXKW currently no mmap support */ + return -EINVAL; + return 0; +} + + +static int cs4297a_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct cs4297a_state *s = + (struct cs4297a_state *) file->private_data; + unsigned long flags; + audio_buf_info abinfo; + count_info cinfo; + int val, mapped, ret; + + CS_DBGOUT(CS_FUNCTION|CS_IOCTL, 4, printk(KERN_INFO + "cs4297a: cs4297a_ioctl(): file=0x%.8x cmd=0x%.8x\n", + (unsigned) file, cmd)); +#if CSDEBUG + cs_printioctl(cmd); +#endif + VALIDATE_STATE(s); + mapped = ((file->f_mode & FMODE_WRITE) && s->dma_dac.mapped) || + ((file->f_mode & FMODE_READ) && s->dma_adc.mapped); + switch (cmd) { + case OSS_GETVERSION: + CS_DBGOUT(CS_IOCTL | CS_PARMS, 4, printk(KERN_INFO + "cs4297a: cs4297a_ioctl(): SOUND_VERSION=0x%.8x\n", + SOUND_VERSION)); + return put_user(SOUND_VERSION, (int *) arg); + + case SNDCTL_DSP_SYNC: + CS_DBGOUT(CS_IOCTL, 4, printk(KERN_INFO + "cs4297a: cs4297a_ioctl(): DSP_SYNC\n")); + if (file->f_mode & FMODE_WRITE) + return drain_dac(s, + 0 /*file->f_flags & O_NONBLOCK */ + ); + return 0; + + case SNDCTL_DSP_SETDUPLEX: + return 0; + + case SNDCTL_DSP_GETCAPS: + return put_user(DSP_CAP_DUPLEX | DSP_CAP_REALTIME | + DSP_CAP_TRIGGER | DSP_CAP_MMAP, + (int *) arg); + + case SNDCTL_DSP_RESET: + CS_DBGOUT(CS_IOCTL, 4, printk(KERN_INFO + "cs4297a: cs4297a_ioctl(): DSP_RESET\n")); + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + synchronize_irq(); + s->dma_dac.count = s->dma_dac.total_bytes = + s->dma_dac.blocks = s->dma_dac.wakeup = 0; + s->dma_dac.swptr = s->dma_dac.hwptr = + (int)(((in64(SS_CSR(R_SER_DMA_CUR_DSCR_ADDR_TX)) & M_DMA_CURDSCR_ADDR) - + s->dma_dac.descrtab_phys) / sizeof(serdma_descr_t)); + } + if (file->f_mode & FMODE_READ) { + stop_adc(s); + synchronize_irq(); + s->dma_adc.count = s->dma_adc.total_bytes = + s->dma_adc.blocks = s->dma_dac.wakeup = 0; + s->dma_adc.swptr = s->dma_adc.hwptr = + (int)(((in64(SS_CSR(R_SER_DMA_CUR_DSCR_ADDR_RX)) & M_DMA_CURDSCR_ADDR) - + s->dma_adc.descrtab_phys) / sizeof(serdma_descr_t)); + } + return 0; + + case SNDCTL_DSP_SPEED: + if (get_user(val, (int *) arg)) + return -EFAULT; + CS_DBGOUT(CS_IOCTL | CS_PARMS, 4, printk(KERN_INFO + "cs4297a: cs4297a_ioctl(): DSP_SPEED val=%d -> 48000\n", val)); + val = 48000; + return put_user(val, (int *) arg); + + case SNDCTL_DSP_STEREO: + if (get_user(val, (int *) arg)) + return -EFAULT; + CS_DBGOUT(CS_IOCTL | CS_PARMS, 4, printk(KERN_INFO + "cs4297a: cs4297a_ioctl(): DSP_STEREO val=%d\n", val)); + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dma_adc.ready = 0; + s->prop_adc.channels = val ? 2 : 1; + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + s->dma_dac.ready = 0; + s->prop_dac.channels = val ? 2 : 1; + } + return 0; + + case SNDCTL_DSP_CHANNELS: + if (get_user(val, (int *) arg)) + return -EFAULT; + CS_DBGOUT(CS_IOCTL | CS_PARMS, 4, printk(KERN_INFO + "cs4297a: cs4297a_ioctl(): DSP_CHANNELS val=%d\n", + val)); + if (val != 0) { + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dma_adc.ready = 0; + if (val >= 2) + s->prop_adc.channels = 2; + else + s->prop_adc.channels = 1; + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + s->dma_dac.ready = 0; + if (val >= 2) + s->prop_dac.channels = 2; + else + s->prop_dac.channels = 1; + } + } + + if (file->f_mode & FMODE_WRITE) + val = s->prop_dac.channels; + else if (file->f_mode & FMODE_READ) + val = s->prop_adc.channels; + + return put_user(val, (int *) arg); + + case SNDCTL_DSP_GETFMTS: // Returns a mask + CS_DBGOUT(CS_IOCTL | CS_PARMS, 4, printk(KERN_INFO + "cs4297a: cs4297a_ioctl(): DSP_GETFMT val=0x%.8x\n", + AFMT_S16_LE | AFMT_U16_LE | AFMT_S8 | + AFMT_U8)); + return put_user(AFMT_S16_LE | AFMT_U16_LE | AFMT_S8 | + AFMT_U8, (int *) arg); + + case SNDCTL_DSP_SETFMT: + if (get_user(val, (int *) arg)) + return -EFAULT; + CS_DBGOUT(CS_IOCTL | CS_PARMS, 4, printk(KERN_INFO + "cs4297a: cs4297a_ioctl(): DSP_SETFMT val=0x%.8x\n", + val)); + if (val != AFMT_QUERY) { + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dma_adc.ready = 0; + if (val != AFMT_S16_LE + && val != AFMT_U16_LE && val != AFMT_S8 + && val != AFMT_U8) + val = AFMT_U8; + s->prop_adc.fmt = val; + s->prop_adc.fmt_original = s->prop_adc.fmt; + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + s->dma_dac.ready = 0; + if (val != AFMT_S16_LE + && val != AFMT_U16_LE && val != AFMT_S8 + && val != AFMT_U8) + val = AFMT_U8; + s->prop_dac.fmt = val; + s->prop_dac.fmt_original = s->prop_dac.fmt; + } + } else { + if (file->f_mode & FMODE_WRITE) + val = s->prop_dac.fmt_original; + else if (file->f_mode & FMODE_READ) + val = s->prop_adc.fmt_original; + } + CS_DBGOUT(CS_IOCTL | CS_PARMS, 4, printk(KERN_INFO + "cs4297a: cs4297a_ioctl(): DSP_SETFMT return val=0x%.8x\n", + val)); + return put_user(val, (int *) arg); + + case SNDCTL_DSP_POST: + CS_DBGOUT(CS_IOCTL, 4, printk(KERN_INFO + "cs4297a: cs4297a_ioctl(): DSP_POST\n")); + return 0; + + case SNDCTL_DSP_GETTRIGGER: + val = 0; + if (file->f_mode & s->ena & FMODE_READ) + val |= PCM_ENABLE_INPUT; + if (file->f_mode & s->ena & FMODE_WRITE) + val |= PCM_ENABLE_OUTPUT; + return put_user(val, (int *) arg); + + case SNDCTL_DSP_SETTRIGGER: + if (get_user(val, (int *) arg)) + return -EFAULT; + if (file->f_mode & FMODE_READ) { + if (val & PCM_ENABLE_INPUT) { + if (!s->dma_adc.ready + && (ret = prog_dmabuf_adc(s))) + return ret; + start_adc(s); + } else + stop_adc(s); + } + if (file->f_mode & FMODE_WRITE) { + if (val & PCM_ENABLE_OUTPUT) { + if (!s->dma_dac.ready + && (ret = prog_dmabuf_dac(s))) + return ret; + start_dac(s); + } else + stop_dac(s); + } + return 0; + + case SNDCTL_DSP_GETOSPACE: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + if (!s->dma_dac.ready && (val = prog_dmabuf_dac(s))) + return val; + spin_lock_irqsave(&s->lock, flags); + cs4297a_update_ptr(s,CS_FALSE); + abinfo.fragsize = s->dma_dac.fragsize; + if (s->dma_dac.mapped) + abinfo.bytes = s->dma_dac.sbufsz; + else + abinfo.bytes = + s->dma_dac.sbufsz - s->dma_dac.count; + abinfo.fragstotal = s->dma_dac.numfrag; + abinfo.fragments = abinfo.bytes >> s->dma_dac.fragshift; + CS_DBGOUT(CS_FUNCTION | CS_PARMS, 4, printk(KERN_INFO + "cs4297a: cs4297a_ioctl(): GETOSPACE .fragsize=%d .bytes=%d .fragstotal=%d .fragments=%d\n", + abinfo.fragsize,abinfo.bytes,abinfo.fragstotal, + abinfo.fragments)); + spin_unlock_irqrestore(&s->lock, flags); + return copy_to_user((void *) arg, &abinfo, + sizeof(abinfo)) ? -EFAULT : 0; + + case SNDCTL_DSP_GETISPACE: + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + if (!s->dma_adc.ready && (val = prog_dmabuf_adc(s))) + return val; + spin_lock_irqsave(&s->lock, flags); + cs4297a_update_ptr(s,CS_FALSE); + if (s->conversion) { + abinfo.fragsize = s->dma_adc.fragsize / 2; + abinfo.bytes = s->dma_adc.count / 2; + abinfo.fragstotal = s->dma_adc.numfrag; + abinfo.fragments = + abinfo.bytes >> (s->dma_adc.fragshift - 1); + } else { + abinfo.fragsize = s->dma_adc.fragsize; + abinfo.bytes = s->dma_adc.count; + abinfo.fragstotal = s->dma_adc.numfrag; + abinfo.fragments = + abinfo.bytes >> s->dma_adc.fragshift; + } + spin_unlock_irqrestore(&s->lock, flags); + return copy_to_user((void *) arg, &abinfo, + sizeof(abinfo)) ? -EFAULT : 0; + + case SNDCTL_DSP_NONBLOCK: + file->f_flags |= O_NONBLOCK; + return 0; + + case SNDCTL_DSP_GETODELAY: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + if(!s->dma_dac.ready && prog_dmabuf_dac(s)) + return 0; + spin_lock_irqsave(&s->lock, flags); + cs4297a_update_ptr(s,CS_FALSE); + val = s->dma_dac.count; + spin_unlock_irqrestore(&s->lock, flags); + return put_user(val, (int *) arg); + + case SNDCTL_DSP_GETIPTR: + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + if(!s->dma_adc.ready && prog_dmabuf_adc(s)) + return 0; + spin_lock_irqsave(&s->lock, flags); + cs4297a_update_ptr(s,CS_FALSE); + cinfo.bytes = s->dma_adc.total_bytes; + if (s->dma_adc.mapped) { + cinfo.blocks = + (cinfo.bytes >> s->dma_adc.fragshift) - + s->dma_adc.blocks; + s->dma_adc.blocks = + cinfo.bytes >> s->dma_adc.fragshift; + } else { + if (s->conversion) { + cinfo.blocks = + s->dma_adc.count / + 2 >> (s->dma_adc.fragshift - 1); + } else + cinfo.blocks = + s->dma_adc.count >> s->dma_adc. + fragshift; + } + if (s->conversion) + cinfo.ptr = s->dma_adc.hwptr / 2; + else + cinfo.ptr = s->dma_adc.hwptr; + if (s->dma_adc.mapped) + s->dma_adc.count &= s->dma_adc.fragsize - 1; + spin_unlock_irqrestore(&s->lock, flags); + return copy_to_user((void *) arg, &cinfo, sizeof(cinfo)); + + case SNDCTL_DSP_GETOPTR: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + if(!s->dma_dac.ready && prog_dmabuf_dac(s)) + return 0; + spin_lock_irqsave(&s->lock, flags); + cs4297a_update_ptr(s,CS_FALSE); + cinfo.bytes = s->dma_dac.total_bytes; + if (s->dma_dac.mapped) { + cinfo.blocks = + (cinfo.bytes >> s->dma_dac.fragshift) - + s->dma_dac.blocks; + s->dma_dac.blocks = + cinfo.bytes >> s->dma_dac.fragshift; + } else { + cinfo.blocks = + s->dma_dac.count >> s->dma_dac.fragshift; + } + cinfo.ptr = s->dma_dac.hwptr; + if (s->dma_dac.mapped) + s->dma_dac.count &= s->dma_dac.fragsize - 1; + spin_unlock_irqrestore(&s->lock, flags); + return copy_to_user((void *) arg, &cinfo, sizeof(cinfo)); + + case SNDCTL_DSP_GETBLKSIZE: + if (file->f_mode & FMODE_WRITE) { + if ((val = prog_dmabuf_dac(s))) + return val; + return put_user(s->dma_dac.fragsize, (int *) arg); + } + if ((val = prog_dmabuf_adc(s))) + return val; + if (s->conversion) + return put_user(s->dma_adc.fragsize / 2, + (int *) arg); + else + return put_user(s->dma_adc.fragsize, (int *) arg); + + case SNDCTL_DSP_SETFRAGMENT: + if (get_user(val, (int *) arg)) + return -EFAULT; + return 0; // Say OK, but do nothing. + + case SNDCTL_DSP_SUBDIVIDE: + if ((file->f_mode & FMODE_READ && s->dma_adc.subdivision) + || (file->f_mode & FMODE_WRITE + && s->dma_dac.subdivision)) return -EINVAL; + if (get_user(val, (int *) arg)) + return -EFAULT; + if (val != 1 && val != 2 && val != 4) + return -EINVAL; + if (file->f_mode & FMODE_READ) + s->dma_adc.subdivision = val; + else if (file->f_mode & FMODE_WRITE) + s->dma_dac.subdivision = val; + return 0; + + case SOUND_PCM_READ_RATE: + if (file->f_mode & FMODE_READ) + return put_user(s->prop_adc.rate, (int *) arg); + else if (file->f_mode & FMODE_WRITE) + return put_user(s->prop_dac.rate, (int *) arg); + + case SOUND_PCM_READ_CHANNELS: + if (file->f_mode & FMODE_READ) + return put_user(s->prop_adc.channels, (int *) arg); + else if (file->f_mode & FMODE_WRITE) + return put_user(s->prop_dac.channels, (int *) arg); + + case SOUND_PCM_READ_BITS: + if (file->f_mode & FMODE_READ) + return + put_user( + (s->prop_adc. + fmt & (AFMT_S8 | AFMT_U8)) ? 8 : 16, + (int *) arg); + else if (file->f_mode & FMODE_WRITE) + return + put_user( + (s->prop_dac. + fmt & (AFMT_S8 | AFMT_U8)) ? 8 : 16, + (int *) arg); + + case SOUND_PCM_WRITE_FILTER: + case SNDCTL_DSP_SETSYNCRO: + case SOUND_PCM_READ_FILTER: + return -EINVAL; + } + return mixer_ioctl(s, cmd, arg); +} + + +static int cs4297a_release(struct inode *inode, struct file *file) +{ + struct cs4297a_state *s = + (struct cs4297a_state *) file->private_data; + + CS_DBGOUT(CS_FUNCTION | CS_RELEASE, 2, printk(KERN_INFO + "cs4297a: cs4297a_release(): inode=0x%.8x file=0x%.8x f_mode=0x%x\n", + (unsigned) inode, (unsigned) file, file->f_mode)); + VALIDATE_STATE(s); + + if (file->f_mode & FMODE_WRITE) { + drain_dac(s, file->f_flags & O_NONBLOCK); + down(&s->open_sem_dac); + stop_dac(s); + dealloc_dmabuf(s, &s->dma_dac); + s->open_mode &= ~FMODE_WRITE; + up(&s->open_sem_dac); + wake_up(&s->open_wait_dac); + MOD_DEC_USE_COUNT; + } + if (file->f_mode & FMODE_READ) { + drain_adc(s, file->f_flags & O_NONBLOCK); + down(&s->open_sem_adc); + stop_adc(s); + dealloc_dmabuf(s, &s->dma_adc); + s->open_mode &= ~FMODE_READ; + up(&s->open_sem_adc); + wake_up(&s->open_wait_adc); + MOD_DEC_USE_COUNT; + } + return 0; +} + +static int cs4297a_open(struct inode *inode, struct file *file) +{ + int minor = MINOR(inode->i_rdev); + struct cs4297a_state *s=NULL; + struct list_head *entry; + + CS_DBGOUT(CS_FUNCTION | CS_OPEN, 2, printk(KERN_INFO + "cs4297a: cs4297a_open(): inode=0x%.8x file=0x%.8x f_mode=0x%x\n", + (unsigned) inode, (unsigned) file, file->f_mode)); + CS_DBGOUT(CS_FUNCTION | CS_OPEN, 2, printk(KERN_INFO + "cs4297a: status = %08x\n", (int)in64(SS_CSR(R_SER_STATUS_DEBUG)))); + + list_for_each(entry, &cs4297a_devs) + { + s = list_entry(entry, struct cs4297a_state, list); + + if (!((s->dev_audio ^ minor) & ~0xf)) + break; + } + if (entry == &cs4297a_devs) + return -ENODEV; + if (!s) { + CS_DBGOUT(CS_FUNCTION | CS_OPEN, 2, printk(KERN_INFO + "cs4297a: cs4297a_open(): Error - unable to find audio state struct\n")); + return -ENODEV; + } + VALIDATE_STATE(s); + file->private_data = s; + + // wait for device to become free + if (!(file->f_mode & (FMODE_WRITE | FMODE_READ))) { + CS_DBGOUT(CS_FUNCTION | CS_OPEN | CS_ERROR, 2, printk(KERN_INFO + "cs4297a: cs4297a_open(): Error - must open READ and/or WRITE\n")); + return -ENODEV; + } + if (file->f_mode & FMODE_WRITE) { + if (in64(SS_CSR(R_SER_DMA_DSCR_COUNT_TX)) != 0) { + printk(KERN_ERR "cs4297a: TX pipe needs to drain\n"); + while (in64(SS_CSR(R_SER_DMA_DSCR_COUNT_TX))) + ; + } + + down(&s->open_sem_dac); + while (s->open_mode & FMODE_WRITE) { + if (file->f_flags & O_NONBLOCK) { + up(&s->open_sem_dac); + return -EBUSY; + } + up(&s->open_sem_dac); + interruptible_sleep_on(&s->open_wait_dac); + + if (signal_pending(current)) { + printk("open - sig pending\n"); + return -ERESTARTSYS; + } + down(&s->open_sem_dac); + } + } + if (file->f_mode & FMODE_READ) { + down(&s->open_sem_adc); + while (s->open_mode & FMODE_READ) { + if (file->f_flags & O_NONBLOCK) { + up(&s->open_sem_adc); + return -EBUSY; + } + up(&s->open_sem_adc); + interruptible_sleep_on(&s->open_wait_adc); + + if (signal_pending(current)) { + printk("open - sig pending\n"); + return -ERESTARTSYS; + } + down(&s->open_sem_adc); + } + } + s->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE); + if (file->f_mode & FMODE_READ) { + s->prop_adc.fmt = AFMT_S16_BE; + s->prop_adc.fmt_original = s->prop_adc.fmt; + s->prop_adc.channels = 2; + s->prop_adc.rate = 48000; + s->conversion = 0; + s->ena &= ~FMODE_READ; + s->dma_adc.ossfragshift = s->dma_adc.ossmaxfrags = + s->dma_adc.subdivision = 0; + up(&s->open_sem_adc); + MOD_INC_USE_COUNT; + + if (prog_dmabuf_adc(s)) { + CS_DBGOUT(CS_OPEN | CS_ERROR, 2, printk(KERN_ERR + "cs4297a: adc Program dmabufs failed.\n")); + cs4297a_release(inode, file); + return -ENOMEM; + } + } + if (file->f_mode & FMODE_WRITE) { + s->prop_dac.fmt = AFMT_S16_BE; + s->prop_dac.fmt_original = s->prop_dac.fmt; + s->prop_dac.channels = 2; + s->prop_dac.rate = 48000; + s->conversion = 0; + s->ena &= ~FMODE_WRITE; + s->dma_dac.ossfragshift = s->dma_dac.ossmaxfrags = + s->dma_dac.subdivision = 0; + up(&s->open_sem_dac); + MOD_INC_USE_COUNT; + + if (prog_dmabuf_dac(s)) { + CS_DBGOUT(CS_OPEN | CS_ERROR, 2, printk(KERN_ERR + "cs4297a: dac Program dmabufs failed.\n")); + cs4297a_release(inode, file); + return -ENOMEM; + } + } + CS_DBGOUT(CS_FUNCTION | CS_OPEN, 2, + printk(KERN_INFO "cs4297a: cs4297a_open()- 0\n")); + return 0; +} + + +// ****************************************************************************************** +// Wave (audio) file operations struct. +// ****************************************************************************************** +static /*const */ struct file_operations cs4297a_audio_fops = { + llseek:cs4297a_llseek, + read:cs4297a_read, + write:cs4297a_write, + poll:cs4297a_poll, + ioctl:cs4297a_ioctl, + mmap:cs4297a_mmap, + open:cs4297a_open, + release:cs4297a_release, +}; + +static irqreturn_t cs4297a_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct cs4297a_state *s = (struct cs4297a_state *) dev_id; + u32 status; + + status = in64(SS_CSR(R_SER_STATUS_DEBUG)); + + CS_DBGOUT(CS_INTERRUPT, 6, printk(KERN_INFO + "cs4297a: cs4297a_interrupt() HISR=0x%.8x\n", status)); + +#if 0 + /* XXXKW what check *should* be done here? */ + if (!(status & (M_SYNCSER_RX_EOP_COUNT | M_SYNCSER_RX_OVERRUN | M_SYNCSER_RX_SYNC_ERR))) { + status = in64(SS_CSR(R_SER_STATUS)); + printk(KERN_ERR "cs4297a: unexpected interrupt (status %08x)\n", status); + return IRQ_HANDLED; + } +#endif + + if (status & M_SYNCSER_RX_SYNC_ERR) { + status = in64(SS_CSR(R_SER_STATUS)); + printk(KERN_ERR "cs4297a: rx sync error (status %08x)\n", status); + return IRQ_HANDLED; + } + + if (status & M_SYNCSER_RX_OVERRUN) { + int newptr, i; + s->stats.rx_ovrrn++; + printk(KERN_ERR "cs4297a: receive FIFO overrun\n"); + + /* Fix things up: get the receive descriptor pool + clean and give them back to the hardware */ + while (in64(SS_CSR(R_SER_DMA_DSCR_COUNT_RX))) + ; + newptr = (unsigned) (((in64(SS_CSR(R_SER_DMA_CUR_DSCR_ADDR_RX)) & M_DMA_CURDSCR_ADDR) - + s->dma_adc.descrtab_phys) / sizeof(serdma_descr_t)); + for (i=0; idma_adc.descrtab[i].descr_a &= ~M_DMA_SERRX_SOP; + } + s->dma_adc.swptr = s->dma_adc.hwptr = newptr; + s->dma_adc.count = 0; + s->dma_adc.sb_swptr = s->dma_adc.sb_hwptr = s->dma_adc.sample_buf; + out64(DMA_DESCR, SS_CSR(R_SER_DMA_DSCR_COUNT_RX)); + } + + spin_lock(&s->lock); + cs4297a_update_ptr(s,CS_TRUE); + spin_unlock(&s->lock); + + CS_DBGOUT(CS_INTERRUPT, 6, printk(KERN_INFO + "cs4297a: cs4297a_interrupt()-\n")); + return IRQ_HANDLED; +} + +static struct initvol { + int mixch; + int vol; +} initvol[] __initdata = { + + {SOUND_MIXER_WRITE_VOLUME, 0x4040}, + {SOUND_MIXER_WRITE_PCM, 0x4040}, + {SOUND_MIXER_WRITE_SYNTH, 0x4040}, + {SOUND_MIXER_WRITE_CD, 0x4040}, + {SOUND_MIXER_WRITE_LINE, 0x4040}, + {SOUND_MIXER_WRITE_LINE1, 0x4040}, + {SOUND_MIXER_WRITE_RECLEV, 0x0000}, + {SOUND_MIXER_WRITE_SPEAKER, 0x4040}, + {SOUND_MIXER_WRITE_MIC, 0x0000} +}; + +static int __init cs4297a_init(void) +{ + struct cs4297a_state *s; + u64 cfg; + u32 pwr, id; + mm_segment_t fs; + int rval, mdio_val; + + CS_DBGOUT(CS_INIT | CS_FUNCTION, 2, printk(KERN_INFO + "cs4297a: cs4297a_init_module()+ \n")); + + mdio_val = in64(KSEG1 + A_MAC_REGISTER(2, R_MAC_MDIO)) & + (M_MAC_MDIO_DIR|M_MAC_MDIO_OUT); + + /* Check syscfg for synchronous serial on port 1 */ + cfg = in64(KSEG1 + A_SCD_SYSTEM_CFG); + if (!(cfg & M_SYS_SER1_ENABLE)) { + out64(cfg | M_SYS_SER1_ENABLE, KSEG1+A_SCD_SYSTEM_CFG); + cfg = in64(KSEG1 + A_SCD_SYSTEM_CFG); + if (!(cfg & M_SYS_SER1_ENABLE)) { + printk(KERN_INFO "cs4297a: serial port 1 not configured for synchronous operation\n"); + return -1; + } + + printk(KERN_INFO "cs4297a: serial port 1 switching to synchronous operation\n"); + + /* Force the codec (on SWARM) to reset by clearing + GENO, preserving MDIO (no effect on CSWARM) */ + out64(mdio_val, KSEG1+A_MAC_REGISTER(2, R_MAC_MDIO)); + udelay(10); + } + + /* Now set GENO */ + out64(mdio_val | M_MAC_GENC, KSEG1+A_MAC_REGISTER(2, R_MAC_MDIO)); + /* Give the codec some time to finish resetting (start the bit clock) */ + udelay(100); + + if (!(s = kmalloc(sizeof(struct cs4297a_state), GFP_KERNEL))) { + CS_DBGOUT(CS_ERROR, 1, printk(KERN_ERR + "cs4297a: probe() no memory for state struct.\n")); + return -1; + } + memset(s, 0, sizeof(struct cs4297a_state)); + s->magic = CS4297a_MAGIC; + init_waitqueue_head(&s->dma_adc.wait); + init_waitqueue_head(&s->dma_dac.wait); + init_waitqueue_head(&s->dma_adc.reg_wait); + init_waitqueue_head(&s->dma_dac.reg_wait); + init_waitqueue_head(&s->open_wait); + init_waitqueue_head(&s->open_wait_adc); + init_waitqueue_head(&s->open_wait_dac); + init_MUTEX(&s->open_sem_adc); + init_MUTEX(&s->open_sem_dac); + spin_lock_init(&s->lock); + + s->irq = K_INT_SER_1; + + if (request_irq + (s->irq, cs4297a_interrupt, 0, "Crystal CS4297a", s)) { + CS_DBGOUT(CS_INIT | CS_ERROR, 1, + printk(KERN_ERR "cs4297a: irq %u in use\n", s->irq)); + goto err_irq; + } + if ((s->dev_audio = register_sound_dsp(&cs4297a_audio_fops, -1)) < + 0) { + CS_DBGOUT(CS_INIT | CS_ERROR, 1, printk(KERN_ERR + "cs4297a: probe() register_sound_dsp() failed.\n")); + goto err_dev1; + } + if ((s->dev_mixer = register_sound_mixer(&cs4297a_mixer_fops, -1)) < + 0) { + CS_DBGOUT(CS_INIT | CS_ERROR, 1, printk(KERN_ERR + "cs4297a: probe() register_sound_mixer() failed.\n")); + goto err_dev2; + } + + if (ser_init(s) || dma_init(s)) { + CS_DBGOUT(CS_INIT | CS_ERROR, 1, printk(KERN_ERR + "cs4297a: ser_init failed.\n")); + goto err_dev3; + } + + do { + udelay(4000); + rval = cs4297a_read_ac97(s, AC97_POWER_CONTROL, &pwr); + } while (!rval && (pwr != 0xf)); + + if (!rval) { + fs = get_fs(); + set_fs(KERNEL_DS); +#if 0 + val = SOUND_MASK_LINE; + mixer_ioctl(s, SOUND_MIXER_WRITE_RECSRC, (unsigned long) &val); + for (i = 0; i < sizeof(initvol) / sizeof(initvol[0]); i++) { + val = initvol[i].vol; + mixer_ioctl(s, initvol[i].mixch, (unsigned long) &val); + } +// cs4297a_write_ac97(s, 0x18, 0x0808); +#else + // cs4297a_write_ac97(s, 0x5e, 0x180); + cs4297a_write_ac97(s, 0x02, 0x0808); + cs4297a_write_ac97(s, 0x18, 0x0808); +#endif + set_fs(fs); + + list_add(&s->list, &cs4297a_devs); + + cs4297a_read_ac97(s, AC97_VENDOR_ID1, &id); + + printk(KERN_INFO "cs4297a: initialized (vendor id = %x)\n", id); + + CS_DBGOUT(CS_INIT | CS_FUNCTION, 2, + printk(KERN_INFO "cs4297a: cs4297a_init_module()-\n")); + + return 0; + } + + err_dev3: + unregister_sound_mixer(s->dev_mixer); + err_dev2: + unregister_sound_dsp(s->dev_audio); + err_dev1: + free_irq(s->irq, s); + err_irq: + kfree(s); + + printk(KERN_INFO "cs4297a: initialization failed\n"); + + return -1; +} + +static void __exit cs4297a_cleanup(void) +{ + /* + XXXKW + disable_irq, free_irq + drain DMA queue + disable DMA + disable TX/RX + free memory + */ + CS_DBGOUT(CS_INIT | CS_FUNCTION, 2, + printk(KERN_INFO "cs4297a: cleanup_cs4297a() finished\n")); +} + +// --------------------------------------------------------------------- + +EXPORT_NO_SYMBOLS; + +MODULE_AUTHOR("Kip Walker, kwalker@broadcom.com"); +MODULE_DESCRIPTION("Cirrus Logic CS4297a Driver for Broadcom SWARM board"); + +// --------------------------------------------------------------------- + +module_init(cs4297a_init); +module_exit(cs4297a_cleanup); diff -Nru a/sound/oss/trident.c b/sound/oss/trident.c --- a/sound/oss/trident.c Mon Jun 9 16:23:09 2003 +++ b/sound/oss/trident.c Fri Jul 11 09:41:49 2003 @@ -37,6 +37,12 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * History + * v0.14.10h + * Sept 10 2002 Pascal Schmidt + * added support for ALi 5451 joystick port + * v0.14.10g + * Sept 05 2002 Alan Cox + * adapt to new pci joystick attachment interface * v0.14.10f * July 24 2002 Muli Ben-Yehuda * patch from Eric Lemar (via Ian Soboroff): in suspend and resume, @@ -212,7 +218,7 @@ #include "trident.h" -#define DRIVER_VERSION "0.14.10f" +#define DRIVER_VERSION "0.14.10h-2.5" /* magic numbers to protect our data structures */ #define TRIDENT_CARD_MAGIC 0x5072696E /* "Prin" */ @@ -538,8 +544,8 @@ outl(global_control, TRID_REG(card, T4D_LFO_GC_CIR)); - pr_debug("trident: Enable Loop Interrupts, globctl = 0x%08X\n", - inl(TRID_REG(card, T4D_LFO_GC_CIR))); + TRDBG("trident: Enable Loop Interrupts, globctl = 0x%08X\n", + inl(TRID_REG(card, T4D_LFO_GC_CIR))); return (TRUE); } @@ -552,8 +558,8 @@ global_control &= ~(ENDLP_IE | MIDLP_IE); outl(global_control, TRID_REG(card, T4D_LFO_GC_CIR)); - pr_debug("trident: Disabled Loop Interrupts, globctl = 0x%08X\n", - global_control); + TRDBG("trident: Disabled Loop Interrupts, globctl = 0x%08X\n", + global_control); return (TRUE); } @@ -570,8 +576,8 @@ #ifdef DEBUG reg = inl(TRID_REG(card, addr)); - pr_debug("trident: enabled IRQ on channel %d, %s = 0x%08x(addr:%X)\n", - channel, addr==T4D_AINTEN_B? "AINTEN_B":"AINTEN_A",reg,addr); + TRDBG("trident: enabled IRQ on channel %d, %s = 0x%08x(addr:%X)\n", + channel, addr==T4D_AINTEN_B? "AINTEN_B":"AINTEN_A",reg,addr); #endif /* DEBUG */ } @@ -590,8 +596,8 @@ #ifdef DEBUG reg = inl(TRID_REG(card, addr)); - pr_debug("trident: disabled IRQ on channel %d, %s = 0x%08x(addr:%X)\n", - channel, addr==T4D_AINTEN_B? "AINTEN_B":"AINTEN_A",reg,addr); + TRDBG("trident: disabled IRQ on channel %d, %s = 0x%08x(addr:%X)\n", + channel, addr==T4D_AINTEN_B? "AINTEN_B":"AINTEN_A",reg,addr); #endif /* DEBUG */ } @@ -609,8 +615,8 @@ #ifdef DEBUG reg = inl(TRID_REG(card, addr)); - pr_debug("trident: start voice on channel %d, %s = 0x%08x(addr:%X)\n", - channel, addr==T4D_START_B? "START_B":"START_A",reg,addr); + TRDBG("trident: start voice on channel %d, %s = 0x%08x(addr:%X)\n", + channel, addr==T4D_START_B? "START_B":"START_A",reg,addr); #endif /* DEBUG */ } @@ -628,8 +634,8 @@ #ifdef DEBUG reg = inl(TRID_REG(card, addr)); - pr_debug("trident: stop voice on channel %d, %s = 0x%08x(addr:%X)\n", - channel, addr==T4D_STOP_B? "STOP_B":"STOP_A",reg,addr); + TRDBG("trident: stop voice on channel %d, %s = 0x%08x(addr:%X)\n", + channel, addr==T4D_STOP_B? "STOP_B":"STOP_A",reg,addr); #endif /* DEBUG */ } @@ -647,8 +653,8 @@ #ifdef DEBUG if (reg & mask) - pr_debug("trident: channel %d has interrupt, %s = 0x%08x\n", - channel,reg==T4D_AINT_B? "AINT_B":"AINT_A", reg); + TRDBG("trident: channel %d has interrupt, %s = 0x%08x\n", + channel,reg==T4D_AINT_B? "AINT_B":"AINT_A", reg); #endif /* DEBUG */ return (reg & mask) ? TRUE : FALSE; } @@ -665,8 +671,8 @@ #ifdef DEBUG reg = inl(TRID_REG(card, T4D_AINT_B)); - pr_debug("trident: Ack channel %d interrupt, AINT_B = 0x%08x\n", - channel, reg); + TRDBG("trident: Ack channel %d interrupt, AINT_B = 0x%08x\n", + channel, reg); #endif /* DEBUG */ } @@ -914,7 +920,7 @@ trident_write_voice_regs(state); - pr_debug("trident: called trident_set_dac_rate : rate = %d\n", rate); + TRDBG("trident: called trident_set_dac_rate : rate = %d\n", rate); return rate; } @@ -934,7 +940,7 @@ trident_write_voice_regs(state); - pr_debug("trident: called trident_set_adc_rate : rate = %d\n", rate); + TRDBG("trident: called trident_set_adc_rate : rate = %d\n", rate); return rate; } @@ -978,9 +984,9 @@ /* stereo */ channel->control |= CHANNEL_STEREO; - pr_debug("trident: trident_play_setup, LBA = 0x%08x, " - "Delta = 0x%08x, ESO = 0x%08x, Control = 0x%08x\n", - channel->lba, channel->delta, channel->eso, channel->control); + TRDBG("trident: trident_play_setup, LBA = 0x%08x, " + "Delta = 0x%08x, ESO = 0x%08x, Control = 0x%08x\n", + channel->lba, channel->delta, channel->eso, channel->control); trident_write_voice_regs(state); } @@ -1064,9 +1070,9 @@ /* stereo */ channel->control |= CHANNEL_STEREO; - pr_debug("trident: trident_rec_setup, LBA = 0x%08x, " - "Delta = 0x%08x, ESO = 0x%08x, Control = 0x%08x\n", - channel->lba, channel->delta, channel->eso, channel->control); + TRDBG("trident: trident_rec_setup, LBA = 0x%08x, " + "Delat = 0x%08x, ESO = 0x%08x, Control = 0x%08x\n", + channel->lba, channel->delta, channel->eso, channel->control); trident_write_voice_regs(state); } @@ -1101,8 +1107,8 @@ } - pr_debug("trident: trident_get_dma_addr: chip reported channel: %d, " - "cso = 0x%04x\n", dmabuf->channel->num, cso); + TRDBG("trident: trident_get_dma_addr: chip reported channel: %d, " + "cso = 0x%04x\n", dmabuf->channel->num, cso); /* ESO and CSO are in units of Samples, convert to byte offset */ cso <<= sample_shift[dmabuf->fmt]; @@ -1211,8 +1217,8 @@ &dmabuf->dma_handle))) return -ENOMEM; - pr_debug("trident: allocated %ld (order = %d) bytes at %p\n", - PAGE_SIZE << order, order, rawbuf); + TRDBG("trident: allocated %ld (order = %d) bytes at %p\n", + PAGE_SIZE << order, order, rawbuf); dmabuf->ready = dmabuf->mapped = 0; dmabuf->rawbuf = rawbuf; @@ -1349,11 +1355,10 @@ /* set the ready flag for the dma buffer */ dmabuf->ready = 1; - pr_debug("trident: prog_dmabuf(%d), sample rate = %d, " - "format = %d, numfrag = %d, fragsize = %d " - "dmasize = %d\n", dmabuf->channel->num, dmabuf->rate, - dmabuf->fmt, dmabuf->numfrag, dmabuf->fragsize, - dmabuf->dmasize); + TRDBG("trident: prog_dmabuf(%d), sample rate = %d, format = %d, numfrag = %d, " + "fragsize = %d dmasize = %d\n", + dmabuf->channel->num, dmabuf->rate, dmabuf->fmt, dmabuf->numfrag, + dmabuf->fragsize, dmabuf->dmasize); } unlock_set_fmt(state); return 0; @@ -1705,7 +1710,7 @@ /* FIXED: read interrupt status only once */ irq_status=inl(TRID_REG(card, T4D_AINT_A) ); - pr_debug("cyber_address_interrupt: irq_status 0x%X\n",irq_status); + TRDBG("cyber_address_interrupt: irq_status 0x%X\n",irq_status); for (i = 0; i < NR_HW_CH; i++) { channel = 31 - i; @@ -1713,7 +1718,7 @@ /* clear bit by writing a 1, zeroes are ignored */ outl( (1 << channel), TRID_REG(card, T4D_AINT_A)); - pr_debug("cyber_interrupt: channel %d\n", channel); + TRDBG("cyber_interrupt: channel %d\n", channel); if ((state = card->states[i]) != NULL) { trident_update_ptr(state); @@ -1736,7 +1741,7 @@ spin_lock(&card->lock); event = inl(TRID_REG(card, T4D_MISCINT)); - pr_debug("trident: trident_interrupt called, MISCINT = 0x%08x\n", event); + TRDBG("trident: trident_interrupt called, MISCINT = 0x%08x\n", event); if (event & ADDRESS_IRQ) { card->address_interrupt(card); @@ -1775,7 +1780,7 @@ unsigned swptr; int cnt; - pr_debug("trident: trident_read called, count = %d\n", count); + TRDBG("trident: trident_read called, count = %d\n", count); VALIDATE_STATE(state); if (ppos != &file->f_pos) @@ -1829,7 +1834,7 @@ which results in a (potential) buffer overrun. And worse, there is NOTHING we can do to prevent it. */ if (!interruptible_sleep_on_timeout(&dmabuf->wait, tmo)) { - pr_debug(KERN_ERR "trident: recording schedule timeout, " + TRDBG(KERN_ERR "trident: recording schedule timeout, " "dmasz %u fragsz %u count %i hwptr %u swptr %u\n", dmabuf->dmasize, dmabuf->fragsize, dmabuf->count, dmabuf->hwptr, dmabuf->swptr); @@ -1887,7 +1892,7 @@ unsigned int state_cnt; unsigned int copy_count; - pr_debug("trident: trident_write called, count = %d\n", count); + TRDBG("trident: trident_write called, count = %d\n", count); VALIDATE_STATE(state); if (ppos != &file->f_pos) @@ -1956,7 +1961,7 @@ which results in a (potential) buffer underrun. And worse, there is NOTHING we can do to prevent it. */ if (!interruptible_sleep_on_timeout(&dmabuf->wait, tmo)) { - pr_debug(KERN_ERR "trident: playback schedule timeout, " + TRDBG(KERN_ERR "trident: playback schedule timeout, " "dmasz %u fragsz %u count %i hwptr %u swptr %u\n", dmabuf->dmasize, dmabuf->fragsize, dmabuf->count, dmabuf->hwptr, dmabuf->swptr); @@ -2139,7 +2144,7 @@ VALIDATE_STATE(state); mapped = ((file->f_mode & FMODE_WRITE) && dmabuf->mapped) || ((file->f_mode & FMODE_READ) && dmabuf->mapped); - pr_debug("trident: trident_ioctl, command = %2d, arg = 0x%08x\n", + TRDBG("trident: trident_ioctl, command = %2d, arg = 0x%08x\n", _IOC_NR(cmd), arg ? *(int *)arg : 0); switch (cmd) @@ -2704,7 +2709,7 @@ state->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE); up(&card->open_sem); - pr_debug("trident: open virtual channel %d, hard channel %d\n", + TRDBG("trident: open virtual channel %d, hard channel %d\n", state->virt, dmabuf->channel->num); return 0; @@ -2726,7 +2731,7 @@ drain_dac(state, file->f_flags & O_NONBLOCK); } - pr_debug("trident: closing virtual channel %d, hard channel %d\n", + TRDBG("trident: closing virtual channel %d, hard channel %d\n", state->virt, dmabuf->channel->num); /* stop DMA state machine and free DMA buffers/channels */ @@ -2925,11 +2930,10 @@ } if(!block) { - pr_debug("accesscodecsemaphore: try unlock\n"); + TRDBG("accesscodecsemaphore: try unlock\n"); block = 1; goto unlock; } - printk(KERN_ERR "accesscodecsemaphore: fail\n"); return 0; } @@ -2954,8 +2958,6 @@ break; udelay(50); } - - printk(KERN_NOTICE "waitforstimertick :BIT_CLK is dead\n"); return 0; } @@ -2967,6 +2969,7 @@ unsigned long aud_reg; u32 data; u16 wcontrol; + unsigned long flags; if(!card) BUG(); @@ -2979,6 +2982,8 @@ if (secondary) mask |= ALI_AC97_SECONDARY; + spin_lock_irqsave(&card->lock, flags); + if (!acquirecodecaccess(card)) printk(KERN_ERR "access codec fail\n"); @@ -2990,7 +2995,7 @@ data = (mask | (reg & AC97_REG_ADDR)); if(!waitforstimertick(card)) { - printk(KERN_ERR "BIT_CLOCK is dead\n"); + printk(KERN_ERR "ali_ac97_read: BIT_CLOCK is dead\n"); goto releasecodec; } @@ -3004,7 +3009,7 @@ if(ncount <=0) break; if(ncount--==1) { - pr_debug("ali_ac97_read :try clear busy flag\n"); + TRDBG("ali_ac97_read :try clear busy flag\n"); aud_reg = inl(TRID_REG(card, ALI_AC97_WRITE)); outl((aud_reg & 0xffff7fff), TRID_REG(card, ALI_AC97_WRITE)); } @@ -3017,7 +3022,8 @@ releasecodec: releasecodecaccess(card); - printk(KERN_ERR "ali: AC97 CODEC read timed out.\n"); + spin_unlock_irqrestore(&card->lock, flags); + printk(KERN_ERR "ali_ac97_read: AC97 CODEC read timed out.\n"); return 0; } @@ -3029,6 +3035,7 @@ unsigned int ncount; u32 data; u16 wcontrol; + unsigned long flags; data = ((u32) val) << 16; @@ -3042,8 +3049,9 @@ if (card->revision == ALI_5451_V02) mask |= ALI_AC97_WRITE_MIXER_REGISTER; + spin_lock_irqsave(&card->lock, flags); if (!acquirecodecaccess(card)) - printk(KERN_ERR "access codec fail\n"); + printk(KERN_ERR "ali_ac97_write: access codec fail\n"); wcontrol = inw(TRID_REG(card, ALI_AC97_WRITE)); wcontrol &= 0xff00; @@ -3063,7 +3071,7 @@ if(ncount <= 0) break; if(ncount-- == 1) { - pr_debug("ali_ac97_set :try clear busy flag!!\n"); + TRDBG("ali_ac97_set :try clear busy flag!!\n"); outw(wcontrol & 0x7fff, TRID_REG(card, ALI_AC97_WRITE)); } udelay(10); @@ -3071,6 +3079,7 @@ releasecodec: releasecodecaccess(card); + spin_unlock_irqrestore(&card->lock, flags); return; } @@ -3099,6 +3108,9 @@ if(!card->mixer_regs_ready) return ali_ac97_get(card, codec->id, reg); + /* + * FIXME: need to stop this caching some registers + */ if(codec->id) id = 1; else @@ -3366,15 +3378,17 @@ pci_dev = pci_find_device(PCI_VENDOR_ID_AL,PCI_DEVICE_ID_AL_M1533, pci_dev); if (pci_dev == NULL) return -1; - temp = 0x80; - pci_write_config_byte(pci_dev, 0x59, ~temp); + pci_read_config_byte(pci_dev, 0x59, &temp); + temp &= ~0x80; + pci_write_config_byte(pci_dev, 0x59, temp); pci_dev = pci_find_device(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M7101, pci_dev); if (pci_dev == NULL) return -1; - temp = 0x20; - pci_write_config_byte(pci_dev, 0xB8, ~temp); + pci_read_config_byte(pci_dev, 0xB8, &temp); + temp &= ~0x20; + pci_write_config_byte(pci_dev, 0xB8, temp); return 0; } @@ -3388,13 +3402,15 @@ pci_dev = pci_find_device(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M1533, pci_dev); if (pci_dev == NULL) return -1; - temp = 0x80; + pci_read_config_byte(pci_dev, 0x59, &temp); + temp |= 0x80; pci_write_config_byte(pci_dev, 0x59, temp); pci_dev = pci_find_device(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M7101, pci_dev); if (pci_dev == NULL) return -1; - temp = 0x20; + pci_read_config_byte(pci_dev, (int)0xB8, &temp); + temp |= 0x20; pci_write_config_byte(pci_dev, (int)0xB8,(u8) temp); if (chan_nums == 6) { dwValue = inl(TRID_REG(card, ALI_SCTRL)) | 0x000f0000; @@ -3934,10 +3950,7 @@ return 0; udelay(5000); } - /* This is non fatal if you have a non PM capable codec.. */ - printk(KERN_ERR "ALi 5451 did not come out of reset " - "- continuing anyway.\n"); return 0; } @@ -4010,9 +4023,8 @@ } for (num_ac97 = 0; num_ac97 < NR_AC97; num_ac97++) { - if ((codec = kmalloc(sizeof(struct ac97_codec), GFP_KERNEL)) == NULL) + if ((codec = ac97_alloc_codec()) == NULL) return -ENOMEM; - memset(codec, 0, sizeof(struct ac97_codec)); /* initialize some basic codec information, other fields will be filled in ac97_probe_codec */ @@ -4033,7 +4045,7 @@ if ((codec->dev_mixer = register_sound_mixer(&trident_mixer_fops, -1)) < 0) { printk(KERN_ERR "trident: couldn't register mixer!\n"); - kfree(codec); + ac97_release_codec(codec); break; } @@ -4258,7 +4270,7 @@ for (i = 0; i < NR_AC97; i++) { if (card->ac97_codec[i] != NULL) { unregister_sound_mixer(card->ac97_codec[i]->dev_mixer); - kfree (card->ac97_codec[i]); + ac97_release_codec(card->ac97_codec[i]); } } goto out_unregister_sound_dsp; @@ -4358,7 +4370,7 @@ for (i = 0; i < NR_AC97; i++) if (card->ac97_codec[i] != NULL) { unregister_sound_mixer(card->ac97_codec[i]->dev_mixer); - kfree (card->ac97_codec[i]); + ac97_release_codec(card->ac97_codec[i]); } unregister_sound_dsp(card->dev_audio); diff -Nru a/sound/oss/trident.h b/sound/oss/trident.h --- a/sound/oss/trident.h Thu Feb 20 05:40:18 2003 +++ b/sound/oss/trident.h Fri Jul 11 09:43:02 2003 @@ -360,4 +360,16 @@ return r; } +#ifdef DEBUG + +#define TRDBG(msg, args...) do { \ + printk(KERN_DEBUG msg , ##args ); \ +} while (0) + +#else /* !defined(DEBUG) */ + +#define TRDBG(msg, args...) do { } while (0) + +#endif /* DEBUG */ + #endif /* __TRID4DWAVE_H */ diff -Nru a/sound/oss/via82cxxx_audio.c b/sound/oss/via82cxxx_audio.c --- a/sound/oss/via82cxxx_audio.c Wed Jun 4 05:28:03 2003 +++ b/sound/oss/via82cxxx_audio.c Fri Jul 11 09:31:26 2003 @@ -2,17 +2,20 @@ * Support for VIA 82Cxxx Audio Codecs * Copyright 1999,2000 Jeff Garzik * + * Updated to support the VIA 8233/8235 audio subsystem + * Alan Cox (C) Copyright 2002, 2003 Red Hat Inc + * * Distributed under the GNU GENERAL PUBLIC LICENSE (GPL) Version 2. * See the "COPYING" file distributed with this software for more info. + * NO WARRANTY * * For a list of known bugs (errata) and documentation, * see via-audio.pdf in linux/Documentation/DocBook. * If this documentation does not exist, run "make pdfdocs". - * */ -#define VIA_VERSION "1.9.1" +#define VIA_VERSION "1.9.1-ac3-2.5" #include @@ -81,10 +84,13 @@ #define VIA_DEFAULT_FRAG_TIME 20 #define VIA_DEFAULT_BUFFER_TIME 500 +/* the hardware has a 256 fragment limit */ +#define VIA_MIN_FRAG_NUMBER 2 +#define VIA_MAX_FRAG_NUMBER 128 + #define VIA_MAX_FRAG_SIZE PAGE_SIZE -#define VIA_MIN_FRAG_SIZE 64 +#define VIA_MIN_FRAG_SIZE (VIA_MAX_BUFFER_DMA_PAGES * PAGE_SIZE / VIA_MAX_FRAG_NUMBER) -#define VIA_MIN_FRAG_NUMBER 2 /* 82C686 function 5 (audio codec) PCI configuration registers */ #define VIA_ACLINK_STATUS 0x40 @@ -116,7 +122,10 @@ #define VIA_PCM_STATUS 0x00 #define VIA_PCM_CONTROL 0x01 #define VIA_PCM_TYPE 0x02 +#define VIA_PCM_LEFTVOL 0x02 +#define VIA_PCM_RIGHTVOL 0x03 #define VIA_PCM_TABLE_ADDR 0x04 +#define VIA_PCM_STOPRATE 0x08 /* 8233+ */ #define VIA_PCM_BLOCK_COUNT 0x0C /* XXX unused DMA channel for FM PCM data */ @@ -125,6 +134,12 @@ #define VIA_BASE0_FM_OUT_CHAN_CTRL 0x21 #define VIA_BASE0_FM_OUT_CHAN_TYPE 0x22 +/* Six channel audio output on 8233 */ +#define VIA_BASE0_MULTI_OUT_CHAN 0x40 +#define VIA_BASE0_MULTI_OUT_CHAN_STATUS 0x40 +#define VIA_BASE0_MULTI_OUT_CHAN_CTRL 0x41 +#define VIA_BASE0_MULTI_OUT_CHAN_TYPE 0x42 + #define VIA_BASE0_AC97_CTRL 0x80 #define VIA_BASE0_SGD_STATUS_SHADOW 0x84 #define VIA_BASE0_GPI_INT_ENABLE 0x8C @@ -133,6 +148,12 @@ #define VIA_INTR_FM ((1<<2) | (1<<6) | (1<<10)) #define VIA_INTR_MASK (VIA_INTR_OUT | VIA_INTR_IN | VIA_INTR_FM) +/* Newer VIA we need to monitor the low 3 bits of each channel. This + mask covers the channels we don't yet use as well + */ + +#define VIA_NEW_INTR_MASK 0x77077777UL + /* VIA_BASE0_AUDIO_xxx_CHAN_TYPE bits */ #define VIA_IRQ_ON_FLAG (1<<0) /* int on each flagged scatter block */ #define VIA_IRQ_ON_EOL (1<<1) /* int at end of scatter list */ @@ -250,11 +271,15 @@ unsigned is_record : 1; unsigned is_mapped : 1; unsigned is_enabled : 1; + unsigned is_multi: 1; /* 8233 6 channel */ u8 pcm_fmt; /* VIA_PCM_FMT_xxx */ + u8 channels; /* Channel count */ unsigned rate; /* sample rate */ unsigned int frag_size; unsigned int frag_number; + + unsigned char intmask; volatile struct via_sgd_table *sgtable; dma_addr_t sgt_handle; @@ -273,19 +298,31 @@ struct pci_dev *pdev; long baseaddr; - struct ac97_codec ac97; + struct ac97_codec *ac97; + spinlock_t ac97_lock; spinlock_t lock; int card_num; /* unique card number, from 0 */ int dev_dsp; /* /dev/dsp index from register_sound_dsp() */ unsigned rev_h : 1; + unsigned legacy: 1; /* Has legacy ports */ + unsigned intmask: 1; /* Needs int bits */ + unsigned sixchannel: 1; /* 8233/35 with 6 channel support */ + unsigned volume: 1; int locked_rate : 1; + + int mixer_vol; /* 8233/35 volume - not yet implemented */ struct semaphore syscall_sem; struct semaphore open_sem; + /* The 8233/8235 have 4 DX audio channels, two record and + one six channel out. We bind ch_in to DX 1, ch_out to multichannel + and ch_fm to DX 2. DX 3 and REC0/REC1 are unused at the + moment */ + struct via_channel ch_in; struct via_channel ch_out; struct via_channel ch_fm; @@ -352,17 +389,19 @@ static struct pci_device_id via_pci_tbl[] __initdata = { { PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C686_5, - PCI_ANY_ID, PCI_ANY_ID, }, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + { PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8233_5, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1}, { 0, } }; MODULE_DEVICE_TABLE(pci,via_pci_tbl); static struct pci_driver via_driver = { - .name = VIA_MODULE_NAME, - .id_table = via_pci_tbl, - .probe = via_init_one, - .remove = __devexit_p(via_remove_one), + name: VIA_MODULE_NAME, + id_table: via_pci_tbl, + probe: via_init_one, + remove: __devexit_p(via_remove_one), }; @@ -426,7 +465,13 @@ static inline void sg_begin (struct via_channel *chan) { - outb (VIA_SGD_START, chan->iobase + VIA_PCM_CONTROL); + DPRINTK("Start with intmask %d\n", chan->intmask); + DPRINTK("About to start from %d to %d\n", + inl(chan->iobase + VIA_PCM_BLOCK_COUNT), + inb(chan->iobase + VIA_PCM_STOPRATE + 3)); + outb (VIA_SGD_START|chan->intmask, chan->iobase + VIA_PCM_CONTROL); + DPRINTK("Status is now %02X\n", inb(chan->iobase + VIA_PCM_STATUS)); + DPRINTK("Control is now %02X\n", inb(chan->iobase + VIA_PCM_CONTROL)); } @@ -442,6 +487,10 @@ return 0; } +static int via_sg_offset(struct via_channel *chan) +{ + return inl (chan->iobase + VIA_PCM_BLOCK_COUNT) & 0x00FFFFFF; +} /**************************************************************** * @@ -505,6 +554,8 @@ via_chan_stop (card->baseaddr + VIA_BASE0_PCM_OUT_CHAN); via_chan_stop (card->baseaddr + VIA_BASE0_PCM_IN_CHAN); via_chan_stop (card->baseaddr + VIA_BASE0_FM_OUT_CHAN); + if(card->sixchannel) + via_chan_stop (card->baseaddr + VIA_BASE0_MULTI_OUT_CHAN); /* * clear any existing stops / flags (sanity check mainly) @@ -512,6 +563,8 @@ via_chan_status_clear (card->baseaddr + VIA_BASE0_PCM_OUT_CHAN); via_chan_status_clear (card->baseaddr + VIA_BASE0_PCM_IN_CHAN); via_chan_status_clear (card->baseaddr + VIA_BASE0_FM_OUT_CHAN); + if(card->sixchannel) + via_chan_status_clear (card->baseaddr + VIA_BASE0_MULTI_OUT_CHAN); /* * clear any enabled interrupt bits @@ -531,6 +584,14 @@ if (tmp != new_tmp) outb (0, card->baseaddr + VIA_BASE0_FM_OUT_CHAN_TYPE); + if(card->sixchannel) + { + tmp = inb (card->baseaddr + VIA_BASE0_MULTI_OUT_CHAN_TYPE); + new_tmp = tmp & ~(VIA_IRQ_ON_FLAG|VIA_IRQ_ON_EOL|VIA_RESTART_SGD_ON_EOL); + if (tmp != new_tmp) + outb (0, card->baseaddr + VIA_BASE0_MULTI_OUT_CHAN_TYPE); + } + udelay(10); /* @@ -561,6 +622,9 @@ { struct via_info *card = ac97->private_data; int rate_reg; + u32 dacp; + u32 mast_vol, phone_vol, mono_vol, pcm_vol; + u32 mute_vol = 0x8000; /* The mute volume? -- Seems to work! */ DPRINTK ("ENTER, rate = %d\n", rate); @@ -577,16 +641,32 @@ rate_reg = chan->is_record ? AC97_PCM_LR_ADC_RATE : AC97_PCM_FRONT_DAC_RATE; - via_ac97_write_reg (ac97, AC97_POWER_CONTROL, - (via_ac97_read_reg (ac97, AC97_POWER_CONTROL) & ~0x0200) | - 0x0200); + /* Save current state */ + dacp=via_ac97_read_reg(ac97, AC97_POWER_CONTROL); + mast_vol = via_ac97_read_reg(ac97, AC97_MASTER_VOL_STEREO); + mono_vol = via_ac97_read_reg(ac97, AC97_MASTER_VOL_MONO); + phone_vol = via_ac97_read_reg(ac97, AC97_HEADPHONE_VOL); + pcm_vol = via_ac97_read_reg(ac97, AC97_PCMOUT_VOL); + /* Mute - largely reduces popping */ + via_ac97_write_reg(ac97, AC97_MASTER_VOL_STEREO, mute_vol); + via_ac97_write_reg(ac97, AC97_MASTER_VOL_MONO, mute_vol); + via_ac97_write_reg(ac97, AC97_HEADPHONE_VOL, mute_vol); + via_ac97_write_reg(ac97, AC97_PCMOUT_VOL, mute_vol); + /* Power down the DAC */ + via_ac97_write_reg(ac97, AC97_POWER_CONTROL, dacp|0x0200); + /* Set new rate */ via_ac97_write_reg (ac97, rate_reg, rate); - via_ac97_write_reg (ac97, AC97_POWER_CONTROL, - via_ac97_read_reg (ac97, AC97_POWER_CONTROL) & ~0x0200); - - udelay (10); + /* Power DAC back up */ + via_ac97_write_reg(ac97, AC97_POWER_CONTROL, dacp); + udelay (200); /* reduces popping */ + + /* Restore volumes */ + via_ac97_write_reg(ac97, AC97_MASTER_VOL_STEREO, mast_vol); + via_ac97_write_reg(ac97, AC97_MASTER_VOL_MONO, mono_vol); + via_ac97_write_reg(ac97, AC97_HEADPHONE_VOL, phone_vol); + via_ac97_write_reg(ac97, AC97_PCMOUT_VOL, pcm_vol); /* the hardware might return a value different than what we * passed to it, so read the rate value back from hardware @@ -626,9 +706,19 @@ { memset (chan, 0, sizeof (*chan)); + if(card->intmask) + chan->intmask = 0x23; /* Turn on the IRQ bits */ + if (chan == &card->ch_out) { chan->name = "PCM-OUT"; - chan->iobase = card->baseaddr + VIA_BASE0_PCM_OUT_CHAN; + if(card->sixchannel) + { + chan->iobase = card->baseaddr + VIA_BASE0_MULTI_OUT_CHAN; + chan->is_multi = 1; + DPRINTK("Using multichannel for pcm out\n"); + } + else + chan->iobase = card->baseaddr + VIA_BASE0_PCM_OUT_CHAN; } else if (chan == &card->ch_in) { chan->name = "PCM-IN"; chan->iobase = card->baseaddr + VIA_BASE0_PCM_IN_CHAN; @@ -659,9 +749,8 @@ * Performs some of the preparations necessary to begin * using a PCM channel. * - * Currently the preparations consist in - * setting the - * PCM channel to a known state. + * Currently the preparations consist of + * setting the PCM channel to a known state. */ @@ -707,6 +796,11 @@ DPRINTK ("ENTER\n"); + + chan->intmask = 0; + if(card->intmask) + chan->intmask = 0x23; /* Turn on the IRQ bits */ + if (chan->sgtable != NULL) { DPRINTK ("EXIT\n"); return 0; @@ -777,6 +871,16 @@ outl (chan->sgt_handle, chan->iobase + VIA_PCM_TABLE_ADDR); udelay (20); via_ac97_wait_idle (card); + /* load no rate adaption, stereo 16bit, set up ring slots */ + if(card->sixchannel) + { + if(!chan->is_multi) + { + outl (0xFFFFF | (0x3 << 20) | (chan->frag_number << 24), chan->iobase + VIA_PCM_STOPRATE); + udelay (20); + via_ac97_wait_idle (card); + } + } DPRINTK ("inl (0x%lX) = %x\n", chan->iobase + VIA_PCM_TABLE_ADDR, @@ -880,8 +984,11 @@ assert (chan != NULL); if (reset) + { /* reset to 8-bit mono mode */ chan->pcm_fmt = 0; + chan->channels = 1; + } /* enable interrupts on FLAG and EOL */ chan->pcm_fmt |= VIA_CHAN_TYPE_MASK; @@ -892,8 +999,83 @@ /* set interrupt select bits where applicable (PCM in & out channels) */ if (!chan->is_record) chan->pcm_fmt |= VIA_CHAN_TYPE_INT_SELECT; + + DPRINTK("SET FMT - %02x %02x\n", chan->intmask , chan->is_multi); + + if(chan->intmask) + { + u32 m; + + /* + * Channel 0x4 is up to 6 x 16bit and has to be + * programmed differently + */ + + if(chan->is_multi) + { + u8 c = 0; + + /* + * Load the type bit for num channels + * and 8/16bit + */ + + if(chan->pcm_fmt & VIA_PCM_FMT_16BIT) + c = 1 << 7; + if(chan->pcm_fmt & VIA_PCM_FMT_STEREO) + c |= (2<<4); + else + c |= (1<<4); + + outb(c, chan->iobase + VIA_PCM_TYPE); + + /* + * Set the channel steering + * Mono + * Channel 0 to slot 3 + * Channel 0 to slot 4 + * Stereo + * Channel 0 to slot 3 + * Channel 1 to slot 4 + */ + + switch(chan->channels) + { + case 1: + outl(0xFF000000 | (1<<0) | (1<<4) , chan->iobase + VIA_PCM_STOPRATE); + break; + case 2: + outl(0xFF000000 | (1<<0) | (2<<4) , chan->iobase + VIA_PCM_STOPRATE); + break; + case 4: + outl(0xFF000000 | (1<<0) | (2<<4) | (3<<8) | (4<<12), chan->iobase + VIA_PCM_STOPRATE); + break; + case 6: + outl(0xFF000000 | (1<<0) | (2<<4) | (5<<8) | (6<<12) | (3<<16) | (4<<20), chan->iobase + VIA_PCM_STOPRATE); + break; + } + } + else + { + /* + * New style, turn off channel volume + * control, set bits in the right register + */ + outb(0x0, chan->iobase + VIA_PCM_LEFTVOL); + outb(0x0, chan->iobase + VIA_PCM_RIGHTVOL); + + m = inl(chan->iobase + VIA_PCM_STOPRATE); + m &= ~(3<<20); + if(chan->pcm_fmt & VIA_PCM_FMT_STEREO) + m |= (1 << 20); + if(chan->pcm_fmt & VIA_PCM_FMT_16BIT) + m |= (1 << 21); + outl(m, chan->iobase + VIA_PCM_STOPRATE); + } + } + else + outb (chan->pcm_fmt, chan->iobase + VIA_PCM_TYPE); - outb (chan->pcm_fmt, chan->iobase + VIA_PCM_TYPE); DPRINTK ("EXIT, pcm_fmt = 0x%02X, reg = 0x%02X\n", chan->pcm_fmt, @@ -948,7 +1130,7 @@ via_chan_clear (card, chan); - val = via_set_rate (&card->ac97, chan, val); + val = via_set_rate (card->ac97, chan, val); DPRINTK ("EXIT, returning %d\n", val); return val; @@ -1034,15 +1216,25 @@ /* mono */ case 1: chan->pcm_fmt &= ~VIA_PCM_FMT_STEREO; + chan->channels = 1; via_chan_pcm_fmt (chan, 0); break; /* stereo */ case 2: chan->pcm_fmt |= VIA_PCM_FMT_STEREO; + chan->channels = 2; via_chan_pcm_fmt (chan, 0); break; + case 4: + case 6: + if(chan->is_multi) + { + chan->pcm_fmt |= VIA_PCM_FMT_STEREO; + chan->channels = val; + break; + } /* unknown */ default: printk (KERN_WARNING PFX "unknown number of channels\n"); @@ -1076,9 +1268,8 @@ DPRINTK ("\n"); - chan->frag_size = (VIA_DEFAULT_FRAG_TIME * chan->rate * - ((chan->pcm_fmt & VIA_PCM_FMT_STEREO) ? 2 : 1) * - ((chan->pcm_fmt & VIA_PCM_FMT_16BIT) ? 2 : 1)) / 1000 - 1; + chan->frag_size = (VIA_DEFAULT_FRAG_TIME * chan->rate * chan->channels + * ((chan->pcm_fmt & VIA_PCM_FMT_16BIT) ? 2 : 1)) / 1000 - 1; shift = 0; while (chan->frag_size) { @@ -1112,6 +1303,8 @@ if (chan->frag_number < VIA_MIN_FRAG_NUMBER) chan->frag_number = VIA_MIN_FRAG_NUMBER; + if (chan->frag_number > VIA_MAX_FRAG_NUMBER) + chan->frag_number = VIA_MAX_FRAG_NUMBER; if ((chan->frag_number * chan->frag_size) / PAGE_SIZE > VIA_MAX_BUFFER_DMA_PAGES) chan->frag_number = (VIA_MAX_BUFFER_DMA_PAGES * PAGE_SIZE) / chan->frag_size; @@ -1156,7 +1349,7 @@ /** * via_chan_flush_frag - Flush partially-full playback buffer to hardware - * @chan: Channel whose DMA table will be displayed + * @chan: Channel whose DMA table will be flushed * * Flushes partially-full playback buffer to hardware. */ @@ -1194,6 +1387,7 @@ { assert (chan->is_active == sg_active(chan->iobase)); + DPRINTK ("MAYBE START %s\n", chan->name); if (!chan->is_active && chan->is_enabled) { chan->is_active = 1; sg_begin (chan); @@ -1267,6 +1461,8 @@ assert (codec->private_data != NULL); card = codec->private_data; + + spin_lock(&card->ac97_lock); /* Every time we write to register 80 we cause a transaction. The only safe way to clear the valid bit is to write it at @@ -1297,6 +1493,7 @@ if (((data & 0x007F0000) >> 16) == reg) { DPRINTK ("EXIT, success, data=0x%lx, retval=0x%lx\n", data, data & 0x0000FFFF); + spin_unlock(&card->ac97_lock); return data & 0x0000FFFF; } @@ -1304,6 +1501,7 @@ reg, ((data & 0x007F0000) >> 16)); err_out: + spin_unlock(&card->ac97_lock); DPRINTK ("EXIT, returning 0\n"); return 0; } @@ -1335,6 +1533,8 @@ card = codec->private_data; + spin_lock(&card->ac97_lock); + data = (reg << 16) + value; outl (data, card->baseaddr + VIA_BASE0_AC97_CTRL); udelay (10); @@ -1349,6 +1549,7 @@ printk (KERN_WARNING PFX "timeout after AC97 codec write (0x%X, 0x%X)\n", reg, value); out: + spin_unlock(&card->ac97_lock); DPRINTK ("EXIT\n"); } @@ -1368,7 +1569,7 @@ assert (pci_get_drvdata (pdev) != NULL); card = pci_get_drvdata (pdev); - if (card->ac97.dev_mixer == minor) + if (card->ac97->dev_mixer == minor) goto match; } } @@ -1377,7 +1578,7 @@ return -ENODEV; match: - file->private_data = &card->ac97; + file->private_data = card->ac97; DPRINTK ("EXIT, returning 0\n"); return 0; @@ -1399,7 +1600,30 @@ rc = via_syscall_down (card, nonblock); if (rc) goto out; - + +#if 0 + /* + * Intercept volume control on 8233 and 8235 + */ + if(card->volume) + { + switch(cmd) + { + case SOUND_MIXER_READ_VOLUME: + return card->mixer_vol; + case SOUND_MIXER_WRITE_VOLUME: + { + int v; + if(get_user(v, (int *)arg)) + { + rc = -EFAULT; + goto out; + } + card->mixer_vol = v; + } + } + } +#endif rc = codec->mixer_ioctl(codec, cmd, arg); up (&card->syscall_sem); @@ -1493,25 +1717,28 @@ udelay (100); } + if(card->legacy) + { #if 0 /* this breaks on K7M */ - /* disable legacy stuff */ - pci_write_config_byte (pdev, 0x42, 0x00); - udelay(10); + /* disable legacy stuff */ + pci_write_config_byte (pdev, 0x42, 0x00); + udelay(10); #endif - /* route FM trap to IRQ, disable FM trap */ - pci_write_config_byte (pdev, 0x48, 0x05); - udelay(10); - + /* route FM trap to IRQ, disable FM trap */ + pci_write_config_byte (pdev, 0x48, 0x05); + udelay(10); + } + /* disable all codec GPI interrupts */ outl (0, pci_resource_start (pdev, 0) + 0x8C); /* WARNING: this line is magic. Remove this * and things break. */ /* enable variable rate */ - tmp16 = via_ac97_read_reg (&card->ac97, AC97_EXTENDED_STATUS); + tmp16 = via_ac97_read_reg (card->ac97, AC97_EXTENDED_STATUS); if ((tmp16 & 1) == 0) - via_ac97_write_reg (&card->ac97, AC97_EXTENDED_STATUS, tmp16 | 1); + via_ac97_write_reg (card->ac97, AC97_EXTENDED_STATUS, tmp16 | 1); DPRINTK ("EXIT, returning 0\n"); return 0; @@ -1534,16 +1761,20 @@ assert (card != NULL); - memset (&card->ac97, 0, sizeof (card->ac97)); - card->ac97.private_data = card; - card->ac97.codec_read = via_ac97_read_reg; - card->ac97.codec_write = via_ac97_write_reg; - card->ac97.codec_wait = via_ac97_codec_wait; + card->ac97 = ac97_alloc_codec(); + if(card->ac97 == NULL) + return -ENOMEM; + + card->ac97->private_data = card; + card->ac97->codec_read = via_ac97_read_reg; + card->ac97->codec_write = via_ac97_write_reg; + card->ac97->codec_wait = via_ac97_codec_wait; - card->ac97.dev_mixer = register_sound_mixer (&via_mixer_fops, -1); - if (card->ac97.dev_mixer < 0) { + card->ac97->dev_mixer = register_sound_mixer (&via_mixer_fops, -1); + if (card->ac97->dev_mixer < 0) { printk (KERN_ERR PFX "unable to register AC97 mixer, aborting\n"); DPRINTK ("EXIT, returning -EIO\n"); + ac97_release_codec(card->ac97); return -EIO; } @@ -1552,25 +1783,27 @@ printk (KERN_ERR PFX "unable to reset AC97 codec, aborting\n"); goto err_out; } - - if (ac97_probe_codec (&card->ac97) == 0) { + + mdelay(10); + + if (ac97_probe_codec (card->ac97) == 0) { printk (KERN_ERR PFX "unable to probe AC97 codec, aborting\n"); rc = -EIO; goto err_out; } /* enable variable rate */ - tmp16 = via_ac97_read_reg (&card->ac97, AC97_EXTENDED_STATUS); - via_ac97_write_reg (&card->ac97, AC97_EXTENDED_STATUS, tmp16 | 1); + tmp16 = via_ac97_read_reg (card->ac97, AC97_EXTENDED_STATUS); + via_ac97_write_reg (card->ac97, AC97_EXTENDED_STATUS, tmp16 | 1); /* * If we cannot enable VRA, we have a locked-rate codec. * We try again to enable VRA before assuming so, however. */ - tmp16 = via_ac97_read_reg (&card->ac97, AC97_EXTENDED_STATUS); + tmp16 = via_ac97_read_reg (card->ac97, AC97_EXTENDED_STATUS); if ((tmp16 & 1) == 0) { - via_ac97_write_reg (&card->ac97, AC97_EXTENDED_STATUS, tmp16 | 1); - tmp16 = via_ac97_read_reg (&card->ac97, AC97_EXTENDED_STATUS); + via_ac97_write_reg (card->ac97, AC97_EXTENDED_STATUS, tmp16 | 1); + tmp16 = via_ac97_read_reg (card->ac97, AC97_EXTENDED_STATUS); if ((tmp16 & 1) == 0) { card->locked_rate = 1; printk (KERN_WARNING PFX "Codec rate locked at 48Khz\n"); @@ -1581,8 +1814,9 @@ return 0; err_out: - unregister_sound_mixer (card->ac97.dev_mixer); + unregister_sound_mixer (card->ac97->dev_mixer); DPRINTK ("EXIT, returning %d\n", rc); + ac97_release_codec(card->ac97); return rc; } @@ -1592,9 +1826,10 @@ DPRINTK ("ENTER\n"); assert (card != NULL); - assert (card->ac97.dev_mixer >= 0); + assert (card->ac97->dev_mixer >= 0); - unregister_sound_mixer (card->ac97.dev_mixer); + unregister_sound_mixer (card->ac97->dev_mixer); + ac97_release_codec(card->ac97); DPRINTK ("EXIT\n"); } @@ -1619,11 +1854,11 @@ * Locking: inside card->lock */ -static void via_intr_channel (struct via_channel *chan) +static void via_intr_channel (struct via_info *card, struct via_channel *chan) { u8 status; int n; - + /* check pertinent bits of status register for action bits */ status = inb (chan->iobase) & (VIA_SGD_FLAG | VIA_SGD_EOL | VIA_SGD_STOPPED); if (!status) @@ -1642,6 +1877,7 @@ assert (n >= 0); assert (n < chan->frag_number); + /* reset SGD data structure in memory to reflect a full buffer, * and advance the h/w ptr, wrapping around to zero if needed */ @@ -1656,48 +1892,44 @@ /* accounting crap for SNDCTL_DSP_GETxPTR */ chan->n_irqs++; chan->bytes += chan->frag_size; + /* FIXME - signed overflow is undefined */ if (chan->bytes < 0) /* handle overflow of 31-bit value */ chan->bytes = chan->frag_size; - + /* all following checks only occur when not in mmap(2) mode */ + if (!chan->is_mapped) + { + /* If we are recording, then n_frags represents the number + * of fragments waiting to be handled by userspace. + * If we are playback, then n_frags represents the number + * of fragments remaining to be filled by userspace. + * We increment here. If we reach max number of fragments, + * this indicates an underrun/overrun. For this case under OSS, + * we stop the record/playback process. + */ + if (atomic_read (&chan->n_frags) < chan->frag_number) + atomic_inc (&chan->n_frags); + assert (atomic_read (&chan->n_frags) <= chan->frag_number); + if (atomic_read (&chan->n_frags) == chan->frag_number) { + chan->is_active = 0; + via_chan_stop (chan->iobase); + } + } /* wake up anyone listening to see when interrupts occur */ - if (waitqueue_active (&chan->wait)) - wake_up_all (&chan->wait); + wake_up_all (&chan->wait); DPRINTK ("%s intr, status=0x%02X, hwptr=0x%lX, chan->hw_ptr=%d\n", chan->name, status, (long) inl (chan->iobase + 0x04), atomic_read (&chan->hw_ptr)); - /* all following checks only occur when not in mmap(2) mode */ - if (chan->is_mapped) - return; - - /* If we are recording, then n_frags represents the number - * of fragments waiting to be handled by userspace. - * If we are playback, then n_frags represents the number - * of fragments remaining to be filled by userspace. - * We increment here. If we reach max number of fragments, - * this indicates an underrun/overrun. For this case under OSS, - * we stop the record/playback process. - */ - if (atomic_read (&chan->n_frags) < chan->frag_number) - atomic_inc (&chan->n_frags); - assert (atomic_read (&chan->n_frags) <= chan->frag_number); - - if (atomic_read (&chan->n_frags) == chan->frag_number) { - chan->is_active = 0; - via_chan_stop (chan->iobase); - } - - DPRINTK ("%s intr, channel n_frags == %d\n", chan->name, - atomic_read (&chan->n_frags)); + DPRINTK ("%s intr, channel n_frags == %d, missed %d\n", chan->name, + atomic_read (&chan->n_frags), missed); } -static irqreturn_t via_interrupt(int irq, void *dev_id, struct pt_regs *regs) +static irqreturn_t via_interrupt(int irq, void *dev_id, struct pt_regs *regs) { struct via_info *card = dev_id; u32 status32; - int handled = 0; /* to minimize interrupt sharing costs, we use the SGD status * shadow register to check the status of all inputs and @@ -1708,12 +1940,10 @@ if (!(status32 & VIA_INTR_MASK)) { #ifdef CONFIG_MIDI_VIA82CXXX - if (card->midi_devc) { + if (card->midi_devc) uart401intr(irq, card->midi_devc, regs); - handled = 1; - } #endif - goto out; + return IRQ_HANDLED; } DPRINTK ("intr, status32 == 0x%08X\n", status32); @@ -1722,21 +1952,42 @@ */ spin_lock (&card->lock); - if (status32 & VIA_INTR_OUT) { - handled = 1; - via_intr_channel (&card->ch_out); - } - if (status32 & VIA_INTR_IN) { - handled = 1; - via_intr_channel (&card->ch_in); - } - if (status32 & VIA_INTR_FM) { - handled = 1; - via_intr_channel (&card->ch_fm); - } + if (status32 & VIA_INTR_OUT) + via_intr_channel (card, &card->ch_out); + if (status32 & VIA_INTR_IN) + via_intr_channel (card, &card->ch_in); + if (status32 & VIA_INTR_FM) + via_intr_channel (card, &card->ch_fm); + spin_unlock (&card->lock); -out: - return IRQ_RETVAL(handled); + + return IRQ_HANDLED; +} + +static irqreturn_t via_new_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct via_info *card = dev_id; + u32 status32; + + /* to minimize interrupt sharing costs, we use the SGD status + * shadow register to check the status of all inputs and + * outputs with a single 32-bit bus read. If no interrupt + * conditions are flagged, we exit immediately + */ + status32 = inl (card->baseaddr + VIA_BASE0_SGD_STATUS_SHADOW); + if (!(status32 & VIA_NEW_INTR_MASK)) + return IRQ_NONE; + /* + * goes away completely on UP + */ + spin_lock (&card->lock); + + via_intr_channel (card, &card->ch_out); + via_intr_channel (card, &card->ch_in); + via_intr_channel (card, &card->ch_fm); + + spin_unlock (&card->lock); + return IRQ_HANDLED; } @@ -1766,18 +2017,32 @@ return -EIO; } - /* make sure FM irq is not routed to us */ - pci_read_config_byte (card->pdev, VIA_FM_NMI_CTRL, &tmp8); - if ((tmp8 & VIA_CR48_FM_TRAP_TO_NMI) == 0) { - tmp8 |= VIA_CR48_FM_TRAP_TO_NMI; - pci_write_config_byte (card->pdev, VIA_FM_NMI_CTRL, tmp8); + /* VIA requires this is done */ + pci_write_config_byte(card->pdev, PCI_INTERRUPT_LINE, card->pdev->irq); + + if(card->legacy) + { + /* make sure FM irq is not routed to us */ + pci_read_config_byte (card->pdev, VIA_FM_NMI_CTRL, &tmp8); + if ((tmp8 & VIA_CR48_FM_TRAP_TO_NMI) == 0) { + tmp8 |= VIA_CR48_FM_TRAP_TO_NMI; + pci_write_config_byte (card->pdev, VIA_FM_NMI_CTRL, tmp8); + } + if (request_irq (card->pdev->irq, via_interrupt, SA_SHIRQ, VIA_MODULE_NAME, card)) { + printk (KERN_ERR PFX "unable to obtain IRQ %d, aborting\n", + card->pdev->irq); + DPRINTK ("EXIT, returning -EBUSY\n"); + return -EBUSY; + } } - - if (request_irq (card->pdev->irq, via_interrupt, SA_SHIRQ, VIA_MODULE_NAME, card)) { - printk (KERN_ERR PFX "unable to obtain IRQ %d, aborting\n", - card->pdev->irq); - DPRINTK ("EXIT, returning -EBUSY\n"); - return -EBUSY; + else + { + if (request_irq (card->pdev->irq, via_new_interrupt, SA_SHIRQ, VIA_MODULE_NAME, card)) { + printk (KERN_ERR PFX "unable to obtain IRQ %d, aborting\n", + card->pdev->irq); + DPRINTK ("EXIT, returning -EBUSY\n"); + return -EBUSY; + } } DPRINTK ("EXIT, returning 0\n"); @@ -1792,15 +2057,15 @@ */ static struct file_operations via_dsp_fops = { - .owner = THIS_MODULE, - .open = via_dsp_open, - .release = via_dsp_release, - .read = via_dsp_read, - .write = via_dsp_write, - .poll = via_dsp_poll, - .llseek = no_llseek, - .ioctl = via_dsp_ioctl, - .mmap = via_dsp_mmap, + owner: THIS_MODULE, + open: via_dsp_open, + release: via_dsp_release, + read: via_dsp_read, + write: via_dsp_write, + poll: via_dsp_poll, + llseek: no_llseek, + ioctl: via_dsp_ioctl, + mmap: via_dsp_mmap, }; @@ -1812,11 +2077,14 @@ assert (card != NULL); - /* turn off legacy features, if not already */ - pci_read_config_byte (card->pdev, VIA_FUNC_ENABLE, &tmp8); - if (tmp8 & (VIA_CR42_SB_ENABLE | VIA_CR42_FM_ENABLE)) { - tmp8 &= ~(VIA_CR42_SB_ENABLE | VIA_CR42_FM_ENABLE); - pci_write_config_byte (card->pdev, VIA_FUNC_ENABLE, tmp8); + if(card->legacy) + { + /* turn off legacy features, if not already */ + pci_read_config_byte (card->pdev, VIA_FUNC_ENABLE, &tmp8); + if (tmp8 & (VIA_CR42_SB_ENABLE | VIA_CR42_FM_ENABLE)) { + tmp8 &= ~(VIA_CR42_SB_ENABLE | VIA_CR42_FM_ENABLE); + pci_write_config_byte (card->pdev, VIA_FUNC_ENABLE, tmp8); + } } via_stop_everything (card); @@ -1911,10 +2179,10 @@ struct vm_operations_struct via_mm_ops = { - .nopage = via_mm_nopage, + nopage: via_mm_nopage, #ifndef VM_RESERVED - .swapout = via_mm_swapout, + swapout: via_mm_swapout, #endif }; @@ -2129,7 +2397,6 @@ file, buffer, count, ppos ? ((unsigned long)*ppos) : 0); assert (file != NULL); - assert (buffer != NULL); card = file->private_data; assert (card != NULL); @@ -2287,11 +2554,12 @@ DPRINTK ("Flushed block %u, sw_ptr now %u, n_frags now %d\n", n, chan->sw_ptr, atomic_read (&chan->n_frags)); - DPRINTK ("regs==%02X %02X %02X %08X %08X %08X %08X\n", + DPRINTK ("regs==S=%02X C=%02X TP=%02X BP=%08X RT=%08X SG=%08X CC=%08X SS=%08X\n", inb (card->baseaddr + 0x00), inb (card->baseaddr + 0x01), inb (card->baseaddr + 0x02), inl (card->baseaddr + 0x04), + inl (card->baseaddr + 0x08), inl (card->baseaddr + 0x0C), inl (card->baseaddr + 0x80), inl (card->baseaddr + 0x84)); @@ -2300,7 +2568,10 @@ goto handle_one_block; out: - return userbuf - orig_userbuf; + if (userbuf - orig_userbuf) + return userbuf - orig_userbuf; + else + return ret; } @@ -2314,7 +2585,6 @@ file, buffer, count, ppos ? ((unsigned long)*ppos) : 0); assert (file != NULL); - assert (buffer != NULL); card = file->private_data; assert (card != NULL); @@ -2410,6 +2680,7 @@ add_wait_queue(&chan->wait, &wait); for (;;) { + DPRINTK ("FRAGS %d FRAGNUM %d\n", atomic_read(&chan->n_frags), chan->frag_number); __set_current_state(TASK_INTERRUPTIBLE); if (atomic_read (&chan->n_frags) >= chan->frag_number) break; @@ -2535,7 +2806,7 @@ info.fragments, info.bytes); - return copy_to_user(arg, &info, sizeof (info)) ? -EFAULT : 0; + return copy_to_user (arg, &info, sizeof (info))?-EFAULT:0; } @@ -2567,7 +2838,7 @@ if (chan->is_active) { unsigned long extra; info.ptr = atomic_read (&chan->hw_ptr) * chan->frag_size; - extra = chan->frag_size - inl (chan->iobase + VIA_PCM_BLOCK_COUNT); + extra = chan->frag_size - via_sg_offset(chan); info.ptr += extra; info.bytes += extra; } else { @@ -2579,7 +2850,7 @@ info.blocks, info.ptr); - return copy_to_user(arg, &info, sizeof (info)) ? -EFAULT : 0; + return copy_to_user (arg, &info, sizeof (info))?-EFAULT:0; } @@ -2688,7 +2959,7 @@ rc = put_user (val, (int *)arg); break; - /* query or set number of channels (1=mono, 2=stereo) */ + /* query or set number of channels (1=mono, 2=stereo, 4/6 for multichannel) */ case SNDCTL_DSP_CHANNELS: if (get_user(val, (int *)arg)) { rc = -EFAULT; @@ -2709,11 +2980,10 @@ val = rc; } else { - if ((rd && (card->ch_in.pcm_fmt & VIA_PCM_FMT_STEREO)) || - (wr && (card->ch_out.pcm_fmt & VIA_PCM_FMT_STEREO))) - val = 2; + if (rd) + val = card->ch_in.channels; else - val = 1; + val = card->ch_out.channels; } DPRINTK ("CHANNELS EXIT, returning %d\n", val); rc = put_user (val, (int *)arg); @@ -2873,10 +3143,11 @@ val = chan->frag_number - atomic_read (&chan->n_frags); + assert(val >= 0); + if (val > 0) { val *= chan->frag_size; - val -= chan->frag_size - - inl (chan->iobase + VIA_PCM_BLOCK_COUNT); + val -= chan->frag_size - via_sg_offset(chan); } val += chan->slop_len % chan->frag_size; } else @@ -2982,7 +3253,7 @@ static int via_dsp_open (struct inode *inode, struct file *file) { int minor = minor(inode->i_rdev); - struct via_info *card = NULL; + struct via_info *card; struct pci_dev *pdev = NULL; struct via_channel *chan; struct pci_driver *drvr; @@ -2995,6 +3266,7 @@ return -EINVAL; } + card = NULL; while ((pdev = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, pdev)) != NULL) { drvr = pci_dev_driver (pdev); if (drvr == &via_driver) { @@ -3037,9 +3309,11 @@ /* why is this forced to 16-bit stereo in all drivers? */ chan->pcm_fmt = VIA_PCM_FMT_16BIT | VIA_PCM_FMT_STEREO; + chan->channels = 2; + // TO DO - use FIFO: via_capture_fifo(card, 1); via_chan_pcm_fmt (chan, 0); - via_set_rate (&card->ac97, chan, 44100); + via_set_rate (card->ac97, chan, 44100); } /* handle output to analog source */ @@ -3052,16 +3326,17 @@ /* if in duplex mode make the recording and playback channels have the same settings */ chan->pcm_fmt = VIA_PCM_FMT_16BIT | VIA_PCM_FMT_STEREO; + chan->channels = 2; via_chan_pcm_fmt (chan, 0); - via_set_rate (&card->ac97, chan, 44100); + via_set_rate (card->ac97, chan, 44100); } else { if ((minor & 0xf) == SND_DEV_DSP16) { chan->pcm_fmt = VIA_PCM_FMT_16BIT; via_chan_pcm_fmt (chan, 0); - via_set_rate (&card->ac97, chan, 44100); + via_set_rate (card->ac97, chan, 44100); } else { via_chan_pcm_fmt (chan, 1); - via_set_rate (&card->ac97, chan, 8000); + via_set_rate (card->ac97, chan, 8000); } } } @@ -3091,7 +3366,7 @@ if (file->f_mode & FMODE_WRITE) { rc = via_dsp_drain_playback (card, &card->ch_out, nonblock); - if (rc) + if (rc && rc != ERESTARTSYS) /* Nobody needs to know about ^C */ printk (KERN_DEBUG "via_audio: ignoring drain playback error %d\n", rc); via_chan_free (card, &card->ch_out); @@ -3130,11 +3405,12 @@ DPRINTK ("ENTER\n"); if (printed_version++ == 0) - printk (KERN_INFO "Via 686a audio driver " VIA_VERSION "\n"); + printk (KERN_INFO "Via 686a/8233/8235 audio driver " VIA_VERSION "\n"); rc = pci_enable_device (pdev); if (rc) goto err_out; + rc = pci_request_regions (pdev, "via82cxxx_audio"); if (rc) @@ -3154,6 +3430,7 @@ card->baseaddr = pci_resource_start (pdev, 0); card->card_num = via_num_cards++; spin_lock_init (&card->lock); + spin_lock_init (&card->ac97_lock); init_MUTEX (&card->syscall_sem); init_MUTEX (&card->open_sem); @@ -3166,7 +3443,15 @@ * which means it has a few extra features */ if (pci_resource_start (pdev, 2) > 0) card->rev_h = 1; - + + /* Overkill for now, but more flexible done right */ + + card->intmask = id->driver_data; + card->legacy = !card->intmask; + card->sixchannel = id->driver_data; + + if(card->sixchannel) + printk(KERN_INFO PFX "Six channel audio available\n"); if (pdev->irq < 1) { printk (KERN_ERR PFX "invalid PCI IRQ %d, aborting\n", pdev->irq); rc = -ENODEV; @@ -3179,6 +3464,8 @@ goto err_out_kfree; } + pci_set_master(pdev); + /* * init AC97 mixer and codec */ @@ -3222,26 +3509,29 @@ /* Disable by default */ card->midi_info.io_base = 0; - pci_read_config_byte (pdev, 0x42, &r42); - /* Disable MIDI interrupt */ - pci_write_config_byte (pdev, 0x42, r42 | VIA_CR42_MIDI_IRQMASK); - if (r42 & VIA_CR42_MIDI_ENABLE) + if(card->legacy) { - if (r42 & VIA_CR42_MIDI_PNP) /* Address selected by iobase 2 - not tested */ - card->midi_info.io_base = pci_resource_start (pdev, 2); - else /* Address selected by byte 0x43 */ + pci_read_config_byte (pdev, 0x42, &r42); + /* Disable MIDI interrupt */ + pci_write_config_byte (pdev, 0x42, r42 | VIA_CR42_MIDI_IRQMASK); + if (r42 & VIA_CR42_MIDI_ENABLE) { - u8 r43; - pci_read_config_byte (pdev, 0x43, &r43); - card->midi_info.io_base = 0x300 + ((r43 & 0x0c) << 2); - } + if (r42 & VIA_CR42_MIDI_PNP) /* Address selected by iobase 2 - not tested */ + card->midi_info.io_base = pci_resource_start (pdev, 2); + else /* Address selected by byte 0x43 */ + { + u8 r43; + pci_read_config_byte (pdev, 0x43, &r43); + card->midi_info.io_base = 0x300 + ((r43 & 0x0c) << 2); + } - card->midi_info.irq = -pdev->irq; - if (probe_uart401(& card->midi_info, THIS_MODULE)) - { - card->midi_devc=midi_devs[card->midi_info.slots[4]]->devc; - pci_write_config_byte(pdev, 0x42, r42 & ~VIA_CR42_MIDI_IRQMASK); - printk("Enabled Via MIDI\n"); + card->midi_info.irq = -pdev->irq; + if (probe_uart401(& card->midi_info, THIS_MODULE)) + { + card->midi_devc=midi_devs[card->midi_info.slots[4]]->devc; + pci_write_config_byte(pdev, 0x42, r42 & ~VIA_CR42_MIDI_IRQMASK); + printk("Enabled Via MIDI\n"); + } } } #endif @@ -3501,7 +3791,7 @@ } sprintf (s, "driver/via/%d/ac97", card->card_num); - if (!create_proc_read_entry (s, 0, 0, ac97_read_proc, &card->ac97)) { + if (!create_proc_read_entry (s, 0, 0, ac97_read_proc, card->ac97)) { rc = -EIO; goto err_out_info; } diff -Nru a/sound/oss/ymfpci.c b/sound/oss/ymfpci.c --- a/sound/oss/ymfpci.c Mon Apr 21 00:32:53 2003 +++ b/sound/oss/ymfpci.c Fri Jul 11 08:59:04 2003 @@ -177,15 +177,16 @@ ymfpci_t *codec = dev->private_data; u32 cmd; + spin_lock(&codec->ac97_lock); /* XXX Do make use of dev->id */ ymfpci_codec_ready(codec, 0, 0); cmd = ((YDSXG_AC97WRITECMD | reg) << 16) | val; ymfpci_writel(codec, YDSXGR_AC97CMDDATA, cmd); + spin_unlock(&codec->ac97_lock); } -static u16 ymfpci_codec_read(struct ac97_codec *dev, u8 reg) +static u16 _ymfpci_codec_read(ymfpci_t *unit, u8 reg) { - ymfpci_t *unit = dev->private_data; int i; if (ymfpci_codec_ready(unit, 0, 0)) @@ -200,6 +201,18 @@ return ymfpci_readw(unit, YDSXGR_PRISTATUSDATA); } +static u16 ymfpci_codec_read(struct ac97_codec *dev, u8 reg) +{ + ymfpci_t *unit = dev->private_data; + u16 ret; + + spin_lock(&unit->ac97_lock); + ret = _ymfpci_codec_read(unit, reg); + spin_unlock(&unit->ac97_lock); + + return ret; +} + /* * Misc routines */ @@ -2444,9 +2457,8 @@ struct ac97_codec *codec; u16 eid; - if ((codec = kmalloc(sizeof(struct ac97_codec), GFP_KERNEL)) == NULL) + if ((codec = ac97_alloc_codec()) == NULL) return -ENOMEM; - memset(codec, 0, sizeof(struct ac97_codec)); /* initialize some basic codec information, other fields will be filled in ac97_probe_codec */ @@ -2462,7 +2474,7 @@ } eid = ymfpci_codec_read(codec, AC97_EXTENDED_ID); - if (eid==0xFFFFFF) { + if (eid==0xFFFF) { printk(KERN_WARNING "ymfpci: no codec attached ?\n"); goto out_kfree; } @@ -2478,7 +2490,7 @@ return 0; out_kfree: - kfree(codec); + ac97_release_codec(codec); return -ENODEV; } @@ -2517,6 +2529,7 @@ spin_lock_init(&codec->reg_lock); spin_lock_init(&codec->voice_lock); + spin_lock_init(&codec->ac97_lock); init_MUTEX(&codec->open_sem); INIT_LIST_HEAD(&codec->states); codec->pci = pcidev; @@ -2615,7 +2628,7 @@ out_release_region: release_mem_region(pci_resource_start(pcidev, 0), 0x8000); out_free: - kfree(codec); + ac97_release_codec(codec->ac97_codec[0]); return -ENODEV; } @@ -2628,7 +2641,7 @@ list_del(&codec->ymf_devs); unregister_sound_mixer(codec->ac97_codec[0]->dev_mixer); - kfree(codec->ac97_codec[0]); + ac97_release_codec(codec->ac97_codec[0]); unregister_sound_dsp(codec->dev_audio); free_irq(pcidev->irq, codec); ymfpci_memfree(codec); @@ -2643,7 +2656,6 @@ unload_uart401(&codec->mpu_data); } #endif /* CONFIG_SOUND_YMFPCI_LEGACY */ - kfree(codec); } MODULE_AUTHOR("Jaroslav Kysela"); diff -Nru a/sound/oss/ymfpci.h b/sound/oss/ymfpci.h --- a/sound/oss/ymfpci.h Sun Feb 10 18:50:15 2002 +++ b/sound/oss/ymfpci.h Fri Jul 11 08:59:13 2003 @@ -275,6 +275,7 @@ spinlock_t reg_lock; spinlock_t voice_lock; + spinlock_t ac97_lock; /* soundcore stuff */ int dev_audio;